summaryrefslogtreecommitdiffstats
path: root/configserver/src/main/java/com/yahoo/vespa/config/server
diff options
context:
space:
mode:
Diffstat (limited to 'configserver/src/main/java/com/yahoo/vespa/config/server')
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/GlobalComponentRegistry.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistry.java11
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java8
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/modelfactory/ActivatedModelsBuilder.java7
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java20
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/session/SessionPreparer.java22
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TlsSecretsKeys.java86
7 files changed, 148 insertions, 8 deletions
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/GlobalComponentRegistry.java b/configserver/src/main/java/com/yahoo/vespa/config/server/GlobalComponentRegistry.java
index d420c3f21fe..1eb18773898 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/GlobalComponentRegistry.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/GlobalComponentRegistry.java
@@ -7,6 +7,7 @@ import com.yahoo.config.model.api.ConfigDefinitionRepo;
import com.yahoo.config.provision.Provisioner;
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.application.PermanentApplicationPackage;
import com.yahoo.vespa.config.server.host.HostRegistries;
import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry;
@@ -46,4 +47,5 @@ public interface GlobalComponentRegistry {
StripedExecutor<TenantName> getZkWatcherExecutor();
FlagSource getFlagSource();
ExecutorService getZkCacheExecutor();
+ SecretStore getSecretStore();
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistry.java b/configserver/src/main/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistry.java
index ff76afd1c98..9badd19009f 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistry.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/InjectedGlobalComponentRegistry.java
@@ -9,6 +9,7 @@ import com.yahoo.config.model.api.ConfigDefinitionRepo;
import com.yahoo.config.provision.Provisioner;
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.application.PermanentApplicationPackage;
import com.yahoo.vespa.config.server.host.HostRegistries;
import com.yahoo.vespa.config.server.modelfactory.ModelFactoryRegistry;
@@ -48,6 +49,7 @@ public class InjectedGlobalComponentRegistry implements GlobalComponentRegistry
private final Zone zone;
private final ConfigServerDB configServerDB;
private final FlagSource flagSource;
+ private final SecretStore secretStore;
private final StripedExecutor<TenantName> zkWatcherExecutor;
private final ExecutorService zkCacheExecutor;
@@ -67,7 +69,8 @@ public class InjectedGlobalComponentRegistry implements GlobalComponentRegistry
HostProvisionerProvider hostProvisionerProvider,
Zone zone,
ConfigServerDB configServerDB,
- FlagSource flagSource) {
+ FlagSource flagSource,
+ SecretStore secretStore) {
this.curator = curator;
this.configCurator = configCurator;
this.metrics = metrics;
@@ -82,6 +85,7 @@ public class InjectedGlobalComponentRegistry implements GlobalComponentRegistry
this.zone = zone;
this.configServerDB = configServerDB;
this.flagSource = flagSource;
+ this.secretStore = secretStore;
this.zkWatcherExecutor = new StripedExecutor<>();
this.zkCacheExecutor = Executors.newFixedThreadPool(1, ThreadFactoryFactory.getThreadFactory(TenantRepository.class.getName()));
}
@@ -137,4 +141,9 @@ public class InjectedGlobalComponentRegistry implements GlobalComponentRegistry
public ExecutorService getZkCacheExecutor() {
return zkCacheExecutor;
}
+
+ @Override
+ public SecretStore getSecretStore() {
+ return secretStore;
+ }
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
index 4627d350eb2..d875385d14d 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ModelContextImpl.java
@@ -11,6 +11,7 @@ import com.yahoo.config.model.api.ContainerEndpoint;
import com.yahoo.config.model.api.HostProvisioner;
import com.yahoo.config.model.api.Model;
import com.yahoo.config.model.api.ModelContext;
+import com.yahoo.config.model.api.TlsSecrets;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.Rotation;
@@ -134,6 +135,7 @@ public class ModelContextImpl implements ModelContext {
private final boolean useFdispatchByDefault;
private final boolean useAdaptiveDispatch;
private final boolean dispatchWithProtobuf;
+ private final Optional<TlsSecrets> tlsSecrets;
public Properties(ApplicationId applicationId,
boolean multitenantFromConfig,
@@ -147,7 +149,8 @@ public class ModelContextImpl implements ModelContext {
Set<ContainerEndpoint> endpoints,
boolean isBootstrap,
boolean isFirstTimeDeployment,
- FlagSource flagSource) {
+ FlagSource flagSource,
+ Optional<TlsSecrets> tlsSecrets) {
this.applicationId = applicationId;
this.multitenant = multitenantFromConfig || hostedVespa || Boolean.getBoolean("multitenant");
this.configServerSpecs = configServerSpecs;
@@ -168,6 +171,7 @@ public class ModelContextImpl implements ModelContext {
.with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value();
this.useAdaptiveDispatch = Flags.USE_ADAPTIVE_DISPATCH.bindTo(flagSource)
.with(FetchVector.Dimension.APPLICATION_ID, applicationId.serializedForm()).value();
+ this.tlsSecrets = tlsSecrets;
}
@Override
@@ -222,6 +226,8 @@ public class ModelContextImpl implements ModelContext {
@Override
public boolean useAdaptiveDispatch() { return useAdaptiveDispatch; }
+ @Override
+ public Optional<TlsSecrets> tlsSecrets() { return tlsSecrets; }
}
}
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 117a9e0cac5..94cd30de28b 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
@@ -13,6 +13,7 @@ import com.yahoo.config.provision.AllocatedHosts;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.TenantName;
+import com.yahoo.container.jdisc.secretstore.SecretStore;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.config.server.ConfigServerSpec;
import com.yahoo.vespa.config.server.GlobalComponentRegistry;
@@ -28,6 +29,7 @@ import com.yahoo.vespa.config.server.session.SilentDeployLogger;
import com.yahoo.vespa.config.server.tenant.ContainerEndpointsCache;
import com.yahoo.vespa.config.server.tenant.Rotations;
import com.yahoo.vespa.config.server.tenant.TenantRepository;
+import com.yahoo.vespa.config.server.tenant.TlsSecretsKeys;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.flags.FlagSource;
@@ -55,6 +57,7 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> {
private final Curator curator;
private final DeployLogger logger;
private final FlagSource flagSource;
+ private final SecretStore secretStore;
public ActivatedModelsBuilder(TenantName tenant,
long appGeneration,
@@ -73,6 +76,7 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> {
this.curator = globalComponentRegistry.getCurator();
this.logger = new SilentDeployLogger();
this.flagSource = globalComponentRegistry.getFlagSource();
+ this.secretStore = globalComponentRegistry.getSecretStore();
}
@Override
@@ -132,7 +136,8 @@ public class ActivatedModelsBuilder extends ModelsBuilder<Application> {
ImmutableSet.copyOf(new ContainerEndpointsCache(TenantRepository.getTenantPath(tenant), curator).read(applicationId)),
false, // We may be bootstrapping, but we only know and care during prepare
false, // Always false, assume no one uses it when activating
- flagSource);
+ flagSource,
+ new TlsSecretsKeys(curator, TenantRepository.getTenantPath(tenant), secretStore).readTlsSecretsKeyFromZookeeper(applicationId));
}
}
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java b/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java
index 00a7625ee87..5bf70c55f9e 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/session/PrepareParams.java
@@ -35,6 +35,7 @@ public final class PrepareParams {
static final String VESPA_VERSION_PARAM_NAME = "vespaVersion";
static final String ROTATIONS_PARAM_NAME = "rotations";
static final String CONTAINER_ENDPOINTS_PARAM_NAME = "containerEndpoints";
+ static final String TLS_SECRETS_KEY_NAME_PARAM_NAME = "tlsSecretsKeyName";
private final ApplicationId applicationId;
private final TimeoutBudget timeoutBudget;
@@ -45,10 +46,11 @@ public final class PrepareParams {
private final Optional<Version> vespaVersion;
private final Set<Rotation> rotations;
private final List<ContainerEndpoint> containerEndpoints;
+ private final Optional<String> tlsSecretsKeyName;
private PrepareParams(ApplicationId applicationId, TimeoutBudget timeoutBudget, boolean ignoreValidationErrors,
- boolean dryRun, boolean verbose, boolean isBootstrap, Optional<Version> vespaVersion,
- Set<Rotation> rotations, List<ContainerEndpoint> containerEndpoints) {
+ boolean dryRun, boolean verbose, boolean isBootstrap, Optional<Version> vespaVersion, Set<Rotation> rotations,
+ List<ContainerEndpoint> containerEndpoints, Optional<String> tlsSecretsKeyName) {
this.timeoutBudget = timeoutBudget;
this.applicationId = applicationId;
this.ignoreValidationErrors = ignoreValidationErrors;
@@ -61,6 +63,7 @@ public final class PrepareParams {
if ((rotations != null && !rotations.isEmpty()) && !containerEndpoints.isEmpty()) {
throw new IllegalArgumentException("Cannot set both rotations and containerEndpoints");
}
+ this.tlsSecretsKeyName = tlsSecretsKeyName;
}
public static class Builder {
@@ -74,6 +77,7 @@ public final class PrepareParams {
private Optional<Version> vespaVersion = Optional.empty();
private Set<Rotation> rotations;
private List<ContainerEndpoint> containerEndpoints = List.of();
+ private Optional<String> tlsSecretsKeyName = Optional.empty();
public Builder() { }
@@ -136,12 +140,18 @@ public final class PrepareParams {
if (serialized == null) return this;
Slime slime = SlimeUtils.jsonToSlime(serialized);
containerEndpoints = ContainerEndpointSerializer.endpointListFromSlime(slime);
+ return this;
+ }
+
+ public Builder tlsSecretsKeyName(String tlsSecretsKeyName) {
+ this.tlsSecretsKeyName = Optional.ofNullable(tlsSecretsKeyName)
+ .filter(s -> ! s.isEmpty());
return this;
}
public PrepareParams build() {
return new PrepareParams(applicationId, timeoutBudget, ignoreValidationErrors, dryRun,
- verbose, isBootstrap, vespaVersion, rotations, containerEndpoints);
+ verbose, isBootstrap, vespaVersion, rotations, containerEndpoints, tlsSecretsKeyName);
}
}
@@ -155,6 +165,7 @@ public final class PrepareParams {
.vespaVersion(request.getProperty(VESPA_VERSION_PARAM_NAME))
.rotations(request.getProperty(ROTATIONS_PARAM_NAME))
.containerEndpoints(request.getProperty(CONTAINER_ENDPOINTS_PARAM_NAME))
+ .tlsSecretsKeyName(request.getProperty(TLS_SECRETS_KEY_NAME_PARAM_NAME))
.build();
}
@@ -212,4 +223,7 @@ public final class PrepareParams {
return timeoutBudget;
}
+ public Optional<String> tlsSecretsKeyName() {
+ return tlsSecretsKeyName;
+ }
}
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 30ba9989343..54c96c0461d 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
@@ -13,11 +13,13 @@ import com.yahoo.config.application.api.DeploymentSpec;
import com.yahoo.config.application.api.FileRegistry;
import com.yahoo.config.model.api.ConfigDefinitionRepo;
import com.yahoo.config.model.api.ModelContext;
+import com.yahoo.config.model.api.TlsSecrets;
import com.yahoo.config.provision.AllocatedHosts;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.HostName;
import com.yahoo.config.provision.Rotation;
import com.yahoo.config.provision.Zone;
+import com.yahoo.container.jdisc.secretstore.SecretStore;
import com.yahoo.lang.SettableOptional;
import com.yahoo.log.LogLevel;
import com.yahoo.path.Path;
@@ -34,6 +36,7 @@ import com.yahoo.vespa.config.server.provision.HostProvisionerProvider;
import com.yahoo.config.model.api.ContainerEndpoint;
import com.yahoo.vespa.config.server.tenant.ContainerEndpointsCache;
import com.yahoo.vespa.config.server.tenant.Rotations;
+import com.yahoo.vespa.config.server.tenant.TlsSecretsKeys;
import com.yahoo.vespa.curator.Curator;
import com.yahoo.vespa.flags.FlagSource;
import org.xml.sax.SAXException;
@@ -69,6 +72,7 @@ public class SessionPreparer {
private final Curator curator;
private final Zone zone;
private final FlagSource flagSource;
+ private final SecretStore secretStore;
@Inject
public SessionPreparer(ModelFactoryRegistry modelFactoryRegistry,
@@ -79,7 +83,8 @@ public class SessionPreparer {
ConfigDefinitionRepo configDefinitionRepo,
Curator curator,
Zone zone,
- FlagSource flagSource) {
+ FlagSource flagSource,
+ SecretStore secretStore) {
this.modelFactoryRegistry = modelFactoryRegistry;
this.fileDistributionFactory = fileDistributionFactory;
this.hostProvisionerProvider = hostProvisionerProvider;
@@ -89,6 +94,7 @@ public class SessionPreparer {
this.curator = curator;
this.zone = zone;
this.flagSource = flagSource;
+ this.secretStore = secretStore;
}
/**
@@ -112,6 +118,7 @@ public class SessionPreparer {
if ( ! params.isDryRun()) {
preparation.writeStateZK();
preparation.writeRotZK();
+ preparation.writeTlsZK();
var globalServiceId = context.getApplicationPackage().getDeployment()
.map(DeploymentSpec::fromXml)
.flatMap(DeploymentSpec::globalServiceId);
@@ -145,6 +152,8 @@ public class SessionPreparer {
final Set<Rotation> rotationsSet;
final Set<ContainerEndpoint> endpointsSet;
final ModelContext.Properties properties;
+ private final TlsSecretsKeys tlsSecretsKeys;
+ private final Optional<TlsSecrets> tlsSecrets;
private ApplicationPackage applicationPackage;
private List<PreparedModelsBuilder.PreparedModelResult> modelResultList;
@@ -165,7 +174,10 @@ public class SessionPreparer {
this.rotations = new Rotations(curator, tenantPath);
this.containerEndpoints = new ContainerEndpointsCache(tenantPath, curator);
this.rotationsSet = getRotations(params.rotations());
+ this.tlsSecretsKeys = new TlsSecretsKeys(curator, tenantPath, secretStore);
+ this.tlsSecrets = tlsSecretsKeys.getTlsSecrets(params.tlsSecretsKeyName(), applicationId);
this.endpointsSet = getEndpoints(params.containerEndpoints());
+
this.properties = new ModelContextImpl.Properties(params.getApplicationId(),
configserverConfig.multitenant(),
ConfigServerSpec.fromConfig(configserverConfig),
@@ -178,7 +190,8 @@ public class SessionPreparer {
endpointsSet,
params.isBootstrap(),
! currentActiveApplicationSet.isPresent(),
- context.getFlagSource());
+ context.getFlagSource(),
+ tlsSecrets);
this.preparedModelsBuilder = new PreparedModelsBuilder(modelFactoryRegistry,
permanentApplicationPackage,
configDefinitionRepo,
@@ -238,6 +251,11 @@ public class SessionPreparer {
checkTimeout("write rotations to zookeeper");
}
+ void writeTlsZK() {
+ tlsSecretsKeys.writeTlsSecretsKeyToZooKeeper(applicationId, params.tlsSecretsKeyName().orElse(null));
+ checkTimeout("write tlsSecretsKey to zookeeper");
+ }
+
void writeContainerEndpointsZK(Optional<String> globalServiceId) {
if (!params.containerEndpoints().isEmpty()) { // Use endpoints from parameter when explicitly given
containerEndpoints.write(applicationId, params.containerEndpoints());
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TlsSecretsKeys.java b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TlsSecretsKeys.java
new file mode 100644
index 00000000000..eaa4916d8fc
--- /dev/null
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/tenant/TlsSecretsKeys.java
@@ -0,0 +1,86 @@
+// Copyright 2017 Yahoo Holdings. 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.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yahoo.config.model.api.TlsSecrets;
+import com.yahoo.config.provision.ApplicationId;
+import com.yahoo.container.jdisc.secretstore.SecretStore;
+import com.yahoo.path.Path;
+import com.yahoo.vespa.curator.Curator;
+import com.yahoo.vespa.curator.transaction.CuratorOperations;
+import com.yahoo.vespa.curator.transaction.CuratorTransaction;
+
+import java.util.Optional;
+
+/**
+ * TLS Secret keys for applications (used to retrieve actual certificate/key from secret store). Persisted in ZooKeeper.
+ *
+ * @author andreer
+ */
+public class TlsSecretsKeys {
+
+ private final Path path;
+ private final SecretStore secretStore;
+ private final Curator curator;
+
+ public TlsSecretsKeys(Curator curator, Path tenantPath, SecretStore secretStore) {
+ this.curator = curator;
+ this.path = tenantPath.append("tlsSecretsKeys/");
+ this.secretStore = secretStore;
+ }
+
+ public Optional<TlsSecrets> readTlsSecretsKeyFromZookeeper(ApplicationId application) {
+ try {
+ Optional<byte[]> data = curator.getData(tlsSecretsKeyOf(application));
+ if (data.isEmpty() || data.get().length == 0) return Optional.empty();
+ String tlsSecretsKey = new ObjectMapper().readValue(data.get(), new TypeReference<String>() {});
+ return readFromSecretStore(Optional.ofNullable(tlsSecretsKey));
+ } catch (Exception e) {
+ throw new RuntimeException("Error reading TLS secret key of " + application, e);
+ }
+ }
+
+ public void writeTlsSecretsKeyToZooKeeper(ApplicationId application, String tlsSecretsKey) {
+ if (tlsSecretsKey == null) return;
+ try {
+ byte[] data = new ObjectMapper().writeValueAsBytes(tlsSecretsKey);
+ curator.set(tlsSecretsKeyOf(application), data);
+ } catch (Exception e) {
+ throw new RuntimeException("Could not write TLS secret key of " + application, e);
+ }
+ }
+
+ public Optional<TlsSecrets> getTlsSecrets(Optional<String> secretKeyname, ApplicationId applicationId) {
+ if (secretKeyname == null || secretKeyname.isEmpty()) {
+ return readTlsSecretsKeyFromZookeeper(applicationId);
+ }
+ return readFromSecretStore(secretKeyname);
+ }
+
+ private Optional<TlsSecrets> readFromSecretStore(Optional<String> secretKeyname) {
+ if(secretKeyname.isEmpty()) return Optional.empty();
+ TlsSecrets tlsSecretParameters = TlsSecrets.MISSING;
+ try {
+ String cert = secretStore.getSecret(secretKeyname.get() + "-cert");
+ String key = secretStore.getSecret(secretKeyname.get() + "-key");
+ tlsSecretParameters = new TlsSecrets(cert, key);
+ } catch (RuntimeException e) {
+ // Assume not ready yet
+// log.log(LogLevel.DEBUG, "Could not fetch certificate/key with prefix: " + secretKeyname.get(), e);
+ }
+ return Optional.of(tlsSecretParameters);
+ }
+
+ /** Returns a transaction which deletes these tls secrets key if they exist */
+ public CuratorTransaction delete(ApplicationId application) {
+ if (!curator.exists(tlsSecretsKeyOf(application))) return CuratorTransaction.empty(curator);
+ return CuratorTransaction.from(CuratorOperations.delete(tlsSecretsKeyOf(application).getAbsolute()), curator);
+ }
+
+ /** Returns the path storing the tls secrets key for an application */
+ private Path tlsSecretsKeyOf(ApplicationId application) {
+ return path.append(application.serializedForm());
+ }
+
+}