summaryrefslogtreecommitdiffstats
path: root/configserver
diff options
context:
space:
mode:
authorMorten Tokle <mortent@yahooinc.com>2024-02-13 13:58:51 +0100
committerMorten Tokle <mortent@yahooinc.com>2024-02-13 14:06:27 +0100
commit05ab6800a9a9d2119aba89b2bf9d15aa29b11a48 (patch)
tree798fb3ef69c3d9447d04967e8332aefc673e1f6e /configserver
parent283af757b42ccb5ac6bfa8339a0a0674ae51c733 (diff)
Proxy endpoint certificate secrets through EndpointCertificateSecretStore
Diffstat (limited to 'configserver')
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java9
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java13
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionRepository.java9
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/DefaultEndpointCertificateSecretStore.java44
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetriever.java30
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TenantRepository.java19
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStore.java4
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java6
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetrieverTest.java105
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRepositoryTest.java8
-rw-r--r--configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TestTenantRepository.java8
11 files changed, 227 insertions, 28 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);
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStore.java b/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStore.java
index ac97fe0ba05..cdee9f5eb65 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStore.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/MockSecretStore.java
@@ -19,7 +19,9 @@ public class MockSecretStore implements SecretStore {
@Override
public String getSecret(String key, int version) {
- return secrets.get(key).get(version);
+ if(secrets.containsKey(key))
+ return secrets.get(key).get(version);
+ throw new SecretNotFoundException("Key not found: " + key);
}
public void put(String key, int version, String value) {
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java
index 240826b93da..f736eab0576 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/session/SessionPreparerTest.java
@@ -41,6 +41,7 @@ import com.yahoo.vespa.config.server.model.TestModelFactory;
import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry;
import com.yahoo.vespa.config.server.provision.HostProvisionerProvider;
import com.yahoo.vespa.config.server.tenant.ContainerEndpointsCache;
+import com.yahoo.vespa.config.server.tenant.DefaultEndpointCertificateSecretStore;
import com.yahoo.vespa.config.server.tenant.EndpointCertificateMetadataStore;
import com.yahoo.vespa.config.server.tenant.EndpointCertificateRetriever;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
@@ -134,7 +135,8 @@ public class SessionPreparerTest {
zone,
flagSource,
secretStore,
- OnnxModelCost.disabled());
+ OnnxModelCost.disabled(),
+ List.of(new DefaultEndpointCertificateSecretStore(secretStore)));
}
@Test(expected = InvalidApplicationException.class)
@@ -312,7 +314,7 @@ public class SessionPreparerTest {
Path tenantPath = TenantRepository.getTenantPath(applicationId.tenant());
Optional<EndpointCertificateSecrets> endpointCertificateSecrets = new EndpointCertificateMetadataStore(curator, tenantPath)
.readEndpointCertificateMetadata(applicationId)
- .flatMap(p -> new EndpointCertificateRetriever(secretStore).readEndpointCertificateSecrets(p));
+ .flatMap(p -> new EndpointCertificateRetriever(List.of(new DefaultEndpointCertificateSecretStore(secretStore))).readEndpointCertificateSecrets(p));
assertTrue(endpointCertificateSecrets.isPresent());
assertTrue(endpointCertificateSecrets.get().key().startsWith("-----BEGIN EC PRIVATE KEY"));
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetrieverTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetrieverTest.java
new file mode 100644
index 00000000000..75dbb7c7815
--- /dev/null
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/EndpointCertificateRetrieverTest.java
@@ -0,0 +1,105 @@
+// 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.config.model.api.EndpointCertificateSecrets;
+import com.yahoo.security.KeyAlgorithm;
+import com.yahoo.security.KeyUtils;
+import com.yahoo.security.SignatureAlgorithm;
+import com.yahoo.security.X509CertificateBuilder;
+import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.vespa.config.server.MockSecretStore;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import javax.security.auth.x500.X500Principal;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.cert.X509Certificate;
+import java.time.Instant;
+import java.time.temporal.ChronoUnit;
+import java.util.List;
+import java.util.Optional;
+
+public class EndpointCertificateRetrieverTest {
+
+
+ private final KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256);
+ private final X509Certificate digicertCertificate = X509CertificateBuilder.fromKeypair(keyPair, new X500Principal("CN=digicert"),
+ Instant.now(), Instant.now().plus(1, ChronoUnit.DAYS), SignatureAlgorithm.SHA512_WITH_ECDSA, BigInteger.valueOf(12345)).build();
+
+ private final X509Certificate zerosslCertificate = X509CertificateBuilder.fromKeypair(keyPair, new X500Principal("CN=zerossl"),
+ Instant.now(), Instant.now().plus(1, ChronoUnit.DAYS), SignatureAlgorithm.SHA512_WITH_ECDSA, BigInteger.valueOf(12345)).build();
+
+
+ @Test
+ void reads_from_correct_endpoint_certificate_store() {
+ MockSecretStore secretStore = new MockSecretStore();
+ secretStore.put("cert", 1, X509CertificateUtils.toPem(digicertCertificate));
+ secretStore.put("key", 1, KeyUtils.toPem(keyPair.getPrivate()));
+ DefaultEndpointCertificateSecretStore defaultEndpointCertificateSecretStore = new DefaultEndpointCertificateSecretStore(secretStore);
+ TestEndpointCertificateSecretStore zerosslStore = new TestEndpointCertificateSecretStore(X509CertificateUtils.toPem(zerosslCertificate), KeyUtils.toPem(keyPair.getPrivate()));
+
+ EndpointCertificateRetriever retriever = new EndpointCertificateRetriever(List.of(defaultEndpointCertificateSecretStore, zerosslStore));
+
+ {
+ Optional<EndpointCertificateSecrets> endpointCertificateSecrets = retriever.readEndpointCertificateSecrets(
+ new EndpointCertificateMetadata("key", "cert", 1, EndpointCertificateMetadata.Provider.digicert));
+ Assertions.assertTrue(endpointCertificateSecrets.isPresent());
+ Assertions.assertEquals("CN=digicert", X509CertificateUtils.fromPem(endpointCertificateSecrets.get().certificate()).getSubjectX500Principal().getName());
+ }
+ {
+ Optional<EndpointCertificateSecrets> endpointCertificateSecrets = retriever.readEndpointCertificateSecrets(
+ new EndpointCertificateMetadata("key", "cert", 1, EndpointCertificateMetadata.Provider.zerossl));
+ Assertions.assertTrue(endpointCertificateSecrets.isPresent());
+ Assertions.assertEquals("CN=zerossl", X509CertificateUtils.fromPem(endpointCertificateSecrets.get().certificate()).getSubjectX500Principal().getName());
+ }
+ }
+
+ @Test
+ void returns_missing_when_cert_version_not_found() {
+ DefaultEndpointCertificateSecretStore defaultEndpointCertificateSecretStore = new DefaultEndpointCertificateSecretStore(new MockSecretStore());
+ TestEndpointCertificateSecretStore zerosslStore = new TestEndpointCertificateSecretStore(null, null);
+ EndpointCertificateRetriever retriever = new EndpointCertificateRetriever(List.of(defaultEndpointCertificateSecretStore, zerosslStore));
+ {
+ Optional<EndpointCertificateSecrets> endpointCertificateSecrets = retriever.readEndpointCertificateSecrets(
+ new EndpointCertificateMetadata("key", "cert", 1, EndpointCertificateMetadata.Provider.digicert));
+ Assertions.assertTrue(endpointCertificateSecrets.isPresent());
+ Assertions.assertTrue(endpointCertificateSecrets.get().isMissing());
+ }
+ {
+ Optional<EndpointCertificateSecrets> endpointCertificateSecrets = retriever.readEndpointCertificateSecrets(
+ new EndpointCertificateMetadata("key", "cert", 1, EndpointCertificateMetadata.Provider.zerossl));
+ Assertions.assertTrue(endpointCertificateSecrets.isPresent());
+ Assertions.assertTrue(endpointCertificateSecrets.get().isMissing());
+ }
+ }
+
+ private static class TestEndpointCertificateSecretStore extends EndpointCertificateSecretStore {
+
+ private final String certificate;
+ private final String privatekey;
+
+ public TestEndpointCertificateSecretStore(String certificate, String privatekey) {
+ this.certificate = certificate;
+ this.privatekey = privatekey;
+ }
+
+ @Override
+ public Optional<String> getPrivateKey(EndpointCertificateMetadata metadata) {
+ return Optional.ofNullable(privatekey);
+ }
+
+ @Override
+ public Optional<String> getCertificate(EndpointCertificateMetadata metadata) {
+ return Optional.ofNullable(certificate);
+ }
+
+ @Override
+ public boolean supports(EndpointCertificateMetadata.Provider provider) {
+ return provider == EndpointCertificateMetadata.Provider.zerossl;
+ }
+ }
+}
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRepositoryTest.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRepositoryTest.java
index c95fee00c9c..515ecf0005e 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRepositoryTest.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TenantRepositoryTest.java
@@ -13,6 +13,7 @@ import com.yahoo.config.provision.ApplicationName;
import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.config.provision.Zone;
+import com.yahoo.container.jdisc.secretstore.SecretStore;
import com.yahoo.vespa.config.server.ConfigServerDB;
import com.yahoo.vespa.config.server.MockSecretStore;
import com.yahoo.vespa.config.server.ServerCache;
@@ -41,6 +42,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
+import org.mockito.Mock;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.time.Clock;
@@ -211,6 +213,7 @@ public class TenantRepositoryTest {
private static class FailingDuringBootstrapTenantRepository extends TenantRepository {
private static final FlagSource flagSource = new InMemoryFlagSource();
+ private static final SecretStore mockSecretStore = new MockSecretStore();
public FailingDuringBootstrapTenantRepository(ConfigserverConfig configserverConfig) {
super(new HostRegistry(),
@@ -221,7 +224,7 @@ public class TenantRepositoryTest {
new FileDistributionFactory(configserverConfig, new FileDirectory(configserverConfig)),
flagSource,
new InThreadExecutorService(),
- new MockSecretStore(),
+ mockSecretStore,
HostProvisionerProvider.empty(),
configserverConfig,
new ConfigServerDB(configserverConfig),
@@ -232,7 +235,8 @@ public class TenantRepositoryTest {
new TenantApplicationsTest.MockConfigActivationListener(),
new MockTenantListener(),
new ZookeeperServerConfig.Builder().myid(0).build(),
- OnnxModelCost.disabled());
+ OnnxModelCost.disabled(),
+ List.of(new DefaultEndpointCertificateSecretStore(mockSecretStore)));
}
@Override
diff --git a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TestTenantRepository.java b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TestTenantRepository.java
index d890864a51b..91f2d7194b1 100644
--- a/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TestTenantRepository.java
+++ b/configserver/src/test/java/com/yahoo/vespa/config/server/tenant/TestTenantRepository.java
@@ -8,6 +8,7 @@ import com.yahoo.concurrent.StripedExecutor;
import com.yahoo.config.model.api.ConfigDefinitionRepo;
import com.yahoo.config.model.api.OnnxModelCost;
import com.yahoo.config.provision.Zone;
+import com.yahoo.container.jdisc.secretstore.SecretStore;
import com.yahoo.vespa.config.server.ConfigServerDB;
import com.yahoo.vespa.config.server.MockSecretStore;
import com.yahoo.vespa.config.server.ConfigActivationListener;
@@ -34,6 +35,8 @@ import java.util.List;
*/
public class TestTenantRepository extends TenantRepository {
+ private static final MockSecretStore mockSecretStore = new MockSecretStore();
+
public TestTenantRepository(HostRegistry hostRegistry,
Curator curator,
Metrics metrics,
@@ -55,7 +58,7 @@ public class TestTenantRepository extends TenantRepository {
fileDistributionFactory,
flagSource,
new InThreadExecutorService(),
- new MockSecretStore(),
+ mockSecretStore,
hostProvisionerProvider,
configserverConfig,
new ConfigServerDB(configserverConfig),
@@ -66,7 +69,8 @@ public class TestTenantRepository extends TenantRepository {
configActivationListener,
tenantListener,
new ZookeeperServerConfig.Builder().myid(0).build(),
- OnnxModelCost.disabled());
+ OnnxModelCost.disabled(),
+ List.of(new DefaultEndpointCertificateSecretStore(mockSecretStore)));
}
public static class Builder {