diff options
author | Håkon Hallingstad <hakon@yahooinc.com> | 2022-12-05 19:29:04 +0100 |
---|---|---|
committer | Håkon Hallingstad <hakon@yahooinc.com> | 2022-12-05 19:29:04 +0100 |
commit | 268b74729c9c3ff74806875e05f52585000b4b9d (patch) | |
tree | 103bef96b641fd09b11cbab239b8886ae11d8f5f /node-repository/src | |
parent | c04788cb28e3b72e19b4ad11c87031ff85f17681 (diff) |
Limit fields allowed to be patched from tenant host
Diffstat (limited to 'node-repository/src')
2 files changed, 40 insertions, 1 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java index 328be3b32b8..e57868dac54 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodePatcher.java @@ -33,6 +33,7 @@ import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -77,10 +78,34 @@ public class NodePatcher { * Note: This may patch more than one node if the field being patched must be applied recursively to host and node. */ public void patch(String hostname, InputStream json) { + unifiedPatch(hostname, json, false); + } + + /** Apply given JSON from a tenant host that may have been compromised. */ + public void patchFromUntrustedTenantHost(String hostname, InputStream json) { + unifiedPatch(hostname, json, true); + } + + private void unifiedPatch(String hostname, InputStream json, boolean untrustedTenantHost) { Inspector root = Exceptions.uncheck(() -> SlimeUtils.jsonToSlime(json.readAllBytes())).get(); Map<String, Inspector> fields = new HashMap<>(); root.traverse(fields::put); + if (untrustedTenantHost) { + var disallowedFields = new HashSet<>(fields.keySet()); + disallowedFields.removeAll(Set.of("currentDockerImage", + "currentFirmwareCheck", + "currentOsVersion", + "currentRebootGeneration", + "currentRestartGeneration", + "reports", + "trustStore", + "vespaVersion")); + if (!disallowedFields.isEmpty()) { + throw new IllegalArgumentException("Patching fields not supported: " + disallowedFields); + } + } + // Create views grouping fields by their locking requirements Map<String, Inspector> regularFields = Maps.filterKeys(fields, k -> !IP_CONFIG_FIELDS.contains(k)); Map<String, Inspector> ipConfigFields = Maps.filterKeys(fields, IP_CONFIG_FIELDS::contains); diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java index 2f35d0e7e81..6e80e559b20 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java @@ -11,6 +11,7 @@ import com.yahoo.config.provision.NodeFlavors; import com.yahoo.config.provision.NodeResources; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.TenantName; +import com.yahoo.config.provision.security.NodePrincipal; import com.yahoo.container.jdisc.HttpRequest; import com.yahoo.container.jdisc.HttpResponse; import com.yahoo.container.jdisc.ThreadedHttpRequestHandler; @@ -173,7 +174,11 @@ public class NodesV2ApiHandler extends ThreadedHttpRequestHandler { if (path.matches("/nodes/v2/node/{hostname}")) { NodePatcher patcher = new NodePatcher(nodeFlavors, nodeRepository); String hostname = path.get("hostname"); - patcher.patch(hostname, request.getData()); + if (isTenantPeer(request)) { + patcher.patchFromUntrustedTenantHost(hostname, request.getData()); + } else { + patcher.patch(hostname, request.getData()); + } return new MessageResponse("Updated " + hostname); } else if (path.matches("/nodes/v2/application/{applicationId}")) { @@ -195,6 +200,15 @@ public class NodesV2ApiHandler extends ThreadedHttpRequestHandler { throw new NotFoundException("Nothing at '" + path + "'"); } + /** Returns true if the peer is a tenant host or node. */ + private boolean isTenantPeer(HttpRequest request) { + return request.getJDiscRequest().getUserPrincipal() instanceof NodePrincipal nodePrincipal && + switch (nodePrincipal.getIdentity().nodeType()) { + case host, tenant -> true; + default -> false; + }; + } + private HttpResponse handlePOST(HttpRequest request) { Path path = new Path(request.getUri()); if (path.matches("/nodes/v2/command/restart")) { |