diff options
Diffstat (limited to 'config-model/src')
3 files changed, 94 insertions, 59 deletions
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java index da81f7a90f2..815c32e3c8f 100644 --- a/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java +++ b/config-model/src/main/java/com/yahoo/config/model/deploy/TestProperties.java @@ -85,7 +85,6 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea private Optional<CloudAccount> cloudAccount = Optional.empty(); private boolean allowUserFilters = true; private List<DataplaneToken> dataplaneTokens; - private boolean enableDataplaneProxy; private int contentLayerMetadataFeatureLevel = 0; @Override public ModelContext.FeatureFlags featureFlags() { return this; } @@ -375,11 +374,6 @@ public class TestProperties implements ModelContext.Properties, ModelContext.Fea return this; } - public TestProperties setEnableDataplaneProxy(boolean enable) { - this.enableDataplaneProxy = enable; - return this; - } - public TestProperties setContentLayerMetadataFeatureLevel(int level) { this.contentLayerMetadataFeatureLevel = level; return this; 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 9804cb0b10d..35b0213bf59 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 @@ -459,7 +459,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { private static void addCloudDataPlaneFilter(DeployState deployState, ApplicationContainerCluster cluster) { if (!deployState.isHosted() || !deployState.zone().system().isPublic()) return; - var dataplanePort = getMtlsDataplanePort(deployState); + var dataplanePort = getMtlsDataplanePort(deployState, cluster); // Setup secure filter chain var secureChain = new HttpFilterChain("cloud-data-plane-secure", HttpFilterChain.Type.SYSTEM); secureChain.addInnerComponent(new CloudDataPlaneFilter(cluster, deployState)); @@ -594,7 +594,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { String serverName = server.getComponentId().getName(); // If the deployment contains certificate/private key reference, setup TLS port - var builder = HostedSslConnectorFactory.builder(serverName, getMtlsDataplanePort(state)) + var builder = HostedSslConnectorFactory.builder(serverName, getMtlsDataplanePort(state, cluster)) .proxyProtocol(true, state.getProperties().featureFlags().enableProxyProtocolMixedMode()) .tlsCiphersOverride(state.getProperties().tlsCiphersOverride()) .endpointConnectionTtl(state.getProperties().endpointConnectionTtl()); @@ -627,23 +627,19 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { private void addCloudTokenSupport(DeployState state, ApplicationContainerCluster cluster) { var server = cluster.getHttp().getHttpServer().get(); + if (!enableTokenSupport(state, cluster)) return; Set<String> tokenEndpoints = tokenEndpoints(state).stream() .map(ContainerEndpoint::names) .flatMap(Collection::stream) .collect(Collectors.toSet()); - - boolean enableTokenSupport = state.isHosted() && state.zone().system().isPublic() - && cluster.getClients().stream().anyMatch(c -> !c.tokens().isEmpty()) - && ! tokenEndpoints.isEmpty(); - if (!enableTokenSupport) return; var endpointCert = state.endpointCertificateSecrets().orElseThrow(); - int tokenPort = getTokenDataplanePort(state).orElseThrow(); + int tokenPort = getTokenDataplanePort(state, cluster).orElseThrow(); // Set up component to generate proxy cert if token support is enabled cluster.addSimpleComponent(DataplaneProxyCredentials.class); cluster.addSimpleComponent(DataplaneProxyService.class); var dataplaneProxy = new DataplaneProxy( - getMtlsDataplanePort(state), + getMtlsDataplanePort(state, cluster), tokenPort, endpointCert.certificate(), endpointCert.key(), @@ -714,7 +710,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { } private Http buildHttp(DeployState deployState, ApplicationContainerCluster cluster, Element httpElement, ConfigModelContext context) { - Http http = new HttpBuilder(portBindingOverride(deployState, context)).build(deployState, cluster, httpElement); + Http http = new HttpBuilder(portBindingOverride(deployState, context, cluster)).build(deployState, cluster, httpElement); if (networking == Networking.disable) http.removeAllServers(); @@ -819,7 +815,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { cluster.addSearchAndDocprocBundles(); addIncludes(processingElement); cluster.setProcessingChains(new DomProcessingBuilder(null).build(deployState, cluster, processingElement), - serverBindings(deployState, context, processingElement, ProcessingChains.defaultBindings).toArray(BindingPattern[]::new)); + serverBindings(deployState, context, processingElement, ProcessingChains.defaultBindings, cluster).toArray(BindingPattern[]::new)); validateAndAddConfiguredComponents(deployState, cluster, processingElement, "renderer", ContainerModelBuilder::validateRendererElement); } @@ -844,7 +840,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { private void addUserHandlers(DeployState deployState, ApplicationContainerCluster cluster, Element spec, ConfigModelContext context) { for (Element component: XML.getChildren(spec, "handler")) { cluster.addComponent( - new DomHandlerBuilder(cluster, portBindingOverride(deployState, context)).build(deployState, cluster, component)); + new DomHandlerBuilder(cluster, portBindingOverride(deployState, context, cluster)).build(deployState, cluster, component)); } } @@ -1132,10 +1128,10 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { private void addSearchHandler(DeployState deployState, ApplicationContainerCluster cluster, Element searchElement, ConfigModelContext context) { var bindingPatterns = List.<BindingPattern>of(SearchHandler.DEFAULT_BINDING); if (isHostedTenantApplication(context)) { - bindingPatterns = SearchHandler.bindingPattern(getDataplanePorts(deployState)); + bindingPatterns = SearchHandler.bindingPattern(getDataplanePorts(deployState, cluster)); } SearchHandler searchHandler = new SearchHandler(cluster, - serverBindings(deployState, context, searchElement, bindingPatterns), + serverBindings(deployState, context, searchElement, bindingPatterns, cluster), ContainerThreadpool.UserOptions.fromXml(searchElement).orElse(null)); cluster.addComponent(searchHandler); @@ -1143,17 +1139,17 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { searchHandler.addComponent(Component.fromClassAndBundle(SearchHandler.EXECUTION_FACTORY, PlatformBundles.SEARCH_AND_DOCPROC_BUNDLE)); } - private List<BindingPattern> serverBindings(DeployState deployState, ConfigModelContext context, Element searchElement, Collection<BindingPattern> defaultBindings) { + private List<BindingPattern> serverBindings(DeployState deployState, ConfigModelContext context, Element searchElement, Collection<BindingPattern> defaultBindings, ApplicationContainerCluster cluster) { List<Element> bindings = XML.getChildren(searchElement, "binding"); if (bindings.isEmpty()) return List.copyOf(defaultBindings); - return toBindingList(deployState, context, bindings); + return toBindingList(deployState, context, bindings, cluster); } - private List<BindingPattern> toBindingList(DeployState deployState, ConfigModelContext context, List<Element> bindingElements) { + private List<BindingPattern> toBindingList(DeployState deployState, ConfigModelContext context, List<Element> bindingElements, ApplicationContainerCluster cluster) { List<BindingPattern> result = new ArrayList<>(); - var portOverride = isHostedTenantApplication(context) ? getDataplanePorts(deployState) : Set.<Integer>of(); + var portOverride = isHostedTenantApplication(context) ? getDataplanePorts(deployState, cluster) : Set.<Integer>of(); for (Element element: bindingElements) { String text = element.getTextContent().trim(); if (!text.isEmpty()) @@ -1178,12 +1174,12 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { ContainerDocumentApi.HandlerOptions documentApiOptions = DocumentApiOptionsBuilder.build(documentApiElement); Element ignoreUndefinedFields = XML.getChild(documentApiElement, "ignore-undefined-fields"); return new ContainerDocumentApi(cluster, documentApiOptions, - "true".equals(XML.getValue(ignoreUndefinedFields)), portBindingOverride(deployState, context)); + "true".equals(XML.getValue(ignoreUndefinedFields)), portBindingOverride(deployState, context, cluster)); } - private Set<Integer> portBindingOverride(DeployState deployState, ConfigModelContext context) { + private Set<Integer> portBindingOverride(DeployState deployState, ConfigModelContext context, ApplicationContainerCluster cluster) { return isHostedTenantApplication(context) - ? getDataplanePorts(deployState) + ? getDataplanePorts(deployState, cluster) : Set.<Integer>of(); } @@ -1442,20 +1438,18 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { } - private static Set<Integer> getDataplanePorts(DeployState ds) { - var tokenPort = getTokenDataplanePort(ds); - var mtlsPort = getMtlsDataplanePort(ds); + private static Set<Integer> getDataplanePorts(DeployState ds, ApplicationContainerCluster cluster) { + var tokenPort = getTokenDataplanePort(ds, cluster); + var mtlsPort = getMtlsDataplanePort(ds, cluster); return tokenPort.isPresent() ? Set.of(mtlsPort, tokenPort.getAsInt()) : Set.of(mtlsPort); } - private static int getMtlsDataplanePort(DeployState ds) { - boolean enableDataplaneProxy = ! tokenEndpoints(ds).isEmpty(); - return enableDataplaneProxy ? 8443 : 4443; + private static int getMtlsDataplanePort(DeployState ds, ApplicationContainerCluster cluster) { + return enableTokenSupport(ds, cluster) ? 8443 : 4443; } - private static OptionalInt getTokenDataplanePort(DeployState ds) { - boolean enableDataplaneProxy = ! tokenEndpoints(ds).isEmpty(); - return enableDataplaneProxy ? OptionalInt.of(8444) : OptionalInt.empty(); + private static OptionalInt getTokenDataplanePort(DeployState ds, ApplicationContainerCluster cluster) { + return enableTokenSupport(ds, cluster) ? OptionalInt.of(8444) : OptionalInt.empty(); } private static Set<ContainerEndpoint> tokenEndpoints(DeployState deployState) { @@ -1464,4 +1458,8 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { .collect(Collectors.toSet()); } + private static boolean enableTokenSupport(DeployState state, ApplicationContainerCluster cluster) { + Set<ContainerEndpoint> tokenEndpoints = tokenEndpoints(state); + return state.isHosted() && state.zone().system().isPublic() && ! tokenEndpoints.isEmpty(); + } } diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/CloudTokenDataPlaneFilterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/CloudTokenDataPlaneFilterTest.java index b4e2f53bb87..fba2ef891be 100644 --- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/CloudTokenDataPlaneFilterTest.java +++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/CloudTokenDataPlaneFilterTest.java @@ -1,6 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.model.container.xml; +import com.yahoo.cloud.config.DataplaneProxyConfig; import com.yahoo.config.model.api.ApplicationClusterEndpoint; import com.yahoo.config.model.api.ContainerEndpoint; import com.yahoo.config.model.api.EndpointCertificateSecrets; @@ -14,6 +15,7 @@ import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.Zone; import com.yahoo.jdisc.http.filter.security.cloud.config.CloudTokenDataPlaneFilterConfig; +import com.yahoo.processing.response.Data; import com.yahoo.vespa.model.container.ContainerModel; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -34,10 +36,30 @@ import java.util.Set; import static com.yahoo.vespa.model.container.xml.CloudDataPlaneFilterTest.createCertificate; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; public class CloudTokenDataPlaneFilterTest extends ContainerModelBuilderTestBase { + private static final String servicesXmlTemplate = """ + <container version='1.0'> + <clients> + <client id="foo" permissions="read,write"> + <certificate file="%s"/> + </client> + <client id="bar" permissions="read"> + <token id="my-token"/> + </client> + </clients> + </container> + """; + + private static final List<DataplaneToken> defaultTokens = List.of(new DataplaneToken("my-token", List.of( + new DataplaneToken.Version("myfingerprint1", "myaccesshash1", Optional.empty()), + new DataplaneToken.Version("myfingerprint2", "myaccesshash2", Optional.of(Instant.EPOCH.plus(Duration.ofDays(100000))))))); + private static final ContainerEndpoint tokenEndpoint = new ContainerEndpoint("cluster", ApplicationClusterEndpoint.Scope.zone, List.of("token"), OptionalInt.empty(), ApplicationClusterEndpoint.RoutingMethod.exclusive, ApplicationClusterEndpoint.AuthMethod.token); + private static final ContainerEndpoint mtlsEndpoint = new ContainerEndpoint("cluster", ApplicationClusterEndpoint.Scope.zone, List.of("mtls"), OptionalInt.empty(), ApplicationClusterEndpoint.RoutingMethod.exclusive, ApplicationClusterEndpoint.AuthMethod.mtls); @TempDir public File applicationFolder; @@ -54,22 +76,9 @@ public class CloudTokenDataPlaneFilterTest extends ContainerModelBuilderTestBase @Test void generates_correct_config_for_tokens() throws IOException { var certFile = securityFolder.resolve("foo.pem"); - var clusterElem = DomBuilderTest.parse( - """ - <container version='1.0'> - <clients> - <client id="foo" permissions="read,write"> - <certificate file="%s"/> - </client> - <client id="bar" permissions="read"> - <token id="my-token"/> - </client> - </clients> - </container> - """ - .formatted(applicationFolder.toPath().relativize(certFile).toString())); + var clusterElem = DomBuilderTest.parse(servicesXmlTemplate.formatted(applicationFolder.toPath().relativize(certFile).toString())); createCertificate(certFile); - buildModel(clusterElem); + buildModel(Set.of(tokenEndpoint, mtlsEndpoint), defaultTokens, clusterElem); var cfg = root.getConfig(CloudTokenDataPlaneFilterConfig.class, filterConfigId); var tokenClient = cfg.clients().stream().filter(c -> c.id().equals("bar")).findAny().orElse(null); @@ -81,13 +90,50 @@ public class CloudTokenDataPlaneFilterTest extends ContainerModelBuilderTestBase assertEquals(List.of(expectedTokenCfg), tokenClient.tokens()); } + @Test + void configures_dataplane_proxy_when_token_defined() throws IOException { + var certFile = securityFolder.resolve("foo.pem"); + var clusterElem = DomBuilderTest.parse(servicesXmlTemplate.formatted(applicationFolder.toPath().relativize(certFile).toString())); + createCertificate(certFile); + buildModel(Set.of(tokenEndpoint, mtlsEndpoint), defaultTokens, clusterElem); + + var configId = "container/component/com.yahoo.container.jdisc.DataplaneProxyConfigurator"; + var cfg = root.getConfig(DataplaneProxyConfig.class, configId); + assertEquals(8443, cfg.mtlsPort()); + assertEquals(8444, cfg.tokenPort()); + } + + @Test + void configures_dataplane_proxy_when_token_defined_but_missing() throws IOException { + var certFile = securityFolder.resolve("foo.pem"); + var clusterElem = DomBuilderTest.parse(servicesXmlTemplate.formatted(applicationFolder.toPath().relativize(certFile).toString())); + createCertificate(certFile); + buildModel(Set.of(tokenEndpoint, mtlsEndpoint), List.of(), clusterElem); + + var configId = "container/component/com.yahoo.container.jdisc.DataplaneProxyConfigurator"; + var cfg = root.getConfig(DataplaneProxyConfig.class, configId); + assertNotNull(cfg); + assertEquals(8443, cfg.mtlsPort()); + assertEquals(8444, cfg.tokenPort()); + } + + @Test + void does_notconfigure_dataplane_proxy_when_token_endpoints_not_defined() throws IOException { + var certFile = securityFolder.resolve("foo.pem"); + var clusterElem = DomBuilderTest.parse(servicesXmlTemplate.formatted(applicationFolder.toPath().relativize(certFile).toString())); + createCertificate(certFile); + buildModel(Set.of(mtlsEndpoint), List.of(), clusterElem); + + assertFalse(root.getConfigIds().stream().anyMatch(id -> id.contains("DataplaneProxyConfigurator"))); + } + private static CloudTokenDataPlaneFilterConfig.Clients.Tokens tokenConfig( String id, Collection<String> fingerprints, Collection<String> accessCheckHashes, Collection<String> expirations) { return new CloudTokenDataPlaneFilterConfig.Clients.Tokens.Builder() .id(id).fingerprints(fingerprints).checkAccessHashes(accessCheckHashes).expirations(expirations).build(); } - public List<ContainerModel> buildModel(Element... clusterElem) { + public List<ContainerModel> buildModel(Set<ContainerEndpoint> endpoints, List<DataplaneToken> definedTokens, Element... clusterElem) { var applicationPackage = new MockApplicationPackage.Builder() .withRoot(applicationFolder) .build(); @@ -96,14 +142,11 @@ public class CloudTokenDataPlaneFilterTest extends ContainerModelBuilderTestBase .applicationPackage(applicationPackage) .properties( new TestProperties() - .setEnableDataplaneProxy(true) - .setEndpointCertificateSecrets(Optional.of(new EndpointCertificateSecrets("CERT", "KEY"))) - .setDataplaneTokens(List.of(new DataplaneToken("my-token", List.of( - new DataplaneToken.Version("myfingerprint1", "myaccesshash1", Optional.empty()), - new DataplaneToken.Version("myfingerprint2", "myaccesshash2", Optional.of(Instant.EPOCH.plus(Duration.ofDays(100000)))))))) - .setHostedVespa(true)) + .setEndpointCertificateSecrets(Optional.of(new EndpointCertificateSecrets("CERT", "KEY"))) + .setDataplaneTokens(definedTokens) + .setHostedVespa(true)) .zone(new Zone(SystemName.PublicCd, Environment.dev, RegionName.defaultName())) - .endpoints(Set.of(new ContainerEndpoint("cluster", ApplicationClusterEndpoint.Scope.zone, List.of("name"), OptionalInt.empty(), ApplicationClusterEndpoint.RoutingMethod.exclusive, ApplicationClusterEndpoint.AuthMethod.token))) + .endpoints(endpoints) .build(); return createModel(root, state, null, clusterElem); } |