summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorten Tokle <mortent@yahooinc.com>2023-06-07 11:29:07 +0200
committerMorten Tokle <mortent@yahooinc.com>2023-06-07 11:29:07 +0200
commite8e31b8f7ff1a77a47a8e9cf1bb884123ca2469a (patch)
tree55ae69cceca99e955f455348dd18c46f97d8b4fb
parent17680e5bd51252b282e011e4f9929653f78be016 (diff)
Generate proxy certificate and inject in trust store
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/CloudSslProvider.java (renamed from config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/ConfiguredDirectSslProvider.java)14
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/HostedSslConnectorFactory.java21
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java12
-rw-r--r--configdefinitions/src/vespa/dataplane-proxy.def9
-rw-r--r--container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/DataplaneProxyCredentials.java70
-rw-r--r--container-core/src/main/java/com/yahoo/jdisc/http/ssl/impl/CloudSslContextProvider.java42
-rw-r--r--container-core/src/main/java/com/yahoo/jdisc/http/ssl/impl/ConfiguredSslContextFactoryProvider.java4
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/DataplaneProxyConfigurator.java6
-rw-r--r--container-disc/src/main/java/com/yahoo/container/jdisc/DataplaneProxyService.java11
9 files changed, 159 insertions, 30 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/ConfiguredDirectSslProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/CloudSslProvider.java
index adc1458ce85..5fa893e9599 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/ConfiguredDirectSslProvider.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/CloudSslProvider.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.model.container.http.ssl;
import com.yahoo.jdisc.http.ConnectorConfig;
+import com.yahoo.jdisc.http.ssl.impl.CloudSslContextProvider;
import com.yahoo.jdisc.http.ssl.impl.ConfiguredSslContextFactoryProvider;
import java.util.Optional;
@@ -14,9 +15,10 @@ import static com.yahoo.jdisc.http.ConnectorConfig.Ssl.ClientAuth;
* @author mortent
* @author andreer
*/
-public class ConfiguredDirectSslProvider extends SslProvider {
+public class CloudSslProvider extends SslProvider {
public static final String COMPONENT_ID_PREFIX = "configured-ssl-provider@";
- public static final String COMPONENT_CLASS = ConfiguredSslContextFactoryProvider.class.getName();
+ public static final String MTLSONLY_COMPONENT_CLASS = ConfiguredSslContextFactoryProvider.class.getName();
+ public static final String TOKEN_COMPONENT_CLASS = CloudSslContextProvider.class.getName();
private final String privateKey;
private final String certificate;
@@ -24,8 +26,8 @@ public class ConfiguredDirectSslProvider extends SslProvider {
private final String caCertificate;
private final ClientAuth.Enum clientAuthentication;
- public ConfiguredDirectSslProvider(String servername, String privateKey, String certificate, String caCertificatePath, String caCertificate, ClientAuth.Enum clientAuthentication) {
- super(COMPONENT_ID_PREFIX, servername, COMPONENT_CLASS, null);
+ 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;
this.caCertificatePath = caCertificatePath;
@@ -33,6 +35,10 @@ public class ConfiguredDirectSslProvider extends SslProvider {
this.clientAuthentication = clientAuthentication;
}
+ private static String componentClass(boolean enableTokenSupport) {
+ return enableTokenSupport ? TOKEN_COMPONENT_CLASS : MTLSONLY_COMPONENT_CLASS;
+ }
+
@Override
public void amendConnectorConfig(ConnectorConfig.Builder builder) {
builder.ssl.enabled(true);
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 72d2927f910..5bf348e5bb5 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
@@ -33,9 +33,9 @@ public class HostedSslConnectorFactory extends ConnectorFactory {
public static HostedSslConnectorFactory withProvidedCertificate(
String serverName, EndpointCertificateSecrets endpointCertificateSecrets, boolean enforceHandshakeClientAuth,
Collection<String> tlsCiphersOverride, boolean enableProxyProtocolMixedMode, int port,
- Duration endpointConnectionTtl) {
- ConfiguredDirectSslProvider sslProvider = createConfiguredDirectSslProvider(
- serverName, endpointCertificateSecrets, DEFAULT_HOSTED_TRUSTSTORE, /*tlsCaCertificates*/null, enforceHandshakeClientAuth);
+ 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);
}
@@ -46,9 +46,9 @@ public class HostedSslConnectorFactory extends ConnectorFactory {
public static HostedSslConnectorFactory withProvidedCertificateAndTruststore(
String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificates,
Collection<String> tlsCiphersOverride, boolean enableProxyProtocolMixedMode, int port,
- Duration endpointConnectionTtl) {
- ConfiguredDirectSslProvider sslProvider = createConfiguredDirectSslProvider(
- serverName, endpointCertificateSecrets, /*tlsCaCertificatesPath*/null, tlsCaCertificates, false);
+ Duration endpointConnectionTtl, boolean enableTokenSupport) {
+ CloudSslProvider sslProvider = createConfiguredDirectSslProvider(
+ serverName, endpointCertificateSecrets, /*tlsCaCertificatesPath*/null, tlsCaCertificates, false, enableTokenSupport);
return new HostedSslConnectorFactory(sslProvider, true, false, tlsCiphersOverride, enableProxyProtocolMixedMode,
port, endpointConnectionTtl);
}
@@ -74,16 +74,17 @@ public class HostedSslConnectorFactory extends ConnectorFactory {
this.endpointConnectionTtl = endpointConnectionTtl;
}
- private static ConfiguredDirectSslProvider createConfiguredDirectSslProvider(
- String serverName, EndpointCertificateSecrets endpointCertificateSecrets, String tlsCaCertificatesPath, String tlsCaCertificates, boolean enforceHandshakeClientAuth) {
+ 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;
- return new ConfiguredDirectSslProvider(
+ return new CloudSslProvider(
serverName,
endpointCertificateSecrets.key(),
endpointCertificateSecrets.certificate(),
tlsCaCertificatesPath,
tlsCaCertificates,
- clientAuthentication);
+ clientAuthentication,
+ enableTokenSupport);
}
@Override
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 c8bd661a00b..3305e596a87 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,7 @@ import com.yahoo.container.bundle.BundleInstantiationSpecification;
import com.yahoo.container.logging.AccessLog;
import com.yahoo.container.logging.FileConnectionLog;
import com.yahoo.io.IOUtils;
+import com.yahoo.jdisc.http.server.jetty.DataplaneProxyCredentials;
import com.yahoo.jdisc.http.server.jetty.VoidRequestLog;
import com.yahoo.osgi.provider.model.ComponentModel;
import com.yahoo.path.Path;
@@ -606,13 +607,20 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
.map(clientAuth -> clientAuth == AccessControl.ClientAuthentication.need)
.orElse(false);
+ // TODO (mortent): Implement token support in model
+ boolean enableTokenSupport = false;
+
+ // Set up component to generate proxy cert if token support is enabled
+ if (enableTokenSupport) {
+ cluster.addSimpleComponent(DataplaneProxyCredentials.class);
+ }
connectorFactory = authorizeClient
? HostedSslConnectorFactory.withProvidedCertificateAndTruststore(
serverName, endpointCertificateSecrets, X509CertificateUtils.toPem(clientCertificates),
- tlsCiphersOverride, proxyProtocolMixedMode, port, endpointConnectionTtl)
+ tlsCiphersOverride, proxyProtocolMixedMode, port, endpointConnectionTtl, enableTokenSupport)
: HostedSslConnectorFactory.withProvidedCertificate(
serverName, endpointCertificateSecrets, enforceHandshakeClientAuth, tlsCiphersOverride,
- proxyProtocolMixedMode, port, endpointConnectionTtl);
+ proxyProtocolMixedMode, port, endpointConnectionTtl, enableTokenSupport);
} else {
connectorFactory = HostedSslConnectorFactory.withDefaultCertificateAndTruststore(
serverName, tlsCiphersOverride, proxyProtocolMixedMode, port,
diff --git a/configdefinitions/src/vespa/dataplane-proxy.def b/configdefinitions/src/vespa/dataplane-proxy.def
index dab54a8b978..27f88583081 100644
--- a/configdefinitions/src/vespa/dataplane-proxy.def
+++ b/configdefinitions/src/vespa/dataplane-proxy.def
@@ -1,10 +1,15 @@
# Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
namespace=cloud.config
+# The port Jdisc will be listening on
port int
-clientCertificate string
-clientKey string
+
+# Server certificate and key to be used when creating server socket
serverCertificate string
serverKey string
+
+# The mTLS endpoint SNI header to route to Jdisc using L4
mTlsEndpoint string
+
+# The endpoint SNI header supporting token authentication
tokenEndpoint string
diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/DataplaneProxyCredentials.java b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/DataplaneProxyCredentials.java
new file mode 100644
index 00000000000..46c840ad607
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/DataplaneProxyCredentials.java
@@ -0,0 +1,70 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.jdisc.http.server.jetty;
+
+import com.yahoo.component.AbstractComponent;
+import com.yahoo.security.KeyUtils;
+import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.security.X509CertificateWithKey;
+import com.yahoo.vespa.defaults.Defaults;
+import com.yahoo.yolean.Exceptions;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.PrivateKey;
+import java.security.cert.X509Certificate;
+import java.time.Duration;
+
+/**
+ * Generates temporary credentials to be used by a proxy for accessing Jdisc.
+ * Credentials are written to vespa_home/tmp/.
+ *
+ * @author mortent
+ */
+public class DataplaneProxyCredentials extends AbstractComponent {
+
+ private final Path certificateFile;
+ private final Path keyFile;
+
+ public DataplaneProxyCredentials() {
+ certificateFile = Paths.get(Defaults.getDefaults().underVespaHome("tmp/proxy_cert.pem"));
+ keyFile = Paths.get(Defaults.getDefaults().underVespaHome("tmp/proxy_key.pem"));
+ if (regenerateCredentials(certificateFile, keyFile)) {
+ X509CertificateWithKey selfSigned = X509CertificateUtils.createSelfSigned("cn=vespa dataplane proxy", Duration.ofDays(30));
+ Exceptions.uncheck(() -> Files.writeString(certificateFile, X509CertificateUtils.toPem(selfSigned.certificate())));
+ Exceptions.uncheck(() -> Files.writeString(keyFile, KeyUtils.toPem(selfSigned.privateKey())));
+ }
+ }
+
+ /*
+ * Returns true if credentials should be regenerated.
+ */
+ private boolean regenerateCredentials(Path certificateFile, Path keyFile) {
+ if (!Files.exists(certificateFile) || !Files.exists(keyFile)) {
+ return true;
+ }
+ try {
+ X509Certificate x509Certificate = X509CertificateUtils.fromPem(Files.readString(certificateFile));
+ PrivateKey privateKey = KeyUtils.fromPemEncodedPrivateKey(Files.readString(keyFile));
+ return !X509CertificateUtils.privateKeyMatchesPublicKey(privateKey, x509Certificate.getPublicKey());
+ } catch (IOException e) {
+ // Some exception occured, assume credentials corrupted and requires a new pair.
+ return true;
+ }
+ }
+
+ public Path certificateFile() {
+ return certificateFile;
+ }
+
+ public Path keyFile() {
+ return keyFile;
+ }
+
+ @Override
+ public void deconstruct() {
+ super.deconstruct();
+ }
+
+}
diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/ssl/impl/CloudSslContextProvider.java b/container-core/src/main/java/com/yahoo/jdisc/http/ssl/impl/CloudSslContextProvider.java
new file mode 100644
index 00000000000..cdfd4aa938e
--- /dev/null
+++ b/container-core/src/main/java/com/yahoo/jdisc/http/ssl/impl/CloudSslContextProvider.java
@@ -0,0 +1,42 @@
+// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.jdisc.http.ssl.impl;
+
+import com.yahoo.jdisc.http.ConnectorConfig;
+import com.yahoo.jdisc.http.server.jetty.DataplaneProxyCredentials;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.util.Optional;
+
+/**
+ * Used to enable token based endpoints in Cloud. Amends trust store to allow proxy.
+ *
+ * @author mortent
+ */
+public class CloudSslContextProvider extends ConfiguredSslContextFactoryProvider {
+
+ private final DataplaneProxyCredentials dataplaneProxyCredentials;
+
+ public CloudSslContextProvider(ConnectorConfig connectorConfig, DataplaneProxyCredentials dataplaneProxyCredentials) {
+ super(connectorConfig);
+ this.dataplaneProxyCredentials = dataplaneProxyCredentials;
+ }
+
+ @Override
+ Optional<String> getCaCertificates(ConnectorConfig.Ssl sslConfig) {
+ String proxyCert;
+ try {
+ proxyCert = Files.readString(dataplaneProxyCredentials.certificateFile(), StandardCharsets.UTF_8);
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Dataplane proxy certificate not available", e);
+ }
+ if (!sslConfig.caCertificate().isBlank()) {
+ return Optional.of(sslConfig.caCertificate() + "\n" + proxyCert);
+ } else if (!sslConfig.caCertificateFile().isBlank()) {
+ return Optional.of(readToString(sslConfig.caCertificateFile()) + "\n" + proxyCert);
+ } else {
+ return Optional.of(proxyCert);
+ }
+ }
+}
diff --git a/container-core/src/main/java/com/yahoo/jdisc/http/ssl/impl/ConfiguredSslContextFactoryProvider.java b/container-core/src/main/java/com/yahoo/jdisc/http/ssl/impl/ConfiguredSslContextFactoryProvider.java
index 27c5aff22a9..b99bc007b32 100644
--- a/container-core/src/main/java/com/yahoo/jdisc/http/ssl/impl/ConfiguredSslContextFactoryProvider.java
+++ b/container-core/src/main/java/com/yahoo/jdisc/http/ssl/impl/ConfiguredSslContextFactoryProvider.java
@@ -110,7 +110,7 @@ public class ConfiguredSslContextFactoryProvider implements SslProvider {
private static boolean hasBoth(String a, String b) { return !a.isBlank() && !b.isBlank(); }
private static boolean hasNeither(String a, String b) { return a.isBlank() && b.isBlank(); }
- private static Optional<String> getCaCertificates(ConnectorConfig.Ssl sslConfig) {
+ Optional<String> getCaCertificates(ConnectorConfig.Ssl sslConfig) {
if (!sslConfig.caCertificate().isBlank()) {
return Optional.of(sslConfig.caCertificate());
} else if (!sslConfig.caCertificateFile().isBlank()) {
@@ -130,7 +130,7 @@ public class ConfiguredSslContextFactoryProvider implements SslProvider {
return readToString(config.certificateFile());
}
- private static String readToString(String filename) {
+ static String readToString(String filename) {
try {
return Files.readString(Paths.get(filename), StandardCharsets.UTF_8);
} catch (IOException e) {
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/DataplaneProxyConfigurator.java b/container-disc/src/main/java/com/yahoo/container/jdisc/DataplaneProxyConfigurator.java
index 4c637b5798d..340c0035052 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/DataplaneProxyConfigurator.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/DataplaneProxyConfigurator.java
@@ -3,7 +3,7 @@ package com.yahoo.container.jdisc;
import com.yahoo.cloud.config.DataplaneProxyConfig;
import com.yahoo.component.AbstractComponent;
-import com.yahoo.jdisc.http.ConnectorConfig;
+import com.yahoo.jdisc.http.server.jetty.DataplaneProxyCredentials;
/**
* Reconfigurable component for supporting data plane proxy. Configures the {@code DataplaneProxyService} by calling {@code DataplaneProxyService#init}
@@ -12,8 +12,8 @@ import com.yahoo.jdisc.http.ConnectorConfig;
*/
public class DataplaneProxyConfigurator extends AbstractComponent {
- public DataplaneProxyConfigurator(DataplaneProxyConfig config, DataplaneProxyService dataplaneProxyService) {
- dataplaneProxyService.reconfigure(config);
+ public DataplaneProxyConfigurator(DataplaneProxyConfig config, DataplaneProxyService dataplaneProxyService, DataplaneProxyCredentials credentialsProvider) {
+ dataplaneProxyService.reconfigure(config, credentialsProvider);
}
@Override
diff --git a/container-disc/src/main/java/com/yahoo/container/jdisc/DataplaneProxyService.java b/container-disc/src/main/java/com/yahoo/container/jdisc/DataplaneProxyService.java
index 5b0cdfaf630..230d017c584 100644
--- a/container-disc/src/main/java/com/yahoo/container/jdisc/DataplaneProxyService.java
+++ b/container-disc/src/main/java/com/yahoo/container/jdisc/DataplaneProxyService.java
@@ -3,6 +3,7 @@ package com.yahoo.container.jdisc;
import com.yahoo.cloud.config.DataplaneProxyConfig;
import com.yahoo.component.AbstractComponent;
+import com.yahoo.jdisc.http.server.jetty.DataplaneProxyCredentials;
import javax.inject.Inject;
import java.io.IOException;
@@ -36,22 +37,18 @@ public class DataplaneProxyService extends AbstractComponent {
this.started = false;
}
- public void reconfigure(DataplaneProxyConfig config) {
+ public void reconfigure(DataplaneProxyConfig config, DataplaneProxyCredentials credentialsProvider) {
try {
String serverCert = config.serverCertificate();
String serverKey = config.serverKey();
- String clientCert = config.clientCertificate();
- String clientKey = config.clientKey();
boolean configChanged = false;
- configChanged |= writeFile(clientCertificateFile, clientCert);
- configChanged |= writeFile(clientKeyFile, clientKey);
configChanged |= writeFile(serverCertificateFile, serverCert);
configChanged |= writeFile(serverKeyFile, serverKey);
configChanged |= writeFile(nginxConf,
nginxConfig(
- clientCertificateFile,
- clientKeyFile,
+ credentialsProvider.certificateFile(),
+ credentialsProvider.keyFile(),
serverCertificateFile,
serverKeyFile,
URI.create(config.mTlsEndpoint()),