diff options
author | Håkon Hallingstad <hakon@oath.com> | 2019-01-11 16:00:25 +0100 |
---|---|---|
committer | Håkon Hallingstad <hakon@oath.com> | 2019-01-11 16:00:25 +0100 |
commit | d0d4ecb80ea2bed27cddf3cead54f1d33ab70095 (patch) | |
tree | e0e1f8ab2b12d36fb564ecc6ac1b7c0743f519ed /node-repository | |
parent | 68807481defb0cc417388d3c3897f649f4bcda83 (diff) |
Authorization of /flags/v1
Diffstat (limited to 'node-repository')
2 files changed, 51 insertions, 19 deletions
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/Authorizer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/Authorizer.java index 95f69dc1c2a..6225a8a4fc4 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/Authorizer.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/Authorizer.java @@ -24,32 +24,52 @@ import java.util.stream.Collectors; /** * Authorizer for config server REST APIs. This contains the rules for all API paths where the authorization process - * requires information from the node-repository to make a decision + * may require information from the node-repository to make a decision * * @author mpolden * @author bjorncs */ public class Authorizer implements BiPredicate<NodePrincipal, URI> { - private final NodeRepository nodeRepository; private final Set<String> whitelistedHostnames; + private final AthenzIdentity controllerIdentity; + private final AthenzIdentity configServerIdentity = new AthenzService("vespa.vespa", "configserver"); + private final AthenzIdentity proxyIdentity = new AthenzService("vespa.vespa", "proxy"); + private final AthenzIdentity tenantIdentity = new AthenzService("vespa.vespa", "tenant-host"); private final Set<AthenzIdentity> trustedIdentities; + private final Set<AthenzIdentity> hostAdminIdentities; // TODO Remove whitelisted hostnames as these nodes should be included through 'trustedIdentities' public Authorizer(SystemName system, NodeRepository nodeRepository, Set<String> whitelistedHostnames) { this.nodeRepository = nodeRepository; this.whitelistedHostnames = whitelistedHostnames; - this.trustedIdentities = getTrustedIdentities(system); + controllerIdentity = system == SystemName.main + ? new AthenzService("vespa.vespa", "hosting") + : new AthenzService("vespa.vespa.cd", "hosting"); + this.trustedIdentities = new HashSet<>(Arrays.asList(controllerIdentity, configServerIdentity)); + this.hostAdminIdentities = new HashSet<>(Arrays.asList(controllerIdentity, configServerIdentity, proxyIdentity, tenantIdentity)); } /** Returns whether principal is authorized to access given URI */ @Override public boolean test(NodePrincipal principal, URI uri) { - // Trusted services can access everything - if (principal.getAthenzIdentityName().isPresent() - && trustedIdentities.contains(principal.getAthenzIdentityName().get())) { - return true; + if (principal.getAthenzIdentityName().isPresent()) { + // All host admins can retrieve flags data + if (uri.getPath().equals("/flags/v1/data") || uri.getPath().equals("/flags/v1/data/")) { + return hostAdminIdentities.contains(principal.getAthenzIdentityName().get()); + } + + // Only controller can access everything else in flags + if (uri.getPath().startsWith("/flags/v1/")) { + return principal.getAthenzIdentityName().get().equals(controllerIdentity); + } + + // Trusted services can access everything + if (trustedIdentities.contains(principal.getAthenzIdentityName().get())) { + return true; + } } + if (principal.getHostname().isPresent()) { String hostname = principal.getHostname().get(); if (isAthenzProviderApi(uri)) { @@ -108,18 +128,6 @@ public class Authorizer implements BiPredicate<NodePrincipal, URI> { return !resources.isEmpty() && resources.stream().anyMatch(resource -> predicate.test(resource, principal)); } - - private static Set<AthenzIdentity> getTrustedIdentities(SystemName system) { - Set<AthenzIdentity> trustedIdentities = new HashSet<>(); - trustedIdentities.add(new AthenzService("vespa.vespa", "configserver")); - AthenzService controllerIdentity = - system == SystemName.main - ? new AthenzService("vespa.vespa", "hosting") - : new AthenzService("vespa.vespa.cd", "hosting"); - trustedIdentities.add(controllerIdentity); - return trustedIdentities; - } - private Optional<Node> getNode(String hostname) { // Ignore potential path traversal. Node repository happily passes arguments unsanitized all the way down to // curator... diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizerTest.java index 38128e66861..5e643bd09ab 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizerTest.java +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/filter/AuthorizerTest.java @@ -139,6 +139,30 @@ public class AuthorizerTest { } @Test + public void flags_authorization() { + // Tenant nodes cannot access flags resources + assertFalse(authorizedTenantNode("node1", "/flags/v1/data")); + assertFalse(authorizedTenantNode("node1", "/flags/v1/data/flagid")); + assertFalse(authorizedTenantNode("node1", "/flags/v1/foo")); + + // Host node can access data + assertTrue(authorizedTenantHostNode("host1", "/flags/v1/data")); + assertFalse(authorizedTenantHostNode("host1", "/flags/v1/data/flagid")); + assertFalse(authorizedTenantHostNode("host1", "/flags/v1/foo")); + assertTrue(authorizedTenantHostNode("proxy1-host", "/flags/v1/data")); + assertFalse(authorizedTenantHostNode("proxy1-host", "/flags/v1/data/flagid")); + assertFalse(authorizedTenantHostNode("proxy1-host", "/flags/v1/foo")); + assertTrue(authorizedController("vespa.vespa.configserver", "/flags/v1/data")); + assertFalse(authorizedController("vespa.vespa.configserver", "/flags/v1/data/flagid")); + assertFalse(authorizedController("vespa.vespa.configserver", "/flags/v1/foo")); + + // Controller can access everything + assertTrue(authorizedController("vespa.vespa.hosting", "/flags/v1/data")); + assertTrue(authorizedController("vespa.vespa.hosting", "/flags/v1/data/flagid")); + assertTrue(authorizedController("vespa.vespa.hosting", "/flags/v1/foo")); + } + + @Test public void routing_authorization() { // Node of proxy or proxyhost type can access routing resource assertFalse(authorizedTenantNode("node1", "/routing/v1/status")); |