From e8e31b8f7ff1a77a47a8e9cf1bb884123ca2469a Mon Sep 17 00:00:00 2001 From: Morten Tokle Date: Wed, 7 Jun 2023 11:29:07 +0200 Subject: Generate proxy certificate and inject in trust store --- .../server/jetty/DataplaneProxyCredentials.java | 70 ++++++++++++++++++++++ .../http/ssl/impl/CloudSslContextProvider.java | 42 +++++++++++++ .../impl/ConfiguredSslContextFactoryProvider.java | 4 +- 3 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 container-core/src/main/java/com/yahoo/jdisc/http/server/jetty/DataplaneProxyCredentials.java create mode 100644 container-core/src/main/java/com/yahoo/jdisc/http/ssl/impl/CloudSslContextProvider.java (limited to 'container-core') 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 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 getCaCertificates(ConnectorConfig.Ssl sslConfig) { + Optional 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) { -- cgit v1.2.3