diff options
5 files changed, 42 insertions, 16 deletions
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java index 81ee0a4c4c3..6f25cecedb1 100644 --- a/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java +++ b/config-model-api/src/main/java/com/yahoo/config/model/api/ModelContext.java @@ -93,7 +93,6 @@ public interface ModelContext { @ModelFeatureFlag(owners = {"tokle"}) default boolean tenantIamRole() { return false; } @ModelFeatureFlag(owners = {"vekterli"}) default int maxActivationInhibitedOutOfSyncGroups() { return 0; } @ModelFeatureFlag(owners = {"hmusum"}) default String jvmOmitStackTraceInFastThrowOption(ClusterSpec.Type type) { return ""; } - @ModelFeatureFlag(owners = {"bjorncs", "jonmv"}, removeAfter = "7.409") default boolean enableJdiscHttp2() { return true; } @ModelFeatureFlag(owners = {"tokle", "bjorncs"}) default boolean enableCustomAclMapping() { return false; } @ModelFeatureFlag(owners = {"geirst", "vekterli"}) default int numDistributorStripes() { return 0; } @ModelFeatureFlag(owners = {"arnej"}) default boolean requireConnectivityCheck() { return false; } @@ -141,6 +140,8 @@ public interface ModelContext { default boolean allowDisableMtls() { return true; } default List<X509Certificate> operatorCertificates() { return List.of(); } + + default List<String> tlsCiphersOverride() { return List.of(); } } @Retention(RetentionPolicy.RUNTIME) 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 766aa46fc01..89f200698fa 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 @@ -8,6 +8,7 @@ import com.yahoo.security.tls.TlsContext; import com.yahoo.vespa.model.container.http.ConnectorFactory; import java.time.Duration; +import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -24,39 +25,43 @@ public class HostedSslConnectorFactory extends ConnectorFactory { private final boolean enforceClientAuth; private final boolean enforceHandshakeClientAuth; + private final Collection<String> tlsCiphersOverride; /** * 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) { + String serverName, EndpointCertificateSecrets endpointCertificateSecrets, boolean enforceHandshakeClientAuth, + Collection<String> tlsCiphersOverride) { ConfiguredDirectSslProvider sslProvider = createConfiguredDirectSslProvider( serverName, endpointCertificateSecrets, DEFAULT_HOSTED_TRUSTSTORE, /*tlsCaCertificates*/null, enforceHandshakeClientAuth); - return new HostedSslConnectorFactory(sslProvider, false, enforceHandshakeClientAuth); + return new HostedSslConnectorFactory(sslProvider, false, enforceHandshakeClientAuth, tlsCiphersOverride); } /** * 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) { + String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificates, + Collection<String> tlsCiphersOverride) { ConfiguredDirectSslProvider sslProvider = createConfiguredDirectSslProvider( serverName, endpointCertificateSecrets, /*tlsCaCertificatesPath*/null, tlsCaCertificates, false); - return new HostedSslConnectorFactory(sslProvider, true, false); + return new HostedSslConnectorFactory(sslProvider, true, false, tlsCiphersOverride); } /** * Create connector factory that uses the default certificate and truststore provided by Vespa (through Vespa-global TLS configuration). */ - public static HostedSslConnectorFactory withDefaultCertificateAndTruststore(String serverName) { - return new HostedSslConnectorFactory(new DefaultSslProvider(serverName), true, false); + public static HostedSslConnectorFactory withDefaultCertificateAndTruststore(String serverName, Collection<String> tlsCiphersOverride) { + return new HostedSslConnectorFactory(new DefaultSslProvider(serverName), true, false, tlsCiphersOverride); } private HostedSslConnectorFactory(SslProvider sslProvider, boolean enforceClientAuth, - boolean enforceHandshakeClientAuth) { + boolean enforceHandshakeClientAuth, Collection<String> tlsCiphersOverride) { super(new Builder("tls4443", 4443).sslProvider(sslProvider)); this.enforceClientAuth = enforceClientAuth; this.enforceHandshakeClientAuth = enforceHandshakeClientAuth; + this.tlsCiphersOverride = tlsCiphersOverride; } private static ConfiguredDirectSslProvider createConfiguredDirectSslProvider( @@ -83,10 +88,15 @@ public class HostedSslConnectorFactory extends ConnectorFactory { // 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")); - // Add TLS_RSA_WITH_AES_256_GCM_SHA384 cipher to list of defalt allowed ciphers - Set<String> ciphers = new HashSet<>(TlsContext.ALLOWED_CIPHER_SUITES); - ciphers.add("TLS_RSA_WITH_AES_256_GCM_SHA384"); - connectorBuilder.ssl.enabledCipherSuites(Set.copyOf(ciphers)); + if (!tlsCiphersOverride.isEmpty()) { + connectorBuilder.ssl.enabledCipherSuites(tlsCiphersOverride); + } else { + // Add TLS_RSA_WITH_AES_256_GCM_SHA384 cipher to list of default allowed ciphers + // TODO Remove TLS_RSA_WITH_AES_256_GCM_SHA384 as it's weak and incompatible with HTTP/2 + Set<String> ciphers = new HashSet<>(TlsContext.ALLOWED_CIPHER_SUITES); + ciphers.add("TLS_RSA_WITH_AES_256_GCM_SHA384"); + connectorBuilder.ssl.enabledCipherSuites(Set.copyOf(ciphers)); + } connectorBuilder .proxyProtocol(new ConnectorConfig.ProxyProtocol.Builder().enabled(true).mixedMode(true)) 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 08ccfe33cd5..67350e634af 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 @@ -92,6 +92,7 @@ import org.w3c.dom.Node; import java.net.URI; import java.security.cert.X509Certificate; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; @@ -433,7 +434,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { // If the deployment contains certificate/private key reference, setup TLS port HostedSslConnectorFactory connectorFactory; - boolean enableHttp2 = deployState.featureFlags().enableJdiscHttp2(); + Collection<String> tlsCiphersOverride = deployState.getProperties().tlsCiphersOverride(); if (deployState.endpointCertificateSecrets().isPresent()) { boolean authorizeClient = deployState.zone().system().isPublic(); if (authorizeClient && deployState.tlsClientAuthority().isEmpty()) { @@ -447,10 +448,12 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { .orElse(false); connectorFactory = authorizeClient - ? HostedSslConnectorFactory.withProvidedCertificateAndTruststore(serverName, endpointCertificateSecrets, getTlsClientAuthorities(deployState)) - : HostedSslConnectorFactory.withProvidedCertificate(serverName, endpointCertificateSecrets, enforceHandshakeClientAuth); + ? HostedSslConnectorFactory.withProvidedCertificateAndTruststore( + serverName, endpointCertificateSecrets, getTlsClientAuthorities(deployState), tlsCiphersOverride) + : HostedSslConnectorFactory.withProvidedCertificate( + serverName, endpointCertificateSecrets, enforceHandshakeClientAuth, tlsCiphersOverride); } else { - connectorFactory = HostedSslConnectorFactory.withDefaultCertificateAndTruststore(serverName); + connectorFactory = HostedSslConnectorFactory.withDefaultCertificateAndTruststore(serverName, tlsCiphersOverride); } cluster.getHttp().getAccessControl().ifPresent(accessControl -> accessControl.configureHostedConnector(connectorFactory)); server.addConnector(connectorFactory); diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java index 94cfba12453..4fa221f3910 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java @@ -298,6 +298,7 @@ public class ModelContextImpl implements ModelContext { private final StringFlag jvmGCOptionsFlag; private final boolean allowDisableMtls; private final List<X509Certificate> operatorCertificates; + private final List<String> tlsCiphersOverride; public Properties(ApplicationId applicationId, ConfigserverConfig configserverConfig, @@ -336,6 +337,8 @@ public class ModelContextImpl implements ModelContext { this.allowDisableMtls = PermanentFlags.ALLOW_DISABLE_MTLS.bindTo(flagSource) .with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value(); this.operatorCertificates = operatorCertificates; + this.tlsCiphersOverride = PermanentFlags.TLS_CIPHERS_OVERRIDE.bindTo(flagSource) + .with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value(); } @Override public ModelContext.FeatureFlags featureFlags() { return featureFlags; } @@ -409,6 +412,8 @@ public class ModelContextImpl implements ModelContext { return operatorCertificates; } + @Override public List<String> tlsCiphersOverride() { return tlsCiphersOverride; } + public String flagValueForClusterType(StringFlag flag, Optional<ClusterSpec.Type> clusterType) { return clusterType.map(type -> flag.with(CLUSTER_TYPE, type.name())) .orElse(flag) diff --git a/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java b/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java index 1b7f0c034a3..511f45a91ab 100644 --- a/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java +++ b/flags/src/main/java/com/yahoo/vespa/flags/PermanentFlags.java @@ -170,6 +170,13 @@ public class PermanentFlags { TENANT_ID ); + public static final UnboundListFlag<String> TLS_CIPHERS_OVERRIDE = defineListFlag( + "tls-ciphers-override", List.of(), String.class, + "Override TLS ciphers enabled for port 4443 on hosted application containers", + "Takes effect on redeployment", + APPLICATION_ID + ); + private PermanentFlags() {} private static UnboundBooleanFlag defineFeatureFlag( |