diff --git a/docs/src/main/asciidoc/security.adoc b/docs/src/main/asciidoc/security.adoc index 3a11c2642386f..e19e4d7edbf40 100644 --- a/docs/src/main/asciidoc/security.adoc +++ b/docs/src/main/asciidoc/security.adoc @@ -71,6 +71,31 @@ The following properties can be used to configure form based auth: include::{generated-dir}/config/quarkus-vertx-http-config-group-form-auth-config.adoc[opts=optional, leveloffset=+1] +=== Mutual TLS Client Authentication + +Quarkus provides mTLS client authentication so that your application can authenticate peers based on X.509 certificates. To authenticate using this method, the TLS connection between the peer and the application must have been established and you need to configure the server to request certificates from peers as well as configure your peers to provide their certificates. + +From an application perspective, you should have a configuration similar to this: + +[source,properties] +-- +quarkus.http.ssl.certificate.key-store-file=server-keystore.jks <1> +quarkus.http.ssl.certificate.key-store-password=password <2> +quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks <3> +quarkus.http.ssl.certificate.trust-store-password=password <4> +quarkus.http.ssl.client-auth=REQUIRED <5> +-- + +<1> The keystore where both private key and certificates of the application are being stored +<2> The keystore password +<3> The truststore where you store the certificates of the peers that you want to authenticate and accept requests from +<4> The truststore password +<5> Defines that the application should always request certificate from peers (`REQUIRED`). You may also use `REQUEST` in case you only want to authenticate if the client presents a certificate + +The steps <1> and <2> are basically enabling HTTP/TLS to your application. Whereas steps <3> to <5> configure the server to authenticate using mTLS client authentication. + +include::{generated-dir}/config/quarkus-vertx-http-config-group-server-ssl-config.adoc[opts=optional, leveloffset=+1] + === Proactive Authentication By default Quarkus does what we call proactive authentication. This means that if an incoming request has a diff --git a/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/MtlsTest.java b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/MtlsTest.java new file mode 100644 index 0000000000000..887539c417adc --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/java/io/quarkus/vertx/http/ssl/MtlsTest.java @@ -0,0 +1,61 @@ +package io.quarkus.vertx.http.ssl; + +import static org.hamcrest.core.Is.is; + +import java.io.File; +import java.net.URL; + +import javax.enterprise.context.ApplicationScoped; +import javax.enterprise.event.Observes; +import javax.net.ssl.SSLPeerUnverifiedException; + +import org.assertj.core.api.Assertions; +import org.jboss.shrinkwrap.api.ShrinkWrap; +import org.jboss.shrinkwrap.api.spec.JavaArchive; +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.restassured.RestAssured; +import io.vertx.ext.web.Router; + +public class MtlsTest { + + @TestHTTPResource(value = "/mtls", ssl = true) + URL url; + + @RegisterExtension + static final QuarkusUnitTest config = new QuarkusUnitTest() + .setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class) + .addClasses(MyBean.class) + .addAsResource(new File("src/test/resources/conf/mtls-jks.conf"), "application.properties") + .addAsResource(new File("src/test/resources/conf/server-keystore.jks"), "server-keystore.jks") + .addAsResource(new File("src/test/resources/conf/server-truststore.jks"), "server-truststore.jks")); + + @Test + public void testSslServerWithJKS() { + RestAssured.given() + .keyStore(new File("src/test/resources/conf/client-keystore.jks"), "password") + .trustStore(new File("src/test/resources/conf/client-truststore.jks"), "password") + .get(url).then().statusCode(200).body(is("CN=client,OU=cert,O=quarkus,L=city,ST=state,C=AU")); + } + + @ApplicationScoped + static class MyBean { + + public void register(@Observes Router router) { + router.get("/mtls").handler(rc -> { + Assertions.assertThat(rc.request().connection().isSsl()).isTrue(); + Assertions.assertThat(rc.request().isSSL()).isTrue(); + Assertions.assertThat(rc.request().connection().sslSession()).isNotNull(); + try { + rc.response().end(rc.request().connection().sslSession().getPeerPrincipal().getName()); + } catch (SSLPeerUnverifiedException cause) { + throw new RuntimeException("Failed to obtain peer principal", cause); + } + }); + } + + } +} diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/client-keystore.jks b/extensions/vertx-http/deployment/src/test/resources/conf/client-keystore.jks new file mode 100644 index 0000000000000..cf6d6ba454864 Binary files /dev/null and b/extensions/vertx-http/deployment/src/test/resources/conf/client-keystore.jks differ diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/client-truststore.jks b/extensions/vertx-http/deployment/src/test/resources/conf/client-truststore.jks new file mode 100644 index 0000000000000..bf6371859c55f Binary files /dev/null and b/extensions/vertx-http/deployment/src/test/resources/conf/client-truststore.jks differ diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/mtls-jks.conf b/extensions/vertx-http/deployment/src/test/resources/conf/mtls-jks.conf new file mode 100644 index 0000000000000..1219f0623d7ed --- /dev/null +++ b/extensions/vertx-http/deployment/src/test/resources/conf/mtls-jks.conf @@ -0,0 +1,5 @@ +quarkus.http.ssl.certificate.key-store-file=server-keystore.jks +quarkus.http.ssl.certificate.key-store-password=secret +quarkus.http.ssl.certificate.trust-store-file=server-truststore.jks +quarkus.http.ssl.certificate.trust-store-password=password +quarkus.http.ssl.client-auth=REQUIRED \ No newline at end of file diff --git a/extensions/vertx-http/deployment/src/test/resources/conf/server-truststore.jks b/extensions/vertx-http/deployment/src/test/resources/conf/server-truststore.jks new file mode 100644 index 0000000000000..8ec8e126507b6 Binary files /dev/null and b/extensions/vertx-http/deployment/src/test/resources/conf/server-truststore.jks differ