summaryrefslogtreecommitdiffstats
path: root/vespa-athenz
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorn.christian@seime.no>2020-06-02 13:56:52 +0200
committerGitHub <noreply@github.com>2020-06-02 13:56:52 +0200
commit7bee3afd465559d2fe0b4994d10c14660bdbad3f (patch)
tree3e65171c5e0f5c1b113ed4dbf0a168101b54b5c6 /vespa-athenz
parent83f00b2337fa9d2c280f407590ac703401dabf79 (diff)
Revert "Revert "Expose underlying certificate and private key from SiaIdentityProvider ""
Diffstat (limited to 'vespa-athenz')
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/ServiceIdentityProvider.java44
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/SiaIdentityProvider.java64
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java3
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java36
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/utils/SiaUtils.java9
-rw-r--r--vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identity/SiaIdentityProviderTest.java29
6 files changed, 122 insertions, 63 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 e5ed885b316..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
@@ -2,18 +2,56 @@
package com.yahoo.vespa.athenz.identity;
import com.yahoo.container.jdisc.athenz.AthenzIdentityProvider;
+import com.yahoo.security.X509CertificateWithKey;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
-import com.yahoo.vespa.athenz.api.AthenzService;
import javax.net.ssl.SSLContext;
+import java.nio.file.Path;
/**
- * A interface for types that provides a service identity.
- * Some similarities to {@link AthenzIdentityProvider}, but this type is not public api and intended for internal use.
+ * A interface for types that provides the Athenz service identity (SIA) from the environment.
+ * Some similarities to {@link AthenzIdentityProvider}, but this type is not public API and intended for internal use.
*
* @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 4981b80998f..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,15 +3,14 @@ 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;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzService;
import com.yahoo.vespa.athenz.utils.SiaUtils;
import javax.net.ssl.SSLContext;
-import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
@@ -26,34 +25,43 @@ public class SiaIdentityProvider extends AbstractComponent implements ServiceIde
private final AutoReloadingX509KeyManager keyManager;
private final SSLContext sslContext;
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) {
this(new AthenzService(config.athenzDomain(), config.athenzService()),
- SiaUtils.getPrivateKeyFile(Paths.get(config.keyPathPrefix()), new AthenzService(config.athenzDomain(), config.athenzService())).toFile(),
- SiaUtils.getCertificateFile(Paths.get(config.keyPathPrefix()), new AthenzService(config.athenzDomain(), config.athenzService())).toFile(),
- new File(config.trustStorePath()),
- config.trustStoreType());
+ 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()),
+ Paths.get(config.athenzTruststorePath()));
}
public SiaIdentityProvider(AthenzIdentity service,
Path siaPath,
- File trustStoreFile) {
+ Path clientTruststoreFile,
+ Path athenzTruststoreFile) {
this(service,
- SiaUtils.getPrivateKeyFile(siaPath, service).toFile(),
- SiaUtils.getCertificateFile(siaPath, service).toFile(),
- trustStoreFile,
- SiaProviderConfig.TrustStoreType.Enum.jks);
+ SiaUtils.getPrivateKeyFile(siaPath, service),
+ SiaUtils.getCertificateFile(siaPath, service),
+ athenzTruststoreFile,
+ clientTruststoreFile);
}
public SiaIdentityProvider(AthenzIdentity service,
- File privateKeyFile,
- File certificateFile,
- File trustStoreFile,
- SiaProviderConfig.TrustStoreType.Enum trustStoreType) {
+ Path privateKeyFile,
+ Path certificateFile,
+ Path athenzTruststoreFile,
+ Path clientTruststoreFile) {
this.service = service;
- this.keyManager = AutoReloadingX509KeyManager.fromPemFiles(privateKeyFile.toPath(), certificateFile.toPath());
- this.sslContext = createIdentitySslContext(keyManager, trustStoreFile.toPath(), trustStoreType);
+ this.keyManager = AutoReloadingX509KeyManager.fromPemFiles(privateKeyFile, certificateFile);
+ this.sslContext = createIdentitySslContext(keyManager, clientTruststoreFile);
+ this.certificateFile = certificateFile;
+ this.privateKeyFile = privateKeyFile;
+ this.athenzTruststoreFile = athenzTruststoreFile;
+ this.clientTruststoreFile = clientTruststoreFile;
}
@Override
@@ -66,17 +74,17 @@ public class SiaIdentityProvider extends AbstractComponent implements ServiceIde
return sslContext;
}
- 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();
+ @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) {
+ return new SslContextBuilder()
+ .withTrustStore(trustStoreFile)
+ .withKeyManager(keyManager)
+ .build();
}
@Override
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java
index 3b733e05708..8e029906c30 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzCredentialsService.java
@@ -64,6 +64,9 @@ class AthenzCredentialsService {
this.clock = clock;
}
+ Path certificatePath() { return SiaUtils.getCertificateFile(VESPA_SIA_DIRECTORY, tenantIdentity); }
+ Path privateKeyPath() { return SiaUtils.getPrivateKeyFile(VESPA_SIA_DIRECTORY, tenantIdentity); }
+
AthenzCredentials registerInstance() {
Optional<AthenzCredentials> athenzCredentialsFromDisk = tryReadCredentialsFromDisk();
if (athenzCredentialsFromDisk.isPresent()) {
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 b816acfad38..62c5820c927 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,9 +11,9 @@ 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;
import com.yahoo.security.tls.MutableX509KeyManager;
import com.yahoo.vespa.athenz.api.AthenzAccessToken;
import com.yahoo.vespa.athenz.api.AthenzDomain;
@@ -47,7 +47,6 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
-import static com.yahoo.security.KeyStoreType.JKS;
import static com.yahoo.security.KeyStoreType.PKCS12;
/**
@@ -56,6 +55,8 @@ import static com.yahoo.security.KeyStoreType.PKCS12;
* @author mortent
* @author bjorncs
*/
+// This class should probably not implement ServiceIdentityProvider,
+// as that interface is intended for providing the node's identity, not the tenant's application identity.
public final class AthenzIdentityProviderImpl extends AbstractComponent implements AthenzIdentityProvider, ServiceIdentityProvider {
private static final Logger log = Logger.getLogger(AthenzIdentityProviderImpl.class.getName());
@@ -67,8 +68,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";
@@ -94,9 +96,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),
@@ -142,7 +144,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();
}
@@ -177,6 +179,20 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen
}
@Override
+ public X509CertificateWithKey getIdentityCertificateWithKey() {
+ AthenzCredentials copy = this.credentials;
+ return new X509CertificateWithKey(copy.getCertificate(), copy.getKeyPair().getPrivate());
+ }
+
+ @Override public Path certificatePath() { return athenzCredentialsService.certificatePath(); }
+
+ @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.
try {
@@ -255,7 +271,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();
}
}
@@ -298,9 +314,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.toFile());
+ 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 ce02860cc78..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;
/**
@@ -52,10 +48,10 @@ public class SiaIdentityProviderTest {
SiaIdentityProvider provider =
new SiaIdentityProvider(
new AthenzService("domain", "service-name"),
- keyFile,
- certificateFile,
- trustStoreFile,
- SiaProviderConfig.TrustStoreType.Enum.jks);
+ keyFile.toPath(),
+ certificateFile.toPath(),
+ trustStoreFile.toPath(),
+ trustStoreFile.toPath());
assertNotNull(provider.getIdentitySslContext());
}
@@ -76,10 +72,10 @@ public class SiaIdentityProviderTest {
SiaIdentityProvider provider =
new SiaIdentityProvider(
new AthenzService("domain", "service-name"),
- keyFile,
- certificateFile,
- trustStoreFile,
- SiaProviderConfig.TrustStoreType.Enum.pem);
+ keyFile.toPath(),
+ certificateFile.toPath(),
+ trustStoreFile.toPath(),
+ 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)));
}
}