-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add tests to the TLS certificate reload
- for both the primary and management server - also update the docs
- Loading branch information
1 parent
b79b11f
commit 1107268
Showing
9 changed files
with
586 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
157 changes: 157 additions & 0 deletions
157
...rc/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
package io.quarkus.vertx.http.certReload; | ||
Check failure on line 1 in extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsCertificateReloadTest.java quarkus-bot / Build summary for 11072682b88e98af1e020e8137c0e72930c6fb93JVM Tests - JDK 17 Windows
Raw output
|
||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.net.URL; | ||
import java.nio.file.Files; | ||
import java.security.cert.X509Certificate; | ||
import java.time.Duration; | ||
import java.util.UUID; | ||
|
||
import javax.net.ssl.SSLHandshakeException; | ||
|
||
import jakarta.enterprise.event.Observes; | ||
import jakarta.inject.Inject; | ||
|
||
import org.eclipse.microprofile.config.inject.ConfigProperty; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.test.QuarkusUnitTest; | ||
import io.quarkus.test.common.http.TestHTTPResource; | ||
import io.quarkus.vertx.http.runtime.options.TlsCertificateReloader; | ||
import io.vertx.core.Vertx; | ||
import io.vertx.core.buffer.Buffer; | ||
import io.vertx.core.http.HttpClientOptions; | ||
import io.vertx.core.http.HttpClientRequest; | ||
import io.vertx.core.http.HttpClientResponse; | ||
import io.vertx.core.http.HttpMethod; | ||
import io.vertx.core.net.PemTrustOptions; | ||
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/certificates", certificates = { | ||
@Certificate(name = "reload-A", formats = Format.PEM), | ||
@Certificate(name = "reload-B", formats = Format.PEM, duration = 365), | ||
}) | ||
public class MainHttpServerTlsCertificateReloadTest { | ||
|
||
@TestHTTPResource(value = "/hello", ssl = true) | ||
URL url; | ||
|
||
public static final File temp = new File("target/test-certificates-" + UUID.randomUUID()); | ||
|
||
@RegisterExtension | ||
static final QuarkusUnitTest config = new QuarkusUnitTest() | ||
.withApplicationRoot((jar) -> jar.addClasses(MyBean.class)) | ||
.overrideConfigKey("quarkus.http.ssl.insecure-requests", "redirect") | ||
.overrideConfigKey("quarkus.http.ssl.certificate.reload-period", "30s") | ||
.overrideConfigKey("quarkus.http.ssl.certificate.files", temp.getAbsolutePath() + "/tls.crt") | ||
.overrideConfigKey("quarkus.http.ssl.certificate.key-files", temp.getAbsolutePath() + "/tls.key") | ||
.overrideConfigKey("loc", temp.getAbsolutePath()) | ||
.setBeforeAllCustomizer(() -> { | ||
try { | ||
// Prepare a random directory to store the certificates. | ||
temp.mkdirs(); | ||
Files.copy(new File("target/certificates/reload-A.crt").toPath(), | ||
new File(temp, "/tls.crt").toPath()); | ||
Files.copy(new File("target/certificates/reload-A.key").toPath(), | ||
new File(temp, "/tls.key").toPath()); | ||
Files.copy(new File("target/certificates/reload-A-ca.crt").toPath(), | ||
new File(temp, "/ca.crt").toPath()); | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
}) | ||
.setAfterAllCustomizer(() -> { | ||
try { | ||
Files.deleteIfExists(new File(temp, "/tls.crt").toPath()); | ||
Files.deleteIfExists(new File(temp, "/tls.key").toPath()); | ||
Files.deleteIfExists(new File(temp, "/ca.crt").toPath()); | ||
Files.deleteIfExists(temp.toPath()); | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
}); | ||
|
||
@Inject | ||
Vertx vertx; | ||
|
||
@ConfigProperty(name = "loc") | ||
File certs; | ||
|
||
@Test | ||
void test() throws IOException { | ||
var options = new HttpClientOptions() | ||
.setSsl(true) | ||
.setDefaultPort(url.getPort()) | ||
.setDefaultHost(url.getHost()) | ||
.setTrustOptions(new PemTrustOptions().addCertPath("target/certificates/reload-A-ca.crt")); | ||
|
||
String response1 = vertx.createHttpClient(options) | ||
.request(HttpMethod.GET, "/hello") | ||
.flatMap(HttpClientRequest::send) | ||
.flatMap(HttpClientResponse::body) | ||
.map(Buffer::toString) | ||
.toCompletionStage().toCompletableFuture().join(); | ||
|
||
// Update certs | ||
Files.copy(new File("target/certificates/reload-B.crt").toPath(), | ||
new File(certs, "/tls.crt").toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING); | ||
Files.copy(new File("target/certificates/reload-B.key").toPath(), | ||
new File(certs, "/tls.key").toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING); | ||
|
||
// Trigger the reload | ||
TlsCertificateReloader.reload().await().atMost(Duration.ofSeconds(10)); | ||
|
||
// The client truststore is not updated, thus it should fail. | ||
assertThatThrownBy(() -> vertx.createHttpClient(options) | ||
.request(HttpMethod.GET, "/hello") | ||
.flatMap(HttpClientRequest::send) | ||
.flatMap(HttpClientResponse::body) | ||
.map(Buffer::toString) | ||
.toCompletionStage().toCompletableFuture().join()).hasCauseInstanceOf(SSLHandshakeException.class); | ||
|
||
var options2 = new HttpClientOptions(options) | ||
.setTrustOptions(new PemTrustOptions().addCertPath("target/certificates/reload-B-ca.crt")); | ||
|
||
var response2 = vertx.createHttpClient(options2) | ||
.request(HttpMethod.GET, "/hello") | ||
.flatMap(HttpClientRequest::send) | ||
.flatMap(HttpClientResponse::body) | ||
.map(Buffer::toString) | ||
.toCompletionStage().toCompletableFuture().join(); | ||
|
||
assertThat(response1).isNotEqualTo(response2); // Because cert duration are different. | ||
|
||
// Trigger another reload | ||
TlsCertificateReloader.reload().await().atMost(Duration.ofSeconds(10)); | ||
|
||
var response3 = vertx.createHttpClient(options2) | ||
.request(HttpMethod.GET, "/hello") | ||
.flatMap(HttpClientRequest::send) | ||
.flatMap(HttpClientResponse::body) | ||
.map(Buffer::toString) | ||
.toCompletionStage().toCompletableFuture().join(); | ||
|
||
assertThat(response2).isEqualTo(response3); | ||
} | ||
|
||
public static class MyBean { | ||
|
||
public void onStart(@Observes Router router) { | ||
router.get("/hello").handler(rc -> { | ||
var exp = ((X509Certificate) rc.request().connection().sslSession().getLocalCertificates()[0]).getNotAfter() | ||
.toInstant().toEpochMilli(); | ||
rc.response().end("Hello " + exp); | ||
}); | ||
} | ||
|
||
} | ||
|
||
} |
150 changes: 150 additions & 0 deletions
150
...t/java/io/quarkus/vertx/http/certReload/MainHttpServerTlsPKCS12CertificateReloadTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
package io.quarkus.vertx.http.certReload; | ||
|
||
import static org.assertj.core.api.Assertions.assertThat; | ||
import static org.assertj.core.api.Assertions.assertThatThrownBy; | ||
|
||
import java.io.File; | ||
import java.io.IOException; | ||
import java.net.URL; | ||
import java.nio.file.Files; | ||
import java.security.cert.X509Certificate; | ||
import java.time.Duration; | ||
import java.util.UUID; | ||
|
||
import javax.net.ssl.SSLHandshakeException; | ||
|
||
import jakarta.enterprise.event.Observes; | ||
import jakarta.inject.Inject; | ||
|
||
import org.eclipse.microprofile.config.inject.ConfigProperty; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.extension.RegisterExtension; | ||
|
||
import io.quarkus.test.QuarkusUnitTest; | ||
import io.quarkus.test.common.http.TestHTTPResource; | ||
import io.quarkus.vertx.http.runtime.options.TlsCertificateReloader; | ||
import io.vertx.core.Vertx; | ||
import io.vertx.core.buffer.Buffer; | ||
import io.vertx.core.http.HttpClientOptions; | ||
import io.vertx.core.http.HttpClientRequest; | ||
import io.vertx.core.http.HttpClientResponse; | ||
import io.vertx.core.http.HttpMethod; | ||
import io.vertx.core.net.PfxOptions; | ||
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/certificates", certificates = { | ||
@Certificate(name = "reload-A", formats = Format.PKCS12, password = "password"), | ||
@Certificate(name = "reload-B", formats = Format.PKCS12, password = "password", duration = 365), | ||
}) | ||
public class MainHttpServerTlsPKCS12CertificateReloadTest { | ||
|
||
@TestHTTPResource(value = "/hello", ssl = true) | ||
URL url; | ||
|
||
public static final File temp = new File("target/test-certificates-" + UUID.randomUUID()); | ||
|
||
@RegisterExtension | ||
static final QuarkusUnitTest config = new QuarkusUnitTest() | ||
.withApplicationRoot((jar) -> jar.addClasses(MyBean.class)) | ||
.overrideConfigKey("quarkus.http.ssl.insecure-requests", "redirect") | ||
.overrideConfigKey("quarkus.http.ssl.certificate.reload-period", "30s") | ||
.overrideConfigKey("quarkus.http.ssl.certificate.key-store-file", temp.getAbsolutePath() + "/tls.p12") | ||
.overrideConfigKey("loc", temp.getAbsolutePath()) | ||
.setBeforeAllCustomizer(() -> { | ||
try { | ||
// Prepare a random directory to store the certificates. | ||
temp.mkdirs(); | ||
Files.copy(new File("target/certificates/reload-A-keystore.p12").toPath(), | ||
new File(temp, "/tls.p12").toPath()); | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
}) | ||
.setAfterAllCustomizer(() -> { | ||
try { | ||
Files.deleteIfExists(new File(temp, "/tls.p12").toPath()); | ||
Files.deleteIfExists(temp.toPath()); | ||
} catch (Exception e) { | ||
throw new RuntimeException(e); | ||
} | ||
}); | ||
|
||
@Inject | ||
Vertx vertx; | ||
|
||
@ConfigProperty(name = "loc") | ||
File certs; | ||
|
||
@Test | ||
void test() throws IOException { | ||
var options = new HttpClientOptions() | ||
.setSsl(true) | ||
.setDefaultPort(url.getPort()) | ||
.setDefaultHost(url.getHost()) | ||
.setTrustOptions( | ||
new PfxOptions().setPath("target/certificates/reload-A-truststore.p12").setPassword("password")); | ||
|
||
String response1 = vertx.createHttpClient(options) | ||
.request(HttpMethod.GET, "/hello") | ||
.flatMap(HttpClientRequest::send) | ||
.flatMap(HttpClientResponse::body) | ||
.map(Buffer::toString) | ||
.toCompletionStage().toCompletableFuture().join(); | ||
|
||
// Update certs | ||
Files.copy(new File("target/certificates/reload-B-keystore.p12").toPath(), | ||
new File(certs, "/tls.p12").toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING); | ||
|
||
// Trigger the reload | ||
TlsCertificateReloader.reload().await().atMost(Duration.ofSeconds(10)); | ||
|
||
// The client truststore is not updated, thus it should fail. | ||
assertThatThrownBy(() -> vertx.createHttpClient(options) | ||
.request(HttpMethod.GET, "/hello") | ||
.flatMap(HttpClientRequest::send) | ||
.flatMap(HttpClientResponse::body) | ||
.map(Buffer::toString) | ||
.toCompletionStage().toCompletableFuture().join()).hasCauseInstanceOf(SSLHandshakeException.class); | ||
|
||
var options2 = new HttpClientOptions(options) | ||
.setTrustOptions( | ||
new PfxOptions().setPath("target/certificates/reload-B-truststore.p12").setPassword("password")); | ||
|
||
var response2 = vertx.createHttpClient(options2) | ||
.request(HttpMethod.GET, "/hello") | ||
.flatMap(HttpClientRequest::send) | ||
.flatMap(HttpClientResponse::body) | ||
.map(Buffer::toString) | ||
.toCompletionStage().toCompletableFuture().join(); | ||
|
||
assertThat(response1).isNotEqualTo(response2); // Because cert duration are different. | ||
|
||
// Trigger another reload | ||
TlsCertificateReloader.reload().await().atMost(Duration.ofSeconds(10)); | ||
|
||
var response3 = vertx.createHttpClient(options2) | ||
.request(HttpMethod.GET, "/hello") | ||
.flatMap(HttpClientRequest::send) | ||
.flatMap(HttpClientResponse::body) | ||
.map(Buffer::toString) | ||
.toCompletionStage().toCompletableFuture().join(); | ||
|
||
assertThat(response2).isEqualTo(response3); | ||
} | ||
|
||
public static class MyBean { | ||
|
||
public void onStart(@Observes Router router) { | ||
router.get("/hello").handler(rc -> { | ||
var exp = ((X509Certificate) rc.request().connection().sslSession().getLocalCertificates()[0]).getNotAfter() | ||
.toInstant().toEpochMilli(); | ||
rc.response().end("Hello " + exp); | ||
}); | ||
} | ||
|
||
} | ||
|
||
} |
Oops, something went wrong.