aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorn.christian@seime.no>2023-07-19 16:03:00 +0200
committerGitHub <noreply@github.com>2023-07-19 16:03:00 +0200
commita06dda08f3a7c7ee20ad939797bfb5972f82d36e (patch)
tree441e9f72fed2ca7d99cde3ca55932f9aed59dc60
parent9819e2c447f2ff54dc458bf33b8d0ea84d1017d6 (diff)
parent9b3ee17b0ec8c844ace83bd7c797d6600ce0b713 (diff)
Merge pull request #27831 from vespa-engine/bjorncs/reapply
Bjorncs/reapply
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/CloudSslProvider.java3
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java121
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java64
3 files changed, 83 insertions, 105 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/CloudSslProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/CloudSslProvider.java
index 5fa893e9599..b231a4ad847 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/CloudSslProvider.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/CloudSslProvider.java
@@ -26,7 +26,8 @@ public class CloudSslProvider extends SslProvider {
private final String caCertificate;
private final ClientAuth.Enum clientAuthentication;
- public CloudSslProvider(String servername, String privateKey, String certificate, String caCertificatePath, String caCertificate, ClientAuth.Enum clientAuthentication, boolean enableTokenSupport) {
+ public CloudSslProvider(String servername, String privateKey, String certificate, String caCertificatePath,
+ String caCertificate, ClientAuth.Enum clientAuthentication, boolean enableTokenSupport) {
super(COMPONENT_ID_PREFIX, servername, componentClass(enableTokenSupport), null);
this.privateKey = privateKey;
this.certificate = certificate;
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 5bf348e5bb5..2b13cd21e99 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
@@ -3,7 +3,6 @@ package com.yahoo.vespa.model.container.http.ssl;
import com.yahoo.config.model.api.EndpointCertificateSecrets;
import com.yahoo.jdisc.http.ConnectorConfig;
-import com.yahoo.jdisc.http.ConnectorConfig.Ssl.ClientAuth;
import com.yahoo.security.tls.TlsContext;
import com.yahoo.vespa.model.container.http.ConnectorFactory;
@@ -18,96 +17,78 @@ import java.util.List;
*/
public class HostedSslConnectorFactory extends ConnectorFactory {
- private static final List<String> INSECURE_WHITELISTED_PATHS = List.of("/status.html");
- private static final String DEFAULT_HOSTED_TRUSTSTORE = "/opt/yahoo/share/ssl/certs/athenz_certificate_bundle.pem";
-
- private final boolean enforceClientAuth;
- private final boolean enforceHandshakeClientAuth;
- private final Collection<String> tlsCiphersOverride;
- private final boolean enableProxyProtocolMixedMode;
+ private final SslClientAuth clientAuth;
+ private final List<String> tlsCiphersOverride;
+ private final boolean proxyProtocolEnabled;
+ private final boolean proxyProtocolMixedMode;
private final Duration endpointConnectionTtl;
- /**
- * Create connector factory that uses a certificate provided by the config-model / configserver and default hosted Vespa truststore.
- */
- public static HostedSslConnectorFactory withProvidedCertificate(
- String serverName, EndpointCertificateSecrets endpointCertificateSecrets, boolean enforceHandshakeClientAuth,
- Collection<String> tlsCiphersOverride, boolean enableProxyProtocolMixedMode, int port,
- Duration endpointConnectionTtl, boolean enableTokenSupport) {
- CloudSslProvider sslProvider = createConfiguredDirectSslProvider(
- serverName, endpointCertificateSecrets, DEFAULT_HOSTED_TRUSTSTORE, /*tlsCaCertificates*/null, enforceHandshakeClientAuth, enableTokenSupport);
- return new HostedSslConnectorFactory(sslProvider, false, enforceHandshakeClientAuth, tlsCiphersOverride,
- enableProxyProtocolMixedMode, port, endpointConnectionTtl);
- }
-
- /**
- * Create connector factory that uses a certificate provided by the config-model / configserver and a truststore configured by the application.
- */
- public static HostedSslConnectorFactory withProvidedCertificateAndTruststore(
- String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificates,
- Collection<String> tlsCiphersOverride, boolean enableProxyProtocolMixedMode, int port,
- Duration endpointConnectionTtl, boolean enableTokenSupport) {
- CloudSslProvider sslProvider = createConfiguredDirectSslProvider(
- serverName, endpointCertificateSecrets, /*tlsCaCertificatesPath*/null, tlsCaCertificates, false, enableTokenSupport);
- return new HostedSslConnectorFactory(sslProvider, true, false, tlsCiphersOverride, enableProxyProtocolMixedMode,
- port, endpointConnectionTtl);
- }
-
- /**
- * 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, int port,
- Duration endpointConnectionTtl) {
- return new HostedSslConnectorFactory(new DefaultSslProvider(serverName), true, false, tlsCiphersOverride,
- enableProxyProtocolMixedMode, port, endpointConnectionTtl);
- }
+ public static Builder builder(String name, int listenPort) { return new Builder(name, listenPort); }
- private HostedSslConnectorFactory(SslProvider sslProvider, boolean enforceClientAuth,
- boolean enforceHandshakeClientAuth, Collection<String> tlsCiphersOverride,
- boolean enableProxyProtocolMixedMode, int port, Duration endpointConnectionTtl) {
- super(new Builder("tls"+port, port).sslProvider(sslProvider));
- this.enforceClientAuth = enforceClientAuth;
- this.enforceHandshakeClientAuth = enforceHandshakeClientAuth;
- this.tlsCiphersOverride = tlsCiphersOverride;
- this.enableProxyProtocolMixedMode = enableProxyProtocolMixedMode;
- this.endpointConnectionTtl = endpointConnectionTtl;
+ private HostedSslConnectorFactory(Builder builder) {
+ super(new ConnectorFactory.Builder("tls"+builder.port, builder.port).sslProvider(createSslProvider(builder)));
+ this.clientAuth = builder.clientAuth;
+ this.tlsCiphersOverride = List.copyOf(builder.tlsCiphersOverride);
+ this.proxyProtocolEnabled = builder.proxyProtocolEnabled;
+ this.proxyProtocolMixedMode = builder.proxyProtocolMixedMode;
+ this.endpointConnectionTtl = builder.endpointConnectionTtl;
}
- private static CloudSslProvider createConfiguredDirectSslProvider(
- String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificatesPath, String tlsCaCertificates, boolean enforceHandshakeClientAuth, boolean enableTokenSupport) {
- var clientAuthentication = enforceHandshakeClientAuth ? ClientAuth.Enum.NEED_AUTH : ClientAuth.Enum.WANT_AUTH;
+ private static SslProvider createSslProvider(Builder builder) {
+ if (builder.endpointCertificate == null) return new DefaultSslProvider(builder.name);
+ var sslClientAuth = builder.clientAuth == SslClientAuth.NEED
+ ? ConnectorConfig.Ssl.ClientAuth.Enum.NEED_AUTH : ConnectorConfig.Ssl.ClientAuth.Enum.WANT_AUTH;
return new CloudSslProvider(
- serverName,
- endpointCertificateSecrets.key(),
- endpointCertificateSecrets.certificate(),
- tlsCaCertificatesPath,
- tlsCaCertificates,
- clientAuthentication,
- enableTokenSupport);
+ builder.name, builder.endpointCertificate.key(), builder.endpointCertificate.certificate(),
+ builder.tlsCaCertificatesPath, builder.tlsCaCertificatesPem, sslClientAuth, builder.tokenEndpoint);
}
@Override
public void getConfig(ConnectorConfig.Builder connectorBuilder) {
super.getConfig(connectorBuilder);
- if (! enforceHandshakeClientAuth) {
- connectorBuilder
- .tlsClientAuthEnforcer(new ConnectorConfig.TlsClientAuthEnforcer.Builder()
- .pathWhitelist(INSECURE_WHITELISTED_PATHS)
- .enable(enforceClientAuth));
+ if (clientAuth == SslClientAuth.WANT_WITH_ENFORCER) {
+ connectorBuilder.tlsClientAuthEnforcer(
+ new ConnectorConfig.TlsClientAuthEnforcer.Builder()
+ .pathWhitelist(List.of("/status.html")).enable(true));
}
// Disables TLSv1.3 as it causes some browsers to prompt user for client certificate (when connector has 'want' auth)
connectorBuilder.ssl.enabledProtocols(List.of("TLSv1.2"));
-
if (!tlsCiphersOverride.isEmpty()) {
connectorBuilder.ssl.enabledCipherSuites(tlsCiphersOverride.stream().sorted().toList());
} else {
connectorBuilder.ssl.enabledCipherSuites(TlsContext.ALLOWED_CIPHER_SUITES.stream().sorted().toList());
}
-
connectorBuilder
- .proxyProtocol(new ConnectorConfig.ProxyProtocol.Builder().enabled(true).mixedMode(enableProxyProtocolMixedMode))
+ .proxyProtocol(new ConnectorConfig.ProxyProtocol.Builder()
+ .enabled(proxyProtocolEnabled).mixedMode(proxyProtocolMixedMode))
.idleTimeout(Duration.ofSeconds(30).toSeconds())
.maxConnectionLife(endpointConnectionTtl != null ? endpointConnectionTtl.toSeconds() : 0);
}
+
+ public enum SslClientAuth { WANT, NEED, WANT_WITH_ENFORCER }
+ public static class Builder {
+ final String name;
+ final int port;
+ SslClientAuth clientAuth;
+ List<String> tlsCiphersOverride;
+ boolean proxyProtocolEnabled;
+ boolean proxyProtocolMixedMode;
+ Duration endpointConnectionTtl;
+ EndpointCertificateSecrets endpointCertificate;
+ String tlsCaCertificatesPem;
+ String tlsCaCertificatesPath;
+ boolean tokenEndpoint;
+
+ private Builder(String name, int port) { this.name = name; this.port = port; }
+ public Builder clientAuth(SslClientAuth auth) { clientAuth = auth; return this; }
+ public Builder endpointConnectionTtl(Duration ttl) { endpointConnectionTtl = ttl; return this; }
+ public Builder tlsCiphersOverride(Collection<String> ciphers) { tlsCiphersOverride = List.copyOf(ciphers); return this; }
+ public Builder proxyProtocol(boolean enabled, boolean mixedMode) { proxyProtocolEnabled = enabled; proxyProtocolMixedMode = mixedMode; return this; }
+ public Builder endpointCertificate(EndpointCertificateSecrets cert) { this.endpointCertificate = cert; return this; }
+ public Builder tlsCaCertificatesPath(String path) { this.tlsCaCertificatesPath = path; return this; }
+ public Builder tlsCaCertificatesPem(String pem) { this.tlsCaCertificatesPem = pem; return this; }
+ public Builder tokenEndpoint(boolean enable) { this.tokenEndpoint = enable; return this; }
+
+ public HostedSslConnectorFactory build() { return new HostedSslConnectorFactory(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 00feb0a1c76..c97ea6671e8 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
@@ -16,7 +16,6 @@ import com.yahoo.config.model.ConfigModelContext;
import com.yahoo.config.model.api.ApplicationClusterEndpoint;
import com.yahoo.config.model.api.ConfigServerSpec;
import com.yahoo.config.model.api.ContainerEndpoint;
-import com.yahoo.config.model.api.EndpointCertificateSecrets;
import com.yahoo.config.model.api.TenantSecretStore;
import com.yahoo.config.model.application.provider.IncludeDirs;
import com.yahoo.config.model.builder.xml.ConfigModelBuilder;
@@ -95,6 +94,7 @@ import com.yahoo.vespa.model.container.http.Http;
import com.yahoo.vespa.model.container.http.HttpFilterChain;
import com.yahoo.vespa.model.container.http.JettyHttpServer;
import com.yahoo.vespa.model.container.http.ssl.HostedSslConnectorFactory;
+import com.yahoo.vespa.model.container.http.ssl.HostedSslConnectorFactory.SslClientAuth;
import com.yahoo.vespa.model.container.http.xml.HttpBuilder;
import com.yahoo.vespa.model.container.processing.ProcessingChains;
import com.yahoo.vespa.model.container.search.ContainerSearch;
@@ -109,7 +109,6 @@ import java.io.IOException;
import java.io.Reader;
import java.net.URI;
import java.security.cert.X509Certificate;
-import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -600,31 +599,36 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
.ifPresent(accessControl -> accessControl.configureDefaultHostedConnector(cluster.getHttp())); ;
}
- private void addAdditionalHostedConnector(DeployState deployState, ApplicationContainerCluster cluster) {
+ private void addAdditionalHostedConnector(DeployState state, ApplicationContainerCluster cluster) {
JettyHttpServer server = cluster.getHttp().getHttpServer().get();
String serverName = server.getComponentId().getName();
// If the deployment contains certificate/private key reference, setup TLS port
- HostedSslConnectorFactory connectorFactory;
- Collection<String> tlsCiphersOverride = deployState.getProperties().tlsCiphersOverride();
- boolean proxyProtocolMixedMode = deployState.getProperties().featureFlags().enableProxyProtocolMixedMode();
- Duration endpointConnectionTtl = deployState.getProperties().endpointConnectionTtl();
- var port = getDataplanePort(deployState);
- if (deployState.endpointCertificateSecrets().isPresent()) {
- boolean authorizeClient = deployState.zone().system().isPublic();
+ var builder = HostedSslConnectorFactory.builder(serverName, getDataplanePort(state))
+ .proxyProtocol(true, state.getProperties().featureFlags().enableProxyProtocolMixedMode())
+ .tlsCiphersOverride(state.getProperties().tlsCiphersOverride())
+ .endpointConnectionTtl(state.getProperties().endpointConnectionTtl());
+ var endpointCert = state.endpointCertificateSecrets().orElse(null);
+ if (endpointCert != null) {
+ builder.endpointCertificate(endpointCert);
+ boolean isPublic = state.zone().system().isPublic();
List<X509Certificate> clientCertificates = getClientCertificates(cluster);
- if (authorizeClient && clientCertificates.isEmpty()) {
- throw new IllegalArgumentException("Client certificate authority security/clients.pem is missing - " +
- "see: https://cloud.vespa.ai/en/security/guide#data-plane");
+ if (isPublic) {
+ if (clientCertificates.isEmpty())
+ throw new IllegalArgumentException("Client certificate authority security/clients.pem is missing - " +
+ "see: https://cloud.vespa.ai/en/security/guide#data-plane");
+ builder.tlsCaCertificatesPem(X509CertificateUtils.toPem(clientCertificates))
+ .clientAuth(SslClientAuth.WANT_WITH_ENFORCER);
+ } else {
+ builder.tlsCaCertificatesPath("/opt/yahoo/share/ssl/certs/athenz_certificate_bundle.pem");
+ var needAuth = cluster.getHttp().getAccessControl()
+ .map(accessControl -> accessControl.clientAuthentication)
+ .map(clientAuth -> clientAuth == AccessControl.ClientAuthentication.need)
+ .orElse(false);
+ builder.clientAuth(needAuth ? SslClientAuth.NEED : SslClientAuth.WANT);
}
- EndpointCertificateSecrets endpointCertificateSecrets = deployState.endpointCertificateSecrets().get();
-
- boolean enforceHandshakeClientAuth = cluster.getHttp().getAccessControl()
- .map(accessControl -> accessControl.clientAuthentication)
- .map(clientAuth -> clientAuth == AccessControl.ClientAuthentication.need)
- .orElse(false);
- boolean enableTokenSupport = deployState.featureFlags().enableDataplaneProxy()
+ boolean enableTokenSupport = state.featureFlags().enableDataplaneProxy()
&& cluster.getClients().stream().anyMatch(c -> !c.tokens().isEmpty());
// Set up component to generate proxy cert if token support is enabled
@@ -633,24 +637,16 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
cluster.addSimpleComponent(DataplaneProxyService.class);
var dataplaneProxy = new DataplaneProxy(
- getDataplanePort(deployState),
- endpointCertificateSecrets.certificate(),
- endpointCertificateSecrets.key());
+ getDataplanePort(state),
+ endpointCert.certificate(),
+ endpointCert.key());
cluster.addComponent(dataplaneProxy);
+ builder.tokenEndpoint(true);
}
-
- connectorFactory = authorizeClient
- ? HostedSslConnectorFactory.withProvidedCertificateAndTruststore(
- serverName, endpointCertificateSecrets, X509CertificateUtils.toPem(clientCertificates),
- tlsCiphersOverride, proxyProtocolMixedMode, port, endpointConnectionTtl, enableTokenSupport)
- : HostedSslConnectorFactory.withProvidedCertificate(
- serverName, endpointCertificateSecrets, enforceHandshakeClientAuth, tlsCiphersOverride,
- proxyProtocolMixedMode, port, endpointConnectionTtl, enableTokenSupport);
} else {
- connectorFactory = HostedSslConnectorFactory.withDefaultCertificateAndTruststore(
- serverName, tlsCiphersOverride, proxyProtocolMixedMode, port,
- endpointConnectionTtl);
+ builder.clientAuth(SslClientAuth.WANT_WITH_ENFORCER);
}
+ var connectorFactory = builder.build();
cluster.getHttp().getAccessControl().ifPresent(accessControl -> accessControl.configureHostedConnector(connectorFactory));
server.addConnector(connectorFactory);
}