Skip to content

Commit

Permalink
Enable TLSv1.3 by default for JDKs with support (#38103)
Browse files Browse the repository at this point in the history
This commit enables the use of TLSv1.3 with security by enabling us to
properly map `TLSv1.3` in the supported protocols setting to the
algorithm for a SSLContext. Additionally, we also enable TLSv1.3 by
default on JDKs that support it.

An issue was uncovered with the MockWebServer when TLSv1.3 is used that
ultimately winds up in an endless loop when the client does not trust
the server's certificate. Due to this, SSLConfigurationReloaderTests
has been pinned to TLSv1.2.

Closes #32276
  • Loading branch information
jaymode authored Feb 1, 2019
1 parent 025bf28 commit 2ca2220
Show file tree
Hide file tree
Showing 9 changed files with 134 additions and 57 deletions.
4 changes: 2 additions & 2 deletions docs/reference/migration/migrate_7_0/settings.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -138,11 +138,11 @@ used.

TLS version 1.0 is now disabled by default as it suffers from
https://www.owasp.org/index.php/Transport_Layer_Protection_Cheat_Sheet#Rule_-_Only_Support_Strong_Protocols[known security issues].
The default protocols are now TLSv1.2 and TLSv1.1.
The default protocols are now TLSv1.3 (if supported), TLSv1.2 and TLSv1.1.
You can enable TLS v1.0 by configuring the relevant `ssl.supported_protocols` setting to include `"TLSv1"`, for example:
[source,yaml]
--------------------------------------------------
xpack.security.http.ssl.supported_protocols: [ "TLSv1.2", "TLSv1.1", "TLSv1" ]
xpack.security.http.ssl.supported_protocols: [ "TLSv1.3", "TLSv1.2", "TLSv1.1", "TLSv1" ]
--------------------------------------------------

[float]
Expand Down
12 changes: 8 additions & 4 deletions docs/reference/settings/security-settings.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,8 @@ and `full`. Defaults to `full`.
See <<ssl-tls-settings,`ssl.verification_mode`>> for an explanation of these values.

`ssl.supported_protocols`::
Supported protocols for TLS/SSL (with versions). Defaults to `TLSv1.2,TLSv1.1`.
Supported protocols for TLS/SSL (with versions). Defaults to `TLSv1.3,TLSv1.2,TLSv1.1` if
the JVM supports TLSv1.3, otherwise `TLSv1.2,TLSv1.1`.

`ssl.cipher_suites`:: Specifies the cipher suites that should be supported when
communicating with the LDAP server.
Expand Down Expand Up @@ -724,7 +725,8 @@ and `full`. Defaults to `full`.
See <<ssl-tls-settings,`ssl.verification_mode`>> for an explanation of these values.

`ssl.supported_protocols`::
Supported protocols for TLS/SSL (with versions). Defaults to `TLSv1.2, TLSv1.1`.
Supported protocols for TLS/SSL (with versions). Defaults to `TLSv1.3,TLSv1.2,TLSv1.1` if
the JVM supports TLSv1.3, otherwise `TLSv1.2,TLSv1.1`.

`ssl.cipher_suites`:: Specifies the cipher suites that should be supported when
communicating with the Active Directory server.
Expand Down Expand Up @@ -1132,7 +1134,8 @@ Defaults to `full`.
See <<ssl-tls-settings,`ssl.verification_mode`>> for a more detailed explanation of these values.

`ssl.supported_protocols`::
Specifies the supported protocols for TLS/SSL.
Specifies the supported protocols for TLS/SSL. Defaults to `TLSv1.3,TLSv1.2,TLSv1.1` if
the JVM supports TLSv1.3, otherwise `TLSv1.2,TLSv1.1`.

`ssl.cipher_suites`::
Specifies the
Expand Down Expand Up @@ -1206,7 +1209,8 @@ settings. For more information, see

`ssl.supported_protocols`::
Supported protocols with versions. Valid protocols: `SSLv2Hello`,
`SSLv3`, `TLSv1`, `TLSv1.1`, `TLSv1.2`. Defaults to `TLSv1.2`, `TLSv1.1`.
`SSLv3`, `TLSv1`, `TLSv1.1`, `TLSv1.2`, `TLSv1.3`. Defaults to `TLSv1.3,TLSv1.2,TLSv1.1` if
the JVM supports TLSv1.3, otherwise `TLSv1.2,TLSv1.1`.
+
--
NOTE: If `xpack.security.fips_mode.enabled` is `true`, you cannot use `SSLv2Hello`
Expand Down
3 changes: 2 additions & 1 deletion docs/reference/settings/ssl-settings.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ endif::server[]

+{ssl-prefix}.ssl.supported_protocols+::
Supported protocols with versions. Valid protocols: `SSLv2Hello`,
`SSLv3`, `TLSv1`, `TLSv1.1`, `TLSv1.2`. Defaults to `TLSv1.2`, `TLSv1.1`.
`SSLv3`, `TLSv1`, `TLSv1.1`, `TLSv1.2`, `TLSv1.3`. Defaults to `TLSv1.3,TLSv1.2,TLSv1.1` if
the JVM supports TLSv1.3, otherwise `TLSv1.2,TLSv1.1`.


ifdef::server[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@
import javax.net.ssl.X509ExtendedTrustManager;
import java.nio.file.Path;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.security.NoSuchAlgorithmException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;

Expand All @@ -40,6 +43,30 @@
*/
public class SslConfiguration {

/**
* An ordered map of protocol algorithms to SSLContext algorithms. The map is ordered from most
* secure to least secure. The names in this map are taken from the
* <a href="https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#sslcontext-algorithms">
* Java Security Standard Algorithm Names Documentation for Java 11</a>.
*/
static final Map<String, String> ORDERED_PROTOCOL_ALGORITHM_MAP;
static {
LinkedHashMap<String, String> protocolAlgorithmMap = new LinkedHashMap<>();
try {
SSLContext.getInstance("TLSv1.3");
protocolAlgorithmMap.put("TLSv1.3", "TLSv1.3");
} catch (NoSuchAlgorithmException e) {
// ignore since we support JVMs that do not support TLSv1.3
}
protocolAlgorithmMap.put("TLSv1.2", "TLSv1.2");
protocolAlgorithmMap.put("TLSv1.1", "TLSv1.1");
protocolAlgorithmMap.put("TLSv1", "TLSv1");
protocolAlgorithmMap.put("SSLv3", "SSLv3");
protocolAlgorithmMap.put("SSLv2", "SSL");
protocolAlgorithmMap.put("SSLv2Hello", "SSL");
ORDERED_PROTOCOL_ALGORITHM_MAP = Collections.unmodifiableMap(protocolAlgorithmMap);
}

private final SslTrustConfig trustConfig;
private final SslKeyConfig keyConfig;
private final SslVerificationMode verificationMode;
Expand Down Expand Up @@ -124,12 +151,13 @@ private String contextProtocol() {
if (supportedProtocols.isEmpty()) {
throw new SslConfigException("no SSL/TLS protocols have been configured");
}
for (String tryProtocol : Arrays.asList("TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3")) {
if (supportedProtocols.contains(tryProtocol)) {
return tryProtocol;
for (Entry<String, String> entry : ORDERED_PROTOCOL_ALGORITHM_MAP.entrySet()) {
if (supportedProtocols.contains(entry.getKey())) {
return entry.getValue();
}
}
return "SSL";
throw new SslConfigException("no supported SSL/TLS protocol was found in the configured supported protocols: "
+ supportedProtocols);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,14 @@
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

import static org.elasticsearch.common.ssl.KeyStoreUtil.inferKeyStoreType;
import static org.elasticsearch.common.ssl.SslConfiguration.ORDERED_PROTOCOL_ALGORITHM_MAP;
import static org.elasticsearch.common.ssl.SslConfigurationKeys.CERTIFICATE;
import static org.elasticsearch.common.ssl.SslConfigurationKeys.CERTIFICATE_AUTHORITIES;
import static org.elasticsearch.common.ssl.SslConfigurationKeys.CIPHERS;
Expand Down Expand Up @@ -68,7 +70,9 @@
*/
public abstract class SslConfigurationLoader {

static final List<String> DEFAULT_PROTOCOLS = Arrays.asList("TLSv1.2", "TLSv1.1");
static final List<String> DEFAULT_PROTOCOLS = Collections.unmodifiableList(
ORDERED_PROTOCOL_ALGORITHM_MAP.containsKey("TLSv1.3") ?
Arrays.asList("TLSv1.3", "TLSv1.2", "TLSv1.1") : Arrays.asList("TLSv1.2", "TLSv1.1"));
static final List<String> DEFAULT_CIPHERS = loadDefaultCiphers();
private static final char[] EMPTY_PASSWORD = new char[0];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

package org.elasticsearch.xpack.core;

import org.apache.logging.log4j.LogManager;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.xpack.core.security.SecurityField;
Expand All @@ -16,6 +17,7 @@

import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.net.ssl.SSLContext;

import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
Expand Down Expand Up @@ -154,7 +156,20 @@ private XPackSettings() {
}
}, Setting.Property.NodeScope);

public static final List<String> DEFAULT_SUPPORTED_PROTOCOLS = Arrays.asList("TLSv1.2", "TLSv1.1");
public static final List<String> DEFAULT_SUPPORTED_PROTOCOLS;

static {
boolean supportsTLSv13 = false;
try {
SSLContext.getInstance("TLSv1.3");
supportsTLSv13 = true;
} catch (NoSuchAlgorithmException e) {
LogManager.getLogger(XPackSettings.class).debug("TLSv1.3 is not supported", e);
}
DEFAULT_SUPPORTED_PROTOCOLS = supportsTLSv13 ?
Arrays.asList("TLSv1.3", "TLSv1.2", "TLSv1.1") : Arrays.asList("TLSv1.2", "TLSv1.1");
}

public static final SSLClientAuth CLIENT_AUTH_DEFAULT = SSLClientAuth.REQUIRED;
public static final SSLClientAuth HTTP_CLIENT_AUTH_DEFAULT = SSLClientAuth.NONE;
public static final VerificationMode VERIFICATION_MODE_DEFAULT = VerificationMode.FULL;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
Expand All @@ -56,13 +57,35 @@
import java.util.function.Supplier;
import java.util.stream.Collectors;

import static org.elasticsearch.xpack.core.XPackSettings.DEFAULT_SUPPORTED_PROTOCOLS;

/**
* Provides access to {@link SSLEngine} and {@link SSLSocketFactory} objects based on a provided configuration. All
* configurations loaded by this service must be configured on construction.
*/
public class SSLService {

private static final Logger logger = LogManager.getLogger(SSLService.class);
/**
* An ordered map of protocol algorithms to SSLContext algorithms. The map is ordered from most
* secure to least secure. The names in this map are taken from the
* <a href="https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#sslcontext-algorithms">
* Java Security Standard Algorithm Names Documentation for Java 11</a>.
*/
private static final Map<String, String> ORDERED_PROTOCOL_ALGORITHM_MAP;
static {
LinkedHashMap<String, String> protocolAlgorithmMap = new LinkedHashMap<>();
if (DEFAULT_SUPPORTED_PROTOCOLS.contains("TLSv1.3")) {
protocolAlgorithmMap.put("TLSv1.3", "TLSv1.3");
}
protocolAlgorithmMap.put("TLSv1.2", "TLSv1.2");
protocolAlgorithmMap.put("TLSv1.1", "TLSv1.1");
protocolAlgorithmMap.put("TLSv1", "TLSv1");
protocolAlgorithmMap.put("SSLv3", "SSLv3");
protocolAlgorithmMap.put("SSLv2", "SSL");
protocolAlgorithmMap.put("SSLv2Hello", "SSL");
ORDERED_PROTOCOL_ALGORITHM_MAP = Collections.unmodifiableMap(protocolAlgorithmMap);
}

private final Settings settings;

Expand Down Expand Up @@ -691,47 +714,19 @@ public SSLConfiguration getSSLConfiguration(String contextName) {
/**
* Maps the supported protocols to an appropriate ssl context algorithm. We make an attempt to use the "best" algorithm when
* possible. The names in this method are taken from the
* <a href="http://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html">JCA Standard Algorithm Name
* Documentation for Java 8</a>.
* <a href="https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#sslcontext-algorithms">Java Security
* Standard Algorithm Names Documentation for Java 11</a>.
*/
private static String sslContextAlgorithm(List<String> supportedProtocols) {
if (supportedProtocols.isEmpty()) {
return "TLSv1.2";
}

String algorithm = "SSL";
for (String supportedProtocol : supportedProtocols) {
switch (supportedProtocol) {
case "TLSv1.2":
return "TLSv1.2";
case "TLSv1.1":
if ("TLSv1.2".equals(algorithm) == false) {
algorithm = "TLSv1.1";
}
break;
case "TLSv1":
switch (algorithm) {
case "TLSv1.2":
case "TLSv1.1":
break;
default:
algorithm = "TLSv1";
}
break;
case "SSLv3":
switch (algorithm) {
case "SSLv2":
case "SSL":
algorithm = "SSLv3";
}
break;
case "SSLv2":
case "SSLv2Hello":
break;
default:
throw new IllegalArgumentException("found unexpected value in supported protocols: " + supportedProtocol);
throw new IllegalArgumentException("no SSL/TLS protocols have been configured");
}
for (Entry<String, String> entry : ORDERED_PROTOCOL_ALGORITHM_MAP.entrySet()) {
if (supportedProtocols.contains(entry.getKey())) {
return entry.getValue();
}
}
return algorithm;
throw new IllegalArgumentException("no supported SSL/TLS protocol was found in the configured supported protocols: "
+ supportedProtocols);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
import org.elasticsearch.test.ESTestCase;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.net.ssl.SSLContext;

import java.security.NoSuchAlgorithmException;

import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.not;
Expand Down Expand Up @@ -48,6 +50,16 @@ public void testPasswordHashingAlgorithmSettingValidation() {
Settings.builder().put(XPackSettings.PASSWORD_HASHING_ALGORITHM.getKey(), bcryptAlgo).build()));
}

public void testDefaultSupportedProtocolsWithTLSv13() throws Exception {
assumeTrue("current JVM does not support TLSv1.3", supportTLSv13());
assertThat(XPackSettings.DEFAULT_SUPPORTED_PROTOCOLS, contains("TLSv1.3", "TLSv1.2", "TLSv1.1"));
}

public void testDefaultSupportedProtocolsWithoutTLSv13() throws Exception {
assumeFalse("current JVM supports TLSv1.3", supportTLSv13());
assertThat(XPackSettings.DEFAULT_SUPPORTED_PROTOCOLS, contains("TLSv1.2", "TLSv1.1"));
}

private boolean isSecretkeyFactoryAlgoAvailable(String algorithmId) {
try {
SecretKeyFactory.getInstance(algorithmId);
Expand All @@ -56,4 +68,13 @@ private boolean isSecretkeyFactoryAlgoAvailable(String algorithmId) {
return false;
}
}

private boolean supportTLSv13() {
try {
SSLContext.getInstance("TLSv1.3");
return true;
} catch (NoSuchAlgorithmException e) {
return false;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLHandshakeException;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
Expand Down Expand Up @@ -263,7 +262,7 @@ public void testReloadingPEMTrustConfig() throws Exception {
try (MockWebServer server = getSslServer(serverKeyPath, serverCertPath, "testnode")) {
final Consumer<SSLContext> trustMaterialPreChecks = (context) -> {
try (CloseableHttpClient client = HttpClients.custom().setSSLContext(context).build()) {
privilegedConnect(() -> client.execute(new HttpGet("https://localhost:" + server.getPort())).close());
privilegedConnect(() -> client.execute(new HttpGet("https://localhost:" + server.getPort())));//.close());
} catch (Exception e) {
throw new RuntimeException("Exception connecting to the mock server", e);
}
Expand Down Expand Up @@ -480,7 +479,9 @@ private static MockWebServer getSslServer(Path keyStorePath, String keyStorePass
try (InputStream is = Files.newInputStream(keyStorePath)) {
keyStore.load(is, keyStorePass.toCharArray());
}
final SSLContext sslContext = new SSLContextBuilder().loadKeyMaterial(keyStore, keyStorePass.toCharArray())
final SSLContext sslContext = new SSLContextBuilder()
.loadKeyMaterial(keyStore, keyStorePass.toCharArray())
.setProtocol("TLSv1.2")
.build();
MockWebServer server = new MockWebServer(sslContext, false);
server.enqueue(new MockResponse().setResponseCode(200).setBody("body"));
Expand All @@ -494,7 +495,9 @@ private static MockWebServer getSslServer(Path keyPath, Path certPath, String pa
keyStore.load(null, password.toCharArray());
keyStore.setKeyEntry("testnode_ec", PemUtils.readPrivateKey(keyPath, password::toCharArray), password.toCharArray(),
CertParsingUtils.readCertificates(Collections.singletonList(certPath)));
final SSLContext sslContext = new SSLContextBuilder().loadKeyMaterial(keyStore, password.toCharArray())
final SSLContext sslContext = new SSLContextBuilder()
.loadKeyMaterial(keyStore, password.toCharArray())
.setProtocol("TLSv1.2")
.build();
MockWebServer server = new MockWebServer(sslContext, false);
server.enqueue(new MockResponse().setResponseCode(200).setBody("body"));
Expand All @@ -509,7 +512,10 @@ private static CloseableHttpClient getSSLClient(Path trustStorePath, String trus
try (InputStream is = Files.newInputStream(trustStorePath)) {
trustStore.load(is, trustStorePass.toCharArray());
}
final SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(trustStore, null).build();
final SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(trustStore, null)
.setProtocol("TLSv1.2")
.build();
return HttpClients.custom().setSSLContext(sslContext).build();
}

Expand All @@ -526,7 +532,10 @@ private static CloseableHttpClient getSSLClient(List<Path> trustedCertificatePat
for (Certificate cert : CertParsingUtils.readCertificates(trustedCertificatePaths)) {
trustStore.setCertificateEntry(cert.toString(), cert);
}
final SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(trustStore, null).build();
final SSLContext sslContext = new SSLContextBuilder()
.loadTrustMaterial(trustStore, null)
.setProtocol("TLSv1.2")
.build();
return HttpClients.custom().setSSLContext(sslContext).build();
}

Expand Down

0 comments on commit 2ca2220

Please sign in to comment.