diff options
author | HÃ¥kon Hallingstad <hakon.hallingstad@gmail.com> | 2022-12-06 13:53:50 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-06 13:53:50 +0100 |
commit | c415b8fdb0974f04c489d46f4c82c2bd001296d9 (patch) | |
tree | f04d23d19998909d239ea59522dd284a4674530d /node-repository/src | |
parent | b01ebe82c392cd08c2cfe736049ad3d61d91e2b5 (diff) | |
parent | 268b74729c9c3ff74806875e05f52585000b4b9d (diff) |
Merge pull request #25120 from vespa-engine/hakonhall/limit-fields-allowed-to-be-patched-from-tenant-host
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 2c93992dcab..f77b98cc02c 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")) { |