diff options
author | Morten Tokle <mortent@yahooinc.com> | 2022-11-30 15:54:42 +0100 |
---|---|---|
committer | Morten Tokle <mortent@yahooinc.com> | 2022-11-30 15:54:42 +0100 |
commit | 0191aeb75947dc7ad5ac45328120aa4465b34826 (patch) | |
tree | 6d0a10c44296ee89920433335050146cf8ec9602 /config-model/src/main/java | |
parent | 1f801694fe726cd3ebf1dfd10161355573a3af69 (diff) |
Validate unique certificates
Diffstat (limited to 'config-model/src/main/java')
2 files changed, 76 insertions, 6 deletions
diff --git a/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudDataPlaneFilterValidator.java b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudDataPlaneFilterValidator.java new file mode 100644 index 00000000000..83f8ea7b510 --- /dev/null +++ b/config-model/src/main/java/com/yahoo/vespa/model/application/validation/CloudDataPlaneFilterValidator.java @@ -0,0 +1,70 @@ +package com.yahoo.vespa.model.application.validation; + +import com.yahoo.config.application.api.ApplicationPackage; +import com.yahoo.config.model.deploy.DeployState; +import com.yahoo.io.IOUtils; +import com.yahoo.io.reader.NamedReader; +import com.yahoo.path.Path; +import com.yahoo.security.X509CertificateUtils; +import com.yahoo.vespa.model.VespaModel; + +import java.io.IOException; +import java.security.cert.X509Certificate; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +public class CloudDataPlaneFilterValidator extends Validator { + + private static final Logger log = Logger.getLogger(CloudDataPlaneFilterValidator.class.getName()); + + @Override + public void validate(VespaModel model, DeployState deployState) { + if (!deployState.isHosted()) return; + if (!deployState.zone().system().isPublic()) return; + + validateUniqueCertificates(deployState); + } + + private void validateUniqueCertificates(DeployState deployState) { + List<NamedReader> certFiles = deployState.getApplicationPackage().getFiles(ApplicationPackage.SECURITY_DIR, ".pem"); + + Map<String, List<X509Certificate>> configuredCertificates = certFiles.stream() + .collect(Collectors.toMap(NamedReader::getName, CloudDataPlaneFilterValidator::readCertificates)); + + Set<X509Certificate> duplicates = new HashSet<>(); + Set<X509Certificate> globalUniqueCerts = new HashSet<>(); + for (Map.Entry<String, List<X509Certificate>> certificateFile : configuredCertificates.entrySet()) { + List<X509Certificate> duplicatesInFile = certificateFile.getValue().stream() + .filter(i -> !globalUniqueCerts.add(i)) + .toList(); + duplicates.addAll(duplicatesInFile); + } + if (!duplicates.isEmpty()) { + List<String> filesWithDuplicates = configuredCertificates.entrySet().stream() + .filter(entry -> entry.getValue().stream().anyMatch(duplicates::contains)) + .map(Map.Entry::getKey) + .map(Path::fromString) + .map(Path::getName) + .map(p -> ApplicationPackage.SECURITY_DIR.append(p).getRelative()) + .sorted() + .toList(); + throw new IllegalArgumentException("Duplicate certificate(s) detected in files: %s. Certificate subject of duplicates: %s" + .formatted(filesWithDuplicates.toString(), + duplicates.stream().map(cert -> cert.getSubjectX500Principal().getName()).toList().toString())); + } + } + + private static List<X509Certificate> readCertificates(NamedReader reader) { + try { + return X509CertificateUtils.certificateListFromPem(IOUtils.readAll(reader)); + } catch (IOException e) { + log.warning("Exception reading certificate list from application package. File: %s, exception message: %s" + .formatted(reader.getName(), e.getMessage())); + throw new RuntimeException("Error reading certificates from application package", e); + } + } +} 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 79d8512e447..007e8401c70 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 @@ -38,6 +38,7 @@ import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.Zone; import com.yahoo.container.bundle.BundleInstantiationSpecification; import com.yahoo.container.logging.FileConnectionLog; +import com.yahoo.io.IOUtils; import com.yahoo.osgi.provider.model.ComponentModel; import com.yahoo.path.Path; import com.yahoo.schema.OnnxModel; @@ -100,9 +101,8 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; import java.io.IOException; -import java.io.InputStream; +import java.io.Reader; import java.net.URI; -import java.nio.charset.StandardCharsets; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; @@ -529,10 +529,10 @@ public class ContainerModelBuilder extends ConfigModelBuilder<ContainerModel> { private List<X509Certificate> getCertificates(ApplicationFile file) { try { - InputStream inputStream = file.createInputStream(); - byte[] bytes = inputStream.readAllBytes(); - inputStream.close(); - return X509CertificateUtils.certificateListFromPem(new String(bytes, StandardCharsets.UTF_8)); + Reader reader = file.createReader(); + String certPem = IOUtils.readAll(reader); + reader.close(); + return X509CertificateUtils.certificateListFromPem(certPem); } catch (IOException e) { throw new RuntimeException(e); } |