summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorten Tokle <mortent@yahooinc.com>2022-09-12 09:35:36 +0200
committerMorten Tokle <mortent@yahooinc.com>2022-09-12 09:35:36 +0200
commit4f8bccce3a80181c5e745acedca0accc8bfa3d68 (patch)
tree1696a3bcd7b9a82b245024c51fae6f950af5bd25
parentaa436622a3e954d9da14847339a8ca1055f1a5d1 (diff)
Restrict data plane bindings
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java18
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java40
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/component/UserBindingPattern.java1
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java16
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java43
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/SearchHandler.java11
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/container/xml/HandlerBuilderTest.java103
7 files changed, 196 insertions, 36 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java
index 7bfe971981e..dde38544924 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/builder/xml/dom/DomHandlerBuilder.java
@@ -14,6 +14,7 @@ import com.yahoo.vespa.model.container.component.UserBindingPattern;
import com.yahoo.vespa.model.container.xml.BundleInstantiationSpecificationBuilder;
import org.w3c.dom.Element;
+import java.util.OptionalInt;
import java.util.Set;
import static com.yahoo.vespa.model.container.ApplicationContainerCluster.METRICS_V2_HANDLER_BINDING_1;
@@ -36,23 +37,38 @@ public class DomHandlerBuilder extends VespaDomBuilder.DomConfigProducerBuilder<
VIP_HANDLER_BINDING);
private final ApplicationContainerCluster cluster;
+ private OptionalInt portBindingOverride;
public DomHandlerBuilder(ApplicationContainerCluster cluster) {
+ this(cluster, OptionalInt.empty());
+ }
+ public DomHandlerBuilder(ApplicationContainerCluster cluster, OptionalInt portBindingOverride) {
this.cluster = cluster;
+ this.portBindingOverride = portBindingOverride;
}
@Override
protected Handler doBuild(DeployState deployState, AbstractConfigProducer<?> parent, Element handlerElement) {
Handler handler = createHandler(handlerElement);
+ OptionalInt port = portBindingOverride.isPresent() && deployState.isHosted() && deployState.featureFlags().useRestrictedDataPlaneBindings()
+ ? portBindingOverride
+ : OptionalInt.empty();
for (Element binding : XML.getChildren(handlerElement, "binding"))
- addServerBinding(handler, UserBindingPattern.fromPattern(XML.getValue(binding)), deployState.getDeployLogger());
+ addServerBinding(handler, userBindingPattern(XML.getValue(binding), port), deployState.getDeployLogger());
DomComponentBuilder.addChildren(deployState, parent, handlerElement, handler);
return handler;
}
+ private static UserBindingPattern userBindingPattern(String path, OptionalInt port) {
+ UserBindingPattern bindingPattern = UserBindingPattern.fromPattern(path);
+ return port.isPresent()
+ ? bindingPattern.withPort(port.getAsInt())
+ : bindingPattern;
+ }
+
Handler createHandler(Element handlerElement) {
BundleInstantiationSpecification bundleSpec = BundleInstantiationSpecificationBuilder.build(handlerElement);
return new Handler(new ComponentModel(bundleSpec));
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java b/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java
index f23f27c0d8e..8163c268d09 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/clients/ContainerDocumentApi.java
@@ -6,6 +6,7 @@ import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.vespa.model.container.ContainerCluster;
import com.yahoo.vespa.model.container.ContainerThreadpool;
import com.yahoo.vespa.model.container.PlatformBundles;
+import com.yahoo.vespa.model.container.component.BindingPattern;
import com.yahoo.vespa.model.container.component.Handler;
import com.yahoo.vespa.model.container.component.SystemBindingPattern;
import com.yahoo.vespa.model.container.component.UserBindingPattern;
@@ -13,6 +14,7 @@ import com.yahoo.vespa.model.container.component.UserBindingPattern;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
+import java.util.OptionalInt;
/**
* @author Einar M R Rosenvinge
@@ -26,10 +28,10 @@ public class ContainerDocumentApi {
private final boolean ignoreUndefinedFields;
- public ContainerDocumentApi(ContainerCluster<?> cluster, HandlerOptions handlerOptions, boolean ignoreUndefinedFields) {
+ public ContainerDocumentApi(ContainerCluster<?> cluster, HandlerOptions handlerOptions, boolean ignoreUndefinedFields, OptionalInt portOverride) {
this.ignoreUndefinedFields = ignoreUndefinedFields;
- addRestApiHandler(cluster, handlerOptions);
- addFeedHandler(cluster, handlerOptions);
+ addRestApiHandler(cluster, handlerOptions, portOverride);
+ addFeedHandler(cluster, handlerOptions, portOverride);
addVespaClientContainerBundle(cluster);
}
@@ -37,18 +39,18 @@ public class ContainerDocumentApi {
c.addPlatformBundle(VESPACLIENT_CONTAINER_BUNDLE);
}
- private static void addFeedHandler(ContainerCluster<?> cluster, HandlerOptions handlerOptions) {
+ private static void addFeedHandler(ContainerCluster<?> cluster, HandlerOptions handlerOptions, OptionalInt portOverride) {
String bindingSuffix = ContainerCluster.RESERVED_URI_PREFIX + "/feedapi";
var executor = new Threadpool("feedapi-handler", handlerOptions.feedApiThreadpoolOptions);
var handler = newVespaClientHandler("com.yahoo.vespa.http.server.FeedHandler",
- bindingSuffix, handlerOptions, executor);
+ bindingSuffix, handlerOptions, executor, portOverride);
cluster.addComponent(handler);
}
- private static void addRestApiHandler(ContainerCluster<?> cluster, HandlerOptions handlerOptions) {
+ private static void addRestApiHandler(ContainerCluster<?> cluster, HandlerOptions handlerOptions, OptionalInt portOverride) {
var handler = newVespaClientHandler("com.yahoo.document.restapi.resource.DocumentV1ApiHandler",
- DOCUMENT_V1_PREFIX + "/*", handlerOptions, null);
+ DOCUMENT_V1_PREFIX + "/*", handlerOptions, null, portOverride);
cluster.addComponent(handler);
// We need to include a dummy implementation of the previous restapi handler (using the same class name).
@@ -62,23 +64,37 @@ public class ContainerDocumentApi {
private static Handler newVespaClientHandler(String componentId,
String bindingSuffix,
HandlerOptions handlerOptions,
- Threadpool executor) {
+ Threadpool executor,
+ OptionalInt portOverride) {
Handler handler = createHandler(componentId, executor);
if (handlerOptions.bindings.isEmpty()) {
handler.addServerBindings(
- SystemBindingPattern.fromHttpPath(bindingSuffix),
- SystemBindingPattern.fromHttpPath(bindingSuffix + '/'));
+ bindingPattern(bindingSuffix, portOverride),
+ bindingPattern(bindingSuffix + '/', portOverride));
} else {
for (String rootBinding : handlerOptions.bindings) {
String pathWithoutLeadingSlash = bindingSuffix.substring(1);
handler.addServerBindings(
- UserBindingPattern.fromPattern(rootBinding + pathWithoutLeadingSlash),
- UserBindingPattern.fromPattern(rootBinding + pathWithoutLeadingSlash + '/'));
+ userBindingPattern(rootBinding + pathWithoutLeadingSlash, portOverride),
+ userBindingPattern(rootBinding + pathWithoutLeadingSlash + '/', portOverride));
}
}
return handler;
}
+ private static BindingPattern bindingPattern(String path, OptionalInt port) {
+ return port.isPresent()
+ ? SystemBindingPattern.fromHttpPortAndPath(Integer.toString(port.getAsInt()), path)
+ : SystemBindingPattern.fromHttpPath(path);
+ }
+
+ private static UserBindingPattern userBindingPattern(String path, OptionalInt port) {
+ UserBindingPattern bindingPattern = UserBindingPattern.fromPattern(path);
+ return port.isPresent()
+ ? bindingPattern.withPort(port.getAsInt())
+ : bindingPattern;
+ }
+
private static Handler createHandler(String className, Threadpool executor) {
return new Handler(new ComponentModel(className, null, "vespaclient-container-plugin"),
executor);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/component/UserBindingPattern.java b/config-model/src/main/java/com/yahoo/vespa/model/container/component/UserBindingPattern.java
index 5da11c06fb1..182eca835c1 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/component/UserBindingPattern.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/component/UserBindingPattern.java
@@ -13,6 +13,7 @@ public class UserBindingPattern extends BindingPattern {
public static UserBindingPattern fromHttpPath(String path) { return new UserBindingPattern("http", "*", null, path); }
public static UserBindingPattern fromPattern(String binding) { return new UserBindingPattern(binding); }
+ public UserBindingPattern withPort(int port) { return new UserBindingPattern(scheme(), host(), Integer.toString(port), path()); }
@Override
public String toString() {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java
index f55fb547bb0..cb52f701da4 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java
@@ -32,10 +32,10 @@ public class HostedSslConnectorFactory extends ConnectorFactory {
*/
public static HostedSslConnectorFactory withProvidedCertificate(
String serverName, EndpointCertificateSecrets endpointCertificateSecrets, boolean enforceHandshakeClientAuth,
- Collection<String> tlsCiphersOverride, boolean enableProxyProtocolMixedMode) {
+ Collection<String> tlsCiphersOverride, boolean enableProxyProtocolMixedMode, int port) {
ConfiguredDirectSslProvider sslProvider = createConfiguredDirectSslProvider(
serverName, endpointCertificateSecrets, DEFAULT_HOSTED_TRUSTSTORE, /*tlsCaCertificates*/null, enforceHandshakeClientAuth);
- return new HostedSslConnectorFactory(sslProvider, false, enforceHandshakeClientAuth, tlsCiphersOverride, enableProxyProtocolMixedMode);
+ return new HostedSslConnectorFactory(sslProvider, false, enforceHandshakeClientAuth, tlsCiphersOverride, enableProxyProtocolMixedMode, port);
}
/**
@@ -43,24 +43,24 @@ public class HostedSslConnectorFactory extends ConnectorFactory {
*/
public static HostedSslConnectorFactory withProvidedCertificateAndTruststore(
String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificates,
- Collection<String> tlsCiphersOverride, boolean enableProxyProtocolMixedMode) {
+ Collection<String> tlsCiphersOverride, boolean enableProxyProtocolMixedMode, int port) {
ConfiguredDirectSslProvider sslProvider = createConfiguredDirectSslProvider(
serverName, endpointCertificateSecrets, /*tlsCaCertificatesPath*/null, tlsCaCertificates, false);
- return new HostedSslConnectorFactory(sslProvider, true, false, tlsCiphersOverride, enableProxyProtocolMixedMode);
+ return new HostedSslConnectorFactory(sslProvider, true, false, tlsCiphersOverride, enableProxyProtocolMixedMode, port);
}
/**
* Create connector factory that uses the default certificate and truststore provided by Vespa (through Vespa-global TLS configuration).
*/
public static HostedSslConnectorFactory withDefaultCertificateAndTruststore(String serverName, Collection<String> tlsCiphersOverride,
- boolean enableProxyProtocolMixedMode) {
- return new HostedSslConnectorFactory(new DefaultSslProvider(serverName), true, false, tlsCiphersOverride, enableProxyProtocolMixedMode);
+ boolean enableProxyProtocolMixedMode, int port) {
+ return new HostedSslConnectorFactory(new DefaultSslProvider(serverName), true, false, tlsCiphersOverride, enableProxyProtocolMixedMode, port);
}
private HostedSslConnectorFactory(SslProvider sslProvider, boolean enforceClientAuth,
boolean enforceHandshakeClientAuth, Collection<String> tlsCiphersOverride,
- boolean enableProxyProtocolMixedMode) {
- super(new Builder("tls4443", 4443).sslProvider(sslProvider));
+ boolean enableProxyProtocolMixedMode, int port) {
+ super(new Builder("tls"+port, port).sslProvider(sslProvider));
this.enforceClientAuth = enforceClientAuth;
this.enforceHandshakeClientAuth = enforceHandshakeClientAuth;
this.tlsCiphersOverride = tlsCiphersOverride;
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 0bf586a089f..4c94708d892 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
@@ -97,6 +97,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
+import java.util.OptionalInt;
import java.util.Set;
import java.util.function.Consumer;
import java.util.logging.Level;
@@ -114,6 +115,9 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
// Default path to vip status file for container in Hosted Vespa.
static final String HOSTED_VESPA_STATUS_FILE = Defaults.getDefaults().underVespaHome("var/vespa/load-balancer/status.html");
+ // Data plane port for hosted Vespa
+ static final int HOSTED_VESPA_DATAPLANE_PORT = 4443;
+
//Path to vip status file for container in Hosted Vespa. Only used if set, else use HOSTED_VESPA_STATUS_FILE
private static final String HOSTED_VESPA_STATUS_FILE_SETTING = "VESPA_LB_STATUS_FILE";
@@ -450,11 +454,11 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
connectorFactory = authorizeClient
? HostedSslConnectorFactory.withProvidedCertificateAndTruststore(
- serverName, endpointCertificateSecrets, getTlsClientAuthorities(deployState), tlsCiphersOverride, proxyProtocolMixedMode)
+ serverName, endpointCertificateSecrets, getTlsClientAuthorities(deployState), tlsCiphersOverride, proxyProtocolMixedMode, HOSTED_VESPA_DATAPLANE_PORT)
: HostedSslConnectorFactory.withProvidedCertificate(
- serverName, endpointCertificateSecrets, enforceHandshakeClientAuth, tlsCiphersOverride, proxyProtocolMixedMode);
+ serverName, endpointCertificateSecrets, enforceHandshakeClientAuth, tlsCiphersOverride, proxyProtocolMixedMode, HOSTED_VESPA_DATAPLANE_PORT);
} else {
- connectorFactory = HostedSslConnectorFactory.withDefaultCertificateAndTruststore(serverName, tlsCiphersOverride, proxyProtocolMixedMode);
+ connectorFactory = HostedSslConnectorFactory.withDefaultCertificateAndTruststore(serverName, tlsCiphersOverride, proxyProtocolMixedMode, HOSTED_VESPA_DATAPLANE_PORT);
}
cluster.getHttp().getAccessControl().ifPresent(accessControl -> accessControl.configureHostedConnector(connectorFactory));
server.addConnector(connectorFactory);
@@ -540,7 +544,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
addIncludes(searchElement);
cluster.setSearch(buildSearch(deployState, cluster, searchElement));
- addSearchHandler(cluster, searchElement);
+ addSearchHandler(deployState, cluster, searchElement);
validateAndAddConfiguredComponents(deployState, cluster, searchElement, "renderer", ContainerModelBuilder::validateRendererElement);
}
@@ -591,7 +595,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
cluster.addSearchAndDocprocBundles();
addIncludes(processingElement);
cluster.setProcessingChains(new DomProcessingBuilder(null).build(deployState, cluster, processingElement),
- serverBindings(processingElement, ProcessingChains.defaultBindings).toArray(BindingPattern[]::new));
+ serverBindings(deployState, processingElement, ProcessingChains.defaultBindings).toArray(BindingPattern[]::new));
validateAndAddConfiguredComponents(deployState, cluster, processingElement, "renderer", ContainerModelBuilder::validateRendererElement);
}
@@ -616,7 +620,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
private void addUserHandlers(DeployState deployState, ApplicationContainerCluster cluster, Element spec) {
for (Element component: XML.getChildren(spec, "handler")) {
cluster.addComponent(
- new DomHandlerBuilder(cluster).build(deployState, cluster, component));
+ new DomHandlerBuilder(cluster, OptionalInt.of(HOSTED_VESPA_DATAPLANE_PORT)).build(deployState, cluster, component));
}
}
@@ -875,9 +879,13 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
container.setPreLoad(nodesElement.getAttribute(VespaDomBuilder.PRELOAD_ATTRIB_NAME));
}
- private void addSearchHandler(ApplicationContainerCluster cluster, Element searchElement) {
+ private void addSearchHandler(DeployState deployState, ApplicationContainerCluster cluster, Element searchElement) {
+ BindingPattern bindingPattern = SearchHandler.DEFAULT_BINDING;
+ if (deployState.isHosted() && deployState.featureFlags().useRestrictedDataPlaneBindings()) {
+ bindingPattern = SearchHandler.bindingPattern(Optional.of(Integer.toString(HOSTED_VESPA_DATAPLANE_PORT)));
+ }
SearchHandler searchHandler = new SearchHandler(cluster,
- serverBindings(searchElement, SearchHandler.DEFAULT_BINDING),
+ serverBindings(deployState, searchElement, bindingPattern),
ContainerThreadpool.UserOptions.fromXml(searchElement).orElse(null));
cluster.addComponent(searchHandler);
@@ -885,25 +893,31 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
searchHandler.addComponent(Component.fromClassAndBundle(SearchHandler.EXECUTION_FACTORY_CLASS, PlatformBundles.SEARCH_AND_DOCPROC_BUNDLE));
}
- private List<BindingPattern> serverBindings(Element searchElement, BindingPattern... defaultBindings) {
+ private List<BindingPattern> serverBindings(DeployState deployState, Element searchElement, BindingPattern... defaultBindings) {
List<Element> bindings = XML.getChildren(searchElement, "binding");
if (bindings.isEmpty())
return List.of(defaultBindings);
- return toBindingList(bindings);
+ return toBindingList(deployState, bindings);
}
- private List<BindingPattern> toBindingList(List<Element> bindingElements) {
+ private List<BindingPattern> toBindingList(DeployState deployState, List<Element> bindingElements) {
List<BindingPattern> result = new ArrayList<>();
-
+ OptionalInt port = deployState.isHosted() && deployState.featureFlags().useRestrictedDataPlaneBindings() ? OptionalInt.of(HOSTED_VESPA_DATAPLANE_PORT) : OptionalInt.empty();
for (Element element: bindingElements) {
String text = element.getTextContent().trim();
if (!text.isEmpty())
- result.add(UserBindingPattern.fromPattern(text));
+ result.add(userBindingPattern(text, port));
}
return result;
}
+ private static UserBindingPattern userBindingPattern(String path, OptionalInt port) {
+ UserBindingPattern bindingPattern = UserBindingPattern.fromPattern(path);
+ return port.isPresent()
+ ? bindingPattern.withPort(port.getAsInt())
+ : bindingPattern;
+ }
private ContainerDocumentApi buildDocumentApi(ApplicationContainerCluster cluster, Element spec) {
Element documentApiElement = XML.getChild(spec, "document-api");
@@ -911,8 +925,9 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
ContainerDocumentApi.HandlerOptions documentApiOptions = DocumentApiOptionsBuilder.build(documentApiElement);
Element ignoreUndefinedFields = XML.getChild(documentApiElement, "ignore-undefined-fields");
+ OptionalInt portBindingOverride = cluster.isHostedVespa()? OptionalInt.of(HOSTED_VESPA_DATAPLANE_PORT) : OptionalInt.empty();
return new ContainerDocumentApi(cluster, documentApiOptions,
- "true".equals(XML.getValue(ignoreUndefinedFields)));
+ "true".equals(XML.getValue(ignoreUndefinedFields)), portBindingOverride);
}
private ContainerDocproc buildDocproc(DeployState deployState, ApplicationContainerCluster cluster, Element spec) {
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/SearchHandler.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/SearchHandler.java
index 54cd061d2c5..596375ea93a 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/SearchHandler.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/SearchHandler.java
@@ -11,6 +11,8 @@ import com.yahoo.vespa.model.container.component.chain.ProcessingHandler;
import com.yahoo.vespa.model.container.search.searchchain.SearchChains;
import java.util.List;
+import java.util.Optional;
+import java.util.OptionalInt;
import static com.yahoo.container.bundle.BundleInstantiationSpecification.fromSearchAndDocproc;
@@ -25,7 +27,7 @@ class SearchHandler extends ProcessingHandler<SearchChains> {
static final String EXECUTION_FACTORY_CLASS = com.yahoo.search.searchchain.ExecutionFactory.class.getName();
static final BundleInstantiationSpecification HANDLER_SPEC = fromSearchAndDocproc(HANDLER_CLASS);
- static final BindingPattern DEFAULT_BINDING = SystemBindingPattern.fromHttpPath("/search/*");
+ static final BindingPattern DEFAULT_BINDING = bindingPattern(Optional.empty());
SearchHandler(ApplicationContainerCluster cluster,
List<BindingPattern> bindings,
@@ -34,6 +36,13 @@ class SearchHandler extends ProcessingHandler<SearchChains> {
bindings.forEach(this::addServerBindings);
}
+ static BindingPattern bindingPattern(Optional<String> port) {
+ String path = "/search/*";
+ return port
+ .filter(s -> !s.isBlank())
+ .map(s -> SystemBindingPattern.fromHttpPortAndPath(s, path))
+ .orElseGet(() -> SystemBindingPattern.fromHttpPath(path));
+ }
private static class Threadpool extends ContainerThreadpool {
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/HandlerBuilderTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/HandlerBuilderTest.java
index 291a5f21305..0c9b87c0ae3 100644
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/xml/HandlerBuilderTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/xml/HandlerBuilderTest.java
@@ -1,6 +1,8 @@
package com.yahoo.vespa.model.container.xml;
import com.yahoo.config.model.builder.xml.test.DomBuilderTest;
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.config.model.deploy.TestProperties;
import com.yahoo.container.ComponentsConfig;
import com.yahoo.container.jdisc.JdiscBindingsConfig;
import com.yahoo.container.usability.BindingsOverviewHandler;
@@ -10,10 +12,14 @@ import com.yahoo.vespa.model.container.component.Handler;
import org.junit.jupiter.api.Test;
import org.w3c.dom.Element;
+import java.util.List;
+import java.util.Map;
+
import static com.yahoo.vespa.model.container.ContainerCluster.ROOT_HANDLER_BINDING;
import static com.yahoo.vespa.model.container.ContainerCluster.STATE_HANDLER_BINDING_1;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.junit.jupiter.api.Assertions.*;
@@ -100,6 +106,103 @@ public class HandlerBuilderTest extends ContainerModelBuilderTestBase {
assertTrue(handler.getInjectedComponentIds().contains("threadpool@default-handler-common"));
}
+ @Test
+ void restricts_default_bindings_in_hosted_vespa() {
+ DeployState deployState = new DeployState.Builder()
+ .properties(new TestProperties().setHostedVespa(true).setUseRestrictedDataPlaneBindings(true))
+ .build();
+ verifyDefaultBindings(deployState, "http://*:4443");
+ }
+
+ @Test
+ void restricts_custom_bindings_in_hosted_vespa() {
+ DeployState deployState = new DeployState.Builder()
+ .properties(new TestProperties().setHostedVespa(true).setUseRestrictedDataPlaneBindings(true))
+ .build();
+ verifyCustomSearchBindings(deployState, "http://*:4443");
+ }
+
+ @Test
+ void does_not_restrict_default_bindings_in_self_hosted() {
+ DeployState deployState = new DeployState.Builder()
+ .properties(new TestProperties().setHostedVespa(false).setUseRestrictedDataPlaneBindings(true))
+ .build();
+ verifyDefaultBindings(deployState, "http://*");
+ }
+
+ @Test
+ void does_not_restrict_custom_bindings_in_self_hosted() {
+ DeployState deployState = new DeployState.Builder()
+ .properties(new TestProperties().setHostedVespa(false).setUseRestrictedDataPlaneBindings(true))
+ .build();
+ verifyCustomSearchBindings(deployState, "http://*");
+ }
+
+ private void verifyDefaultBindings(DeployState deployState, String bindingPrefix) {
+ Element clusterElem = DomBuilderTest.parse(
+ "<container id='default' version='1.0'>",
+ " <search/>",
+ " <document-api/>",
+ " <handler id='FooHandler'>",
+ " <binding>http://*/foo</binding>",
+ " </handler>",
+ nodesXml,
+ "</container>");
+
+ createModel(root, deployState, null, clusterElem);
+ JdiscBindingsConfig bindingsConfig = root.getConfig(JdiscBindingsConfig.class, "default");
+
+ // Verify /search /feed /document and custom handler are bound correctly
+ Map<String, JdiscBindingsConfig.Handlers> handlers = bindingsConfig.handlers();
+ Map<String, List<String>> expectedHandlerMappings = Map.of(
+ "com.yahoo.search.handler.SearchHandler", List.of("/search/*"),
+ "com.yahoo.document.restapi.resource.DocumentV1ApiHandler", List.of("/document/v1/*", "/document/v1/*/"),
+ "com.yahoo.vespa.http.server.FeedHandler", List.of("/reserved-for-internal-use/feedapi", "/reserved-for-internal-use/feedapi/"),
+ "FooHandler", List.of("/foo"));
+ expectedHandlerMappings.forEach((handler, bindings) -> validateHandler(handlers.get(handler), bindingPrefix, bindings));
+
+ // All other handlers should be bound to default (http://*/...)
+ handlers.entrySet().stream()
+ .filter(e -> ! expectedHandlerMappings.containsKey(e.getKey()))
+ .forEach(e -> assertTrue(e.getValue().serverBindings().stream().allMatch(s -> s.startsWith("http://*/"))));
+ }
+
+ private void verifyCustomSearchBindings(DeployState deployState, String bindingPrefix) {
+ Element clusterElem = DomBuilderTest.parse(
+ "<container id='default' version='1.0'>",
+ " <search>",
+ " <binding>http://*/search-binding/</binding>",
+ " </search>",
+ " <document-api>",
+ " <binding>http://*/docapi-binding/</binding>",
+ " </document-api>",
+ nodesXml,
+ "</container>");
+
+ createModel(root, deployState, null, clusterElem);
+ JdiscBindingsConfig bindingsConfig = root.getConfig(JdiscBindingsConfig.class, "default");
+
+ // Verify search feed and document handler are bound correctly
+ Map<String, JdiscBindingsConfig.Handlers> handlers = bindingsConfig.handlers();
+ Map<String, List<String>> expectedHandlerMappings = Map.of(
+ "com.yahoo.search.handler.SearchHandler", List.of("/search-binding/"),
+ "com.yahoo.document.restapi.resource.DocumentV1ApiHandler", List.of("/docapi-binding/document/v1/*", "/docapi-binding/document/v1/*/"),
+ "com.yahoo.vespa.http.server.FeedHandler", List.of("/docapi-binding/reserved-for-internal-use/feedapi", "/docapi-binding/reserved-for-internal-use/feedapi/"));
+ expectedHandlerMappings.forEach((handler, bindings) -> validateHandler(handlers.get(handler), bindingPrefix, bindings));
+
+ // All other handlers should be bound to default (http://*/...)
+ handlers.entrySet().stream()
+ .filter(e -> ! expectedHandlerMappings.containsKey(e.getKey()))
+ .forEach(e -> assertTrue(e.getValue().serverBindings().stream().allMatch(s -> s.startsWith("http://*/"))));
+
+ }
+
+ private void validateHandler(JdiscBindingsConfig.Handlers handler, String bindingPrefix, List<String> expectedBindings) {
+ assertNotNull(handler);
+ assertEquals(expectedBindings.size(), handler.serverBindings().size());
+ assertThat(handler.serverBindings(), containsInAnyOrder(expectedBindings.stream().map(s->bindingPrefix+s).toArray()));
+ }
+
private void createClusterWithJDiscHandler() {
Element clusterElem = DomBuilderTest.parse(
"<container id='default' version='1.0'>",