summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config-model-api/abi-spec.json2
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java6
-rw-r--r--config-model-api/src/main/java/com/yahoo/config/model/api/TlsSecrets.java16
-rw-r--r--config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java19
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java19
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/ConfiguredDirectSslProvider.java5
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java6
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerServiceBuilder.java9
-rw-r--r--config-model/src/main/java/com/yahoo/vespa/model/content/Content.java2
-rwxr-xr-xconfig-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java2
-rw-r--r--configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java3
-rw-r--r--jdisc_http_service/abi-spec.json2
-rw-r--r--jdisc_http_service/src/main/resources/configdefinitions/jdisc.http.connector.def7
14 files changed, 83 insertions, 21 deletions
diff --git a/config-model-api/abi-spec.json b/config-model-api/abi-spec.json
index 9f86fe4dea2..8d83d16a272 100644
--- a/config-model-api/abi-spec.json
+++ b/config-model-api/abi-spec.json
@@ -111,6 +111,7 @@
"public java.util.List getQueryProfileFiles()",
"public java.util.List getQueryProfileTypeFiles()",
"public java.util.List getPageTemplateFiles()",
+ "public com.yahoo.config.application.api.ApplicationFile getClientSecurityFile()",
"public abstract java.lang.String getHostSource()",
"public abstract java.lang.String getServicesSource()",
"public abstract java.util.Optional getDeployment()",
@@ -148,6 +149,7 @@
"public static final com.yahoo.path.Path RULES_DIR",
"public static final com.yahoo.path.Path DEPLOYMENT_FILE",
"public static final com.yahoo.path.Path VALIDATION_OVERRIDES",
+ "public static final com.yahoo.path.Path SECURITY_DIR",
"public static final java.lang.String SD_NAME_SUFFIX",
"public static final java.lang.String RANKEXPRESSION_NAME_SUFFIX",
"public static final java.lang.String RULES_NAME_SUFFIX",
diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java
index 06f8034453d..5cd119dcf65 100644
--- a/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java
+++ b/config-model-api/src/main/java/com/yahoo/config/application/api/ApplicationPackage.java
@@ -27,7 +27,6 @@ import java.util.Map;
import java.util.Optional;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
-import java.util.stream.Collectors;
/**
* Represents an application package, that is, used as input when creating a VespaModel and as
@@ -73,6 +72,8 @@ public interface ApplicationPackage {
Path DEPLOYMENT_FILE = Path.fromString("deployment.xml");
Path VALIDATION_OVERRIDES = Path.fromString("validation-overrides.xml");
+ Path SECURITY_DIR = Path.fromString("security");
+
String SD_NAME_SUFFIX = ".sd";
String RANKEXPRESSION_NAME_SUFFIX = ".expression";
String RULES_NAME_SUFFIX = ".sr";
@@ -178,6 +179,9 @@ public interface ApplicationPackage {
/** Does {@link #getFiles} on the page template directory and gets all xml files */
default List<NamedReader> getPageTemplateFiles() { return getFiles(PAGE_TEMPLATES_DIR,".xml"); }
+ /** Returns handle for the file containing client certificate authorities */
+ default ApplicationFile getClientSecurityFile() { return getFile(SECURITY_DIR.append("clients.pem")); }
+
//For generating error messages
String getHostSource();
String getServicesSource();
diff --git a/config-model-api/src/main/java/com/yahoo/config/model/api/TlsSecrets.java b/config-model-api/src/main/java/com/yahoo/config/model/api/TlsSecrets.java
index 3cb4cedcbac..6a8b5a237ab 100644
--- a/config-model-api/src/main/java/com/yahoo/config/model/api/TlsSecrets.java
+++ b/config-model-api/src/main/java/com/yahoo/config/model/api/TlsSecrets.java
@@ -1,30 +1,30 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.config.model.api;
- public class TlsSecrets {
+public class TlsSecrets {
public static final TlsSecrets MISSING = new TlsSecrets();
- private final String certificate;
+ private final String certificate;
private final String key;
- private TlsSecrets() {
- this(null,null);
+ private TlsSecrets() {
+ this(null, null);
}
- public TlsSecrets(String certificate, String key) {
+ public TlsSecrets(String certificate, String key) {
this.certificate = certificate;
this.key = key;
}
- public String certificate() {
+ public String certificate() {
return certificate;
}
- public String key() {
+ public String key() {
return key;
}
- public boolean isMissing() {
+ public boolean isMissing() {
return this == MISSING;
}
}
diff --git a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java
index 977946cbf71..46e380e1ebd 100644
--- a/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java
+++ b/config-model/src/main/java/com/yahoo/config/model/deploy/DeployState.java
@@ -24,6 +24,7 @@ import com.yahoo.config.model.provision.SingleNodeProvisioner;
import com.yahoo.config.model.test.MockApplicationPackage;
import com.yahoo.config.provision.Rotation;
import com.yahoo.config.provision.Zone;
+import com.yahoo.io.IOUtils;
import com.yahoo.io.reader.NamedReader;
import com.yahoo.searchdefinition.RankProfileRegistry;
import com.yahoo.searchdefinition.SearchBuilder;
@@ -39,8 +40,10 @@ import com.yahoo.vespa.model.container.search.SemanticRules;
import com.yahoo.vespa.model.search.SearchDefinition;
import java.io.File;
+import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
+import java.io.UncheckedIOException;
import java.time.Instant;
import java.util.Collection;
import java.util.Collections;
@@ -263,6 +266,22 @@ public class DeployState implements ConfigDefinitionStore {
public Optional<TlsSecrets> tlsSecrets() { return properties.tlsSecrets(); }
+ public Optional<String> tlsClientAuthority() {
+ var caFile = applicationPackage.getClientSecurityFile();
+ if (caFile.exists()) {
+ try {
+ var caPem = IOUtils.readAll(caFile.createReader());
+ return Optional.of(caPem);
+ } catch (FileNotFoundException e) {
+ return Optional.empty();
+ } catch (IOException e) {
+ throw new UncheckedIOException("Failed reading certificate from application: " + caFile.getPath(), e);
+ }
+ } else {
+ return Optional.empty();
+ }
+ }
+
public static class Builder {
private ApplicationPackage applicationPackage = MockApplicationPackage.createEmpty();
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java
index 48f7fa3c1a2..f00b617de46 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainer.java
@@ -22,11 +22,11 @@ public final class ApplicationContainer extends Container {
private final boolean isHostedVespa;
- public ApplicationContainer(AbstractConfigProducer parent, String name, int index, boolean isHostedVespa, Optional<TlsSecrets> tlsSecrets) {
- this(parent, name, false, index, isHostedVespa, tlsSecrets);
+ public ApplicationContainer(AbstractConfigProducer parent, String name, int index, boolean isHostedVespa, Optional<TlsSecrets> tlsSecrets, Optional<String> tlsCa) {
+ this(parent, name, false, index, isHostedVespa, tlsSecrets, tlsCa);
}
- public ApplicationContainer(AbstractConfigProducer parent, String name, boolean retired, int index, boolean isHostedVespa, Optional<TlsSecrets> tlsSecrets) {
+ public ApplicationContainer(AbstractConfigProducer parent, String name, boolean retired, int index, boolean isHostedVespa, Optional<TlsSecrets> tlsSecrets, Optional<String> tlsCa) {
super(parent, name, retired, index);
this.isHostedVespa = isHostedVespa;
@@ -36,8 +36,17 @@ public final class ApplicationContainer extends Container {
JettyHttpServer server = Optional.ofNullable(getHttp())
.map(Http::getHttpServer)
.orElse(getDefaultHttpServer());
- server.addConnector(new ConnectorFactory(connectorName, 4443,
- new ConfiguredDirectSslProvider(server.getComponentId().getName(), tlsSecrets.get().key(), tlsSecrets.get().certificate(), null, null)));
+
+ var sslProvider = new ConfiguredDirectSslProvider(
+ server.getComponentId().getName(),
+ tlsSecrets.get().key(),
+ tlsSecrets.get().certificate(),
+ null,
+ tlsCa.orElse(null),
+ null
+ );
+
+ server.addConnector(new ConnectorFactory(connectorName, 4443, sslProvider));
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
index 473971c5e7a..5656299b302 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/ApplicationContainerCluster.java
@@ -56,6 +56,7 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
private ContainerModelEvaluation modelEvaluation;
private Optional<TlsSecrets> tlsSecrets;
+ private Optional<String> tlsClientAuthority;
private final boolean enableGroupingSessionCache;
private MbusParams mbusParams;
@@ -65,6 +66,7 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
super(parent, subId, name, deployState);
this.tlsSecrets = deployState.tlsSecrets();
+ this.tlsClientAuthority = deployState.tlsClientAuthority();
this.enableGroupingSessionCache = deployState.getProperties().enableGroupingSessionCache();
restApiGroup = new ConfigProducerGroup<>(this, "rest-api");
servletGroup = new ConfigProducerGroup<>(this, "servlet");
@@ -183,6 +185,10 @@ public final class ApplicationContainerCluster extends ContainerCluster<Applicat
return tlsSecrets;
}
+ public Optional<String> getTlsClientAuthority() {
+ return tlsClientAuthority;
+ }
+
public boolean enableGroupingSessionCache() {
return enableGroupingSessionCache;
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/ConfiguredDirectSslProvider.java b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/ConfiguredDirectSslProvider.java
index 28dba3331d3..b47aa501ece 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/ConfiguredDirectSslProvider.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/http/ssl/ConfiguredDirectSslProvider.java
@@ -26,9 +26,10 @@ public class ConfiguredDirectSslProvider extends SimpleComponent implements Conn
private final String privateKey;
private final String certificate;
private final String caCertificatePath;
+ private final String caCertificate;
private final ConnectorConfig.Ssl.ClientAuth.Enum clientAuthentication;
- public ConfiguredDirectSslProvider(String servername, String privateKey, String certificate, String caCertificatePath, String clientAuthentication) {
+ public ConfiguredDirectSslProvider(String servername, String privateKey, String certificate, String caCertificatePath, String caCertificate, String clientAuthentication) {
super(new ComponentModel(
new BundleInstantiationSpecification(new ComponentId(COMPONENT_ID_PREFIX+servername),
fromString(COMPONENT_CLASS),
@@ -36,6 +37,7 @@ public class ConfiguredDirectSslProvider extends SimpleComponent implements Conn
this.privateKey = privateKey;
this.certificate = certificate;
this.caCertificatePath = caCertificatePath;
+ this.caCertificate = caCertificate;
this.clientAuthentication = mapToConfigEnum(clientAuthentication);
}
@@ -45,6 +47,7 @@ public class ConfiguredDirectSslProvider extends SimpleComponent implements Conn
builder.ssl.privateKey(privateKey);
builder.ssl.certificate(certificate);
builder.ssl.caCertificateFile(Optional.ofNullable(caCertificatePath).orElse(""));
+ builder.ssl.caCertificate(Optional.ofNullable(caCertificate).orElse(""));
builder.ssl.clientAuth(clientAuthentication);
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
index 2bfb1da9dcb..a7a4e0fb540 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerModelBuilder.java
@@ -440,7 +440,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
}
private void addStandaloneNode(ApplicationContainerCluster cluster) {
- ApplicationContainer container = new ApplicationContainer(cluster, "standalone", cluster.getContainers().size(), cluster.isHostedVespa(), cluster.getTlsSecrets());
+ ApplicationContainer container = new ApplicationContainer(cluster, "standalone", cluster.getContainers().size(), cluster.isHostedVespa(), cluster.getTlsSecrets(), cluster.getTlsClientAuthority());
cluster.addContainers(Collections.singleton(container));
}
@@ -506,7 +506,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
Element nodesElement = XML.getChild(containerElement, "nodes");
Element rotationsElement = XML.getChild(containerElement, "rotations");
if (nodesElement == null) { // default single node on localhost
- ApplicationContainer node = new ApplicationContainer(cluster, "container.0", 0, cluster.isHostedVespa(), cluster.getTlsSecrets());
+ ApplicationContainer node = new ApplicationContainer(cluster, "container.0", 0, cluster.isHostedVespa(), cluster.getTlsSecrets(), cluster.getTlsClientAuthority());
HostResource host = allocateSingleNodeHost(cluster, log, containerElement, context);
node.setHostResource(host);
node.initService(context.getDeployLogger());
@@ -695,7 +695,7 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> {
List<ApplicationContainer> nodes = new ArrayList<>();
for (Map.Entry<HostResource, ClusterMembership> entry : hosts.entrySet()) {
String id = "container." + entry.getValue().index();
- ApplicationContainer container = new ApplicationContainer(cluster, id, entry.getValue().retired(), entry.getValue().index(), cluster.isHostedVespa(), cluster.getTlsSecrets());
+ ApplicationContainer container = new ApplicationContainer(cluster, id, entry.getValue().retired(), entry.getValue().index(), cluster.isHostedVespa(), cluster.getTlsSecrets(), cluster.getTlsClientAuthority());
container.setHostResource(entry.getKey());
container.initService(deployLogger);
nodes.add(container);
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerServiceBuilder.java b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerServiceBuilder.java
index 46271d3c0a2..c976a7fb153 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerServiceBuilder.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/container/xml/ContainerServiceBuilder.java
@@ -22,7 +22,14 @@ public class ContainerServiceBuilder extends VespaDomBuilder.DomConfigProducerBu
@Override
protected ApplicationContainer doBuild(DeployState deployState, AbstractConfigProducer parent, Element nodeElem) {
- return new ApplicationContainer(parent, id, index, deployState.isHosted(), deployState.tlsSecrets());
+ return new ApplicationContainer(
+ parent,
+ id,
+ index,
+ deployState.isHosted(),
+ deployState.tlsSecrets(),
+ deployState.tlsClientAuthority()
+ );
}
}
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/content/Content.java b/config-model/src/main/java/com/yahoo/vespa/model/content/Content.java
index 8eda707be99..f028f0ac0cc 100644
--- a/config-model/src/main/java/com/yahoo/vespa/model/content/Content.java
+++ b/config-model/src/main/java/com/yahoo/vespa/model/content/Content.java
@@ -324,7 +324,7 @@ public class Content extends ConfigModel {
if (!processedHosts.contains(host)) {
String containerName = String.valueOf(searchNode.getDistributionKey());
ApplicationContainer docprocService = new ApplicationContainer(indexingCluster, containerName, index,
- modelContext.getDeployState().isHosted(), modelContext.getDeployState().tlsSecrets());
+ modelContext.getDeployState().isHosted(), modelContext.getDeployState().tlsSecrets(), Optional.empty());
index++;
docprocService.useDynamicPorts();
docprocService.setHostResource(host);
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
index ac85a958ed5..89169c44079 100755
--- a/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
+++ b/config-model/src/test/java/com/yahoo/vespa/model/container/ContainerClusterTest.java
@@ -252,7 +252,7 @@ public class ContainerClusterTest {
}
private static void addContainer(DeployLogger deployLogger, ApplicationContainerCluster cluster, String name, String hostName) {
- ApplicationContainer container = new ApplicationContainer(cluster, name, 0, cluster.isHostedVespa(), cluster.getTlsSecrets());
+ ApplicationContainer container = new ApplicationContainer(cluster, name, 0, cluster.isHostedVespa(), cluster.getTlsSecrets(), cluster.getTlsClientAuthority());
container.setHostResource(new HostResource(new Host(null, hostName)));
container.initService(deployLogger);
cluster.addContainer(container);
diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java
index 484124991d9..a99a2a2917d 100644
--- a/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java
+++ b/configserver/src/main/java/com/yahoo/vespa/config/server/deploy/ZooKeeperClient.java
@@ -202,6 +202,9 @@ public class ZooKeeperClient {
writeDir(app.getFile(ApplicationPackage.MODELS_GENERATED_REPLICATED_DIR),
getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH).append(ApplicationPackage.MODELS_GENERATED_REPLICATED_DIR),
true);
+ writeDir(app.getFile(ApplicationPackage.SECURITY_DIR),
+ getZooKeeperAppPath(ConfigCurator.USERAPP_ZK_SUBPATH).append(ApplicationPackage.SECURITY_DIR),
+ true);
}
private void writeDir(ApplicationFile file, Path zooKeeperAppPath, boolean recurse) throws IOException {
diff --git a/jdisc_http_service/abi-spec.json b/jdisc_http_service/abi-spec.json
index f915dc1e8c1..b06250c4593 100644
--- a/jdisc_http_service/abi-spec.json
+++ b/jdisc_http_service/abi-spec.json
@@ -82,6 +82,7 @@
"public com.yahoo.jdisc.http.ConnectorConfig$Ssl$Builder certificateFile(java.lang.String)",
"public com.yahoo.jdisc.http.ConnectorConfig$Ssl$Builder certificate(java.lang.String)",
"public com.yahoo.jdisc.http.ConnectorConfig$Ssl$Builder caCertificateFile(java.lang.String)",
+ "public com.yahoo.jdisc.http.ConnectorConfig$Ssl$Builder caCertificate(java.lang.String)",
"public com.yahoo.jdisc.http.ConnectorConfig$Ssl$Builder clientAuth(com.yahoo.jdisc.http.ConnectorConfig$Ssl$ClientAuth$Enum)",
"public com.yahoo.jdisc.http.ConnectorConfig$Ssl build()"
],
@@ -137,6 +138,7 @@
"public java.lang.String certificateFile()",
"public java.lang.String certificate()",
"public java.lang.String caCertificateFile()",
+ "public java.lang.String caCertificate()",
"public com.yahoo.jdisc.http.ConnectorConfig$Ssl$ClientAuth$Enum clientAuth()"
],
"fields": []
diff --git a/jdisc_http_service/src/main/resources/configdefinitions/jdisc.http.connector.def b/jdisc_http_service/src/main/resources/configdefinitions/jdisc.http.connector.def
index c6c6fad345b..9b6fb5401e2 100644
--- a/jdisc_http_service/src/main/resources/configdefinitions/jdisc.http.connector.def
+++ b/jdisc_http_service/src/main/resources/configdefinitions/jdisc.http.connector.def
@@ -69,7 +69,14 @@ ssl.certificateFile string default=""
ssl.certificate string default=""
# with trusted CA certificates in PEM format. Used to verify clients
+# - this is the name of a file on the local container file system
+# - only one of caCertificateFile and caCertificate
ssl.caCertificateFile string default=""
+# with trusted CA certificates in PEM format. Used to verify clients
+# - this is the actual certificates instead of a pointer to the file
+# - only one of caCertificateFile and caCertificate
+ssl.caCertificate string default=""
+
# Client authentication mode. See SSLEngine.getNeedClientAuth()/getWantClientAuth() for details.
ssl.clientAuth enum { DISABLED, WANT_AUTH, NEED_AUTH } default=DISABLED