diff options
3 files changed, 97 insertions, 1 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java index 643c2943e38..f8dfe5c82a1 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java @@ -39,6 +39,8 @@ import com.yahoo.container.jdisc.DataplaneProxyService; import com.yahoo.container.logging.AccessLog; import com.yahoo.container.logging.FileConnectionLog; import com.yahoo.io.IOUtils; +import com.yahoo.jdisc.http.filter.security.cloud.config.CloudTokenDataPlaneFilterConfig; +import com.yahoo.jdisc.http.filter.security.cloud.config.CloudTokenDataPlaneFilterConfig.Builder; import com.yahoo.jdisc.http.server.jetty.DataplaneProxyCredentials; import com.yahoo.jdisc.http.server.jetty.VoidRequestLog; import com.yahoo.osgi.provider.model.ComponentModel; @@ -658,13 +660,24 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { // Setup token filter chain var tokenChain = new HttpFilterChain("cloud-token-data-plane-secure", HttpFilterChain.Type.SYSTEM); - tokenChain.addInnerComponent(new CloudTokenDataPlaneFilter(cluster, state)); + var tokenFilter = new CloudTokenDataPlaneFilter(cluster, state); + tokenChain.addInnerComponent(tokenFilter); cluster.getHttp().getFilterChains().add(tokenChain); // Set as default filter for token port cluster.getHttp().getHttpServer().orElseThrow().getConnectorFactories().stream() .filter(c -> c.getListenPort() == tokenPort).findAny().orElseThrow() .setDefaultRequestFilterChain(tokenChain.getComponentId()); + + // Set up handler that tells what fingerprints are known to the container + class CloudTokenDataPlaneHandler extends Handler implements CloudTokenDataPlaneFilterConfig.Producer { + CloudTokenDataPlaneHandler() { + super(new ComponentModel("com.yahoo.jdisc.http.filter.security.cloud.CloudTokenDataPlaneHandler", null, "jdisc-security-filters", null)); + addServerBindings(SystemBindingPattern.fromHttpPortAndPath(Defaults.getDefaults().vespaWebServicePort(), "cloud-token-data-plane-fingerprints")); + } + @Override public void getConfig(Builder builder) { tokenFilter.getConfig(builder); } + } + cluster.addComponent(new CloudTokenDataPlaneHandler()); } // Returns the client certificates of the clients defined for an application cluster diff --git a/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cloud/CloudTokenDataPlaneHandler.java b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cloud/CloudTokenDataPlaneHandler.java new file mode 100644 index 00000000000..2270f514fb7 --- /dev/null +++ b/jdisc-security-filters/src/main/java/com/yahoo/jdisc/http/filter/security/cloud/CloudTokenDataPlaneHandler.java @@ -0,0 +1,31 @@ +package com.yahoo.jdisc.http.filter.security.cloud; + +import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.container.jdisc.ThreadedHttpRequestHandler; +import com.yahoo.jdisc.http.filter.security.cloud.config.CloudTokenDataPlaneFilterConfig; +import com.yahoo.restapi.SlimeJsonResponse; + +import javax.inject.Inject; +import java.util.List; +import java.util.concurrent.Executor; + +public class CloudTokenDataPlaneHandler extends ThreadedHttpRequestHandler { + + private final List<String> fingerprints; + + @Inject + public CloudTokenDataPlaneHandler(CloudTokenDataPlaneFilterConfig config, Executor executor) { + super(executor); + fingerprints = config.clients().stream() + .flatMap(client -> client.tokens().stream()) + .flatMap(token -> token.fingerprints().stream()) + .distinct().sorted().toList(); + } + + @Override + public HttpResponse handle(HttpRequest request) { + return new SlimeJsonResponse() {{ fingerprints.forEach(slime.setObject().setArray("fingerprints")::addString); }}; + } + +} diff --git a/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/cloud/CloudTokenDataPlaneHandlerTest.java b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/cloud/CloudTokenDataPlaneHandlerTest.java new file mode 100644 index 00000000000..b84d35841da --- /dev/null +++ b/jdisc-security-filters/src/test/java/com/yahoo/jdisc/http/filter/security/cloud/CloudTokenDataPlaneHandlerTest.java @@ -0,0 +1,52 @@ +package com.yahoo.jdisc.http.filter.security.cloud; + +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.jdisc.http.filter.security.cloud.config.CloudTokenDataPlaneFilterConfig.Builder; +import com.yahoo.jdisc.http.filter.security.cloud.config.CloudTokenDataPlaneFilterConfig.Clients; +import com.yahoo.jdisc.http.filter.security.cloud.config.CloudTokenDataPlaneFilterConfig.Clients.Tokens; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; + +import static com.yahoo.container.jdisc.HttpRequest.createTestRequest; +import static com.yahoo.jdisc.http.HttpRequest.Method.GET; +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class CloudTokenDataPlaneHandlerTest { + + @Test + void testFingerprints() throws IOException { + CloudTokenDataPlaneHandler handler = new CloudTokenDataPlaneHandler( + new Builder().tokenContext("context") + .clients(new Clients.Builder().id("client1") + .permissions("read") + .tokens(new Tokens.Builder().id("id1") + .fingerprints(List.of("pinky", "ring", "middle", "index", "thumb")) + .checkAccessHashes(List.of("a", "b", "c", "d", "e")) + .expirations(List.of("<none>", "<none>", "<none>", "<none>", "<none>"))) + .tokens(new Tokens.Builder().id("id2") + .fingerprints("toasty") + .checkAccessHashes("hash") + .expirations("<none>"))) + .clients(new Clients.Builder().id("client2") + .permissions("write") + .tokens(new Tokens.Builder().id("id2") + .fingerprints("toasty") + .checkAccessHashes("hash") + .expirations("<none>"))) + .build(), + Runnable::run + ); + + HttpResponse response = handler.handle(createTestRequest("", GET)); + assertEquals(200, + response.getStatus()); + assertEquals(""" + {"fingerprints":["index","middle","pinky","ring","thumb","toasty"]}""", + new ByteArrayOutputStream() {{ response.render(this); }}.toString(UTF_8)); + } + +} |