summaryrefslogtreecommitdiffstats
path: root/athenz-identity-provider-service
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@oath.com>2018-06-05 14:04:16 +0200
committerBjørn Christian Seime <bjorncs@oath.com>2018-06-05 14:04:16 +0200
commit93736dace106d7a0ae4ee2508393a16cdc7c2f5c (patch)
treeda6b4cd7fc03b191c58adecce820973479c8f685 /athenz-identity-provider-service
parent5a28eb136ad06e05529fe1014f3e9bb00505e882 (diff)
Stop using a fixed keystore password
Diffstat (limited to 'athenz-identity-provider-service')
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzSslKeyStoreConfigurator.java98
1 files changed, 67 insertions, 31 deletions
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 4af64286e7c..f1fc938d3ea 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
@@ -15,13 +15,15 @@ import com.yahoo.vespa.defaults.Defaults;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.AthenzCertificateClient;
-import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.PrivateKey;
+import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.time.Instant;
@@ -35,13 +37,16 @@ import static com.yahoo.vespa.athenz.tls.KeyStoreUtils.writeKeyStoreToFile;
import static com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.Utils.getZoneConfig;
/**
+ * A component that is responsible for retrieving an Athenz TLS certificate and configuring the configserver to use
+ * that certificate for its HTTPS endpoint.
+ *
* @author bjorncs
*/
@SuppressWarnings("unused") // Component injected into Jetty connector factory
public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements SslKeyStoreConfigurator {
private static final Logger log = Logger.getLogger(AthenzSslKeyStoreConfigurator.class.getName());
+ private static final SecureRandom secureRandom = new SecureRandom();
private static final String CERTIFICATE_ALIAS = "athenz";
- private static final String CERTIFICATE_PASSWORD = "athenz";
private static final Duration EXPIRATION_MARGIN = Duration.ofHours(6);
private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
@@ -49,8 +54,8 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
private final KeyProvider keyProvider;
private final AthenzProviderServiceConfig.Zones zoneConfig;
private final Duration updatePeriod;
- private final Path keystoreCachePath;
- private volatile KeyStore currentKeyStore;
+ private final Path keystoreDirectory;
+ private volatile KeyStoreAndPassword currentKeyStore;
@Inject
public AthenzSslKeyStoreConfigurator(ServiceIdentityProvider bootstrapIdentity,
@@ -59,51 +64,60 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
Zone zone,
ConfigserverConfig configserverConfig) {
AthenzProviderServiceConfig.Zones zoneConfig = getZoneConfig(config, zone);
- Path keystoreCachePath = createKeystoreCachePath(configserverConfig);
+ Path keystoreDirectory = createKeystoreCacheDirectory(configserverConfig);
AthenzCertificateClient certificateClient = new AthenzCertificateClient(bootstrapIdentity, zoneConfig);
Duration updatePeriod = Duration.ofDays(config.updatePeriodDays());
this.certificateClient = certificateClient;
this.keyProvider = keyProvider;
this.zoneConfig = zoneConfig;
- this.currentKeyStore = initializeKeystore(keyProvider, certificateClient, zoneConfig, keystoreCachePath, updatePeriod);
+ this.currentKeyStore = initializeKeystore(keyProvider, certificateClient, zoneConfig, keystoreDirectory, updatePeriod);
this.updatePeriod = updatePeriod;
- this.keystoreCachePath = keystoreCachePath;
+ this.keystoreDirectory = keystoreDirectory;
}
- private static KeyStore initializeKeystore(KeyProvider keyProvider,
+ private static KeyStoreAndPassword initializeKeystore(KeyProvider keyProvider,
AthenzCertificateClient certificateClient,
AthenzProviderServiceConfig.Zones zoneConfig,
- Path keystoreCachePath,
+ Path keystoreCacheDirectory,
Duration updatePeriod) {
- return tryReadKeystoreFile(keystoreCachePath.toFile(), updatePeriod)
- .orElseGet(() -> downloadCertificate(keyProvider, certificateClient, zoneConfig, keystoreCachePath));
+ return tryReadKeystoreFile(keystoreCacheDirectory, updatePeriod)
+ .orElseGet(() -> downloadCertificate(keyProvider, certificateClient, zoneConfig, keystoreCacheDirectory));
}
- private static Optional<KeyStore> tryReadKeystoreFile(File certificateFile, Duration updatePeriod) {
+ private static Optional<KeyStoreAndPassword> tryReadKeystoreFile(Path keystoreDirectory, Duration updatePeriod) {
try {
- if (!certificateFile.exists()) return Optional.empty();
+ Path keystoreFile = keystoreFile(keystoreDirectory);
+ Path keystoreSecretFile = keystoreSecretFile(keystoreDirectory);
+ if (!Files.exists(keystoreFile) || !Files.exists(keystoreSecretFile)) return Optional.empty();
KeyStore keyStore = KeyStoreBuilder.withType(KeyStoreType.JKS)
- .fromFile(certificateFile)
+ .fromFile(keystoreFile.toFile())
.build();
Instant minimumExpiration = Instant.now().plus(updatePeriod).plus(EXPIRATION_MARGIN);
boolean isExpired = getCertificateExpiry(keyStore).isBefore(minimumExpiration);
if (isExpired) return Optional.empty();
- return Optional.of(keyStore);
- } catch (GeneralSecurityException e) {
+ char[] pwd = new String(Files.readAllBytes(keystoreSecretFile)).toCharArray();
+ return Optional.of(new KeyStoreAndPassword(keyStore, pwd));
+ } catch (GeneralSecurityException | IOException e) {
log.log(LogLevel.ERROR, "Failed to read keystore from disk: " + e.getMessage(), e);
return Optional.empty();
}
}
- private static Path createKeystoreCachePath(ConfigserverConfig configserverConfig) {
- return Paths.get(
- Defaults.getDefaults().underVespaHome(configserverConfig.configServerDBDir()),
- "server-x509-athenz-cert.jks");
+ private static Path createKeystoreCacheDirectory(ConfigserverConfig configserverConfig) {
+ return Paths.get(Defaults.getDefaults().underVespaHome(configserverConfig.configServerDBDir()));
+ }
+
+ private static Path keystoreFile(Path keystoreDirectory) {
+ return keystoreDirectory.resolve("server-x509-athenz-cert.jks");
+ }
+
+ private static Path keystoreSecretFile(Path keystoreDirectory) {
+ return keystoreDirectory.resolve("keystore-secret");
}
@Override
public void configure(SslKeyStoreContext sslKeyStoreContext) {
- sslKeyStoreContext.updateKeyStore(currentKeyStore, CERTIFICATE_PASSWORD);
+ sslKeyStoreContext.updateKeyStore(currentKeyStore.keyStore, new String(currentKeyStore.password));
scheduler.scheduleAtFixedRate(new AthenzCertificateUpdater(sslKeyStoreContext),
updatePeriod.toDays()/*initial delay*/,
updatePeriod.toDays(),
@@ -121,7 +135,7 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
}
Instant getCertificateExpiry() throws KeyStoreException {
- return getCertificateExpiry(currentKeyStore);
+ return getCertificateExpiry(currentKeyStore.keyStore);
}
private static Instant getCertificateExpiry(KeyStore keyStore) throws KeyStoreException {
@@ -129,31 +143,43 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
return certificate.getNotAfter().toInstant();
}
- private static KeyStore downloadCertificate(KeyProvider keyProvider,
+ private static KeyStoreAndPassword downloadCertificate(KeyProvider keyProvider,
AthenzCertificateClient certificateClient,
AthenzProviderServiceConfig.Zones zoneConfig,
- Path keystoreCachePath) {
+ Path keystoreDirectory) {
PrivateKey privateKey = keyProvider.getPrivateKey(zoneConfig.secretVersion());
X509Certificate certificate = certificateClient.updateCertificate(privateKey);
Instant expirationTime = certificate.getNotAfter().toInstant();
Duration expiry = Duration.between(certificate.getNotBefore().toInstant(), expirationTime);
log.log(LogLevel.INFO, String.format("Got Athenz x509 certificate with expiry %s (expires %s)", expiry, expirationTime));
+ char[] keystorePassword = generateKeystorePassword();
KeyStore keyStore = KeyStoreBuilder.withType(KeyStoreType.JKS)
- .withKeyEntry(CERTIFICATE_ALIAS, privateKey, CERTIFICATE_PASSWORD.toCharArray(), certificate)
+ .withKeyEntry(CERTIFICATE_ALIAS, privateKey, keystorePassword, certificate)
.build();
- tryWriteKeystore(keyStore, keystoreCachePath);
- return keyStore;
+ KeyStoreAndPassword keyStoreAndPassword = new KeyStoreAndPassword(keyStore, keystorePassword);
+ tryWriteKeystore(keyStoreAndPassword, keystoreDirectory);
+ return keyStoreAndPassword;
}
- private static void tryWriteKeystore(KeyStore keyStore, Path keystoreCachePath) {
+ private static void tryWriteKeystore(KeyStoreAndPassword keyStore, Path keystoreDirectory) {
try {
- writeKeyStoreToFile(keyStore, keystoreCachePath.toFile());
+ writeKeyStoreToFile(keyStore.keyStore, keystoreFile(keystoreDirectory).toFile());
+ Files.write(keystoreSecretFile(keystoreDirectory), new String(keyStore.password).getBytes());
} catch (Exception e) {
log.log(LogLevel.ERROR, "Failed to write keystore to disk: " + e.getMessage(), e);
}
}
+ private static char[] generateKeystorePassword() {
+ int length = 128;
+ char[] pwd = new char[length];
+ for (int i = 0; i < length; i++) {
+ pwd[i] = (char) secureRandom.nextInt();
+ }
+ return pwd;
+ }
+
private class AthenzCertificateUpdater implements Runnable {
private final SslKeyStoreContext sslKeyStoreContext;
@@ -166,8 +192,8 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
public void run() {
try {
log.log(LogLevel.INFO, "Updating Athenz certificate from ZTS");
- currentKeyStore = downloadCertificate(keyProvider, certificateClient, zoneConfig, keystoreCachePath);
- sslKeyStoreContext.updateKeyStore(currentKeyStore, CERTIFICATE_PASSWORD);
+ currentKeyStore = downloadCertificate(keyProvider, certificateClient, zoneConfig, keystoreDirectory);
+ sslKeyStoreContext.updateKeyStore(currentKeyStore.keyStore, new String(currentKeyStore.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);
@@ -175,4 +201,14 @@ public class AthenzSslKeyStoreConfigurator extends AbstractComponent implements
}
}
+
+ private static class KeyStoreAndPassword {
+ final KeyStore keyStore;
+ final char[] password;
+
+ KeyStoreAndPassword(KeyStore keyStore, char[] password) {
+ this.keyStore = keyStore;
+ this.password = password;
+ }
+ }
}