From 82fddbd366844b4ff210a62d3d5667a8c15eec9e Mon Sep 17 00:00:00 2001 From: Morten Tokle Date: Tue, 5 Mar 2024 10:20:42 +0100 Subject: Include default trust store in clients from public systems --- .../vespa/athenz/identity/SiaIdentityProvider.java | 63 ++++++++++++++++++---- .../client/LegacyAthenzIdentityProviderImpl.java | 2 +- .../vespa.athenz.identity.sia-provider.def | 1 + .../athenz/identity/SiaIdentityProviderTest.java | 6 ++- 4 files changed, 58 insertions(+), 14 deletions(-) diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/SiaIdentityProvider.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/SiaIdentityProvider.java index 4b88b440cc6..ed3a52abb87 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/SiaIdentityProvider.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identity/SiaIdentityProvider.java @@ -4,6 +4,8 @@ package com.yahoo.vespa.athenz.identity; import com.yahoo.component.annotation.Inject; import com.yahoo.component.AbstractComponent; import com.yahoo.security.SslContextBuilder; +import com.yahoo.security.TrustManagerUtils; +import com.yahoo.security.X509CertificateUtils; import com.yahoo.security.X509CertificateWithKey; import com.yahoo.security.AutoReloadingX509KeyManager; import com.yahoo.vespa.athenz.api.AthenzIdentity; @@ -11,8 +13,19 @@ import com.yahoo.vespa.athenz.api.AthenzService; import com.yahoo.vespa.athenz.utils.SiaUtils; import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.List; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * A {@link ServiceIdentityProvider} that provides the credentials stored on file system. @@ -33,25 +46,28 @@ public class SiaIdentityProvider extends AbstractComponent implements ServiceIde this(new AthenzService(config.athenzDomain(), config.athenzService()), SiaUtils.getPrivateKeyFile(Paths.get(config.keyPathPrefix()), new AthenzService(config.athenzDomain(), config.athenzService())), SiaUtils.getCertificateFile(Paths.get(config.keyPathPrefix()), new AthenzService(config.athenzDomain(), config.athenzService())), - Paths.get(config.trustStorePath())); + Paths.get(config.trustStorePath()), config.publicSystem()); } public SiaIdentityProvider(AthenzIdentity service, Path siaPath, - Path clientTruststoreFile) { + Path clientTruststoreFile, + boolean publicSystem) { this(service, SiaUtils.getPrivateKeyFile(siaPath, service), SiaUtils.getCertificateFile(siaPath, service), - clientTruststoreFile); + clientTruststoreFile, + publicSystem); } public SiaIdentityProvider(AthenzIdentity service, Path privateKeyFile, Path certificateFile, - Path clientTruststoreFile) { + Path clientTruststoreFile, + boolean publicSystem) { this.service = service; this.keyManager = AutoReloadingX509KeyManager.fromPemFiles(privateKeyFile, certificateFile); - this.sslContext = createIdentitySslContext(keyManager, clientTruststoreFile); + this.sslContext = createIdentitySslContext(keyManager, clientTruststoreFile, publicSystem); this.certificateFile = certificateFile; this.privateKeyFile = privateKeyFile; } @@ -71,14 +87,39 @@ public class SiaIdentityProvider extends AbstractComponent implements ServiceIde @Override public Path privateKeyPath() { return privateKeyFile; } public SSLContext createIdentitySslContextWithTrustStore(Path trustStoreFile) { - return createIdentitySslContext(keyManager, trustStoreFile); + return createIdentitySslContext(keyManager, trustStoreFile, false); } - private static SSLContext createIdentitySslContext(AutoReloadingX509KeyManager keyManager, Path trustStoreFile) { - return new SslContextBuilder() - .withTrustStore(trustStoreFile) - .withKeyManager(keyManager) - .build(); + /** + * Create an SSL context with the given trust store and the key manager from this provider. + * If the {code includeDefaultTruststore} is true, the default trust store will be included. + * + * @param keyManager the key manager + * @param trustStoreFile the trust store file + * @param includeDefaultTruststore whether to include the default trust store + */ + private static SSLContext createIdentitySslContext(AutoReloadingX509KeyManager keyManager, Path trustStoreFile, boolean includeDefaultTruststore) { + List defaultTrustStore = List.of(); + if (includeDefaultTruststore) { + try { + // load the default java trust store and extract the certificates + defaultTrustStore = Stream.of(TrustManagerUtils.createDefaultX509TrustManager().getAcceptedIssuers()).toList(); + } catch (Exception e) { + throw new RuntimeException("Failed to load default trust store", e); + } + } + try { + List caCertList = Stream.concat( + X509CertificateUtils.certificateListFromPem(new String(Files.readAllBytes(trustStoreFile))).stream(), + defaultTrustStore.stream()) + .toList(); + return new SslContextBuilder() + .withTrustStore(caCertList) + .withKeyManager(keyManager) + .build(); + } catch (IOException e) { + throw new RuntimeException(e); + } } @Override diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImpl.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImpl.java index a881dc2b100..5569eef192c 100644 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImpl.java +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/identityprovider/client/LegacyAthenzIdentityProviderImpl.java @@ -363,7 +363,7 @@ public final class LegacyAthenzIdentityProviderImpl extends AbstractComponent im private static SiaIdentityProvider createNodeIdentityProvider(IdentityConfig config) { return new SiaIdentityProvider( - new AthenzService(config.nodeIdentityName()), SiaUtils.DEFAULT_SIA_DIRECTORY, CLIENT_TRUST_STORE); + new AthenzService(config.nodeIdentityName()), SiaUtils.DEFAULT_SIA_DIRECTORY, CLIENT_TRUST_STORE, false); } private boolean isExpired(AthenzCredentials credentials) { diff --git a/vespa-athenz/src/main/resources/configdefinitions/vespa.athenz.identity.sia-provider.def b/vespa-athenz/src/main/resources/configdefinitions/vespa.athenz.identity.sia-provider.def index b97b5b7859f..a2587a4a3e8 100644 --- a/vespa-athenz/src/main/resources/configdefinitions/vespa.athenz.identity.sia-provider.def +++ b/vespa-athenz/src/main/resources/configdefinitions/vespa.athenz.identity.sia-provider.def @@ -7,3 +7,4 @@ keyPathPrefix string trustStorePath string trustStoreType enum {pem, jks} default=jks athenzTruststorePath string default="/opt/yahoo/share/ssl/certs/athenz_certificate_bundle.pem" +publicSystem bool default=false \ No newline at end of file diff --git a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identity/SiaIdentityProviderTest.java b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identity/SiaIdentityProviderTest.java index 5ca6a53a4c7..19a81691b76 100644 --- a/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identity/SiaIdentityProviderTest.java +++ b/vespa-athenz/src/test/java/com/yahoo/vespa/athenz/identity/SiaIdentityProviderTest.java @@ -49,7 +49,8 @@ public class SiaIdentityProviderTest { new AthenzService("domain", "service-name"), keyFile.toPath(), certificateFile.toPath(), - trustStoreFile.toPath()); + trustStoreFile.toPath(), + false); assertNotNull(provider.getIdentitySslContext()); } @@ -72,7 +73,8 @@ public class SiaIdentityProviderTest { new AthenzService("domain", "service-name"), keyFile.toPath(), certificateFile.toPath(), - trustStoreFile.toPath()); + trustStoreFile.toPath(), + false); assertNotNull(provider.getIdentitySslContext()); } -- cgit v1.2.3