summaryrefslogtreecommitdiffstats
path: root/athenz-identity-provider-service
diff options
context:
space:
mode:
Diffstat (limited to 'athenz-identity-provider-service')
-rw-r--r--athenz-identity-provider-service/pom.xml1
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java69
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslTrustStoreConfigurator.java110
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/KeyProvider.java5
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSigner.java13
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/SecretStoreKeyProvider.java3
-rw-r--r--athenz-identity-provider-service/src/main/resources/configdefinitions/athenz-provider-service.def3
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/TestUtils.java3
8 files changed, 172 insertions, 35 deletions
diff --git a/athenz-identity-provider-service/pom.xml b/athenz-identity-provider-service/pom.xml
index c87589d7be2..bfd02d54d43 100644
--- a/athenz-identity-provider-service/pom.xml
+++ b/athenz-identity-provider-service/pom.xml
@@ -11,6 +11,7 @@
<groupId>com.yahoo.vespa</groupId>
<artifactId>parent</artifactId>
<version>6-SNAPSHOT</version>
+ <relativePath>../parent/pom.xml</relativePath>
</parent>
<dependencies>
<!-- COMPILE -->
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java
index 7910650ed5e..706f797cd2c 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
import com.google.inject.Inject;
import com.yahoo.component.AbstractComponent;
-import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.Zone;
import com.yahoo.jdisc.http.ssl.SslKeyStoreConfigurator;
import com.yahoo.jdisc.http.ssl.SslKeyStoreContext;
@@ -11,12 +10,15 @@ import com.yahoo.log.LogLevel;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.AthenzCertificateClient;
+import java.io.IOException;
import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.time.Duration;
-import java.time.Instant;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -35,13 +37,14 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
// TODO Make expiry and update frequency configurable parameters
private static final Duration CERTIFICATE_EXPIRY_TIME = Duration.ofDays(30);
private static final Duration CERTIFICATE_UPDATE_PERIOD = Duration.ofDays(7);
+ private static final String DUMMY_PASSWORD = "athenz";
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
private final AthenzCertificateClient certificateClient;
private final KeyProvider keyProvider;
private final AthenzProviderServiceConfig.Zones zoneConfig;
private final AtomicBoolean alreadyConfigured = new AtomicBoolean();
- private final Zone zone;
+ private KeyStore initialKeyStore;
@Inject
public AthenzSslKeyStoreConfigurator(KeyProvider keyProvider,
@@ -51,20 +54,20 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
this.certificateClient = new AthenzCertificateClient(config, zoneConfig);
this.keyProvider = keyProvider;
this.zoneConfig = zoneConfig;
- this.zone = zone;
+ this.initialKeyStore = downloadCertificate(keyProvider, certificateClient, zoneConfig);
}
@Override
public void configure(SslKeyStoreContext sslKeyStoreContext) {
- // TODO Remove this when main is ready
- if (zone.system() != SystemName.cd) {
- return;
- }
if (alreadyConfigured.getAndSet(true)) { // For debugging purpose of SslKeyStoreConfigurator interface
throw new IllegalStateException("Already configured. configure() can only be called once.");
}
- AthenzCertificateUpdater updater = new AthenzCertificateUpdater(sslKeyStoreContext);
- scheduler.scheduleAtFixedRate(updater, /*initialDelay*/0, CERTIFICATE_UPDATE_PERIOD.toMinutes(), TimeUnit.MINUTES);
+ sslKeyStoreContext.updateKeyStore(initialKeyStore, DUMMY_PASSWORD);
+ initialKeyStore = null;
+ scheduler.scheduleAtFixedRate(new AthenzCertificateUpdater(sslKeyStoreContext),
+ CERTIFICATE_UPDATE_PERIOD.toMinutes()/*initial delay*/,
+ CERTIFICATE_UPDATE_PERIOD.toMinutes(),
+ TimeUnit.MINUTES);
}
@Override
@@ -77,6 +80,32 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
}
}
+ private static KeyStore downloadCertificate(KeyProvider keyProvider,
+ AthenzCertificateClient certificateClient,
+ AthenzProviderServiceConfig.Zones zoneConfig) {
+ try {
+ PrivateKey privateKey = keyProvider.getPrivateKey(zoneConfig.secretVersion());
+ X509Certificate certificate = certificateClient.updateCertificate(privateKey, CERTIFICATE_EXPIRY_TIME);
+ verifyActualExpiry(certificate);
+
+ KeyStore keyStore = KeyStore.getInstance("JKS");
+ keyStore.load(null);
+ keyStore.setKeyEntry("athenz", privateKey, DUMMY_PASSWORD.toCharArray(), new Certificate[]{certificate});
+ return keyStore;
+ } catch (IOException | NoSuchAlgorithmException | CertificateException | KeyStoreException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static void verifyActualExpiry(X509Certificate certificate) {
+ Duration actualExpiry =
+ Duration.between(certificate.getNotBefore().toInstant(), certificate.getNotAfter().toInstant());
+ if (CERTIFICATE_EXPIRY_TIME.compareTo(actualExpiry) > 0) {
+ log.log(LogLevel.WARNING,
+ String.format("Expected expiry %s, got %s", CERTIFICATE_EXPIRY_TIME, actualExpiry));
+ }
+ }
+
private class AthenzCertificateUpdater implements Runnable {
private final SslKeyStoreContext sslKeyStoreContext;
@@ -89,29 +118,13 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
public void run() {
try {
log.log(LogLevel.INFO, "Updating Athenz certificate from ZTS");
- PrivateKey privateKey = keyProvider.getPrivateKey(zoneConfig.secretVersion());
- X509Certificate certificate = certificateClient.updateCertificate(privateKey, CERTIFICATE_EXPIRY_TIME);
- verifyActualExperiy(certificate);
-
- String dummyPassword = "athenz";
- KeyStore keyStore = KeyStore.getInstance("JKS");
- keyStore.load(null);
- keyStore.setKeyEntry("athenz", privateKey, dummyPassword.toCharArray(), new Certificate[]{certificate});
- sslKeyStoreContext.updateKeyStore(keyStore, dummyPassword);
+ KeyStore keyStore = downloadCertificate(keyProvider, certificateClient, zoneConfig);
+ sslKeyStoreContext.updateKeyStore(keyStore, DUMMY_PASSWORD);
log.log(LogLevel.INFO, "Athenz certificate reload successfully completed");
} catch (Throwable e) {
log.log(LogLevel.ERROR, "Failed to update certificate from ZTS: " + e.getMessage(), e);
}
}
- private void verifyActualExperiy(X509Certificate certificate) {
- Instant notAfter = certificate.getNotAfter().toInstant();
- Instant notBefore = certificate.getNotBefore().toInstant();
- if (!notBefore.plus(CERTIFICATE_EXPIRY_TIME).equals(notAfter)) {
- Duration actualExpiry = Duration.between(notBefore, notAfter);
- log.log(LogLevel.WARNING,
- String.format("Expected expiry %s, got %s", CERTIFICATE_EXPIRY_TIME, actualExpiry));
- }
- }
}
}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslTrustStoreConfigurator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslTrustStoreConfigurator.java
new file mode 100644
index 00000000000..8c8b5de2a30
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslTrustStoreConfigurator.java
@@ -0,0 +1,110 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
+
+import com.google.inject.Inject;
+import com.yahoo.cloud.config.ConfigserverConfig;
+import com.yahoo.jdisc.http.ssl.SslTrustStoreConfigurator;
+import com.yahoo.jdisc.http.ssl.SslTrustStoreContext;
+import com.yahoo.log.LogLevel;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyStore;
+import java.security.Provider;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.time.Duration;
+import java.time.Instant;
+import java.util.Date;
+import java.util.logging.Logger;
+
+/**
+ * @author bjorncs
+ */
+public class AthenzSslTrustStoreConfigurator implements SslTrustStoreConfigurator {
+
+ private static final Logger log = Logger.getLogger(AthenzSslTrustStoreConfigurator.class.getName());
+
+ private static final Provider provider = new BouncyCastleProvider();
+ private final KeyStore trustStore;
+
+ @Inject
+ public AthenzSslTrustStoreConfigurator(KeyProvider keyProvider,
+ ConfigserverConfig configserverConfig,
+ AthenzProviderServiceConfig athenzProviderServiceConfig) {
+ this.trustStore = createTrustStore(keyProvider, configserverConfig, athenzProviderServiceConfig);
+ }
+
+ @Override
+ public void configure(SslTrustStoreContext sslTrustStoreContext) {
+ sslTrustStoreContext.updateTrustStore(trustStore);
+ log.log(LogLevel.INFO, "Configured JDisc trust store with self-signed certificate");
+ }
+
+ private static KeyStore createTrustStore(KeyProvider keyProvider,
+ ConfigserverConfig configserverConfig,
+ AthenzProviderServiceConfig athenzProviderServiceConfig) {
+ try {
+ KeyPair keyPair = getKeyPair(keyProvider, configserverConfig, athenzProviderServiceConfig);
+ X509Certificate selfSignedCertificate = createSelfSignedCertificate(keyPair, configserverConfig);
+ log.log(LogLevel.FINE, "Generated self-signed certificate: " + selfSignedCertificate);
+ KeyStore trustStore = KeyStore.getInstance("JKS");
+ try (FileInputStream in = new FileInputStream(athenzProviderServiceConfig.athenzCaTrustStore())) {
+ trustStore.load(in, "changeit".toCharArray());
+ }
+ trustStore.setCertificateEntry("cfgselfsigned", selfSignedCertificate);
+ return trustStore;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private static KeyPair getKeyPair(KeyProvider keyProvider,
+ ConfigserverConfig configserverConfig,
+ AthenzProviderServiceConfig athenzProviderServiceConfig) {
+ String key = configserverConfig.environment() + "." + configserverConfig.region();
+ AthenzProviderServiceConfig.Zones zoneConfig = athenzProviderServiceConfig.zones(key);
+ return keyProvider.getKeyPair(zoneConfig.secretVersion());
+ }
+
+ private static X509Certificate createSelfSignedCertificate(KeyPair keyPair, ConfigserverConfig config)
+ throws IOException, CertificateException, OperatorCreationException {
+ ContentSigner contentSigner = new JcaContentSignerBuilder("SHA256WithRSA").build(keyPair.getPrivate());
+ X500Name x500Name = new X500Name("CN="+ config.loadBalancerAddress());
+ Instant now = Instant.now();
+ Date notBefore = Date.from(now);
+ Date notAfter = Date.from(now.plus(Duration.ofDays(30)));
+
+ GeneralNames generalNames = new GeneralNames(
+ config.zookeeperserver().stream()
+ .map(server -> new GeneralName(GeneralName.dNSName, server.hostname()))
+ .toArray(GeneralName[]::new));
+
+ X509v3CertificateBuilder certificateBuilder =
+ new JcaX509v3CertificateBuilder(
+ x500Name, BigInteger.valueOf(now.toEpochMilli()), notBefore, notAfter, x500Name, keyPair.getPublic()
+ )
+ .addExtension(Extension.basicConstraints, true, new BasicConstraints(true))
+ .addExtension(Extension.subjectAlternativeName, false, generalNames);
+
+ return new JcaX509CertificateConverter()
+ .setProvider(provider)
+ .getCertificate(certificateBuilder.build(contentSigner));
+ }
+
+}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/KeyProvider.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/KeyProvider.java
index a72a2fcbc6c..1d141099428 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/KeyProvider.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/KeyProvider.java
@@ -1,6 +1,7 @@
// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
+import java.security.KeyPair;
import java.security.PrivateKey;
import java.security.PublicKey;
@@ -11,4 +12,8 @@ public interface KeyProvider {
PrivateKey getPrivateKey(int version);
PublicKey getPublicKey(int version);
+
+ default KeyPair getKeyPair(int version) {
+ return new KeyPair(getPublicKey(version), getPrivateKey(version));
+ }
}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSigner.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSigner.java
index 2dc3f24664c..1014fc4afdf 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSigner.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/ca/CertificateSigner.java
@@ -2,9 +2,9 @@ package com.yahoo.vespa.hosted.athenz.instanceproviderservice.ca;
import com.google.common.collect.ImmutableList;
import com.google.inject.Inject;
+import com.yahoo.cloud.config.ConfigserverConfig;
import com.yahoo.config.provision.Zone;
import com.yahoo.log.LogLevel;
-import com.yahoo.net.HostName;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.KeyProvider;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -68,13 +68,16 @@ public class CertificateSigner {
private final Clock clock;
@Inject
- public CertificateSigner(KeyProvider keyProvider, AthenzProviderServiceConfig config, Zone zone) {
- this(getPrivateKey(keyProvider, config, zone), HostName.getLocalhost(), Clock.systemUTC());
+ public CertificateSigner(KeyProvider keyProvider,
+ ConfigserverConfig configserverConfig,
+ AthenzProviderServiceConfig config,
+ Zone zone) {
+ this(getPrivateKey(keyProvider, config, zone), configserverConfig.loadBalancerAddress(), Clock.systemUTC());
}
- CertificateSigner(PrivateKey caPrivateKey, String configServerHostname, Clock clock) {
+ CertificateSigner(PrivateKey caPrivateKey, String loadBalancerAddress, Clock clock) {
this.caPrivateKey = caPrivateKey;
- this.issuer = new X500Name("CN=" + configServerHostname);
+ this.issuer = new X500Name("CN=" + loadBalancerAddress);
this.clock = clock;
}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/SecretStoreKeyProvider.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/SecretStoreKeyProvider.java
index e66131b6cf7..ac8c0eabf31 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/SecretStoreKeyProvider.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/SecretStoreKeyProvider.java
@@ -45,7 +45,8 @@ public class SecretStoreKeyProvider implements KeyProvider {
return getKeyPair(version).getPublic();
}
- private KeyPair getKeyPair(int version) {
+ @Override
+ public KeyPair getKeyPair(int version) {
synchronized (secrets) {
KeyPair keyPair = secrets.get(version);
if (keyPair == null) {
diff --git a/athenz-identity-provider-service/src/main/resources/configdefinitions/athenz-provider-service.def b/athenz-identity-provider-service/src/main/resources/configdefinitions/athenz-provider-service.def
index 13cc78b0bd0..21f2aea6ab0 100644
--- a/athenz-identity-provider-service/src/main/resources/configdefinitions/athenz-provider-service.def
+++ b/athenz-identity-provider-service/src/main/resources/configdefinitions/athenz-provider-service.def
@@ -21,3 +21,6 @@ ztsUrl string
# Certificate DNS suffix
certDnsSuffix string
+
+# Path to Athenz CA JKS trust store
+athenzCaTrustStore string
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/TestUtils.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/TestUtils.java
index c09a9fb1740..da2bf929e82 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/TestUtils.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/TestUtils.java
@@ -25,7 +25,8 @@ public class TestUtils {
.zones(ImmutableMap.of(zone.environment().value() + "." + zone.region().value(), zoneConfig))
.certDnsSuffix(dnsSuffix)
.ztsUrl("localhost/zts")
- .athenzPrincipalHeaderName("Athenz-Principal-Auth"));
+ .athenzPrincipalHeaderName("Athenz-Principal-Auth")
+ .athenzCaTrustStore("/dummy/path/to/athenz-ca.jks"));
}
}