diff options
author | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2020-05-15 13:20:13 +0200 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2020-05-18 13:31:05 +0200 |
commit | 848fa86f8836ba6b7d5840cc59b46dff9111010e (patch) | |
tree | 292fc162b0b8c7d7d2a8ef7ba546fe63bfc167f9 /vespa-athenz | |
parent | 480d205734db359a707c75768facf1aabea5acf4 (diff) |
Provide path to trust stores from ServiceIdentityProvider
Model distinction between the Athenz and client trust store.
Cleanup constructors and config for SiaIdentityProvider.
Diffstat (limited to 'vespa-athenz')
5 files changed, 78 insertions, 47 deletions
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/ServiceIdentityProvider.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/ServiceIdentityProvider.java index 180d052c8dc..f6888acb018 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/ServiceIdentityProvider.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/ServiceIdentityProvider.java @@ -15,9 +15,43 @@ import java.nio.file.Path; * @author bjorncs */ public interface ServiceIdentityProvider { + /** + * + * @return The Athenz identity of the environment + */ AthenzIdentity identity(); + + /** + * @return {@link SSLContext} that is automatically updated. + */ SSLContext getIdentitySslContext(); + + /** + * @return Current certificate and private key. Unlike {@link #getIdentitySslContext()} underlying credentials are not automatically updated. + */ X509CertificateWithKey getIdentityCertificateWithKey(); + + /** + * @return Path to X.509 certificate in PEM format + */ Path certificatePath(); + + /** + * @return Path to private key in PEM format + */ Path privateKeyPath(); + + /** + * @return Path to Athenz truststore in PEM format + */ + Path athenzTruststorePath(); + + /** + * The client truststore contains the Athenz certificates from {@link #athenzTruststorePath()} + * and additional certificate authorities that issues trusted server certificates. + * + * @return Path to client truststore in PEM format + */ + Path clientTruststorePath(); + } diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/SiaIdentityProvider.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/SiaIdentityProvider.java index 082925048cb..a2a773ac751 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/SiaIdentityProvider.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/SiaIdentityProvider.java @@ -3,7 +3,6 @@ package com.yahoo.vespa.athenz.identity; import com.google.inject.Inject; import com.yahoo.component.AbstractComponent; -import com.yahoo.security.KeyStoreType; import com.yahoo.security.SslContextBuilder; import com.yahoo.security.X509CertificateWithKey; import com.yahoo.security.tls.AutoReloadingX509KeyManager; @@ -28,6 +27,8 @@ public class SiaIdentityProvider extends AbstractComponent implements ServiceIde private final AthenzIdentity service; private final Path certificateFile; private final Path privateKeyFile; + private final Path clientTruststoreFile; + private final Path athenzTruststoreFile; @Inject public SiaIdentityProvider(SiaProviderConfig config) { @@ -35,29 +36,32 @@ public class SiaIdentityProvider extends AbstractComponent implements ServiceIde SiaUtils.getPrivateKeyFile(Paths.get(config.keyPathPrefix()), new AthenzService(config.athenzDomain(), config.athenzService())), SiaUtils.getCertificateFile(Paths.get(config.keyPathPrefix()), new AthenzService(config.athenzDomain(), config.athenzService())), Paths.get(config.trustStorePath()), - config.trustStoreType()); + Paths.get(config.athenzTruststorePath())); } public SiaIdentityProvider(AthenzIdentity service, Path siaPath, - Path trustStoreFile) { + Path clientTruststoreFile, + Path athenzTruststoreFile) { this(service, - SiaUtils.getPrivateKeyFile(siaPath, service), - SiaUtils.getCertificateFile(siaPath, service), - trustStoreFile, - SiaProviderConfig.TrustStoreType.Enum.jks); + SiaUtils.getPrivateKeyFile(siaPath, service), + SiaUtils.getCertificateFile(siaPath, service), + athenzTruststoreFile, + clientTruststoreFile); } public SiaIdentityProvider(AthenzIdentity service, Path privateKeyFile, Path certificateFile, - Path trustStoreFile, - SiaProviderConfig.TrustStoreType.Enum trustStoreType) { + Path athenzTruststoreFile, + Path clientTruststoreFile) { this.service = service; this.keyManager = AutoReloadingX509KeyManager.fromPemFiles(privateKeyFile, certificateFile); - this.sslContext = createIdentitySslContext(keyManager, trustStoreFile, trustStoreType); + this.sslContext = createIdentitySslContext(keyManager, clientTruststoreFile); this.certificateFile = certificateFile; this.privateKeyFile = privateKeyFile; + this.athenzTruststoreFile = athenzTruststoreFile; + this.clientTruststoreFile = clientTruststoreFile; } @Override @@ -73,18 +77,14 @@ public class SiaIdentityProvider extends AbstractComponent implements ServiceIde @Override public X509CertificateWithKey getIdentityCertificateWithKey() { return keyManager.getCurrentCertificateWithKey(); } @Override public Path certificatePath() { return certificateFile; } @Override public Path privateKeyPath() { return privateKeyFile; } + @Override public Path athenzTruststorePath() { return athenzTruststoreFile; } + @Override public Path clientTruststorePath() { return clientTruststoreFile; } - private static SSLContext createIdentitySslContext(AutoReloadingX509KeyManager keyManager, Path trustStoreFile, - SiaProviderConfig.TrustStoreType.Enum trustStoreType) { - var builder = new SslContextBuilder(); - if (trustStoreType == SiaProviderConfig.TrustStoreType.Enum.pem) { - builder = builder.withTrustStore(trustStoreFile); - } else if (trustStoreType == SiaProviderConfig.TrustStoreType.Enum.jks) { - builder = builder.withTrustStore(trustStoreFile, KeyStoreType.JKS); - } else { - throw new IllegalArgumentException("Unsupported trust store type: " + trustStoreType); - } - return builder.withKeyManager(keyManager).build(); + private static SSLContext createIdentitySslContext(AutoReloadingX509KeyManager keyManager, Path trustStoreFile) { + return new SslContextBuilder() + .withTrustStore(trustStoreFile) + .withKeyManager(keyManager) + .build(); } @Override diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java index cc6061cb172..4cbfd4f9698 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java @@ -11,7 +11,6 @@ import com.yahoo.container.jdisc.athenz.AthenzIdentityProvider; import com.yahoo.container.jdisc.athenz.AthenzIdentityProviderException; import com.yahoo.jdisc.Metric; import com.yahoo.security.KeyStoreBuilder; -import com.yahoo.security.KeyStoreType; import com.yahoo.security.Pkcs10Csr; import com.yahoo.security.SslContextBuilder; import com.yahoo.security.X509CertificateWithKey; @@ -47,7 +46,6 @@ import java.util.function.Function; import java.util.logging.Level; import java.util.logging.Logger; -import static com.yahoo.security.KeyStoreType.JKS; import static com.yahoo.security.KeyStoreType.PKCS12; /** @@ -69,8 +67,9 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen private final static Duration ROLE_SSL_CONTEXT_EXPIRY = Duration.ofHours(24); private final static Duration ROLE_TOKEN_EXPIRY = Duration.ofMinutes(30); - // TODO Make path to trust store config - private static final Path DEFAULT_TRUST_STORE = Paths.get("/opt/yahoo/share/ssl/certs/yahoo_certificate_bundle.jks"); + // TODO Make path to trust store paths config + private static final Path CLIENT_TRUST_STORE = Paths.get("/opt/yahoo/share/ssl/certs/yahoo_certificate_bundle.pem"); + private static final Path ATHENZ_TRUST_STORE = Paths.get("/opt/yahoo/share/ssl/certs/athenz_certificate_bundle.pem"); public static final String CERTIFICATE_EXPIRY_METRIC_NAME = "athenz-tenant-cert.expiry.seconds"; @@ -96,9 +95,9 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen public AthenzIdentityProviderImpl(IdentityConfig config, Metric metric) { this(config, metric, - DEFAULT_TRUST_STORE, + CLIENT_TRUST_STORE, new AthenzCredentialsService(config, - createNodeIdentityProvider(config, DEFAULT_TRUST_STORE), + createNodeIdentityProvider(config), Defaults.getDefaults().vespaHostname(), Clock.systemUTC()), new ScheduledThreadPoolExecutor(1), @@ -144,7 +143,7 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen private static SSLContext createIdentitySslContext(X509ExtendedKeyManager keyManager, Path trustStore) { return new SslContextBuilder() .withKeyManager(keyManager) - .withTrustStore(trustStore, JKS) + .withTrustStore(trustStore) .build(); } @@ -188,6 +187,10 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen @Override public Path privateKeyPath() { return athenzCredentialsService.privateKeyPath(); } + @Override public Path athenzTruststorePath() { return ATHENZ_TRUST_STORE; } + + @Override public Path clientTruststorePath() { return CLIENT_TRUST_STORE; } + @Override public SSLContext getRoleSslContext(String domain, String role) { // This ssl context should ideally be cached as it is quite expensive to create. @@ -256,7 +259,7 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen X509Certificate roleCertificate = client.getRoleCertificate(role, csr); return new SslContextBuilder() .withKeyStore(credentials.getKeyPair().getPrivate(), roleCertificate) - .withTrustStore(trustStore, KeyStoreType.JKS) + .withTrustStore(trustStore) .build(); } } @@ -299,9 +302,9 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen } } - private static SiaIdentityProvider createNodeIdentityProvider(IdentityConfig config, Path trustStore) { + private static SiaIdentityProvider createNodeIdentityProvider(IdentityConfig config) { return new SiaIdentityProvider( - new AthenzService(config.nodeIdentityName()), SiaUtils.DEFAULT_SIA_DIRECTORY, trustStore); + new AthenzService(config.nodeIdentityName()), SiaUtils.DEFAULT_SIA_DIRECTORY, CLIENT_TRUST_STORE, ATHENZ_TRUST_STORE); } private boolean isExpired(AthenzCredentials credentials) { diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java index 12a8c3f911e..894c04df233 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java @@ -53,10 +53,11 @@ public class SiaUtils { } public static Path getCaCertificatesFile() { - // The contents of this is the same as /opt/yahoo/share/ssl/certs/athenz_certificate_bundle.pem installed - // by the yahoo_certificates_bundle RPM package, except the latter also contains a textual description - // (decoded) of the certificates. - return DEFAULT_SIA_DIRECTORY.resolve("certs").resolve("ca.cert.pem"); + return getCaCertificatesFile(DEFAULT_SIA_DIRECTORY); + } + + public static Path getCaCertificatesFile(Path root) { + return root.resolve("certs").resolve("ca.cert.pem"); } public static Optional<PrivateKey> readPrivateKeyFile(AthenzIdentity service) { diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identity/SiaIdentityProviderTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identity/SiaIdentityProviderTest.java index 1fe32561f82..b7db502b1d0 100644 --- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identity/SiaIdentityProviderTest.java +++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identity/SiaIdentityProviderTest.java @@ -2,15 +2,11 @@ package com.yahoo.vespa.athenz.identity; import com.yahoo.security.KeyAlgorithm; -import com.yahoo.security.KeyStoreBuilder; -import com.yahoo.security.KeyStoreType; -import com.yahoo.security.KeyStoreUtils; import com.yahoo.security.KeyUtils; import com.yahoo.security.SignatureAlgorithm; import com.yahoo.security.X509CertificateBuilder; import com.yahoo.security.X509CertificateUtils; import com.yahoo.vespa.athenz.api.AthenzService; -import com.yahoo.yolean.Exceptions; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -21,11 +17,11 @@ import java.io.IOException; import java.math.BigInteger; import java.nio.file.Files; import java.security.KeyPair; -import java.security.KeyStore; import java.security.cert.X509Certificate; import java.time.Duration; import java.time.Instant; +import static com.yahoo.yolean.Exceptions.uncheck; import static org.junit.Assert.assertNotNull; /** @@ -55,7 +51,7 @@ public class SiaIdentityProviderTest { keyFile.toPath(), certificateFile.toPath(), trustStoreFile.toPath(), - SiaProviderConfig.TrustStoreType.Enum.jks); + trustStoreFile.toPath()); assertNotNull(provider.getIdentitySslContext()); } @@ -79,7 +75,7 @@ public class SiaIdentityProviderTest { keyFile.toPath(), certificateFile.toPath(), trustStoreFile.toPath(), - SiaProviderConfig.TrustStoreType.Enum.pem); + trustStoreFile.toPath()); assertNotNull(provider.getIdentitySslContext()); } @@ -109,14 +105,11 @@ public class SiaIdentityProviderTest { private void createPemTrustStoreFile(X509Certificate certificate, File trustStoreFile) { var pemEncoded = X509CertificateUtils.toPem(certificate); - Exceptions.uncheck(() -> Files.writeString(trustStoreFile.toPath(), pemEncoded)); + uncheck(() -> Files.writeString(trustStoreFile.toPath(), pemEncoded)); } private void createTrustStoreFile(X509Certificate certificate, File trustStoreFile) { - KeyStore keystore = KeyStoreBuilder.withType(KeyStoreType.JKS) - .withCertificateEntry("dummy-cert", certificate) - .build(); - KeyStoreUtils.writeKeyStoreToFile(keystore, trustStoreFile.toPath()); + uncheck(() -> Files.writeString(trustStoreFile.toPath(), X509CertificateUtils.toPem(certificate))); } } |