sslquarkusevent-loop

Quarkus: How to run long-ish IO tasks during the SSL-handshake without blocking the event loop?


We have a non-negotiable (compliance) requirement to verify a client certificate against a third-party REST service during SSL handshake for our Quarkus-based webservice. We implemented this using a Vertx customizer like this:

@ApplicationScoped
class VertxCustomizer(private val externalService: ExternalService) : HttpServerOptionsCustomizer {

    @Override
    override fun customizeHttpsServer(options: HttpServerOptions) {
        // we inject a custom trust manager here which verifies certificates categories
        options.trustOptions = CustomTrustOptions(options.trustOptions, externalService)
    }
}

In there we create a custom TrustManagerFactory which finally produces this custom TrustManager that verifies the client certificate during SSL handshake:

class CustomTrustManager(private val manager: X509TrustManager, private val externalService: ExternalService) : X509TrustManager {
    override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
       val certificate = chain[0]
       // this next call will block the thread.
       if (!externalService.verifyCertificate(certificate)) {
           throw CertificateException("Certificate is unknown in external service")
       }
    }

    ...
}

The problem with this is, that this call will block the vert.x event loop while the call to the external system is running which can take anywhere from 1-3 seconds. This means that during this time no other requests to the server will be processed, which obviously is a bad thing. For code that is running after the TLS handshake we can have worker threads or coroutines which fix this problem nicely. However for stuff that is running during the TLS handshake, we only have this non-reactive JDK TrustManager API and we were unable to find anything that would allow us to do this call without blocking the whole server. Has anyone ever done such a thing during a TLS-handshake with Quarkus and could give some hints on how we could approach the situation?


Solution

  • Unfortunately, we cannot make the handshake asynchronous. However, a potential solution would be to delay the verification to a later stage (like in a Quarkus REST interceptor or an HTTP route). In these later stages, you can execute your blocking I/O on a worker thread.