Skip to content

Commit

Permalink
Allows the HTTP server to use PEM truststore
Browse files Browse the repository at this point in the history
Previously, configuring the truststore with PEM files for mTLS (mutual TLS) usage was not supported, limiting options to JKS and P12 formats. This commit addresses this limitation by introducing support for PEM truststores in the HTTP server configuration.

Additionally, this commit accomplishes the following tasks:

- Removes hand-crafted certificates.
- Fixes issues related to alias passwords.
- Deprecates the usage of "key" properties, replacing them with "alias" properties for clarity and consistency.
  • Loading branch information
cescoffier committed Mar 3, 2024
1 parent 962bae3 commit 0fddc50
Show file tree
Hide file tree
Showing 41 changed files with 769 additions and 490 deletions.
2 changes: 1 addition & 1 deletion build-parent/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@
<dependency>
<groupId>me.escoffier.certs</groupId>
<artifactId>certificate-generator-junit5</artifactId>
<version>0.4.0</version>
<version>0.4.3</version>
<scope>test</scope>
</dependency>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import jakarta.enterprise.event.Observes;

import org.assertj.core.api.Assertions;
import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
Expand All @@ -19,20 +20,33 @@
import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;
import io.vertx.ext.web.Router;
import me.escoffier.certs.Format;
import me.escoffier.certs.junit5.Certificate;
import me.escoffier.certs.junit5.Certificates;

@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = {
Format.JKS, Format.PKCS12, Format.PEM }))
public class DisableHttpPortTest {

private static final String configuration = """
# Enable SSL, configure the key store
quarkus.http.insecure-requests=REDIRECT
quarkus.http.ssl.certificate.files=server-cert.crt
quarkus.http.ssl.certificate.key-files=server-key.key
""";

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(MyBean.class)
.addAsResource(new File("src/test/resources/conf/disable-http.conf"), "application.properties")
.addAsResource(new File("src/test/resources/conf/server-key.pem"), "server-key.pem")
.addAsResource(new File("src/test/resources/conf/server-cert.pem"), "server-cert.pem"));
.addAsResource(new StringAsset(configuration), "application.properties")
.addAsResource(new File("target/certs/ssl-test.key"), "server-key.key")
.addAsResource(new File("target/certs/ssl-test.crt"), "server-cert.crt"));

@BeforeAll
public static void setupRestAssured() {
RestAssured.useRelaxedHTTPSValidation();
RestAssured
.trustStore(new File("target/certs/ssl-test-truststore.jks"), "secret");
}

@AfterAll
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ public class MainHttpServerTlsPKCS12CertificateReloadTest {
@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar.addClasses(MyBean.class))
.overrideConfigKey("quarkus.http.ssl.insecure-requests", "redirect")
.overrideConfigKey("quarkus.http.insecure-requests", "redirect")
.overrideConfigKey("quarkus.http.ssl.certificate.reload-period", "30s")
.overrideConfigKey("quarkus.http.ssl.certificate.key-store-file", temp.getAbsolutePath() + "/tls.p12")
.overrideConfigKey("quarkus.http.ssl.certificate.key-store-password", "password")

.overrideConfigKey("loc", temp.getAbsolutePath())
.setBeforeAllCustomizer(() -> {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;

import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
Expand All @@ -26,14 +27,28 @@
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpVersion;
import io.vertx.core.net.JdkSSLEngineOptions;
import io.vertx.core.net.JksOptions;
import io.vertx.ext.web.Router;
import me.escoffier.certs.Format;
import me.escoffier.certs.junit5.Certificate;
import me.escoffier.certs.junit5.Certificates;

/**
* Configuration of the RST flood protection (CVE-2023-44487)
*/
@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = {
Format.JKS, Format.PKCS12, Format.PEM }))
@DisabledOnOs(OS.WINDOWS)
public class Http2RSTFloodProtectionConfigTest {

private static final String configuration = """
quarkus.http.ssl.certificate.key-store-file=server-keystore.jks
quarkus.http.ssl.certificate.key-store-password=secret
quarkus.http.limits.rst-flood-max-rst-frame-per-window=10
quarkus.http.limits.rst-flood-window-duration=10s
""";

@TestHTTPResource(value = "/ping", ssl = true)
URL sslUrl;

Expand All @@ -44,9 +59,8 @@ public class Http2RSTFloodProtectionConfigTest {
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(MyBean.class)
.addAsResource(new File("src/test/resources/conf/ssl-jks-rst-flood-protection.conf"),
"application.properties")
.addAsResource(new File("src/test/resources/conf/server-keystore.jks"), "server-keystore.jks"));
.addAsResource(new StringAsset(configuration), "application.properties")
.addAsResource(new File("target/certs/ssl-test-keystore.jks"), "server-keystore.jks"));

