diff options
author | Morten Tokle <mortent@yahooinc.com> | 2024-02-13 13:58:51 +0100 |
---|---|---|
committer | Morten Tokle <mortent@yahooinc.com> | 2024-02-13 14:06:27 +0100 |
commit | 05ab6800a9a9d2119aba89b2bf9d15aa29b11a48 (patch) | |
tree | 798fb3ef69c3d9447d04967e8332aefc673e1f6e /configserver/src/main/java/com/yahoo | |
parent | 283af757b42ccb5ac6bfa8339a0a0674ae51c733 (diff) |
Proxy endpoint certificate secrets through EndpointCertificateSecretStore
Diffstat (limited to 'configserver/src/main/java/com/yahoo')
6 files changed, 103 insertions, 21 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java index ad785a33d5b..5017f25b2f8 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java @@ -6,6 +6,7 @@ import com.yahoo.cloud.config.ConfigserverConfig; import com.yahoo.component.Version; import com.yahoo.config.application.api.ApplicationPackage; import com.yahoo.config.model.api.ConfigDefinitionRepo; +import com.yahoo.config.model.api.EndpointCertificateSecretStore; import com.yahoo.config.model.api.Model; import com.yahoo.config.model.api.ModelContext; import com.yahoo.config.model.api.ModelFactory; @@ -39,6 +40,7 @@ import com.yahoo.vespa.model.container.ApplicationContainerCluster; import com.yahoo.vespa.model.content.cluster.ContentCluster; import java.util.Comparator; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ExecutorService; @@ -65,6 +67,7 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> { private final SecretStore secretStore; private final ExecutorService executor; private final OnnxModelCost onnxModelCost; + private final List<EndpointCertificateSecretStore> endpointCertificateSecretStores; public ActivatedModelsBuilder(TenantName tenant, long applicationGeneration, @@ -80,7 +83,8 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> { Zone zone, ModelFactoryRegistry modelFactoryRegistry, ConfigDefinitionRepo configDefinitionRepo, - OnnxModelCost onnxModelCost) { + OnnxModelCost onnxModelCost, + List<EndpointCertificateSecretStore> endpointCertificateSecretStores) { super(modelFactoryRegistry, configserverConfig, zone, hostProvisionerProvider, new SilentDeployLogger()); this.tenant = tenant; this.applicationGeneration = applicationGeneration; @@ -93,6 +97,7 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> { this.secretStore = secretStore; this.executor = executor; this.onnxModelCost = onnxModelCost; + this.endpointCertificateSecretStores = endpointCertificateSecretStores; } @Override @@ -160,7 +165,7 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> { LegacyFlags.from(applicationPackage, flagSource), new EndpointCertificateMetadataStore(curator, TenantRepository.getTenantPath(tenant)) .readEndpointCertificateMetadata(applicationId) - .flatMap(new EndpointCertificateRetriever(secretStore)::readEndpointCertificateSecrets), + .flatMap(new EndpointCertificateRetriever(endpointCertificateSecretStores)::readEndpointCertificateSecrets), zkClient.readAthenzDomain(), zkClient.readQuota(), zkClient.readTenantSecretStores(), diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java index b3772127636..fffa77fc419 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java @@ -16,6 +16,7 @@ import com.yahoo.config.application.api.FileRegistry; import com.yahoo.config.model.api.ConfigDefinitionRepo; import com.yahoo.config.model.api.ContainerEndpoint; import com.yahoo.config.model.api.EndpointCertificateMetadata; +import com.yahoo.config.model.api.EndpointCertificateSecretStore; import com.yahoo.config.model.api.EndpointCertificateSecrets; import com.yahoo.config.model.api.FileDistribution; import com.yahoo.config.model.api.OnnxModelCost; @@ -95,6 +96,7 @@ public class SessionPreparer { private final ExecutorService executor; private final BooleanFlag writeSessionData; private final OnnxModelCost onnxModelCost; + private final List<EndpointCertificateSecretStore> endpointCertificateSecretStores; public SessionPreparer(ModelFactoryRegistry modelFactoryRegistry, FileDistributionFactory fileDistributionFactory, @@ -106,7 +108,8 @@ public class SessionPreparer { Zone zone, FlagSource flagSource, SecretStore secretStore, - OnnxModelCost onnxModelCost) { + OnnxModelCost onnxModelCost, + List<EndpointCertificateSecretStore> endpointCertificateSecretStores) { this.modelFactoryRegistry = modelFactoryRegistry; this.fileDistributionFactory = fileDistributionFactory; this.hostProvisionerProvider = hostProvisionerProvider; @@ -119,6 +122,7 @@ public class SessionPreparer { this.executor = executor; this.writeSessionData = Flags.WRITE_CONFIG_SERVER_SESSION_DATA_AS_ONE_BLOB.bindTo(flagSource); this.onnxModelCost = onnxModelCost; + this.endpointCertificateSecretStores = endpointCertificateSecretStores; } ExecutorService getExecutor() { return executor; } @@ -139,7 +143,7 @@ public class SessionPreparer { Preparation preparation = new Preparation(hostValidator, logger, params, activeApplicationVersions, TenantRepository.getTenantPath(applicationId.tenant()), serverDbSessionDir, applicationPackage, sessionZooKeeperClient, - onnxModelCost); + onnxModelCost, endpointCertificateSecretStores); preparation.preprocess(); try { AllocatedHosts allocatedHosts = preparation.buildModels(now); @@ -191,7 +195,8 @@ public class SessionPreparer { Preparation(HostValidator hostValidator, DeployLogger logger, PrepareParams params, Optional<ApplicationVersions> activeApplicationVersions, Path tenantPath, File serverDbSessionDir, ApplicationPackage applicationPackage, - SessionZooKeeperClient sessionZooKeeperClient, OnnxModelCost onnxModelCost) { + SessionZooKeeperClient sessionZooKeeperClient, OnnxModelCost onnxModelCost, + List<EndpointCertificateSecretStore> endpointCertificateSecretStores) { this.logger = logger; this.params = params; this.applicationPackage = applicationPackage; @@ -201,7 +206,7 @@ public class SessionPreparer { this.vespaVersion = params.vespaVersion().orElse(Vtag.currentVersion); this.containerEndpointsCache = new ContainerEndpointsCache(tenantPath, curator); this.endpointCertificateMetadataStore = new EndpointCertificateMetadataStore(curator, tenantPath); - EndpointCertificateRetriever endpointCertificateRetriever = new EndpointCertificateRetriever(secretStore); + EndpointCertificateRetriever endpointCertificateRetriever = new EndpointCertificateRetriever(endpointCertificateSecretStores); this.endpointCertificateMetadata = params.endpointCertificateMetadata(); Optional<EndpointCertificateSecrets> endpointCertificateSecrets = endpointCertificateMetadata .or(() -> endpointCertificateMetadataStore.readEndpointCertificateMetadata(applicationId)) diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java index 2f0d8b4065d..546277c4aba 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java @@ -9,6 +9,7 @@ import com.yahoo.concurrent.StripedExecutor; import com.yahoo.config.application.api.ApplicationPackage; import com.yahoo.config.application.api.DeployLogger; import com.yahoo.config.model.api.ConfigDefinitionRepo; +import com.yahoo.config.model.api.EndpointCertificateSecretStore; import com.yahoo.config.model.api.OnnxModelCost; import com.yahoo.config.model.application.provider.DeployData; import com.yahoo.config.model.application.provider.FilesApplicationPackage; @@ -121,6 +122,7 @@ public class SessionRepository { private final Path sessionsPath; private final TenantName tenantName; private final OnnxModelCost onnxModelCost; + private final List<EndpointCertificateSecretStore> endpointCertificateSecretStores; private final SessionCounter sessionCounter; private final SecretStore secretStore; private final HostProvisionerProvider hostProvisionerProvider; @@ -152,9 +154,11 @@ public class SessionRepository { ModelFactoryRegistry modelFactoryRegistry, ConfigDefinitionRepo configDefinitionRepo, int maxNodeSize, - OnnxModelCost onnxModelCost) { + OnnxModelCost onnxModelCost, + List<EndpointCertificateSecretStore> endpointCertificateSecretStores) { this.tenantName = tenantName; this.onnxModelCost = onnxModelCost; + this.endpointCertificateSecretStores = endpointCertificateSecretStores; sessionCounter = new SessionCounter(curator, tenantName); this.sessionsPath = TenantRepository.getSessionsPath(tenantName); this.clock = clock; @@ -561,7 +565,8 @@ public class SessionRepository { zone, modelFactoryRegistry, configDefinitionRepo, - onnxModelCost); + onnxModelCost, + endpointCertificateSecretStores); return ApplicationVersions.fromList(builder.buildModels(session.getApplicationId(), session.getDockerImageRepository(), session.getVespaVersion(), diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/DefaultEndpointCertificateSecretStore.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/DefaultEndpointCertificateSecretStore.java new file mode 100644 index 00000000000..575fd155bdd --- /dev/null +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/DefaultEndpointCertificateSecretStore.java @@ -0,0 +1,44 @@ +// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + +package com.yahoo.vespa.config.server.tenant; + +import com.yahoo.config.model.api.EndpointCertificateMetadata; +import com.yahoo.config.model.api.EndpointCertificateSecretStore; +import com.yahoo.container.jdisc.secretstore.SecretNotFoundException; +import com.yahoo.container.jdisc.secretstore.SecretStore; + +import javax.inject.Inject; +import java.util.Optional; + +public class DefaultEndpointCertificateSecretStore extends EndpointCertificateSecretStore { + + private final SecretStore secretStore; + + @Inject + public DefaultEndpointCertificateSecretStore(SecretStore secretStore) { + this.secretStore = secretStore; + } + + + @Override + public Optional<String> getPrivateKey(EndpointCertificateMetadata metadata) { + return getValue(metadata.keyName(), metadata.version()); + } + + @Override + public Optional<String> getCertificate(EndpointCertificateMetadata metadata) { + return getValue(metadata.certName(), metadata.version()); + } + + private Optional<String> getValue(String key, int version) { + try { + return Optional.ofNullable(secretStore.getSecret(key, version)); + } catch (SecretNotFoundException e) { + return Optional.empty(); + } + } + @Override + public boolean supports(EndpointCertificateMetadata.Provider provider) { + return provider == EndpointCertificateMetadata.Provider.digicert || provider == EndpointCertificateMetadata.Provider.globalsign; + } +} diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java index 10f59290572..43baac8fec9 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java @@ -2,14 +2,17 @@ package com.yahoo.vespa.config.server.tenant; import com.yahoo.config.model.api.EndpointCertificateMetadata; +import com.yahoo.config.model.api.EndpointCertificateSecretStore; import com.yahoo.config.model.api.EndpointCertificateSecrets; import com.yahoo.container.jdisc.secretstore.SecretStore; import com.yahoo.security.KeyUtils; import com.yahoo.security.X509CertificateUtils; +import com.yahoo.stream.CustomCollectors; import java.security.PrivateKey; import java.security.PublicKey; import java.security.cert.X509Certificate; +import java.util.List; import java.util.Optional; import java.util.logging.Level; import java.util.logging.Logger; @@ -19,7 +22,13 @@ import java.util.logging.Logger; * * @author andreer */ -public record EndpointCertificateRetriever(SecretStore secretStore) { +public class EndpointCertificateRetriever { + + private final List<EndpointCertificateSecretStore> secretStores; + + public EndpointCertificateRetriever(List<EndpointCertificateSecretStore> secretStores) { + this.secretStores = List.copyOf(secretStores); + } private static final Logger log = Logger.getLogger(EndpointCertificateRetriever.class.getName()); @@ -29,12 +38,17 @@ public record EndpointCertificateRetriever(SecretStore secretStore) { private EndpointCertificateSecrets readFromSecretStore(EndpointCertificateMetadata endpointCertificateMetadata) { try { - String cert = secretStore.getSecret(endpointCertificateMetadata.certName(), endpointCertificateMetadata.version()); - String key = secretStore.getSecret(endpointCertificateMetadata.keyName(), endpointCertificateMetadata.version()); + EndpointCertificateSecrets endpointCertificateSecrets = secretStores.stream() + .filter(store -> store.supports(endpointCertificateMetadata.issuer())) + .collect(CustomCollectors.singleton()) + .orElseThrow(() -> new RuntimeException("No provider of secrets for issuer " + endpointCertificateMetadata.issuer())) + .getSecret(endpointCertificateMetadata); - verifyKeyMatchesCertificate(endpointCertificateMetadata, cert, key); + if (endpointCertificateSecrets.isMissing()) + return endpointCertificateSecrets; - return new EndpointCertificateSecrets(cert, key, endpointCertificateMetadata.version()); + verifyKeyMatchesCertificate(endpointCertificateMetadata, endpointCertificateSecrets); + return endpointCertificateSecrets; } catch (RuntimeException e) { log.log(Level.WARNING, "Exception thrown during certificate retrieval", e); // Assume not ready yet @@ -42,10 +56,10 @@ public record EndpointCertificateRetriever(SecretStore secretStore) { } } - private void verifyKeyMatchesCertificate(EndpointCertificateMetadata endpointCertificateMetadata, String cert, String key) { - X509Certificate x509Certificate = X509CertificateUtils.fromPem(cert); + private void verifyKeyMatchesCertificate(EndpointCertificateMetadata endpointCertificateMetadata, EndpointCertificateSecrets endpointCertificateSecrets) { + X509Certificate x509Certificate = X509CertificateUtils.fromPem(endpointCertificateSecrets.certificate()); - PrivateKey privateKey = KeyUtils.fromPemEncodedPrivateKey(key); + PrivateKey privateKey = KeyUtils.fromPemEncodedPrivateKey(endpointCertificateSecrets.key()); PublicKey publicKey = x509Certificate.getPublicKey(); if(!X509CertificateUtils.privateKeyMatchesPublicKey(privateKey, publicKey)) { diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java index 895c9819a03..8c4445f897c 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java @@ -5,12 +5,14 @@ import com.google.common.collect.ImmutableSet; import com.yahoo.cloud.config.ConfigserverConfig; import com.yahoo.cloud.config.ZookeeperServerConfig; import com.yahoo.component.annotation.Inject; +import com.yahoo.component.provider.ComponentRegistry; import com.yahoo.concurrent.DaemonThreadFactory; import com.yahoo.concurrent.Lock; import com.yahoo.concurrent.Locks; import com.yahoo.concurrent.StripedExecutor; import com.yahoo.concurrent.ThreadFactoryFactory; import com.yahoo.config.model.api.ConfigDefinitionRepo; +import com.yahoo.config.model.api.EndpointCertificateSecretStore; import com.yahoo.config.model.api.OnnxModelCost; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.TenantName; @@ -120,6 +122,7 @@ public class TenantRepository { new ScheduledThreadPoolExecutor(1, new DaemonThreadFactory("check for removed applications")); private final Curator.DirectoryCache directoryCache; private final ZookeeperServerConfig zookeeperServerConfig; + private final List<EndpointCertificateSecretStore> endpointCertificateSecretStores; private final OnnxModelCost onnxModelCost; /** @@ -141,7 +144,8 @@ public class TenantRepository { TenantListener tenantListener, ZookeeperServerConfig zookeeperServerConfig, FileDirectory fileDirectory, - OnnxModelCost onnxModelCost) { + OnnxModelCost onnxModelCost, + ComponentRegistry<EndpointCertificateSecretStore> endpointCertificateSecretStores) { this(hostRegistry, curator, metrics, @@ -161,7 +165,8 @@ public class TenantRepository { configActivationListener, tenantListener, zookeeperServerConfig, - onnxModelCost); + onnxModelCost, + endpointCertificateSecretStores.allComponents()); } public TenantRepository(HostRegistry hostRegistry, @@ -183,7 +188,8 @@ public class TenantRepository { ConfigActivationListener configActivationListener, TenantListener tenantListener, ZookeeperServerConfig zookeeperServerConfig, - OnnxModelCost onnxModelCost) { + OnnxModelCost onnxModelCost, + List<EndpointCertificateSecretStore> endpointCertificateSecretStores) { this.hostRegistry = hostRegistry; this.configserverConfig = configserverConfig; this.curator = curator; @@ -204,6 +210,7 @@ public class TenantRepository { this.configActivationListener = configActivationListener; this.tenantListener = tenantListener; this.zookeeperServerConfig = zookeeperServerConfig; + this.endpointCertificateSecretStores = endpointCertificateSecretStores; // This we should control with a feature flag. this.deployHelperExecutor = createModelBuilderExecutor(); this.onnxModelCost = onnxModelCost; @@ -360,7 +367,8 @@ public class TenantRepository { zone, flagSource, secretStore, - onnxModelCost); + onnxModelCost, + endpointCertificateSecretStores); SessionRepository sessionRepository = new SessionRepository(tenantName, applicationRepo, sessionPreparer, @@ -379,7 +387,8 @@ public class TenantRepository { modelFactoryRegistry, configDefinitionRepo, zookeeperServerConfig.juteMaxBuffer(), - onnxModelCost); + onnxModelCost, + endpointCertificateSecretStores); log.log(Level.FINE, "Adding tenant '" + tenantName + "'" + ", created " + created + ". Bootstrapping in " + Duration.between(start, clock.instant())); Tenant tenant = new Tenant(tenantName, sessionRepository, applicationRepo, created); |