summaryrefslogtreecommitdiffstats
path: root/config-model/src/test
diff options
context:
space:
mode:
authorMorten Tokle <mortent@yahooinc.com>2022-12-01 11:19:16 +0100
committerGitHub <noreply@github.com>2022-12-01 11:19:16 +0100
commit0da74401d91e543f2f2505fd650e170792d58b31 (patch)
tree98839ee137dd2c30c41390a466e99c9289e964f3 /config-model/src/test
parentced78a22d4eb39927845db9a2446b988ba1f5802 (diff)
parenta17e5f4a90299dc9256d7b1f2aed7ebf51c7f082 (diff)
Merge pull request #25059 from vespa-engine/mortent/validate-clients
Add client validations
Diffstat (limited to 'config-model/src/test')
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudDataPlaneFilterValidatorTest.java165
-rw-r--r--config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/CertificateRemovalChangeValidatorTest.java51
2 files changed, 216 insertions, 0 deletions
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudDataPlaneFilterValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudDataPlaneFilterValidatorTest.java
new file mode 100644
index 00000000000..ed39260bdd2
--- /dev/null
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/CloudDataPlaneFilterValidatorTest.java
@@ -0,0 +1,165 @@
+package com.yahoo.vespa.model.application.validation;
+
+import com.yahoo.config.application.api.ApplicationPackage;
+import com.yahoo.config.model.NullConfigModelRegistry;
+import com.yahoo.config.model.api.EndpointCertificateSecrets;
+import com.yahoo.config.model.deploy.DeployState;
+import com.yahoo.config.model.deploy.TestProperties;
+import com.yahoo.config.model.test.MockApplicationPackage;
+import com.yahoo.config.provision.Environment;
+import com.yahoo.config.provision.RegionName;
+import com.yahoo.config.provision.SystemName;
+import com.yahoo.config.provision.Zone;
+import com.yahoo.path.Path;
+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.model.VespaModel;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.xml.sax.SAXException;
+
+import javax.security.auth.x500.X500Principal;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+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.Map;
+import java.util.Optional;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CloudDataPlaneFilterValidatorTest {
+
+ @TempDir
+ public File applicationFolder;
+
+ @Test
+ void validator_accepts_distinct_client_certificates() throws IOException, SAXException {
+ String certFile1 = "security/foo.pem";
+ String certFile2 = "security/bar.pem";
+ String servicesXml = """
+ <services version='1.0'>
+ <container version='1.0'>
+ <clients>
+ <client id="a" permissions="read,write">
+ <certificate file="%s"/>
+ </client>
+ <client id="b" permissions="read,write">
+ <certificate file="%s"/>
+ </client>
+ </clients>
+ </container>
+ </services>
+ """.formatted(certFile1, certFile2);
+
+ DeployState deployState = createDeployState(servicesXml,
+ Map.of(
+ certFile1, List.of(createCertificate("foo")),
+ certFile2, List.of(createCertificate("bar"))));
+
+ VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState);
+ new CloudDataPlaneFilterValidator().validate(model, deployState);
+ }
+
+ @Test
+ void validator_rejects_duplicate_client_certificates_different_files() throws IOException, SAXException {
+ String certFile1 = "security/a.pem";
+ String certFile2 = "security/b.pem";
+ X509Certificate certificate = createCertificate("a");
+ String servicesXml = """
+ <services version='1.0'>
+ <container version='1.0'>
+ <clients>
+ <client id="a" permissions="read,write">
+ <certificate file="%s"/>
+ </client>
+ <client id="b" permissions="read,write">
+ <certificate file="%s"/>
+ </client>
+ </clients>
+ </container>
+ </services>
+ """.formatted(certFile1, certFile2);
+
+ DeployState deployState = createDeployState(servicesXml,
+ Map.of(
+ certFile1, List.of(certificate),
+ certFile2, List.of(certificate)));
+
+ VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState);
+ IllegalArgumentException illegalArgumentException = Assertions.assertThrows(IllegalArgumentException.class, () ->
+ new CloudDataPlaneFilterValidator().validate(model, deployState));
+ assertEquals(
+ "Duplicate certificate(s) detected in files: [%s, %s]. Certificate subject of duplicates: [%s]".formatted(certFile1, certFile2, certificate.getSubjectX500Principal().getName()),
+ illegalArgumentException.getMessage());
+ }
+
+ @Test
+ void validator_rejects_duplicate_client_certificates_same_file() throws IOException, SAXException {
+ String certFile1 = "security/a.pem";
+ X509Certificate certificate = createCertificate("a");
+ String servicesXml = """
+ <services version='1.0'>
+ <container version='1.0'>
+ <clients>
+ <client id="a" permissions="read,write">
+ <certificate file="%s"/>
+ </client>
+ </clients>
+ </container>
+ </services>
+ """.formatted(certFile1);
+
+ DeployState deployState = createDeployState(servicesXml,
+ Map.of(certFile1, List.of(certificate, certificate)));
+
+ VespaModel model = new VespaModel(new NullConfigModelRegistry(), deployState);
+ IllegalArgumentException illegalArgumentException = Assertions.assertThrows(IllegalArgumentException.class, () ->
+ new CloudDataPlaneFilterValidator().validate(model, deployState));
+ assertEquals(
+ "Duplicate certificate(s) detected in files: [%s]. Certificate subject of duplicates: [%s]".formatted(certFile1, certificate.getSubjectX500Principal().getName()),
+ illegalArgumentException.getMessage());
+ }
+
+
+ private DeployState createDeployState(String servicesXml, Map<String, List<X509Certificate>> certificates) {
+
+ ApplicationPackage applicationPackage = new MockApplicationPackage.Builder()
+ .withRoot(applicationFolder)
+ .withServices(servicesXml)
+ .build();
+
+ applicationPackage.getFile(Path.fromString("security")).createDirectory();
+ certificates.forEach((file, certList) ->
+ applicationPackage.getFile(Path.fromString(file)).writeFile(new StringReader(X509CertificateUtils.toPem(certList))));
+
+ return new DeployState.Builder()
+ .applicationPackage(applicationPackage)
+ .properties(
+ new TestProperties()
+ .setEndpointCertificateSecrets(Optional.of(new EndpointCertificateSecrets("CERT", "KEY")))
+ .setHostedVespa(true)
+ .setEnableDataPlaneFilter(true))
+ .zone(new Zone(SystemName.PublicCd, Environment.dev, RegionName.defaultName()))
+ .build();
+ }
+
+ static X509Certificate createCertificate(String cn) throws IOException {
+ KeyPair keyPair = KeyUtils.generateKeypair(KeyAlgorithm.EC, 256);
+ X500Principal subject = new X500Principal("CN=" + cn);
+ return X509CertificateBuilder
+ .fromKeypair(
+ keyPair, subject, Instant.now(), Instant.now().plus(1, ChronoUnit.DAYS), SignatureAlgorithm.SHA512_WITH_ECDSA, BigInteger.valueOf(1))
+ .build();
+ }
+
+}
diff --git a/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/CertificateRemovalChangeValidatorTest.java b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/CertificateRemovalChangeValidatorTest.java
new file mode 100644
index 00000000000..f89c75362da
--- /dev/null
+++ b/config-model/src/test/java/com/yahoo/vespa/model/application/validation/change/CertificateRemovalChangeValidatorTest.java
@@ -0,0 +1,51 @@
+package com.yahoo.vespa.model.application.validation.change;
+
+import com.yahoo.config.application.api.ValidationOverrides;
+import com.yahoo.security.X509CertificateUtils;
+import com.yahoo.vespa.model.container.http.Client;
+import org.junit.jupiter.api.Test;
+
+import java.security.cert.X509Certificate;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDate;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class CertificateRemovalChangeValidatorTest {
+
+ private static final String validationOverrides =
+ "<validation-overrides>\n" +
+ " <allow until='2000-01-14' comment='test override'>certificate-removal</allow>\n" +
+ "</validation-overrides>\n";
+
+ @Test
+ void validate() {
+ Instant now = LocalDate.parse("2000-01-01", DateTimeFormatter.ISO_DATE).atStartOfDay().atZone(ZoneOffset.UTC).toInstant();
+
+ Client c1 = new Client("c1", List.of(), List.of(certificate("cn=c1")));
+ Client c2 = new Client("c2", List.of(), List.of(certificate("cn=c2")));
+ Client c3 = new Client("c3", List.of(), List.of(certificate("cn=c3")));
+
+ CertificateRemovalChangeValidator validator = new CertificateRemovalChangeValidator();
+
+ // Adding certs -> ok
+ validator.validateClients("clusterId", List.of(c1,c2), List.of(c1, c2, c3), ValidationOverrides.empty, now);
+
+ // Removing certs -> fails
+ assertThrows(ValidationOverrides.ValidationException.class,
+ () ->validator.validateClients("clusterId", List.of(c1, c2, c3), List.of(c1, c3), ValidationOverrides.empty, now));
+
+ // Removing certs with validationoverrides -> ok
+ validator.validateClients("clusterId", List.of(c1, c2, c3), List.of(c1, c3), ValidationOverrides.fromXml(validationOverrides), now);
+
+ }
+
+ static X509Certificate certificate(String cn) {
+ return X509CertificateUtils.createSelfSigned(cn, Duration.ofHours(1)).certificate();
+ }
+
+}