@Test
void testRstFloodProtectionWithTlsEnabled() throws Exception {
Expand All @@ -55,7 +69,8 @@ void testRstFloodProtectionWithTlsEnabled() throws Exception {
.setUseAlpn(true)
.setProtocolVersion(HttpVersion.HTTP_2)
.setSsl(true)
.setTrustAll(true);
.setTrustOptions(new JksOptions().setPath(new File("target/certs/ssl-test-truststore.jks").getAbsolutePath())
.setPassword("secret"));

var client = VertxCoreRecorder.getVertx().get().createHttpClient(options);
int port = sslUrl.getPort();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;

import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
Expand All @@ -26,14 +27,25 @@
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpVersion;
import io.vertx.core.net.JdkSSLEngineOptions;
import io.vertx.core.net.JksOptions;
import io.vertx.ext.web.Router;
import me.escoffier.certs.Format;
import me.escoffier.certs.junit5.Certificate;
import me.escoffier.certs.junit5.Certificates;

/**
* Reproduce CVE-2023-44487.
*/
@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = {
Format.JKS, Format.PKCS12, Format.PEM }))
@DisabledOnOs(OS.WINDOWS)
public class Http2RSTFloodProtectionTest {

private static final String configuration = """
quarkus.http.ssl.certificate.key-store-file=server-keystore.jks
quarkus.http.ssl.certificate.key-store-password=secret
""";

@TestHTTPResource(value = "/ping", ssl = true)
URL sslUrl;

Expand All @@ -44,8 +56,8 @@ public class Http2RSTFloodProtectionTest {
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(MyBean.class)
.addAsResource(new File("src/test/resources/conf/ssl-jks.conf"), "application.properties")
.addAsResource(new File("src/test/resources/conf/server-keystore.jks"), "server-keystore.jks"));
.addAsResource(new StringAsset(configuration), "application.properties")
.addAsResource(new File("target/certs/ssl-test-keystore.jks"), "server-keystore.jks"));

@Test
void testRstFloodProtectionWithTlsEnabled() throws Exception {
Expand All @@ -54,7 +66,8 @@ void testRstFloodProtectionWithTlsEnabled() throws Exception {
.setUseAlpn(true)
.setProtocolVersion(HttpVersion.HTTP_2)
.setSsl(true)
.setTrustAll(true);
.setTrustOptions(new JksOptions().setPath(new File("target/certs/ssl-test-truststore.jks").getAbsolutePath())
.setPassword("secret"));

var client = VertxCoreRecorder.getVertx().get().createHttpClient(options);
int port = sslUrl.getPort();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,34 @@
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpVersion;
import io.vertx.core.net.JdkSSLEngineOptions;
import io.vertx.core.net.JksOptions;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
import me.escoffier.certs.Format;
import me.escoffier.certs.junit5.Certificate;
import me.escoffier.certs.junit5.Certificates;

@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-test", password = "secret", formats = {
Format.JKS, Format.PKCS12, Format.PEM }))
public class Http2Test {

protected static final String PING_DATA = "12345678";

@TestHTTPResource(value = "/ping", ssl = true)
URL sslUrl;

@TestHTTPResource(value = "/ping", ssl = false)
URL url;
@TestHTTPResource(value = "/ping")
URL plainUrl;

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(MyBean.class)
.addAsResource(new File("src/test/resources/conf/ssl-jks.conf"), "application.properties")
.addAsResource(new File("src/test/resources/conf/server-keystore.jks"), "server-keystore.jks"));
.addAsResource(new File("target/certs/ssl-test-keystore.jks"), "server-keystore.jks"))
.overrideConfigKey("quarkus.http.ssl.certificate.key-store-file", "server-keystore.jks")
.overrideConfigKey("quarkus.http.ssl.certificate.key-store-password", "secret");

@Test
public void testHttp2EnabledSsl() throws ExecutionException, InterruptedException {
Expand All @@ -50,7 +57,7 @@ public void testHttp2EnabledSsl() throws ExecutionException, InterruptedExceptio
.setUseAlpn(true)
.setProtocolVersion(HttpVersion.HTTP_2)
.setSsl(true)
.setTrustAll(true);
.setTrustOptions(new JksOptions().setPath("target/certs/ssl-test-truststore.jks").setPassword("secret"));
WebClient client = WebClient.create(VertxCoreRecorder.getVertx().get(), options);
int port = sslUrl.getPort();

Expand All @@ -63,7 +70,7 @@ public void testHttp2EnabledPlain() throws ExecutionException, InterruptedExcept
.setProtocolVersion(HttpVersion.HTTP_2)
.setHttp2ClearTextUpgrade(true);
WebClient client = WebClient.create(VertxCoreRecorder.getVertx().get(), options);
runTest(client, url.getPort());
runTest(client, plainUrl.getPort());
}

private void runTest(WebClient client, int port) throws InterruptedException, ExecutionException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,25 @@
import io.restassured.RestAssured;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import me.escoffier.certs.Format;
import me.escoffier.certs.junit5.Certificate;
import me.escoffier.certs.junit5.Certificates;

