diff options
author | Tor Brede Vekterli <vekterli@oath.com> | 2019-01-24 16:34:54 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-01-24 16:34:54 +0100 |
commit | 25f41d6c5fd105b47d9f0d0c1642f25fd9ac8795 (patch) | |
tree | b5e22db7bcee5a9d41da33f261c33f351307cad1 | |
parent | 1ed75a5681fc19966fdb1940f3f55e6c8f5c2c76 (diff) | |
parent | e9fb2bbd3ceb780b48c9aa60026f4f096ba2cc50 (diff) |
Merge pull request #8218 from vespa-engine/bjorncs/tls
bjorncs/tls
16 files changed, 80 insertions, 37 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tls/ControllerSslContextFactoryProvider.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tls/ControllerSslContextFactoryProvider.java index 81a0a314dc5..d20c86528a5 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tls/ControllerSslContextFactoryProvider.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/tls/ControllerSslContextFactoryProvider.java @@ -17,7 +17,6 @@ import java.nio.file.Paths; import java.security.KeyStore; import java.security.PrivateKey; import java.security.cert.X509Certificate; -import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -58,12 +57,6 @@ public class ControllerSslContextFactoryProvider extends AbstractComponent imple /** Create a SslContextFactory backed by an in-memory key and trust store */ private SslContextFactory createSslContextFactory(int port) { SslContextFactory factory = new SslContextFactory(); - // TODO Remove cipher exclusions on Vespa 7 (require ciphers with forward secrecy) - // Do not exclude TLS_RSA_* ciphers - String[] excludedCiphers = Arrays.stream(factory.getExcludeCipherSuites()) - .filter(cipherPattern -> !cipherPattern.equals("^TLS_RSA_.*$")) - .toArray(String[]::new); - factory.setExcludeCipherSuites(excludedCiphers); if (port != 443) { factory.setWantClientAuth(true); } diff --git a/jrt/src/com/yahoo/jrt/Acceptor.java b/jrt/src/com/yahoo/jrt/Acceptor.java index d27700a5f8f..3da978fb90e 100644 --- a/jrt/src/com/yahoo/jrt/Acceptor.java +++ b/jrt/src/com/yahoo/jrt/Acceptor.java @@ -30,7 +30,7 @@ public class Acceptor { private final static Logger log = Logger.getLogger(Acceptor.class.getName()); - private final Thread thread = new Thread(new Run(), "<acceptor>"); + private final Thread thread = new Thread(new Run(), "<jrt-acceptor>"); private final CountDownLatch shutdownGate = new CountDownLatch(1); private final Transport parent; private final Supervisor owner; diff --git a/jrt/src/com/yahoo/jrt/Closer.java b/jrt/src/com/yahoo/jrt/Closer.java index aa7dedd8a26..71d99807253 100644 --- a/jrt/src/com/yahoo/jrt/Closer.java +++ b/jrt/src/com/yahoo/jrt/Closer.java @@ -14,7 +14,7 @@ class Closer { } } - private Thread thread = new Thread(new Run(), "<closer>"); + private Thread thread = new Thread(new Run(), "<jrt-closer>"); private Transport parent; private ThreadQueue closeQueue = new ThreadQueue(); diff --git a/jrt/src/com/yahoo/jrt/Connector.java b/jrt/src/com/yahoo/jrt/Connector.java index ee387e732cb..a4cbd07d3f8 100644 --- a/jrt/src/com/yahoo/jrt/Connector.java +++ b/jrt/src/com/yahoo/jrt/Connector.java @@ -14,7 +14,7 @@ class Connector { } } - private Thread thread = new Thread(new Run(), "<connector>"); + private Thread thread = new Thread(new Run(), "<jrt-connector>"); private Transport parent; private ThreadQueue connectQueue = new ThreadQueue(); private boolean done = false; diff --git a/jrt/src/com/yahoo/jrt/Transport.java b/jrt/src/com/yahoo/jrt/Transport.java index 8da4c737f79..0a2f2a4b7cb 100644 --- a/jrt/src/com/yahoo/jrt/Transport.java +++ b/jrt/src/com/yahoo/jrt/Transport.java @@ -170,7 +170,7 @@ public class Transport { this.fatalHandler = fatalHandler; // NB: this must be set first } this.cryptoEngine = cryptoEngine; - thread = new Thread(new Run(), "<transport>"); + thread = new Thread(new Run(), "<jrt-transport>"); queue = new Queue(); myQueue = new Queue(); connector = new Connector(this); diff --git a/jrt/tests/com/yahoo/jrt/CryptoUtils.java b/jrt/tests/com/yahoo/jrt/CryptoUtils.java index 1c2280567cb..6890fe88da5 100644 --- a/jrt/tests/com/yahoo/jrt/CryptoUtils.java +++ b/jrt/tests/com/yahoo/jrt/CryptoUtils.java @@ -18,8 +18,8 @@ import java.security.KeyPair; import java.security.cert.X509Certificate; import java.time.Instant; -import static com.yahoo.security.KeyAlgorithm.RSA; -import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_RSA; +import static com.yahoo.security.KeyAlgorithm.EC; +import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_ECDSA; import static com.yahoo.security.X509CertificateBuilder.generateRandomSerialNumber; import static java.time.Instant.EPOCH; import static java.time.temporal.ChronoUnit.DAYS; @@ -29,13 +29,12 @@ import static java.util.Collections.singletonList; /** * @author bjorncs */ -// TODO Use EC. Java/JSSE is currently unable to find compatible ciphers when using elliptic curve crypto from BouncyCastle class CryptoUtils { - static final KeyPair keyPair = KeyUtils.generateKeypair(RSA); + static final KeyPair keyPair = KeyUtils.generateKeypair(EC); static final X509Certificate certificate = X509CertificateBuilder - .fromKeypair(keyPair, new X500Principal("CN=dummy"), EPOCH, Instant.now().plus(1, DAYS), SHA256_WITH_RSA, generateRandomSerialNumber()) + .fromKeypair(keyPair, new X500Principal("CN=dummy"), EPOCH, Instant.now().plus(1, DAYS), SHA256_WITH_ECDSA, generateRandomSerialNumber()) .build(); static final AuthorizedPeers authorizedPeers = new AuthorizedPeers( @@ -49,7 +48,7 @@ class CryptoUtils { Field.CN, new HostGlobPattern("dummy")))))); static TlsContext createTestTlsContext() { - return new DefaultTlsContext(singletonList(certificate), keyPair.getPrivate(), singletonList(certificate), authorizedPeers, AuthorizationMode.ENFORCE); + return new DefaultTlsContext(singletonList(certificate), keyPair.getPrivate(), singletonList(certificate), authorizedPeers, AuthorizationMode.ENFORCE, DefaultTlsContext.ALLOWED_CIPHER_SUITES); } } diff --git a/security-utils/src/main/java/com/yahoo/security/KeyAlgorithm.java b/security-utils/src/main/java/com/yahoo/security/KeyAlgorithm.java index 3218f81f0d6..732ac2bb12c 100644 --- a/security-utils/src/main/java/com/yahoo/security/KeyAlgorithm.java +++ b/security-utils/src/main/java/com/yahoo/security/KeyAlgorithm.java @@ -1,20 +1,28 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.security; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.ECGenParameterSpec; +import java.util.Optional; + /** * @author bjorncs */ public enum KeyAlgorithm { - RSA("RSA"), - EC("EC"); + RSA("RSA", null), + EC("EC", new ECGenParameterSpec("prime256v1")); // TODO Make curve configurable final String algorithmName; + private final AlgorithmParameterSpec spec; - KeyAlgorithm(String algorithmName) { + KeyAlgorithm(String algorithmName, AlgorithmParameterSpec spec) { this.algorithmName = algorithmName; + this.spec = spec; } String getAlgorithmName() { return algorithmName; } + + Optional<AlgorithmParameterSpec> getSpec() { return Optional.ofNullable(spec); } } diff --git a/security-utils/src/main/java/com/yahoo/security/KeyUtils.java b/security-utils/src/main/java/com/yahoo/security/KeyUtils.java index 0d45a62f193..76e0f5419a3 100644 --- a/security-utils/src/main/java/com/yahoo/security/KeyUtils.java +++ b/security-utils/src/main/java/com/yahoo/security/KeyUtils.java @@ -46,6 +46,9 @@ public class KeyUtils { if (keySize != -1) { keyGen.initialize(keySize); } + if (algorithm.getSpec().isPresent()) { + keyGen.initialize(algorithm.getSpec().get()); + } return keyGen.genKeyPair(); } catch (GeneralSecurityException e) { throw new RuntimeException(e); diff --git a/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java b/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java index f2ae1dd0d38..a42c678edab 100644 --- a/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java +++ b/security-utils/src/main/java/com/yahoo/security/tls/DefaultTlsContext.java @@ -28,37 +28,48 @@ public class DefaultTlsContext implements TlsContext { "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", - "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_AES_128_GCM_SHA256", // TLSv1.3 + "TLS_AES_256_GCM_SHA384", // TLSv1.3 + "TLS_CHACHA20_POLY1305_SHA256"); // TLSv1.3 private static final Logger log = Logger.getLogger(DefaultTlsContext.class.getName()); private final SSLContext sslContext; + private final List<String> acceptedCiphers; public DefaultTlsContext(List<X509Certificate> certificates, PrivateKey privateKey, List<X509Certificate> caCertificates, AuthorizedPeers authorizedPeers, - AuthorizationMode mode) { + AuthorizationMode mode, + List<String> acceptedCiphers) { this.sslContext = createSslContext(certificates, privateKey, caCertificates, authorizedPeers, mode); + this.acceptedCiphers = acceptedCiphers; } public DefaultTlsContext(Path tlsOptionsConfigFile, AuthorizationMode mode) { - this.sslContext = createSslContext(tlsOptionsConfigFile, mode); + TransportSecurityOptions options = TransportSecurityOptions.fromJsonFile(tlsOptionsConfigFile); + this.sslContext = createSslContext(options, mode); + this.acceptedCiphers = options.getAcceptedCiphers(); } @Override public SSLEngine createSslEngine() { SSLEngine sslEngine = sslContext.createSSLEngine(); - restrictSetOfEnabledCiphers(sslEngine); + restrictSetOfEnabledCiphers(sslEngine, acceptedCiphers); return sslEngine; } - private static void restrictSetOfEnabledCiphers(SSLEngine sslEngine) { + private static void restrictSetOfEnabledCiphers(SSLEngine sslEngine, List<String> acceptedCiphers) { String[] validCipherSuites = Arrays.stream(sslEngine.getSupportedCipherSuites()) - .filter(ALLOWED_CIPHER_SUITES::contains) + .filter(suite -> ALLOWED_CIPHER_SUITES.contains(suite) && (acceptedCiphers.isEmpty() || acceptedCiphers.contains(suite))) .toArray(String[]::new); if (validCipherSuites.length == 0) { - throw new IllegalStateException("None of the allowed cipher suites are supported"); + throw new IllegalStateException( + String.format("None of the allowed cipher suites are supported " + + "(allowed-cipher-suites=%s, supported-cipher-suites=%s, accepted-cipher-suites=%s)", + ALLOWED_CIPHER_SUITES, List.of(sslEngine.getSupportedCipherSuites()), acceptedCiphers)); } log.log(Level.FINE, () -> String.format("Allowed cipher suites that are supported: %s", Arrays.toString(validCipherSuites))); sslEngine.setEnabledCipherSuites(validCipherSuites); @@ -82,8 +93,7 @@ public class DefaultTlsContext implements TlsContext { return builder.build(); } - private static SSLContext createSslContext(Path tlsOptionsConfigFile, AuthorizationMode mode) { - TransportSecurityOptions options = TransportSecurityOptions.fromJsonFile(tlsOptionsConfigFile); + private static SSLContext createSslContext(TransportSecurityOptions options, AuthorizationMode mode) { SslContextBuilder builder = new SslContextBuilder(); options.getCertificatesFile() .ifPresent(certificates -> builder.withKeyStore(options.getPrivateKeyFile().get(), certificates)); diff --git a/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityOptions.java b/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityOptions.java index 82caf02223f..c0e9e1053c3 100644 --- a/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityOptions.java +++ b/security-utils/src/main/java/com/yahoo/security/tls/TransportSecurityOptions.java @@ -13,6 +13,8 @@ import java.io.UncheckedIOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.Optional; @@ -27,12 +29,14 @@ public class TransportSecurityOptions { private final Path certificatesFile; private final Path caCertificatesFile; private final AuthorizedPeers authorizedPeers; + private final List<String> acceptedCiphers; private TransportSecurityOptions(Builder builder) { this.privateKeyFile = builder.privateKeyFile; this.certificatesFile = builder.certificatesFile; this.caCertificatesFile = builder.caCertificatesFile; this.authorizedPeers = builder.authorizedPeers; + this.acceptedCiphers = builder.acceptedCiphers; } public Optional<Path> getPrivateKeyFile() { @@ -51,6 +55,8 @@ public class TransportSecurityOptions { return Optional.ofNullable(authorizedPeers); } + public List<String> getAcceptedCiphers() { return acceptedCiphers; } + public static TransportSecurityOptions fromJsonFile(Path file) { try (InputStream in = Files.newInputStream(file)) { return new TransportSecurityOptionsJsonSerializer().deserialize(in); @@ -83,6 +89,7 @@ public class TransportSecurityOptions { private Path certificatesFile; private Path caCertificatesFile; private AuthorizedPeers authorizedPeers; + private List<String> acceptedCiphers = new ArrayList<>(); public Builder() {} @@ -102,6 +109,11 @@ public class TransportSecurityOptions { return this; } + public Builder withAcceptedCiphers(List<String> acceptedCiphers) { + this.acceptedCiphers = acceptedCiphers; + return this; + } + public TransportSecurityOptions build() { return new TransportSecurityOptions(this); } @@ -114,6 +126,7 @@ public class TransportSecurityOptions { ", certificatesFile=" + certificatesFile + ", caCertificatesFile=" + caCertificatesFile + ", authorizedPeers=" + authorizedPeers + + ", acceptedCiphers=" + acceptedCiphers + '}'; } @@ -125,11 +138,12 @@ public class TransportSecurityOptions { return Objects.equals(privateKeyFile, that.privateKeyFile) && Objects.equals(certificatesFile, that.certificatesFile) && Objects.equals(caCertificatesFile, that.caCertificatesFile) && - Objects.equals(authorizedPeers, that.authorizedPeers); + Objects.equals(authorizedPeers, that.authorizedPeers) && + Objects.equals(acceptedCiphers, that.acceptedCiphers); } @Override public int hashCode() { - return Objects.hash(privateKeyFile, certificatesFile, caCertificatesFile, authorizedPeers); + return Objects.hash(privateKeyFile, certificatesFile, caCertificatesFile, authorizedPeers, acceptedCiphers); } }
\ No newline at end of file diff --git a/security-utils/src/main/java/com/yahoo/security/tls/json/TransportSecurityOptionsEntity.java b/security-utils/src/main/java/com/yahoo/security/tls/json/TransportSecurityOptionsEntity.java index fbb98d7c382..6594fa84255 100644 --- a/security-utils/src/main/java/com/yahoo/security/tls/json/TransportSecurityOptionsEntity.java +++ b/security-utils/src/main/java/com/yahoo/security/tls/json/TransportSecurityOptionsEntity.java @@ -19,6 +19,7 @@ class TransportSecurityOptionsEntity { @JsonProperty("files") Files files; @JsonProperty("authorized-peers") @JsonInclude(NON_EMPTY) List<AuthorizedPeer> authorizedPeers; + @JsonProperty("accepted-ciphers") @JsonInclude(NON_EMPTY) List<String> acceptedCiphers; static class Files { @JsonProperty("private-key") String privateKeyFile; diff --git a/security-utils/src/main/java/com/yahoo/security/tls/json/TransportSecurityOptionsJsonSerializer.java b/security-utils/src/main/java/com/yahoo/security/tls/json/TransportSecurityOptionsJsonSerializer.java index f75cb4bcfff..a6291477942 100644 --- a/security-utils/src/main/java/com/yahoo/security/tls/json/TransportSecurityOptionsJsonSerializer.java +++ b/security-utils/src/main/java/com/yahoo/security/tls/json/TransportSecurityOptionsJsonSerializer.java @@ -70,6 +70,12 @@ public class TransportSecurityOptionsJsonSerializer { } builder.withAuthorizedPeers(new AuthorizedPeers(toPeerPolicies(authorizedPeersEntity))); } + if (entity.acceptedCiphers != null) { + if (entity.acceptedCiphers.isEmpty()) { + throw new IllegalArgumentException("'accepted-ciphers' cannot be empty"); + } + builder.withAcceptedCiphers(entity.acceptedCiphers); + } return builder.build(); } @@ -145,6 +151,9 @@ public class TransportSecurityOptionsJsonSerializer { entity.authorizedPeers.add(authorizedPeer); } }); + if (!options.getAcceptedCiphers().isEmpty()) { + entity.acceptedCiphers = options.getAcceptedCiphers(); + } return entity; } diff --git a/security-utils/src/test/java/com/yahoo/security/tls/DefaultTlsContextTest.java b/security-utils/src/test/java/com/yahoo/security/tls/DefaultTlsContextTest.java index 608ddcd2c1d..cfaa7ba06df 100644 --- a/security-utils/src/test/java/com/yahoo/security/tls/DefaultTlsContextTest.java +++ b/security-utils/src/test/java/com/yahoo/security/tls/DefaultTlsContextTest.java @@ -15,9 +15,10 @@ import javax.security.auth.x500.X500Principal; import java.security.KeyPair; import java.security.cert.X509Certificate; import java.time.Instant; +import java.util.List; -import static com.yahoo.security.KeyAlgorithm.RSA; -import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_RSA; +import static com.yahoo.security.KeyAlgorithm.EC; +import static com.yahoo.security.SignatureAlgorithm.SHA256_WITH_ECDSA; import static com.yahoo.security.X509CertificateBuilder.generateRandomSerialNumber; import static java.time.Instant.EPOCH; import static java.time.temporal.ChronoUnit.DAYS; @@ -32,10 +33,10 @@ public class DefaultTlsContextTest { @Test public void can_create_sslcontext_from_credentials() { - KeyPair keyPair = KeyUtils.generateKeypair(RSA); + KeyPair keyPair = KeyUtils.generateKeypair(EC); X509Certificate certificate = X509CertificateBuilder - .fromKeypair(keyPair, new X500Principal("CN=dummy"), EPOCH, Instant.now().plus(1, DAYS), SHA256_WITH_RSA, generateRandomSerialNumber()) + .fromKeypair(keyPair, new X500Principal("CN=dummy"), EPOCH, Instant.now().plus(1, DAYS), SHA256_WITH_ECDSA, generateRandomSerialNumber()) .build(); AuthorizedPeers authorizedPeers = new AuthorizedPeers( @@ -46,7 +47,7 @@ public class DefaultTlsContextTest { singletonList(new RequiredPeerCredential(RequiredPeerCredential.Field.CN, new HostGlobPattern("dummy")))))); DefaultTlsContext tlsContext = - new DefaultTlsContext(singletonList(certificate), keyPair.getPrivate(), singletonList(certificate), authorizedPeers, AuthorizationMode.ENFORCE); + new DefaultTlsContext(singletonList(certificate), keyPair.getPrivate(), singletonList(certificate), authorizedPeers, AuthorizationMode.ENFORCE, List.of()); SSLEngine sslEngine = tlsContext.createSslEngine(); assertThat(sslEngine).isNotNull(); diff --git a/security-utils/src/test/java/com/yahoo/security/tls/TransportSecurityOptionsTest.java b/security-utils/src/test/java/com/yahoo/security/tls/TransportSecurityOptionsTest.java index aa5509a23b2..9d8f26cdd2c 100644 --- a/security-utils/src/test/java/com/yahoo/security/tls/TransportSecurityOptionsTest.java +++ b/security-utils/src/test/java/com/yahoo/security/tls/TransportSecurityOptionsTest.java @@ -8,6 +8,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.List; import static org.junit.Assert.*; @@ -20,6 +21,7 @@ public class TransportSecurityOptionsTest { private static final TransportSecurityOptions OPTIONS = new TransportSecurityOptions.Builder() .withCertificates(Paths.get("certs.pem"), Paths.get("myhost.key")) .withCaCertificates(Paths.get("my_cas.pem")) + .withAcceptedCiphers(List.of("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" , "TLS_AES_256_GCM_SHA384")) .build(); @Test diff --git a/security-utils/src/test/java/com/yahoo/security/tls/json/TransportSecurityOptionsJsonSerializerTest.java b/security-utils/src/test/java/com/yahoo/security/tls/json/TransportSecurityOptionsJsonSerializerTest.java index 5e611b1eba5..03489a60784 100644 --- a/security-utils/src/test/java/com/yahoo/security/tls/json/TransportSecurityOptionsJsonSerializerTest.java +++ b/security-utils/src/test/java/com/yahoo/security/tls/json/TransportSecurityOptionsJsonSerializerTest.java @@ -22,6 +22,7 @@ import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; +import java.util.List; import static com.yahoo.security.tls.policy.RequiredPeerCredential.Field.CN; import static com.yahoo.security.tls.policy.RequiredPeerCredential.Field.SAN_DNS; @@ -64,6 +65,7 @@ public class TransportSecurityOptionsJsonSerializerTest { TransportSecurityOptions options = new TransportSecurityOptions.Builder() .withCertificates(Paths.get("certs.pem"), Paths.get("myhost.key")) .withCaCertificates(Paths.get("my_cas.pem")) + .withAcceptedCiphers(List.of("TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384" , "TLS_AES_256_GCM_SHA384")) .build(); File outputFile = tempDirectory.newFile(); try (OutputStream out = Files.newOutputStream(outputFile.toPath())) { diff --git a/security-utils/src/test/resources/transport-security-options.json b/security-utils/src/test/resources/transport-security-options.json index 0506c130722..2e55c8fd931 100644 --- a/security-utils/src/test/resources/transport-security-options.json +++ b/security-utils/src/test/resources/transport-security-options.json @@ -3,5 +3,6 @@ "private-key": "myhost.key", "ca-certificates": "my_cas.pem", "certificates": "certs.pem" - } + }, + "accepted-ciphers": ["TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", "TLS_AES_256_GCM_SHA384"] }
\ No newline at end of file |