diff options
author | Ola Aunronning <olaa@yahooinc.com> | 2023-04-25 11:37:55 +0200 |
---|---|---|
committer | Ola Aunronning <olaa@yahooinc.com> | 2023-04-25 11:39:34 +0200 |
commit | cf4c4d51f2853f094717e957a06eb409c1b1503e (patch) | |
tree | 63444b5113a84b25d9f79f2062e2b5834f7c1b28 /vespa-athenz/src | |
parent | 8436d1e3127f6e5d1f131f23ac6d35be3692362f (diff) |
Rename AthenzIdentityProviderImpl classes
Diffstat (limited to 'vespa-athenz/src')
-rw-r--r-- | vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImpl.java | 109 | ||||
-rw-r--r-- | vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderProvider.java | 4 | ||||
-rw-r--r-- | vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImpl.java (renamed from vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplV2.java) | 125 | ||||
-rw-r--r-- | vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImplTest.java (renamed from vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java) | 16 |
4 files changed, 126 insertions, 128 deletions
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 b9f9f3862c2..3d8f12e559d 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,9 @@ import com.yahoo.container.jdisc.athenz.AthenzIdentityProvider; import com.yahoo.container.jdisc.athenz.AthenzIdentityProviderException; import com.yahoo.jdisc.Metric; import com.yahoo.metrics.ContainerMetrics; +import com.yahoo.security.AutoReloadingX509KeyManager; import com.yahoo.security.KeyStoreBuilder; +import com.yahoo.security.KeyUtils; import com.yahoo.security.MutableX509KeyManager; import com.yahoo.security.Pkcs10Csr; import com.yahoo.security.SslContextBuilder; @@ -24,9 +26,9 @@ import com.yahoo.vespa.athenz.api.ZToken; import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient; import com.yahoo.vespa.athenz.client.zts.ZtsClient; import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider; -import com.yahoo.vespa.athenz.identity.SiaIdentityProvider; +import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; +import com.yahoo.vespa.athenz.tls.AthenzX509CertificateUtils; import com.yahoo.vespa.athenz.utils.SiaUtils; -import com.yahoo.vespa.defaults.Defaults; import javax.net.ssl.SSLContext; import javax.net.ssl.X509ExtendedKeyManager; @@ -38,7 +40,6 @@ import java.security.cert.X509Certificate; import java.time.Clock; import java.time.Duration; import java.time.Instant; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -65,7 +66,6 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen // TODO Make some of these values configurable through config. Match requested expiration of register/update requests. // TODO These should match the requested expiration - static final Duration UPDATE_PERIOD = Duration.ofDays(1); static final Duration AWAIT_TERMINTATION_TIMEOUT = Duration.ofSeconds(90); private final static Duration ROLE_SSL_CONTEXT_EXPIRY = Duration.ofHours(2); // TODO CMS expects 10min or less token ttl. Use 10min default until we have configurable expiry @@ -73,20 +73,17 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen // 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 = ContainerMetrics.ATHENZ_TENANT_CERT_EXPIRY_SECONDS.baseName(); - private volatile AthenzCredentials credentials; private final Metric metric; private final Path trustStore; - private final AthenzCredentialsService athenzCredentialsService; private final ScheduledExecutorService scheduler; private final Clock clock; private final AthenzService identity; private final URI ztsEndpoint; - private final MutableX509KeyManager identityKeyManager = new MutableX509KeyManager(); + private final AutoReloadingX509KeyManager autoReloadingX509KeyManager; private final SSLContext identitySslContext; private final LoadingCache<AthenzRole, X509Certificate> roleSslCertCache; private final Map<AthenzRole, MutableX509KeyManager> roleKeyManagerCache; @@ -98,40 +95,31 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen @Inject public AthenzIdentityProviderImpl(IdentityConfig config, Metric metric) { - this(config, - metric, - CLIENT_TRUST_STORE, - new AthenzCredentialsService(config, - createNodeIdentityProvider(config), - Defaults.getDefaults().vespaHostname(), - Clock.systemUTC()), - new ScheduledThreadPoolExecutor(1), - Clock.systemUTC()); + this(config, metric, CLIENT_TRUST_STORE, new ScheduledThreadPoolExecutor(1), Clock.systemUTC()); } // Test only AthenzIdentityProviderImpl(IdentityConfig config, Metric metric, Path trustStore, - AthenzCredentialsService athenzCredentialsService, ScheduledExecutorService scheduler, Clock clock) { this.metric = metric; this.trustStore = trustStore; - this.athenzCredentialsService = athenzCredentialsService; this.scheduler = scheduler; this.clock = clock; this.identity = new AthenzService(config.domain(), config.service()); this.ztsEndpoint = URI.create(config.ztsUrl()); - roleSslCertCache = crateAutoReloadableCache(ROLE_SSL_CONTEXT_EXPIRY, this::requestRoleCertificate, this.scheduler); - roleKeyManagerCache = new HashMap<>(); - roleSpecificRoleTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createRoleToken); - domainSpecificRoleTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createRoleToken); - domainSpecificAccessTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createAccessToken); - roleSpecificAccessTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createAccessToken); + this.roleSslCertCache = crateAutoReloadableCache(ROLE_SSL_CONTEXT_EXPIRY, this::requestRoleCertificate, this.scheduler); + this.roleKeyManagerCache = new HashMap<>(); + this.roleSpecificRoleTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createRoleToken); + this.domainSpecificRoleTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createRoleToken); + this.domainSpecificAccessTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createAccessToken); + this.roleSpecificAccessTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createAccessToken); this.csrGenerator = new CsrGenerator(config.athenzDnsSuffix(), config.configserverIdentityName()); - this.identitySslContext = createIdentitySslContext(identityKeyManager, trustStore); - registerInstance(); + this.autoReloadingX509KeyManager = AutoReloadingX509KeyManager.fromPemFiles(privateKeyPath(),certificatePath()); + this.identitySslContext = createIdentitySslContext(autoReloadingX509KeyManager, trustStore); + this.scheduler.scheduleAtFixedRate(this::reportMetrics, 0, 5, TimeUnit.MINUTES); } private static <KEY, VALUE> LoadingCache<KEY, VALUE> createCache(Duration expiry, Function<KEY, VALUE> cacheLoader) { @@ -165,16 +153,6 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen .build(); } - private void registerInstance() { - try { - updateIdentityCredentials(this.athenzCredentialsService.registerInstance()); - this.scheduler.scheduleAtFixedRate(this::refreshCertificate, UPDATE_PERIOD.toMinutes(), UPDATE_PERIOD.toMinutes(), TimeUnit.MINUTES); - this.scheduler.scheduleAtFixedRate(this::reportMetrics, 0, 5, TimeUnit.MINUTES); - } catch (Throwable t) { - throw new AthenzIdentityProviderException("Could not retrieve Athenz credentials", t); - } - } - @Override public AthenzService identity() { return identity; @@ -197,13 +175,13 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen @Override public X509CertificateWithKey getIdentityCertificateWithKey() { - AthenzCredentials copy = this.credentials; - return new X509CertificateWithKey(copy.getCertificate(), copy.getKeyPair().getPrivate()); + var copy = this.autoReloadingX509KeyManager.getCurrentCertificateWithKey(); + return new X509CertificateWithKey(copy.certificate(), copy.privateKey()); } - @Override public Path certificatePath() { return athenzCredentialsService.certificatePath(); } + @Override public Path certificatePath() { return SiaUtils.getCertificateFile(identity); } - @Override public Path privateKeyPath() { return athenzCredentialsService.privateKeyPath(); } + @Override public Path privateKeyPath() { return SiaUtils.getPrivateKeyFile(identity); } @Override public SSLContext getRoleSslContext(String domain, String role) { @@ -262,7 +240,7 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen @Override public PrivateKey getPrivateKey() { - return credentials.getKeyPair().getPrivate(); + return autoReloadingX509KeyManager.getPrivateKey(AutoReloadingX509KeyManager.CERTIFICATE_ALIAS); } @Override @@ -272,7 +250,7 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen @Override public List<X509Certificate> getIdentityCertificate() { - return Collections.singletonList(credentials.getCertificate()); + return List.of(autoReloadingX509KeyManager.getCertificateChain(AutoReloadingX509KeyManager.CERTIFICATE_ALIAS)); } @Override @@ -288,19 +266,15 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen } } - private void updateIdentityCredentials(AthenzCredentials credentials) { - this.credentials = credentials; - this.identityKeyManager.updateKeystore( - KeyStoreBuilder.withType(PKCS12) - .withKeyEntry("default", credentials.getKeyPair().getPrivate(), credentials.getCertificate()) - .build(), - new char[0]); - } - private X509Certificate requestRoleCertificate(AthenzRole role) { - var doc = credentials.getIdentityDocument(); + var credentials = autoReloadingX509KeyManager.getCurrentCertificateWithKey(); + var athenzUniqueInstanceId = VespaUniqueInstanceId.fromDottedString( + AthenzX509CertificateUtils.getInstanceId(credentials.certificate()) + .orElseThrow() + ); + var keyPair = KeyUtils.toKeyPair(credentials.privateKey()); Pkcs10Csr csr = csrGenerator.generateRoleCsr( - identity, role, doc.providerUniqueId(), doc.clusterType(), credentials.getKeyPair()); + identity, role, athenzUniqueInstanceId, null, keyPair); try (ZtsClient client = createZtsClient()) { X509Certificate roleCertificate = client.getRoleCertificate(role, csr); updateRoleKeyManager(role, roleCertificate); @@ -313,7 +287,7 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen MutableX509KeyManager keyManager = roleKeyManagerCache.computeIfAbsent(role, r -> new MutableX509KeyManager()); keyManager.updateKeystore( KeyStoreBuilder.withType(PKCS12) - .withKeyEntry("default", credentials.getKeyPair().getPrivate(), certificate) + .withKeyEntry("default", autoReloadingX509KeyManager.getCurrentCertificateWithKey().privateKey(), certificate) .build(), new char[0]); } @@ -356,32 +330,13 @@ public final class AthenzIdentityProviderImpl extends AbstractComponent implemen } } - private static SiaIdentityProvider createNodeIdentityProvider(IdentityConfig config) { - return new SiaIdentityProvider( - new AthenzService(config.nodeIdentityName()), SiaUtils.DEFAULT_SIA_DIRECTORY, CLIENT_TRUST_STORE); - } - - private boolean isExpired(AthenzCredentials credentials) { - return clock.instant().isAfter(getExpirationTime(credentials)); - } - - private static Instant getExpirationTime(AthenzCredentials credentials) { - return credentials.getCertificate().getNotAfter().toInstant(); - } - - void refreshCertificate() { - try { - updateIdentityCredentials(isExpired(credentials) - ? athenzCredentialsService.registerInstance() - : athenzCredentialsService.updateCredentials(credentials.getIdentityDocument(), identitySslContext)); - } catch (Throwable t) { - log.log(Level.WARNING, "Failed to update credentials: " + t.getMessage(), t); - } + private static Instant getExpirationTime(X509Certificate certificate) { + return certificate.getNotAfter().toInstant(); } void reportMetrics() { try { - Instant expirationTime = getExpirationTime(credentials); + Instant expirationTime = getExpirationTime(autoReloadingX509KeyManager.getCurrentCertificateWithKey().certificate()); Duration remainingLifetime = Duration.between(clock.instant(), expirationTime); metric.set(CERTIFICATE_EXPIRY_METRIC_NAME, remainingLifetime.getSeconds(), null); } catch (Throwable t) { diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderProvider.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderProvider.java index d53b68bffc9..66dad931815 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderProvider.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderProvider.java @@ -21,9 +21,9 @@ public class AthenzIdentityProviderProvider implements Provider<AthenzIdentityPr @Inject public AthenzIdentityProviderProvider(IdentityConfig config, Metric metric) { if (Files.exists(NODE_ADMIN_MANAGED_IDENTITY_DOCUMENT)) - athenzIdentityProvider = new AthenzIdentityProviderImplV2(config, metric); - else athenzIdentityProvider = new AthenzIdentityProviderImpl(config, metric); + else + athenzIdentityProvider = new LegacyAthenzIdentityProviderImpl(config, metric); } @Override diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplV2.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImpl.java index b03ad244ee4..e67af028402 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplV2.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImpl.java @@ -11,9 +11,7 @@ import com.yahoo.container.jdisc.athenz.AthenzIdentityProvider; import com.yahoo.container.jdisc.athenz.AthenzIdentityProviderException; import com.yahoo.jdisc.Metric; import com.yahoo.metrics.ContainerMetrics; -import com.yahoo.security.AutoReloadingX509KeyManager; import com.yahoo.security.KeyStoreBuilder; -import com.yahoo.security.KeyUtils; import com.yahoo.security.MutableX509KeyManager; import com.yahoo.security.Pkcs10Csr; import com.yahoo.security.SslContextBuilder; @@ -27,9 +25,8 @@ import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient; import com.yahoo.vespa.athenz.client.zts.ZtsClient; import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider; import com.yahoo.vespa.athenz.identity.SiaIdentityProvider; -import com.yahoo.vespa.athenz.identityprovider.api.VespaUniqueInstanceId; -import com.yahoo.vespa.athenz.tls.AthenzX509CertificateUtils; import com.yahoo.vespa.athenz.utils.SiaUtils; +import com.yahoo.vespa.defaults.Defaults; import javax.net.ssl.SSLContext; import javax.net.ssl.X509ExtendedKeyManager; @@ -41,6 +38,7 @@ import java.security.cert.X509Certificate; import java.time.Clock; import java.time.Duration; import java.time.Instant; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -58,16 +56,16 @@ import static com.yahoo.security.KeyStoreType.PKCS12; * * @author mortent * @author bjorncs - * @author olaa */ // 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 AthenzIdentityProviderImplV2 extends AbstractComponent implements AthenzIdentityProvider, ServiceIdentityProvider { +public final class LegacyAthenzIdentityProviderImpl extends AbstractComponent implements AthenzIdentityProvider, ServiceIdentityProvider { - private static final Logger log = Logger.getLogger(AthenzIdentityProviderImplV2.class.getName()); + private static final Logger log = Logger.getLogger(LegacyAthenzIdentityProviderImpl.class.getName()); // TODO Make some of these values configurable through config. Match requested expiration of register/update requests. // TODO These should match the requested expiration + static final Duration UPDATE_PERIOD = Duration.ofDays(1); static final Duration AWAIT_TERMINTATION_TIMEOUT = Duration.ofSeconds(90); private final static Duration ROLE_SSL_CONTEXT_EXPIRY = Duration.ofHours(2); // TODO CMS expects 10min or less token ttl. Use 10min default until we have configurable expiry @@ -75,17 +73,20 @@ public final class AthenzIdentityProviderImplV2 extends AbstractComponent implem // 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 = ContainerMetrics.ATHENZ_TENANT_CERT_EXPIRY_SECONDS.baseName(); + private volatile AthenzCredentials credentials; private final Metric metric; private final Path trustStore; + private final AthenzCredentialsService athenzCredentialsService; private final ScheduledExecutorService scheduler; private final Clock clock; private final AthenzService identity; private final URI ztsEndpoint; - private final AutoReloadingX509KeyManager autoReloadingX509KeyManager; + private final MutableX509KeyManager identityKeyManager = new MutableX509KeyManager(); private final SSLContext identitySslContext; private final LoadingCache<AthenzRole, X509Certificate> roleSslCertCache; private final Map<AthenzRole, MutableX509KeyManager> roleKeyManagerCache; @@ -96,32 +97,41 @@ public final class AthenzIdentityProviderImplV2 extends AbstractComponent implem private final CsrGenerator csrGenerator; @Inject - public AthenzIdentityProviderImplV2(IdentityConfig config, Metric metric) { - this(config, metric, CLIENT_TRUST_STORE, new ScheduledThreadPoolExecutor(1), Clock.systemUTC()); + public LegacyAthenzIdentityProviderImpl(IdentityConfig config, Metric metric) { + this(config, + metric, + CLIENT_TRUST_STORE, + new AthenzCredentialsService(config, + createNodeIdentityProvider(config), + Defaults.getDefaults().vespaHostname(), + Clock.systemUTC()), + new ScheduledThreadPoolExecutor(1), + Clock.systemUTC()); } // Test only - AthenzIdentityProviderImplV2(IdentityConfig config, - Metric metric, - Path trustStore, - ScheduledExecutorService scheduler, - Clock clock) { + LegacyAthenzIdentityProviderImpl(IdentityConfig config, + Metric metric, + Path trustStore, + AthenzCredentialsService athenzCredentialsService, + ScheduledExecutorService scheduler, + Clock clock) { this.metric = metric; this.trustStore = trustStore; + this.athenzCredentialsService = athenzCredentialsService; this.scheduler = scheduler; this.clock = clock; this.identity = new AthenzService(config.domain(), config.service()); this.ztsEndpoint = URI.create(config.ztsUrl()); - this.roleSslCertCache = crateAutoReloadableCache(ROLE_SSL_CONTEXT_EXPIRY, this::requestRoleCertificate, this.scheduler); - this.roleKeyManagerCache = new HashMap<>(); - this.roleSpecificRoleTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createRoleToken); - this.domainSpecificRoleTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createRoleToken); - this.domainSpecificAccessTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createAccessToken); - this.roleSpecificAccessTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createAccessToken); + roleSslCertCache = crateAutoReloadableCache(ROLE_SSL_CONTEXT_EXPIRY, this::requestRoleCertificate, this.scheduler); + roleKeyManagerCache = new HashMap<>(); + roleSpecificRoleTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createRoleToken); + domainSpecificRoleTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createRoleToken); + domainSpecificAccessTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createAccessToken); + roleSpecificAccessTokenCache = createCache(ROLE_TOKEN_EXPIRY, this::createAccessToken); this.csrGenerator = new CsrGenerator(config.athenzDnsSuffix(), config.configserverIdentityName()); - this.autoReloadingX509KeyManager = AutoReloadingX509KeyManager.fromPemFiles(privateKeyPath(),certificatePath()); - this.identitySslContext = createIdentitySslContext(autoReloadingX509KeyManager, trustStore); - this.scheduler.scheduleAtFixedRate(this::reportMetrics, 0, 5, TimeUnit.MINUTES); + this.identitySslContext = createIdentitySslContext(identityKeyManager, trustStore); + registerInstance(); } private static <KEY, VALUE> LoadingCache<KEY, VALUE> createCache(Duration expiry, Function<KEY, VALUE> cacheLoader) { @@ -155,6 +165,16 @@ public final class AthenzIdentityProviderImplV2 extends AbstractComponent implem .build(); } + private void registerInstance() { + try { + updateIdentityCredentials(this.athenzCredentialsService.registerInstance()); + this.scheduler.scheduleAtFixedRate(this::refreshCertificate, UPDATE_PERIOD.toMinutes(), UPDATE_PERIOD.toMinutes(), TimeUnit.MINUTES); + this.scheduler.scheduleAtFixedRate(this::reportMetrics, 0, 5, TimeUnit.MINUTES); + } catch (Throwable t) { + throw new AthenzIdentityProviderException("Could not retrieve Athenz credentials", t); + } + } + @Override public AthenzService identity() { return identity; @@ -177,13 +197,13 @@ public final class AthenzIdentityProviderImplV2 extends AbstractComponent implem @Override public X509CertificateWithKey getIdentityCertificateWithKey() { - var copy = this.autoReloadingX509KeyManager.getCurrentCertificateWithKey(); - return new X509CertificateWithKey(copy.certificate(), copy.privateKey()); + AthenzCredentials copy = this.credentials; + return new X509CertificateWithKey(copy.getCertificate(), copy.getKeyPair().getPrivate()); } - @Override public Path certificatePath() { return SiaUtils.getCertificateFile(identity); } + @Override public Path certificatePath() { return athenzCredentialsService.certificatePath(); } - @Override public Path privateKeyPath() { return SiaUtils.getPrivateKeyFile(identity); } + @Override public Path privateKeyPath() { return athenzCredentialsService.privateKeyPath(); } @Override public SSLContext getRoleSslContext(String domain, String role) { @@ -242,7 +262,7 @@ public final class AthenzIdentityProviderImplV2 extends AbstractComponent implem @Override public PrivateKey getPrivateKey() { - return autoReloadingX509KeyManager.getPrivateKey(AutoReloadingX509KeyManager.CERTIFICATE_ALIAS); + return credentials.getKeyPair().getPrivate(); } @Override @@ -252,7 +272,7 @@ public final class AthenzIdentityProviderImplV2 extends AbstractComponent implem @Override public List<X509Certificate> getIdentityCertificate() { - return List.of(autoReloadingX509KeyManager.getCertificateChain(AutoReloadingX509KeyManager.CERTIFICATE_ALIAS)); + return Collections.singletonList(credentials.getCertificate()); } @Override @@ -268,15 +288,19 @@ public final class AthenzIdentityProviderImplV2 extends AbstractComponent implem } } + private void updateIdentityCredentials(AthenzCredentials credentials) { + this.credentials = credentials; + this.identityKeyManager.updateKeystore( + KeyStoreBuilder.withType(PKCS12) + .withKeyEntry("default", credentials.getKeyPair().getPrivate(), credentials.getCertificate()) + .build(), + new char[0]); + } + private X509Certificate requestRoleCertificate(AthenzRole role) { - var credentials = autoReloadingX509KeyManager.getCurrentCertificateWithKey(); - var athenzUniqueInstanceId = VespaUniqueInstanceId.fromDottedString( - AthenzX509CertificateUtils.getInstanceId(credentials.certificate()) - .orElseThrow() - ); - var keyPair = KeyUtils.toKeyPair(credentials.privateKey()); + var doc = credentials.getIdentityDocument(); Pkcs10Csr csr = csrGenerator.generateRoleCsr( - identity, role, athenzUniqueInstanceId, null, keyPair); + identity, role, doc.providerUniqueId(), doc.clusterType(), credentials.getKeyPair()); try (ZtsClient client = createZtsClient()) { X509Certificate roleCertificate = client.getRoleCertificate(role, csr); updateRoleKeyManager(role, roleCertificate); @@ -289,7 +313,7 @@ public final class AthenzIdentityProviderImplV2 extends AbstractComponent implem MutableX509KeyManager keyManager = roleKeyManagerCache.computeIfAbsent(role, r -> new MutableX509KeyManager()); keyManager.updateKeystore( KeyStoreBuilder.withType(PKCS12) - .withKeyEntry("default", autoReloadingX509KeyManager.getCurrentCertificateWithKey().privateKey(), certificate) + .withKeyEntry("default", credentials.getKeyPair().getPrivate(), certificate) .build(), new char[0]); } @@ -332,13 +356,32 @@ public final class AthenzIdentityProviderImplV2 extends AbstractComponent implem } } - private static Instant getExpirationTime(X509Certificate certificate) { - return certificate.getNotAfter().toInstant(); + private static SiaIdentityProvider createNodeIdentityProvider(IdentityConfig config) { + return new SiaIdentityProvider( + new AthenzService(config.nodeIdentityName()), SiaUtils.DEFAULT_SIA_DIRECTORY, CLIENT_TRUST_STORE); + } + + private boolean isExpired(AthenzCredentials credentials) { + return clock.instant().isAfter(getExpirationTime(credentials)); + } + + private static Instant getExpirationTime(AthenzCredentials credentials) { + return credentials.getCertificate().getNotAfter().toInstant(); + } + + void refreshCertificate() { + try { + updateIdentityCredentials(isExpired(credentials) + ? athenzCredentialsService.registerInstance() + : athenzCredentialsService.updateCredentials(credentials.getIdentityDocument(), identitySslContext)); + } catch (Throwable t) { + log.log(Level.WARNING, "Failed to update credentials: " + t.getMessage(), t); + } } void reportMetrics() { try { - Instant expirationTime = getExpirationTime(autoReloadingX509KeyManager.getCurrentCertificateWithKey().certificate()); + Instant expirationTime = getExpirationTime(credentials); Duration remainingLifetime = Duration.between(clock.instant(), expirationTime); metric.set(CERTIFICATE_EXPIRY_METRIC_NAME, remainingLifetime.getSeconds(), null); } catch (Throwable t) { diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImplTest.java index c9d2ea581bb..75dc42cd4a6 100644 --- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/AthenzIdentityProviderImplTest.java +++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImplTest.java @@ -44,7 +44,7 @@ import static org.mockito.Mockito.when; * @author mortent * @author bjorncs */ -public class AthenzIdentityProviderImplTest { +public class LegacyAthenzIdentityProviderImplTest { @TempDir public File tempDir; @@ -91,7 +91,7 @@ public class AthenzIdentityProviderImplTest { when(credentialService.registerInstance()) .thenThrow(new RuntimeException("athenz unavailable")); - new AthenzIdentityProviderImpl(IDENTITY_CONFIG, mock(Metric.class), trustStoreFile, credentialService, mock(ScheduledExecutorService.class), new ManualClock(Instant.EPOCH)); + new LegacyAthenzIdentityProviderImpl(IDENTITY_CONFIG, mock(Metric.class), trustStoreFile, credentialService, mock(ScheduledExecutorService.class), new ManualClock(Instant.EPOCH)); }); } @@ -113,29 +113,29 @@ public class AthenzIdentityProviderImplTest { .thenThrow(new RuntimeException("#2")) .thenReturn(new AthenzCredentials(certificate, keyPair, null)); - AthenzIdentityProviderImpl identityProvider = - new AthenzIdentityProviderImpl(IDENTITY_CONFIG, metric, trustStoreFile, athenzCredentialsService, mock(ScheduledExecutorService.class), clock); + LegacyAthenzIdentityProviderImpl identityProvider = + new LegacyAthenzIdentityProviderImpl(IDENTITY_CONFIG, metric, trustStoreFile, athenzCredentialsService, mock(ScheduledExecutorService.class), clock); identityProvider.reportMetrics(); - verify(metric).set(eq(AthenzIdentityProviderImpl.CERTIFICATE_EXPIRY_METRIC_NAME), eq(certificateValidity.getSeconds()), any()); + verify(metric).set(eq(LegacyAthenzIdentityProviderImpl.CERTIFICATE_EXPIRY_METRIC_NAME), eq(certificateValidity.getSeconds()), any()); // Advance 1 day, refresh fails, cert is 1 day old clock.advance(Duration.ofDays(1)); identityProvider.refreshCertificate(); identityProvider.reportMetrics(); - verify(metric).set(eq(AthenzIdentityProviderImpl.CERTIFICATE_EXPIRY_METRIC_NAME), eq(certificateValidity.minus(Duration.ofDays(1)).getSeconds()), any()); + verify(metric).set(eq(LegacyAthenzIdentityProviderImpl.CERTIFICATE_EXPIRY_METRIC_NAME), eq(certificateValidity.minus(Duration.ofDays(1)).getSeconds()), any()); // Advance 1 more day, refresh fails, cert is 2 days old clock.advance(Duration.ofDays(1)); identityProvider.refreshCertificate(); identityProvider.reportMetrics(); - verify(metric).set(eq(AthenzIdentityProviderImpl.CERTIFICATE_EXPIRY_METRIC_NAME), eq(certificateValidity.minus(Duration.ofDays(2)).getSeconds()), any()); + verify(metric).set(eq(LegacyAthenzIdentityProviderImpl.CERTIFICATE_EXPIRY_METRIC_NAME), eq(certificateValidity.minus(Duration.ofDays(2)).getSeconds()), any()); // Advance 1 more day, refresh succeds, cert is new clock.advance(Duration.ofDays(1)); identityProvider.refreshCertificate(); identityProvider.reportMetrics(); - verify(metric).set(eq(AthenzIdentityProviderImpl.CERTIFICATE_EXPIRY_METRIC_NAME), eq(certificateValidity.getSeconds()), any()); + verify(metric).set(eq(LegacyAthenzIdentityProviderImpl.CERTIFICATE_EXPIRY_METRIC_NAME), eq(certificateValidity.getSeconds()), any()); } |