@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = {
Format.JKS, Format.PKCS12, Format.PEM }))
public class ManagementWithJksTest {
private static final String APP_PROPS = "" +
"quarkus.management.enabled=true\n" +
"quarkus.management.root-path=/management\n" +
"quarkus.management.ssl.certificate.key-store-file=server-keystore.jks\n" +
"quarkus.management.ssl.certificate.key-store-password=secret\n";
private static final String configuration = """
quarkus.management.enabled=true
quarkus.management.root-path=/management
quarkus.management.ssl.certificate.key-store-file=server-keystore.jks
quarkus.management.ssl.certificate.key-store-password=secret
""";

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addAsResource(new StringAsset(APP_PROPS), "application.properties")
.addAsResource(new File("src/test/resources/conf/server-keystore.jks"), "server-keystore.jks")
.addAsResource(new StringAsset(configuration), "application.properties")
.addAsResource(new File("target/certs/ssl-management-interface-test-keystore.jks"), "server-keystore.jks")
.addClasses(MyObserver.class))
.addBuildChainCustomizer(buildCustomizer());

Expand Down Expand Up @@ -72,7 +78,7 @@ public void handle(RoutingContext rc) {
@Test
public void testSslWithJks() {
RestAssured.given()
.relaxedHTTPSValidation()
.trustStore(new File("target/certs/ssl-management-interface-test-truststore.jks"), "secret")
.get("https://0.0.0.0:9001/management/my-route")
.then().statusCode(200).body(Matchers.equalTo("ssl"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,26 @@
import io.restassured.RestAssured;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import me.escoffier.certs.Format;
import me.escoffier.certs.junit5.Certificate;
import me.escoffier.certs.junit5.Certificates;

@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = {
Format.JKS, Format.PKCS12, Format.PEM }))
public class ManagementWithP12Test {
private static final String APP_PROPS = "" +
"quarkus.management.enabled=true\n" +
"quarkus.management.root-path=/management\n" +
"quarkus.management.ssl.certificate.key-store-file=server-keystore.p12\n" +
"quarkus.management.ssl.certificate.key-store-password=secret\n";
private static final String configuration = """
quarkus.management.enabled=true
quarkus.management.root-path=/management
quarkus.management.ssl.certificate.key-store-file=server-keystore.p12
quarkus.management.ssl.certificate.key-store-password=secret
""";

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addAsResource(new StringAsset(APP_PROPS), "application.properties")
.addAsResource(new File("src/test/resources/conf/server-keystore.p12"), "server-keystore.p12")
.addClasses(MyObserver.class))
.addAsResource(new StringAsset(configuration), "application.properties")
.addAsResource(new File("target/certs/ssl-management-interface-test-keystore.p12"), "server-keystore.p12")
.addClasses(ManagementWithJksTest.MyObserver.class))
.addBuildChainCustomizer(buildCustomizer());

static Consumer<BuildChainBuilder> buildCustomizer() {
Expand Down Expand Up @@ -72,7 +78,7 @@ public void handle(RoutingContext rc) {
@Test
public void testSslWithP12() {
RestAssured.given()
.relaxedHTTPSValidation()
.trustStore(new File("target/certs/ssl-management-interface-test-truststore.jks"), "secret")
.get("https://0.0.0.0:9001/management/my-route")
.then().statusCode(200).body(Matchers.equalTo("ssl"));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,27 @@
import io.restassured.RestAssured;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import me.escoffier.certs.Format;
import me.escoffier.certs.junit5.Certificate;
import me.escoffier.certs.junit5.Certificates;

@Certificates(baseDir = "target/certs", certificates = @Certificate(name = "ssl-management-interface-test", password = "secret", formats = {
Format.JKS, Format.PKCS12, Format.PEM }))
public class ManagementWithPemTest {
private static final String APP_PROPS = "" +
"quarkus.management.enabled=true\n" +
"quarkus.management.root-path=/management\n" +
"quarkus.management.ssl.certificate.files=server-cert.pem\n" +
"quarkus.management.ssl.certificate.key-files=server-key.pem\n";

private static final String configuration = """
quarkus.management.enabled=true
quarkus.management.root-path=/management
quarkus.management.ssl.certificate.files=server.crt
quarkus.management.ssl.certificate.key-files=server.key
""";

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addAsResource(new StringAsset(APP_PROPS), "application.properties")
.addAsResource(new File("src/test/resources/conf/server-key.pem"), "server-key.pem")
.addAsResource(new File("src/test/resources/conf/server-cert.pem"), "server-cert.pem")
.addAsResource(new StringAsset(configuration), "application.properties")
.addAsResource(new File("target/certs/ssl-management-interface-test.key"), "server.key")
.addAsResource(new File("target/certs/ssl-management-interface-test.crt"), "server.crt")
.addClasses(MyObserver.class))
.addBuildChainCustomizer(buildCustomizer());

Expand Down Expand Up @@ -73,7 +80,8 @@ public void handle(RoutingContext rc) {
@Test
public void testSslWithPem() {
RestAssured.given()
.relaxedHTTPSValidation()
.given()
.trustStore(new File("target/certs/ssl-management-interface-test-truststore.jks"), "secret")
.get("https://0.0.0.0:9001/management/my-route")
.then().statusCode(200).body(Matchers.equalTo("ssl"));
}
Expand Down
Loading

0 comments on commit 0fddc50

Please sign in to comment.