Bootstrapping
Let’s create both the server and client applications we will secure.
mvn io.quarkus:quarkus-maven-plugin:1.4.1.Final:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=quarkus-server-mtls \
-DclassName="org.acme.server.mtls.GreetingResource" \
-Dextensions="rest-client, resteasy-jsonb, kubernetes-client" \
-Dpath="/hello-server"
mvn io.quarkus:quarkus-maven-plugin:1.4.1.Final:create \
-DprojectGroupId=org.acme \
-DprojectArtifactId=quarkus-client-mtls \
-DclassName="org.acme.client.mtls.GreetingResource" \
-Dextensions="rest-client, resteasy-jsonb, kubernetes-client" \
-Dpath="/hello-client"
Certificate and Truststore generation
Of course, you need a server, client certificates and a truststore :)
keytool -genkeypair -storepass password -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore quarkus-server-mtls/src/main/resources/META-INF/resources/server.keystore
keytool -genkeypair -storepass password -keyalg RSA -keysize 2048 -dname "CN=client" -alias client -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore quarkus-client-mtls/src/main/resources/META-INF/resources/client.keystore
For this example, we are simulating a truststore using:
- client.keystoreas truststore for the server application.
- server.keystoreas truststore for the client application.
cp quarkus-server-mtls/src/main/resources/META-INF/resources/server.keystore quarkus-client-mtls/src/main/resources/META-INF/resources/client.truststore
cp quarkus-client-mtls/src/main/resources/META-INF/resources/client.keystore quarkus-server-mtls/src/main/resources/META-INF/resources/server.truststore
Hello Server Application
Let’s open and configure the server quarkus-server-mtls
Enable SSL on the Hello Server Application
Add the following properties to enable SSL in your application src/main/resources/application.properties
application.properties
quarkus.ssl.native=true
quarkus.http.ssl-port=8443
quarkus.http.ssl.certificate.key-store-file=META-INF/resources/server.keystore
quarkus.http.ssl.certificate.key-store-password=password
quarkus.http.port=0
quarkus.http.test-port=0
See the guide Using SSL with Nativeto explore in details how SSL works in Quarkus.
Activate client authentication
application.properties
quarkus.http.ssl.client-auth=required
quarkus.http.ssl.certificate.trust-store-file=META-INF/resources/server.truststore
quarkus.http.ssl.certificate.trust-store-password=password
Update GreetingResource
To better see that the response is coming from the server application, let’s update the org.acme.server.mtls.GreetingResourceclass.
org.acme.server.mtls.GreetingResource
@Path("/hello-server")
public class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "hello from server";
}
}
Change the return statement in "hello from server" And the test class.
org.acme.server.mtls.GreetingResourceTest
@QuarkusTest
public class GreetingResourceTest {
@Test
public void testHelloEndpoint() {
given()
.when().get("/hello-server")
.then()
.statusCode(200)
.body(is("hello from server"));
}
}
Change the matcher in "hello from server"
Run it
mvn quarkus:dev
If everything is correct when you try to hit the /hello-serverendpoint, you should expect the following error.
curl -k https://localhost:8443/hello-server
curl: (35) error:1401E412:SSL routines:CONNECT_CR_FINISHED:sslv3 alert bad certificate
This means that your client (curl) did not provide a trusted certificate when it connected to the server.
Hello Client Application
At this point, the server application is ready to accomplish Mutual TLS. Let’s open and configure the client quarkus-client-mtls
Add Rest client for the server application
org.acme.client.mtls.GreetingService
@Path("/")
@ApplicationScoped
@RegisterRestClient
public interface GreetingService {
@GET
@Path("/hello-server")
@Produces(MediaType.TEXT_PLAIN)
String hello();
}
Inject the GreetingService rest client on org.acme.client.mtls.GreetingResource.
org.acme.client.mtls.GreetingResource
@Path("/hello-client")
public class GreetingResource {
@Inject
@RestClient
GreetingService greetingService;
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return greetingService.hello();
}
}
Update the unit test
Add quarkus-junit5-mockitodependency to your project.
pom.xml
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5-mockito</artifactId>
</dependency>
org.acme.client.mtls.GreetingResourceTest
@QuarkusTest
public class GreetingResourceTest {
@InjectMock
@RestClient
GreetingService greetingService;
@Test
public void testHelloEndpoint() {
Mockito.when(greetingService.hello()).thenReturn("hello from server");
given()
.when().get("/hello-client")
.then()
.statusCode(200)
.body(is("hello from server"));
}
}
Configure MicroProfile rest client for Mutual TLS
Add the following properties to enable SSL in your application src/main/resources/application.properties
application.properties
org.acme.client.mtls.GreetingService/mp-rest/url=https://localhost:8443
org.acme.client.mtls.GreetingService/mp-rest/trustStore=classpath:/META-INF/resources/client.truststore
org.acme.client.mtls.GreetingService/mp-rest/trustStorePassword=password
org.acme.client.mtls.GreetingService/mp-rest/keyStore=classpath:/META-INF/resources/client.keystore
org.acme.client.mtls.GreetingService/mp-rest/keyStorePassword=password
quarkus.ssl.native=true
Run it
mvn quarkus:dev
Now let’s hit the client /hello-client endpoint, and you should expect the following.
curl http://localhost:8080/hello-client
hello from server
Source: https://quarkus.io/blog/quarkus-mutual-tls/ and GIT source: https://github.com/openlab-red/quarkus-mtls-quickstart