summaryrefslogtreecommitdiffstats
path: root/athenz-identity-provider-service
diff options
context:
space:
mode:
authorMorten Tokle <mortent@oath.com>2017-11-02 11:34:41 +0100
committerMorten Tokle <mortent@oath.com>2017-11-02 14:01:16 +0100
commitfd194925b4b0f9fabea24d783d2114e57aa42069 (patch)
treeda7ea7dca533f48609dff04ca16b53b707cca6f6 /athenz-identity-provider-service
parent451f936c4d19ae8dc08332682d06c6caa3a39000 (diff)
Read key from secretstore, refactor config
Diffstat (limited to 'athenz-identity-provider-service')
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderService.java25
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java14
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentGenerator.java11
-rw-r--r--athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/SecretStoreKeyProvider.java56
-rw-r--r--athenz-identity-provider-service/src/main/resources/configdefinitions/athenz-provider-service.def24
-rw-r--r--athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderServiceTest.java35
6 files changed, 126 insertions, 39 deletions
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderService.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderService.java
index e66130332ac..74b697fb004 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderService.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderService.java
@@ -5,16 +5,17 @@ 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.SecretStore;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.config.AthenzProviderServiceConfig;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.AthenzCertificateClient;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.CertificateClient;
-import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.FileBackedKeyProvider;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.IdentityDocumentGenerator;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.IdentityDocumentServlet;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.InstanceConfirmationServlet;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.InstanceValidator;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.KeyProvider;
+import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.SecretStoreKeyProvider;
import com.yahoo.vespa.hosted.athenz.instanceproviderservice.impl.StatusServlet;
import com.yahoo.vespa.hosted.provision.NodeRepository;
import org.eclipse.jetty.server.Server;
@@ -47,9 +48,9 @@ public class AthenzInstanceProviderService extends AbstractComponent {
private final Server jetty;
@Inject
- public AthenzInstanceProviderService(AthenzProviderServiceConfig config, NodeRepository nodeRepository, Zone zone) {
- this(config, new FileBackedKeyProvider(config.keyPathPrefix()), Executors.newSingleThreadScheduledExecutor(),
- nodeRepository, zone, new AthenzCertificateClient(config));
+ public AthenzInstanceProviderService(AthenzProviderServiceConfig config, NodeRepository nodeRepository, Zone zone, SecretStore secretStore) {
+ this(config, new SecretStoreKeyProvider(secretStore, getZoneConfig(config, zone).secretName()), Executors.newSingleThreadScheduledExecutor(),
+ nodeRepository, zone, new AthenzCertificateClient(config, getZoneConfig(config, zone)));
}
AthenzInstanceProviderService(AthenzProviderServiceConfig config,
@@ -65,7 +66,7 @@ public class AthenzInstanceProviderService extends AbstractComponent {
this.jetty = createJettyServer(
config, keyProvider, sslContextFactory, nodeRepository, zone);
AthenzCertificateUpdater reloader =
- new AthenzCertificateUpdater(certificateClient, sslContextFactory, keyProvider, config);
+ new AthenzCertificateUpdater(certificateClient, sslContextFactory, keyProvider, config, getZoneConfig(config, zone));
// TODO Configurable update frequency
scheduler.scheduleAtFixedRate(reloader, 0, 1, TimeUnit.DAYS);
try {
@@ -95,7 +96,7 @@ public class AthenzInstanceProviderService extends AbstractComponent {
handler.addServletWithMapping(new ServletHolder(instanceConfirmationServlet), config.apiPath() + "/instance");
IdentityDocumentServlet identityDocumentServlet =
- new IdentityDocumentServlet(new IdentityDocumentGenerator(config, nodeRepository, zone, keyProvider));
+ new IdentityDocumentServlet(new IdentityDocumentGenerator(config, getZoneConfig(config, zone), nodeRepository, zone, keyProvider));
handler.addServletWithMapping(new ServletHolder(identityDocumentServlet), config.apiPath() + "/identity-document");
handler.addServletWithMapping(StatusServlet.class, "/status.html");
@@ -104,6 +105,11 @@ public class AthenzInstanceProviderService extends AbstractComponent {
}
+ private static AthenzProviderServiceConfig.Zones getZoneConfig(AthenzProviderServiceConfig config, Zone zone) {
+ String key = zone.environment().value() + "." + zone.region().value();
+ return config.zones(key);
+ }
+
private static SslContextFactory createSslContextFactory() {
try {
SslContextFactory sslContextFactory = new SslContextFactory();
@@ -126,22 +132,25 @@ public class AthenzInstanceProviderService extends AbstractComponent {
private final SslContextFactory sslContextFactory;
private final KeyProvider keyProvider;
private final AthenzProviderServiceConfig config;
+ private final AthenzProviderServiceConfig.Zones zoneConfig;
private AthenzCertificateUpdater(CertificateClient certificateClient,
SslContextFactory sslContextFactory,
KeyProvider keyProvider,
- AthenzProviderServiceConfig config) {
+ AthenzProviderServiceConfig config,
+ AthenzProviderServiceConfig.Zones zoneConfig) {
this.certificateClient = certificateClient;
this.sslContextFactory = sslContextFactory;
this.keyProvider = keyProvider;
this.config = config;
+ this.zoneConfig = zoneConfig;
}
@Override
public void run() {
try {
log.log(LogLevel.INFO, "Updating Athenz certificate through ZTS");
- PrivateKey privateKey = keyProvider.getPrivateKey(config.keyVersion());
+ PrivateKey privateKey = keyProvider.getPrivateKey(zoneConfig.secretVersion());
X509Certificate certificate = certificateClient.updateCertificate(privateKey, EXPIRY_TIME);
String dummyPassword = "athenz";
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java
index 031133ade19..dab1581f580 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/AthenzCertificateClient.java
@@ -21,24 +21,26 @@ public class AthenzCertificateClient implements CertificateClient {
private final AthenzProviderServiceConfig config;
private final AthenzPrincipalAuthority authority;
+ private final AthenzProviderServiceConfig.Zones zoneConfig;
- public AthenzCertificateClient(AthenzProviderServiceConfig config) {
+ public AthenzCertificateClient(AthenzProviderServiceConfig config, AthenzProviderServiceConfig.Zones zoneConfig) {
this.config = config;
this.authority = new AthenzPrincipalAuthority(config.athenzPrincipalHeaderName());
+ this.zoneConfig = zoneConfig;
}
@Override
public X509Certificate updateCertificate(PrivateKey privateKey, TemporalAmount expiryTime) {
SimpleServiceIdentityProvider identityProvider = new SimpleServiceIdentityProvider(
- authority, config.domain(), config.serviceName(),
- privateKey, Integer.toString(config.keyVersion()), TimeUnit.MINUTES.toSeconds(10));
+ authority, zoneConfig.domain(), zoneConfig.serviceName(),
+ privateKey, Integer.toString(zoneConfig.secretVersion()), TimeUnit.MINUTES.toSeconds(10));
ZTSClient ztsClient = new ZTSClient(
- config.ztsUrl(), config.domain(), config.serviceName(), identityProvider);
+ config.ztsUrl(), zoneConfig.domain(), zoneConfig.serviceName(), identityProvider);
InstanceRefreshRequest req =
ZTSClient.generateInstanceRefreshRequest(
- config.domain(), config.serviceName(), privateKey,
+ zoneConfig.domain(), zoneConfig.serviceName(), privateKey,
config.certDnsSuffix(), (int)expiryTime.get(ChronoUnit.SECONDS));
- String pemEncoded = ztsClient.postInstanceRefreshRequest(config.domain(), config.serviceName(), req)
+ String pemEncoded = ztsClient.postInstanceRefreshRequest(zoneConfig.domain(), zoneConfig.serviceName(), req)
.getCertificate();
return Crypto.loadX509Certificate(pemEncoded);
}
diff --git a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentGenerator.java b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentGenerator.java
index 0e8ca0017f4..55acf0b796c 100644
--- a/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentGenerator.java
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/IdentityDocumentGenerator.java
@@ -27,15 +27,18 @@ public class IdentityDocumentGenerator {
private final String providerService;
private final String ztsUrl;
private final String providerDomain;
+ private final int signingSecretVersion;
- public IdentityDocumentGenerator(AthenzProviderServiceConfig config, NodeRepository nodeRepository, Zone zone, KeyProvider keyProvider) {
+ public IdentityDocumentGenerator(AthenzProviderServiceConfig config, AthenzProviderServiceConfig.Zones zoneConfig,
+ NodeRepository nodeRepository, Zone zone, KeyProvider keyProvider) {
this.nodeRepository = nodeRepository;
this.zone = zone;
this.keyProvider = keyProvider;
this.dnsSuffix = config.certDnsSuffix();
- this.providerService = config.serviceName();
+ this.providerService = zoneConfig.serviceName();
this.ztsUrl = config.ztsUrl();
- this.providerDomain = config.domain();
+ this.providerDomain = zoneConfig.domain();
+ this.signingSecretVersion = zoneConfig.secretVersion();
}
public String generateSignedIdentityDocument(String hostname) {
@@ -49,7 +52,7 @@ public class IdentityDocumentGenerator {
Signature sigGenerator = Signature.getInstance("SHA512withRSA");
// TODO: Get the correct version 0 ok for now
- PrivateKey privateKey = keyProvider.getPrivateKey(0);
+ PrivateKey privateKey = keyProvider.getPrivateKey(signingSecretVersion);
sigGenerator.initSign(privateKey);
sigGenerator.update(encodedIdentityDocument.getBytes());
String signature = Base64.getEncoder().encodeToString(sigGenerator.sign());
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
new file mode 100644
index 00000000000..93abda1f9ea
--- /dev/null
+++ b/athenz-identity-provider-service/src/main/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/impl/SecretStoreKeyProvider.java
@@ -0,0 +1,56 @@
+// 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.impl;
+
+import com.yahoo.athenz.auth.util.Crypto;
+import com.yahoo.jdisc.http.SecretStore;
+
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author mortent
+ */
+public class SecretStoreKeyProvider implements KeyProvider {
+
+ private final SecretStore secretStore;
+ private final String secretName;
+ private final Map<Integer, KeyPair> secrets;
+
+
+ public SecretStoreKeyProvider(SecretStore secretStore, String secretName) {
+ this.secretStore = secretStore;
+ this.secretName = secretName;
+ this.secrets = new HashMap<>();
+ }
+
+ @Override
+ public PrivateKey getPrivateKey(int version) {
+ return getKeyPair(version).getPrivate();
+ }
+
+ @Override
+ public PublicKey getPublicKey(int version) {
+ return getKeyPair(version).getPublic();
+ }
+
+ private KeyPair getKeyPair(int version) {
+ synchronized (secrets) {
+ KeyPair keyPair = secrets.get(version);
+ if (keyPair == null) {
+ keyPair = readKeyPair(version);
+ secrets.put(version, keyPair);
+ }
+ return keyPair;
+ }
+ }
+
+ // TODO: Consider moving to cryptoutils
+ private KeyPair readKeyPair(int version) {
+ PrivateKey privateKey = Crypto.loadPrivateKey(secretStore.getSecret(secretName, version));
+ PublicKey publicKey = Crypto.extractPublicKey(privateKey);
+ return new KeyPair(publicKey, privateKey);
+ }
+}
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 7e9c19cb86a..4aad9a4eae2 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
@@ -2,28 +2,28 @@
namespace=vespa.hosted.athenz.instanceproviderservice.config
# Athenz domain
-domain string
+zones{}.domain string
# Athenz service name
-serviceName string
+zones{}.serviceName string
-# Current key version
-keyVersion int default=0
+# Secret name of private Key
+zones{}.secretName string
-# HTTPS port for Athenz Provider Service endpoint
-port int default=8443
+# Secret version
+zones{}.secretVersion int
-# File name prefix for private and public key. Component assumes suffix .[priv|pub].<version>.
-keyPathPrefix string
+# HTTPS port for Athenz Provider Service endpoint
+port int default=8443
# InstanceConfirmation API path
-apiPath string default="/athenz/v1/provider"
+apiPath string default="/athenz/v1/provider"
# Athenz principal authority header name
-athenzPrincipalHeaderName string default="Athenz-Principal-Auth"
+athenzPrincipalHeaderName string default="Athenz-Principal-Auth"
# Athenz ZTS server url
-ztsUrl string
+ztsUrl string
# Certificate DNS suffix
-certDnsSuffix string
+certDnsSuffix string
diff --git a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderServiceTest.java b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderServiceTest.java
index c49122a07b8..20a56359eff 100644
--- a/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderServiceTest.java
+++ b/athenz-identity-provider-service/src/test/java/com/yahoo/vespa/hosted/athenz/instanceproviderservice/AthenzInstanceProviderServiceTest.java
@@ -3,6 +3,7 @@ package com.yahoo.vespa.hosted.athenz.instanceproviderservice;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
@@ -103,7 +104,7 @@ public class AthenzInstanceProviderServiceTest {
String service = "service";
AutoGeneratedKeyProvider keyProvider = new AutoGeneratedKeyProvider();
PrivateKey privateKey = keyProvider.getPrivateKey(0);
- AthenzProviderServiceConfig config = getAthenzProviderConfig(domain, service, "vespa.dns.suffix");
+ AthenzProviderServiceConfig config = getAthenzProviderConfig(domain, service, "vespa.dns.suffix", ZONE);
ScheduledExecutorServiceMock executor = new ScheduledExecutorServiceMock();
AthenzInstanceProviderService athenzInstanceProviderService =
@@ -112,7 +113,8 @@ public class AthenzInstanceProviderServiceTest {
executor,
mock(NodeRepository.class),
ZONE,
- new SelfSignedCertificateClient(keyProvider.getKeyPair(), config));
+ new SelfSignedCertificateClient(keyProvider.getKeyPair(), config,
+ getZoneConfig(config, ZONE)));
try (CloseableHttpClient client = createHttpClient(domain, service)) {
Runnable certificateRefreshCommand = executor.getCommand()
@@ -153,8 +155,10 @@ public class AthenzInstanceProviderServiceTest {
AutoGeneratedKeyProvider keyProvider = new AutoGeneratedKeyProvider();
String dnsSuffix = "vespa.dns.suffix";
+ AthenzProviderServiceConfig athenzProviderConfig = getAthenzProviderConfig("domain", "service", dnsSuffix, ZONE);
IdentityDocumentGenerator identityDocumentGenerator = new IdentityDocumentGenerator(
- getAthenzProviderConfig("domain", "service", dnsSuffix),
+ athenzProviderConfig,
+ getZoneConfig(athenzProviderConfig, ZONE),
nodeRepository,
ZONE,
keyProvider);
@@ -182,19 +186,29 @@ public class AthenzInstanceProviderServiceTest {
signedIdentityDocument.signature));
}
- private static AthenzProviderServiceConfig getAthenzProviderConfig(String domain, String service, String dnsSuffix) {
+ private static AthenzProviderServiceConfig getAthenzProviderConfig(String domain, String service, String dnsSuffix, Zone zone) {
+ AthenzProviderServiceConfig.Zones.Builder zoneConfig =
+ new AthenzProviderServiceConfig.Zones.Builder()
+ .serviceName(service)
+ .secretVersion(0)
+ .domain(domain)
+ .secretName("s3cr3t");
+
return new AthenzProviderServiceConfig(
new AthenzProviderServiceConfig.Builder()
- .domain(domain)
- .serviceName(service)
+ .zones(ImmutableMap.of(zone.environment().value() + "." + zone.region().value(), zoneConfig))
.port(PORT)
- .keyPathPrefix("dummy-path")
.certDnsSuffix(dnsSuffix)
.ztsUrl("localhost/zts")
.athenzPrincipalHeaderName("Athenz-Principal-Auth")
.apiPath(""));
}
+
+ private AthenzProviderServiceConfig.Zones getZoneConfig(AthenzProviderServiceConfig config, Zone zone) {
+ return config.zones(zone.environment().value() + "." + zone.region().value());
+ }
+
private static boolean getStatus(HttpClient client) {
try {
HttpResponse response = client.execute(new HttpGet("https://localhost:" + PORT + "/status.html"));
@@ -288,17 +302,20 @@ public class AthenzInstanceProviderServiceTest {
private final KeyPair keyPair;
private final AthenzProviderServiceConfig config;
+ private final AthenzProviderServiceConfig.Zones zoneConfig;
- private SelfSignedCertificateClient(KeyPair keyPair, AthenzProviderServiceConfig config) {
+ private SelfSignedCertificateClient(KeyPair keyPair, AthenzProviderServiceConfig config,
+ AthenzProviderServiceConfig.Zones zoneConfig) {
this.keyPair = keyPair;
this.config = config;
+ this.zoneConfig = zoneConfig;
}
@Override
public X509Certificate updateCertificate(PrivateKey privateKey, TemporalAmount expiryTime) {
try {
ContentSigner contentSigner = new JcaContentSignerBuilder("SHA512WithRSA").build(keyPair.getPrivate());
- X500Name dnName = new X500Name("CN=" + config.domain() + "." + config.serviceName());
+ X500Name dnName = new X500Name("CN=" + zoneConfig.domain() + "." + zoneConfig.serviceName());
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.HOUR, 1);
Date endDate = calendar.getTime();