From 283f366d15a61c665e0d887b490bd0d71e8e7705 Mon Sep 17 00:00:00 2001 From: Clement Escoffier Date: Thu, 8 Feb 2024 16:26:19 +0100 Subject: [PATCH] Implement TLS Certificate Reload for the Management Interface This commit extends the functionality present in the main HTTP endpoint to the management interface. This feature enables the periodic reloading of TLS certificates, key stores, and trust stores. --- .../vertx/http/runtime/VertxHttpRecorder.java | 20 +++++++++++-- .../options/TlsCertificateReloadUtils.java | 28 +++++++++---------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java index c399eb4b2fa6a..cd61b39fedda1 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/VertxHttpRecorder.java @@ -170,6 +170,8 @@ private boolean uriValid(HttpServerRequest httpServerRequest) { private static HttpServerOptions httpMainServerOptions; private static HttpServerOptions httpMainDomainSocketOptions; private static HttpServerOptions httpManagementServerOptions; + + private static final List taskIds = new CopyOnWriteArrayList<>(); final HttpBuildTimeConfig httpBuildTimeConfig; final ManagementInterfaceBuildTimeConfig managementBuildTimeConfig; final RuntimeValue httpConfiguration; @@ -308,7 +310,8 @@ public void startServer(Supplier vertx, ShutdownContext shutdown, ManagementInterfaceConfiguration managementConfig = this.managementConfiguration == null ? null : this.managementConfiguration.getValue(); if (startSocket && (httpConfiguration.hostEnabled || httpConfiguration.domainSocketEnabled - || managementConfig.hostEnabled || managementConfig.domainSocketEnabled)) { + || (managementConfig != null && managementConfig.hostEnabled) + || (managementConfig != null && managementConfig.domainSocketEnabled))) { // Start the server if (closeTask == null) { var insecureRequestStrategy = getInsecureRequestStrategy(httpBuildTimeConfig, @@ -622,6 +625,7 @@ private static CompletableFuture initializeManagementInterface(Vertx } if (httpManagementServerOptions != null) { + vertx.createHttpServer(httpManagementServerOptions) .requestHandler(managementRouter) .listen(ar -> { @@ -629,6 +633,15 @@ private static CompletableFuture initializeManagementInterface(Vertx managementInterfaceFuture.completeExceptionally( new IllegalStateException("Unable to start the management interface", ar.cause())); } else { + if (httpManagementServerOptions.isSsl() + && managementConfig.ssl.certificate.reloadPeriod.isPresent()) { + long l = TlsCertificateReloadUtils.handleCertificateReloading( + vertx, ar.result(), httpManagementServerOptions, managementConfig.ssl); + if (l != -1) { + taskIds.add(l); + } + } + actualManagementPort = ar.result().actualPort(); managementInterfaceFuture.complete(ar.result()); } @@ -809,6 +822,9 @@ public void handle(AsyncResult event) { // shutdown the management interface try { + for (Long id : taskIds) { + vertx.cancelTimer(id); + } if (managementServer != null && !isVertxClose) { managementServer.close(handler); } @@ -1190,7 +1206,7 @@ public void handle(AsyncResult event) { if (https && quarkusConfig.ssl.certificate.reloadPeriod.isPresent()) { long l = TlsCertificateReloadUtils.handleCertificateReloading( - vertx, httpsServer, httpsOptions, quarkusConfig); + vertx, httpsServer, httpsOptions, quarkusConfig.ssl); if (l != -1) { reloadingTasks.add(l); } diff --git a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/TlsCertificateReloadUtils.java b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/TlsCertificateReloadUtils.java index 2acf224b5d5c8..4b1512775268d 100644 --- a/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/TlsCertificateReloadUtils.java +++ b/extensions/vertx-http/runtime/src/main/java/io/quarkus/vertx/http/runtime/options/TlsCertificateReloadUtils.java @@ -11,7 +11,7 @@ import org.jboss.logging.Logger; -import io.quarkus.vertx.http.runtime.HttpConfiguration; +import io.quarkus.vertx.http.runtime.ServerSslConfig; import io.vertx.core.AsyncResult; import io.vertx.core.Future; import io.vertx.core.Handler; @@ -29,12 +29,12 @@ public class TlsCertificateReloadUtils { public static long handleCertificateReloading(Vertx vertx, HttpServer server, - HttpServerOptions options, HttpConfiguration configuration) { + HttpServerOptions options, ServerSslConfig configuration) { // Validation - if (configuration.ssl.certificate.reloadPeriod.isEmpty()) { + if (configuration.certificate.reloadPeriod.isEmpty()) { return -1; } - if (configuration.ssl.certificate.reloadPeriod.get().toMillis() < 30_000) { + if (configuration.certificate.reloadPeriod.get().toMillis() < 30_000) { throw new IllegalArgumentException( "Unable to configure TLS reloading - The reload period cannot be less than 30 seconds"); } @@ -47,7 +47,7 @@ public static long handleCertificateReloading(Vertx vertx, HttpServer server, } Logger log = Logger.getLogger(TlsCertificateReloadUtils.class); - return vertx.setPeriodic(configuration.ssl.certificate.reloadPeriod.get().toMillis(), new Handler() { + return vertx.setPeriodic(configuration.certificate.reloadPeriod.get().toMillis(), new Handler() { @Override public void handle(Long id) { @@ -89,17 +89,17 @@ public void handle(AsyncResult ar) { }); } - private static SSLOptions reloadFileContent(SSLOptions ssl, HttpConfiguration configuration) throws IOException { + private static SSLOptions reloadFileContent(SSLOptions ssl, ServerSslConfig configuration) throws IOException { var copy = new SSLOptions(ssl); final List keys = new ArrayList<>(); final List certificates = new ArrayList<>(); - if (configuration.ssl.certificate.keyFiles.isPresent()) { - keys.addAll(configuration.ssl.certificate.keyFiles.get()); + if (configuration.certificate.keyFiles.isPresent()) { + keys.addAll(configuration.certificate.keyFiles.get()); } - if (configuration.ssl.certificate.files.isPresent()) { - certificates.addAll(configuration.ssl.certificate.files.get()); + if (configuration.certificate.files.isPresent()) { + certificates.addAll(configuration.certificate.files.get()); } if (!certificates.isEmpty() && !keys.isEmpty()) { @@ -119,15 +119,15 @@ private static SSLOptions reloadFileContent(SSLOptions ssl, HttpConfiguration co .setCertValues(certBuffer) .setKeyValues(keysBuffer); copy.setKeyCertOptions(opts); - } else if (configuration.ssl.certificate.keyStoreFile.isPresent()) { + } else if (configuration.certificate.keyStoreFile.isPresent()) { var opts = ((KeyStoreOptions) copy.getKeyCertOptions()); - opts.setValue(Buffer.buffer(getFileContent(configuration.ssl.certificate.keyStoreFile.get()))); + opts.setValue(Buffer.buffer(getFileContent(configuration.certificate.keyStoreFile.get()))); copy.setKeyCertOptions(opts); } - if (configuration.ssl.certificate.trustStoreFile.isPresent()) { + if (configuration.certificate.trustStoreFile.isPresent()) { var opts = ((KeyStoreOptions) copy.getKeyCertOptions()); - opts.setValue(Buffer.buffer(getFileContent(configuration.ssl.certificate.trustStoreFile.get()))); + opts.setValue(Buffer.buffer(getFileContent(configuration.certificate.trustStoreFile.get()))); copy.setTrustOptions(opts); }