Skip to content

Commit

Permalink
Merge pull request #34469 from cescoffier/2.16-cve-2023-2974
Browse files Browse the repository at this point in the history
2.16 - Enforce the configured TLS version
  • Loading branch information
gsmet authored Jul 4, 2023
2 parents 1100b8b + d5fc954 commit 2f856a1
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Optional;
import java.util.Set;

import org.jboss.logging.Logger;

Expand Down Expand Up @@ -114,12 +115,7 @@ static boolean applySslOptions(GrpcServerConfiguration config, HttpServerOptions
for (String cipher : sslConfig.cipherSuites.orElse(Collections.emptyList())) {
options.addEnabledCipherSuite(cipher);
}

for (String protocol : sslConfig.protocols) {
if (!protocol.isEmpty()) {
options.addEnabledSecureTransportProtocol(protocol);
}
}
options.setEnabledSecureTransportProtocols(Set.copyOf(sslConfig.protocols));
options.setClientAuth(sslConfig.clientAuth);
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public class SslServerConfig {

/**
* An optional trust store which holds the certificate information of the certificates to trust
*
* <p>
* The trust store can be either on classpath or an external file.
*/
@ConfigItem
Expand All @@ -75,7 +75,14 @@ public class SslServerConfig {
public Optional<List<String>> cipherSuites;

/**
* The list of protocols to explicitly enable.
* Sets the ordered list of enabled SSL/TLS protocols.
* <p>
* If not set, it defaults to {@code "TLSv1.3, TLSv1.2"}.
* The following list of protocols are supported: {@code TLSv1, TLSv1.1, TLSv1.2, TLSv1.3}.
* To only enable {@code TLSv1.3}, set the value to {@code to "TLSv1.3"}.
* <p>
* Note that setting an empty list, and enabling SSL/TLS is invalid.
* You must at least have one protocol.
*/
@DefaultConverter
@ConfigItem(defaultValue = "TLSv1.3,TLSv1.2")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,14 @@ public class ServerSslConfig {
public Optional<List<String>> cipherSuites;

/**
* The list of protocols to explicitly enable.
* Sets the ordered list of enabled SSL/TLS protocols.
* <p>
* If not set, it defaults to {@code "TLSv1.3, TLSv1.2"}.
* The following list of protocols are supported: {@code TLSv1, TLSv1.1, TLSv1.2, TLSv1.3}.
* To only enable {@code TLSv1.3}, set the value to {@code to "TLSv1.3"}.
* <p>
* Note that setting an empty list, and enabling SSL/TLS is invalid.
* You must at least have one protocol.
*/
@DefaultConverter
@ConfigItem(defaultValue = "TLSv1.3,TLSv1.2")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,7 @@
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.TreeMap;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
Expand Down Expand Up @@ -890,11 +881,7 @@ private static HttpServerOptions createSslOptions(HttpBuildTimeConfig buildTimeC
serverOptions.addEnabledCipherSuite(cipher);
}

for (String protocol : sslConfig.protocols) {
if (!protocol.isEmpty()) {
serverOptions.addEnabledSecureTransportProtocol(protocol);
}
}
serverOptions.setEnabledSecureTransportProtocols(Set.copyOf(sslConfig.protocols));
serverOptions.setSsl(true);
serverOptions.setSni(sslConfig.sni);
int sslPort = httpConfiguration.determineSslPort(launchMode);
Expand Down
5 changes: 5 additions & 0 deletions integration-tests/vertx-http/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@
<artifactId>awaitility</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.smallrye.reactive</groupId>
<artifactId>smallrye-mutiny-vertx-web-client</artifactId>
<scope>test</scope>
</dependency>

<!-- Minimal test dependencies to *-deployment artifacts for consistent build order -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package io.quarkus.it.vertx;

import javax.ws.rs.GET;
import javax.ws.rs.Path;

@Path("/hello")
public class HelloResource {
@GET
public String hello() {
return "hello";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.quarkus.it.vertx;

import java.util.Map;

import io.quarkus.test.junit.QuarkusTestProfile;

public class ServerWithTLS13Only implements QuarkusTestProfile {

@Override
public Map<String, String> getConfigOverrides() {
return Map.of(
"quarkus.http.ssl.protocols", "TLSv1.3");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package io.quarkus.it.vertx;

import java.util.Set;
import java.util.concurrent.CompletionException;

import javax.inject.Inject;
import javax.net.ssl.SSLHandshakeException;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.test.junit.QuarkusTest;
import io.vertx.core.net.JksOptions;
import io.vertx.ext.web.client.WebClientOptions;
import io.vertx.mutiny.core.Vertx;
import io.vertx.mutiny.ext.web.client.WebClient;

@QuarkusTest
public class TlsProtocolVersionDefaultTestCase {

@TestHTTPResource(value = "/hello", ssl = true)
String url;

@Inject
Vertx vertx;

@Test
void testWithWebClientRequestingMultipleTlsVersions() {
// The Web client is requesting "TLSv1", "TLSv1.1" or "TLSv1.2", the server is exposing TLSv1.3 and TLSv1.2 - all good
WebClient client = WebClient.create(vertx, new WebClientOptions().setSsl(true)
.setKeyStoreOptions(
new JksOptions().setPath("src/test/resources/client-keystore-1.jks").setPassword("password"))
.setTrustStoreOptions(
new JksOptions().setPath("src/test/resources/client-truststore.jks").setPassword("password"))
.setVerifyHost(false));
var resp = client.getAbs(url).sendAndAwait();
Assertions.assertEquals(200, resp.statusCode());
}

@Test
void testWithWebClientRequestingTls13() {
// The Web client is requesting "TLSv1.3", the server is exposing TLSv1.3 and TLSv1.2 - all good
WebClient client = WebClient.create(vertx, new WebClientOptions().setSsl(true)
.setEnabledSecureTransportProtocols(Set.of("TLSv1.3"))
.setKeyStoreOptions(
new JksOptions().setPath("src/test/resources/client-keystore-1.jks").setPassword("password"))
.setTrustStoreOptions(
new JksOptions().setPath("src/test/resources/client-truststore.jks").setPassword("password"))
.setVerifyHost(false));
var resp = client.getAbs(url).sendAndAwait();
Assertions.assertEquals(200, resp.statusCode());
}

@Test
void testWithWebClientRequestingTls12() {
// The Web client is requesting "TLSv1.2", the server is exposing TLSv1.3 and TLSv1.2 - all good
WebClient client = WebClient.create(vertx, new WebClientOptions().setSsl(true)
.setEnabledSecureTransportProtocols(Set.of("TLSv1.2"))
.setKeyStoreOptions(
new JksOptions().setPath("src/test/resources/client-keystore-1.jks").setPassword("password"))
.setTrustStoreOptions(
new JksOptions().setPath("src/test/resources/client-truststore.jks").setPassword("password"))
.setVerifyHost(false));
var resp = client.getAbs(url).sendAndAwait();
Assertions.assertEquals(200, resp.statusCode());
}

@Test
void testWithWebClientRequestingTls12And13() {
// The Web client is requesting TLS 1.2 or 1.3, the server is exposing TLSv1.3 and TLSv1.2 - all good
WebClient client = WebClient.create(vertx, new WebClientOptions().setSsl(true)
.setEnabledSecureTransportProtocols(Set.of("TLSv1.2", "TLSv1.3"))
.setKeyStoreOptions(
new JksOptions().setPath("src/test/resources/client-keystore-1.jks").setPassword("password"))
.setTrustStoreOptions(
new JksOptions().setPath("src/test/resources/client-truststore.jks").setPassword("password"))
.setVerifyHost(false));
var resp = client.getAbs(url).sendAndAwait();
Assertions.assertEquals(200, resp.statusCode());
}

@Test
void testWithWebClientRequestingTls11() {
// The Web client is requesting "TLSv1.1", the server is exposing TLSv1.3 and TLSv1.2 - KO
WebClient client = WebClient.create(vertx, new WebClientOptions().setSsl(true)
.setEnabledSecureTransportProtocols(Set.of("TLSv1.1"))
.setKeyStoreOptions(
new JksOptions().setPath("src/test/resources/client-keystore-1.jks").setPassword("password"))
.setTrustStoreOptions(
new JksOptions().setPath("src/test/resources/client-truststore.jks").setPassword("password"))
.setVerifyHost(false));
Throwable exception = Assertions.assertThrows(CompletionException.class, () -> client.getAbs(url).sendAndAwait());
Assertions.assertTrue(exception.getCause() instanceof SSLHandshakeException);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package io.quarkus.it.vertx;

import java.util.Set;
import java.util.concurrent.CompletionException;

import javax.inject.Inject;
import javax.net.ssl.SSLHandshakeException;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.TestProfile;
import io.vertx.core.net.JksOptions;
import io.vertx.ext.web.client.WebClientOptions;
import io.vertx.mutiny.core.Vertx;
import io.vertx.mutiny.ext.web.client.WebClient;

@QuarkusTest
@TestProfile(ServerWithTLS13Only.class)
public class TlsProtocolVersionSelectionTestCase {

@TestHTTPResource(value = "/hello", ssl = true)
String url;

@Inject
Vertx vertx;

@Test
void testWithWebClientRequestingMultipleTlsVersions() {
// The Web client is requesting "TLSv1", "TLSv1.1" or "TLSv1.2", the server is exposing 1.3 - KO
WebClient client = WebClient.create(vertx, new WebClientOptions().setSsl(true)
.setKeyStoreOptions(
new JksOptions().setPath("src/test/resources/client-keystore-1.jks").setPassword("password"))
.setTrustStoreOptions(
new JksOptions().setPath("src/test/resources/client-truststore.jks").setPassword("password"))
.setVerifyHost(false));
Throwable exception = Assertions.assertThrows(CompletionException.class, () -> client.getAbs(url).sendAndAwait());
Assertions.assertTrue(exception.getCause() instanceof SSLHandshakeException);
}

@Test
void testWithWebClientRequestingTls13() {
// The Web client is requesting TLS 1.3, the server is exposing 1.3 - all good
WebClient client = WebClient.create(vertx, new WebClientOptions().setSsl(true)
.setEnabledSecureTransportProtocols(Set.of("TLSv1.3"))
.setKeyStoreOptions(
new JksOptions().setPath("src/test/resources/client-keystore-1.jks").setPassword("password"))
.setTrustStoreOptions(
new JksOptions().setPath("src/test/resources/client-truststore.jks").setPassword("password"))
.setVerifyHost(false));
var resp = client.getAbs(url).sendAndAwait();
Assertions.assertEquals(200, resp.statusCode());
}

@Test
void testWithWebClientRequestingTls12And13() {
// The Web client is requesting TLS q.2 or 1.3, the server is exposing 1.3 - all good
WebClient client = WebClient.create(vertx, new WebClientOptions().setSsl(true)
.setEnabledSecureTransportProtocols(Set.of("TLSv1.2", "TLSv1.3"))
.setKeyStoreOptions(
new JksOptions().setPath("src/test/resources/client-keystore-1.jks").setPassword("password"))
.setTrustStoreOptions(
new JksOptions().setPath("src/test/resources/client-truststore.jks").setPassword("password"))
.setVerifyHost(false));
var resp = client.getAbs(url).sendAndAwait();
Assertions.assertEquals(200, resp.statusCode());
}

@Test
void testWithWebClientRequestingTls12() {
// The Web client is requesting TLS 1.2, the server is exposing 1.3 only - KO
WebClient client = WebClient.create(vertx, new WebClientOptions().setSsl(true)
.setEnabledSecureTransportProtocols(Set.of("TLSv1.2"))
.setKeyStoreOptions(
new JksOptions().setPath("src/test/resources/client-keystore-1.jks").setPassword("password"))
.setTrustStoreOptions(
new JksOptions().setPath("src/test/resources/client-truststore.jks").setPassword("password"))
.setVerifyHost(false));
Throwable exception = Assertions.assertThrows(CompletionException.class, () -> client.getAbs(url).sendAndAwait());
Assertions.assertTrue(exception.getCause() instanceof SSLHandshakeException);
}

@Test
void testWithWebClientRequestingTls11() {
// The Web client is requesting TLS 1.1, the server is exposing 1.3 - KO
WebClient client = WebClient.create(vertx, new WebClientOptions().setSsl(true)
.setEnabledSecureTransportProtocols(Set.of("TLSv1.1"))
.setKeyStoreOptions(
new JksOptions().setPath("src/test/resources/client-keystore-1.jks").setPassword("password"))
.setTrustStoreOptions(
new JksOptions().setPath("src/test/resources/client-truststore.jks").setPassword("password"))
.setVerifyHost(false));
Throwable exception = Assertions.assertThrows(CompletionException.class, () -> client.getAbs(url).sendAndAwait());
Assertions.assertTrue(exception.getCause() instanceof SSLHandshakeException);
}
}

0 comments on commit 2f856a1

Please sign in to comment.