-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Test TLS registry manual trigger of cert reloading
- Loading branch information
1 parent
700c52a
commit 29fbd81
Showing
8 changed files
with
170 additions
and
36 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
39 changes: 39 additions & 0 deletions
39
security/https/src/main/java/io/quarkus/ts/security/https/CertificateReloader.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,39 @@ | ||
package io.quarkus.ts.security.https; | ||
|
||
import jakarta.enterprise.event.Event; | ||
import jakarta.enterprise.event.Observes; | ||
|
||
import io.quarkus.tls.CertificateUpdatedEvent; | ||
import io.quarkus.tls.TlsConfiguration; | ||
import io.quarkus.tls.TlsConfigurationRegistry; | ||
import io.vertx.core.Vertx; | ||
import io.vertx.ext.web.Router; | ||
|
||
public class CertificateReloader { | ||
|
||
private static final String MTLS_CONFIG_NAME = "mtls-http"; | ||
|
||
void setupCertificateReloadingTriggerRoute(@Observes Router router, Event<CertificateUpdatedEvent> certUpdatedEvent, | ||
TlsConfigurationRegistry registry, Vertx vertx) { | ||
router.route("/reload-mtls-certificates").handler(ctx -> { | ||
TlsConfiguration config = registry.get(MTLS_CONFIG_NAME).orElseThrow(); | ||
vertx | ||
.executeBlocking(() -> { | ||
boolean reloaded = config.reload(); | ||
if (reloaded) { | ||
certUpdatedEvent.fire(new CertificateUpdatedEvent(MTLS_CONFIG_NAME, config)); | ||
} | ||
return reloaded; | ||
}) | ||
.onSuccess(reloaded -> { | ||
if (reloaded) { | ||
ctx.response().end("Certificates reloaded."); | ||
} else { | ||
ctx.fail(500, new RuntimeException("Could not reload mTLS certificates.")); | ||
} | ||
}) | ||
.onFailure(ctx::fail); | ||
}); | ||
} | ||
|
||
} |
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
97 changes: 97 additions & 0 deletions
97
security/https/src/test/java/io/quarkus/ts/security/https/secured/MutualTlsSecurityIT.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,97 @@ | ||
package io.quarkus.ts.security.https.secured; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertEquals; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
|
||
import org.apache.http.HttpStatus; | ||
import org.junit.jupiter.api.Assertions; | ||
import org.junit.jupiter.api.MethodOrderer; | ||
import org.junit.jupiter.api.Order; | ||
import org.junit.jupiter.api.Tag; | ||
import org.junit.jupiter.api.Test; | ||
import org.junit.jupiter.api.TestMethodOrder; | ||
|
||
import io.quarkus.test.bootstrap.RestService; | ||
import io.quarkus.test.scenarios.QuarkusScenario; | ||
import io.quarkus.test.security.certificate.CertificateBuilder; | ||
import io.quarkus.test.security.certificate.ClientCertificateRequest; | ||
import io.quarkus.test.services.Certificate; | ||
import io.quarkus.test.services.Certificate.ClientCertificate; | ||
import io.quarkus.test.services.QuarkusApplication; | ||
import io.quarkus.test.utils.AwaitilityUtils; | ||
|
||
@Tag("QUARKUS-4592") | ||
@TestMethodOrder(MethodOrderer.OrderAnnotation.class) | ||
@QuarkusScenario | ||
public class MutualTlsSecurityIT { | ||
|
||
private static final String MTLS_PATH = "/secured/mtls"; | ||
private static final String CERT_PREFIX = "qe-test"; | ||
private static final String CLIENT = "client"; | ||
private static final String GUEST_CLIENT = "guest-client"; | ||
|
||
@QuarkusApplication(ssl = true, certificates = @Certificate(prefix = CERT_PREFIX, clientCertificates = { | ||
@ClientCertificate(cnAttribute = CLIENT), | ||
@ClientCertificate(cnAttribute = GUEST_CLIENT, unknownToServer = true) | ||
}, configureKeystore = true, configureTruststore = true, tlsConfigName = "mtls-http", configureHttpServer = true)) | ||
static final RestService app = new RestService() | ||
.withProperty("quarkus.http.auth.proactive", "false") | ||
.withProperty("quarkus.http.ssl.client-auth", "required") | ||
.withProperty("quarkus.http.insecure-requests", "disabled"); | ||
|
||
@Order(1) | ||
@Test | ||
public void testMutualTlsForHttpServer() { | ||
var client1 = app.mutinyHttps(CLIENT); | ||
var httpResponse = client1.get(MTLS_PATH).sendAndAwait(); | ||
assertEquals(HttpStatus.SC_OK, httpResponse.statusCode()); | ||
assertEquals("Client certificate: CN=" + CLIENT, httpResponse.bodyAsString()); | ||
|
||
callSecuredEndpointAndExpectFailure(GUEST_CLIENT); | ||
} | ||
|
||
@Order(2) | ||
@Test | ||
public void testCertificateReloading() { | ||
// important to load before certificates are reloaded because otherwise | ||
// we would have new invalid certificate on test side and expected different certs on the server side | ||
var clientBeforeReload = app.mutinyHttps(CLIENT); | ||
|
||
app | ||
.<CertificateBuilder> getPropertyFromContext(CertificateBuilder.INSTANCE_KEY) | ||
.regenerateCertificate(CERT_PREFIX, certRequest -> { | ||
// regenerate client certificates | ||
// make the first client invalid | ||
var clientOneCert = new ClientCertificateRequest(CLIENT, true); | ||
// make the second client valid | ||
var clientTwoCert = new ClientCertificateRequest(GUEST_CLIENT, false); | ||
certRequest.withClientRequests(clientOneCert, clientTwoCert); | ||
}); | ||
|
||
var response = clientBeforeReload.get("/reload-mtls-certificates").sendAndAwait(); | ||
assertEquals(HttpStatus.SC_OK, response.statusCode()); | ||
assertEquals("Certificates reloaded.", response.bodyAsString()); | ||
|
||
// now we expect opposite from what we tested in step one: client 1 must fail and client 2 must succeed | ||
AwaitilityUtils.untilAsserted(() -> { | ||
var httpResponse = app.mutinyHttps(GUEST_CLIENT).get(MTLS_PATH).sendAndAwait(); | ||
assertEquals(HttpStatus.SC_OK, httpResponse.statusCode()); | ||
assertEquals("Client certificate: CN=" + GUEST_CLIENT, httpResponse.bodyAsString()); | ||
|
||
callSecuredEndpointAndExpectFailure(CLIENT); | ||
}); | ||
} | ||
|
||
private static void callSecuredEndpointAndExpectFailure(String clientCn) { | ||
// this client certs are not in the server truststore, therefore they cannot be trusted | ||
try { | ||
app.mutinyHttps(clientCn).get(MTLS_PATH).sendAndAwait(); | ||
// this must never happen, basically as SSL handshake must throw exception | ||
Assertions.fail("SSL handshake didn't fail even though certificate host is unknown"); | ||
} catch (Exception e) { | ||
// failure is expected | ||
assertTrue(e.getMessage().contains("Received fatal alert: bad_certificate"), | ||
"Expected failure over bad certificate, but got: " + e.getMessage()); | ||
} | ||
} | ||
} |