From ff73ef4d222f12c20e8417c7f3a855bcc34e3e12 Mon Sep 17 00:00:00 2001 From: Hakky54 Date: Mon, 19 Feb 2024 21:50:35 +0100 Subject: [PATCH] Added example for spring with tomcat --- README.MD | 1 + .../README.md | 44 +++++++++ .../pom.xml | 80 +++++++++++++++++ .../src/main/java/nl/altindag/server/App.java | 30 +++++++ .../nl/altindag/server/config/SSLConfig.java | 42 +++++++++ .../server/config/SSLConnectorCustomizer.java | 55 ++++++++++++ .../altindag/server/config/ServerConfig.java | 33 +++++++ .../server/config/TomcatSSLContext.java | 75 ++++++++++++++++ .../controller/HelloWorldController.java | 31 +++++++ .../service/FileBasedSslUpdateService.java | 84 ++++++++++++++++++ .../src/main/resources/application.yml | 9 ++ .../src/main/resources/identity.jks | Bin 0 -> 3703 bytes .../src/main/resources/truststore.jks | Bin 0 -> 1170 bytes pom.xml | 4 +- 14 files changed, 487 insertions(+), 1 deletion(-) create mode 100644 instant-ssl-reloading-with-spring-tomcat/README.md create mode 100644 instant-ssl-reloading-with-spring-tomcat/pom.xml create mode 100644 instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/App.java create mode 100644 instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/SSLConfig.java create mode 100644 instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/SSLConnectorCustomizer.java create mode 100644 instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/ServerConfig.java create mode 100644 instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/TomcatSSLContext.java create mode 100644 instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/controller/HelloWorldController.java create mode 100644 instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/service/FileBasedSslUpdateService.java create mode 100644 instant-ssl-reloading-with-spring-tomcat/src/main/resources/application.yml create mode 100644 instant-ssl-reloading-with-spring-tomcat/src/main/resources/identity.jks create mode 100644 instant-ssl-reloading-with-spring-tomcat/src/main/resources/truststore.jks diff --git a/README.MD b/README.MD index 1026e15..ebe62e1 100644 --- a/README.MD +++ b/README.MD @@ -24,6 +24,7 @@ A repository containing different java tutorials ## Security 🔐 - [Instant Server SSL Reloading with Spring Boot and Jetty](instant-server-ssl-reloading) +- [Instant Server SSL Reloading with Spring Boot and Tomcat](instant-ssl-reloading-with-spring-tomcat) - [Instant Server SSL Reloading with Vert.x](instant-server-ssl-reloading-with-vertx/vertx-server) - [Instant Server SSL Reloading with Netty](instant-server-ssl-reloading-with-netty/netty-server) - [Instant Server SSL Reloading with gRPC](grpc-client-server-with-ssl/instant-server-ssl-reloading-with-grpc) diff --git a/instant-ssl-reloading-with-spring-tomcat/README.md b/instant-ssl-reloading-with-spring-tomcat/README.md new file mode 100644 index 0000000..2340375 --- /dev/null +++ b/instant-ssl-reloading-with-spring-tomcat/README.md @@ -0,0 +1,44 @@ +# Instant SSL Reloading 🔐 +A server configured with ssl has a key material and trust material. These materials are generated from a keypair and certificate which always have an expiration date. +In a traditional server configuration a reboot of the server is required to apply the latest key material and trust material changes when the keystore and truststore are changed. +A downtime is therefore unavoidable. This project demonstrates with a basic setup how update the server certificate from an external source without the need of restarting your server. In this way you can achieve zero downtime. + +The repository contains: +- Server, based on Spring Boot with Tomcat as a server engine + +### SSL Updating entrypoint for the server: +The server has two ways to update the existing ssl material: +- File based aka file change listener, see here for the implementation: [FilesBasedSslUpdateService](src/main/java/nl/altindag/server/service/FileBasedSslUpdateService.java) +- Databased based, aka database change listener. This option is hosted in a separate module within this repository, see here: [Instant SSL Reloading With Database](https://github.com/Hakky54/java-tutorials/tree/main/instant-ssl-reloading-with-spring-jetty-database) +#### Requirements +- Java 11 +- Terminal + +#### Start the server +``` +mvn spring-boot:run +``` +Visit the server with the following url on your browser: https://localhost:8443/api/hello +Open the certificate details in your browser by clicking on the lock logo (on Chrome). You will see a similar certificate detail as shown below: + +![alt text](https://github.com/Hakky54/java-tutorials/blob/main/instant-server-ssl-reloading/images/before-reloading.png?raw=true) + +Please note down the expiration date. Afterwords you will compare it when you have run the admin application. + +#### Refresh the server certificates with the file listener +The file based ssl update service will listen to changes on a specific file on the file system. Adjust the path to your identity and truststore within [FilesBasedSslUpdateService](server/src/main/java/nl/altindag/server/service/FileBasedSslUpdateService.java). +```java +private static final Path identityPath = Path.of("/path/to/your/identity.jks"); +private static final Path trustStorePath = Path.of("/path/to/your/truststore.jks"); +``` +Also change the passwords if it is different. +```java +private static final char[] identityPassword = "secret".toCharArray(); +private static final char[] trustStorePassword = "secret".toCharArray(); +``` +Adjust the content of the identity and truststore and after 10 seonds the cron job will be triggered to validate if the content has been changed, and it will update the ssl configuration if there are any changes on it. + +Refresh your browser tab and open the certificate details again and compare the expiration date with the one you have noted down. +You should have a similar certificate detail as shown below: + +![alt text](https://github.com/Hakky54/java-tutorials/blob/main/instant-server-ssl-reloading/images/after-reloading.png?raw=true) diff --git a/instant-ssl-reloading-with-spring-tomcat/pom.xml b/instant-ssl-reloading-with-spring-tomcat/pom.xml new file mode 100644 index 0000000..f2d678a --- /dev/null +++ b/instant-ssl-reloading-with-spring-tomcat/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + + io.github.hakky54 + java-tutorials + 1.0.0-SNAPSHOT + + + instant-ssl-reloading-with-spring-tomcat + 1.0.0-SNAPSHOT + jar + + + + io.github.hakky54 + sslcontext-kickstart + ${version.sslcontext-kickstart} + + + + org.springframework.boot + spring-boot-starter-web + ${version.spring} + + + + org.springframework.boot + spring-boot-starter-test + ${version.spring} + test + + + org.junit.jupiter + junit-jupiter-api + ${version.junit} + test + + + org.junit.jupiter + junit-jupiter-engine + ${version.junit} + test + + + + + + + org.apache.tomcat.embed + tomcat-embed-core + ${version-tomcat} + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + ${version.spring} + + nl.altindag.server.App + server + + + + + repackage + + + + + + + + \ No newline at end of file diff --git a/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/App.java b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/App.java new file mode 100644 index 0000000..8abd17d --- /dev/null +++ b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/App.java @@ -0,0 +1,30 @@ +/* + * Copyright 2022 Thunderberry. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.altindag.server; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; + +@EnableScheduling +@SpringBootApplication +public class App { + + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } + +} diff --git a/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/SSLConfig.java b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/SSLConfig.java new file mode 100644 index 0000000..66e67fd --- /dev/null +++ b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/SSLConfig.java @@ -0,0 +1,42 @@ +/* + * Copyright 2022 Thunderberry. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.altindag.server.config; + +import nl.altindag.ssl.SSLFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SSLConfig { + + @Bean + public SSLFactory sslFactory(@Value("${ssl.keystore-path}") String keyStorePath, + @Value("${ssl.keystore-password}") char[] keyStorePassword, + @Value("${ssl.truststore-path}") String trustStorePath, + @Value("${ssl.truststore-password}") char[] trustStorePassword, + @Value("${ssl.client-auth}") boolean isClientAuthenticationRequired) { + + return SSLFactory.builder() + .withSwappableIdentityMaterial() + .withSwappableTrustMaterial() + .withIdentityMaterial(keyStorePath, keyStorePassword) + .withTrustMaterial(trustStorePath, trustStorePassword) + .withNeedClientAuthentication(isClientAuthenticationRequired) + .build(); + } + +} diff --git a/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/SSLConnectorCustomizer.java b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/SSLConnectorCustomizer.java new file mode 100644 index 0000000..b5afde7 --- /dev/null +++ b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/SSLConnectorCustomizer.java @@ -0,0 +1,55 @@ +/* + * Copyright 2022 Thunderberry. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.altindag.server.config; + +import nl.altindag.ssl.SSLFactory; +import org.apache.catalina.connector.Connector; +import org.apache.coyote.http11.AbstractHttp11Protocol; +import org.apache.tomcat.util.net.SSLHostConfig; +import org.apache.tomcat.util.net.SSLHostConfigCertificate; +import org.apache.tomcat.util.net.SSLHostConfigCertificate.Type; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SSLConnectorCustomizer implements TomcatConnectorCustomizer { + + private final SSLFactory sslFactory; + private final int port; + + public SSLConnectorCustomizer(SSLFactory sslFactory, @Value("${server.port}") int port) { + this.sslFactory = sslFactory; + this.port = port; + } + + @Override + public void customize(Connector connector) { + connector.setScheme("https"); + connector.setSecure(true); + connector.setPort(port); + + AbstractHttp11Protocol protocol = (AbstractHttp11Protocol) connector.getProtocolHandler(); + protocol.setSSLEnabled(true); + + SSLHostConfig sslHostConfig = new SSLHostConfig(); + SSLHostConfigCertificate certificate = new SSLHostConfigCertificate(sslHostConfig, Type.UNDEFINED); + certificate.setSslContext(new TomcatSSLContext(sslFactory)); + sslHostConfig.addCertificate(certificate); + protocol.addSslHostConfig(sslHostConfig); + } + +} diff --git a/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/ServerConfig.java b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/ServerConfig.java new file mode 100644 index 0000000..d4a6f83 --- /dev/null +++ b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/ServerConfig.java @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Thunderberry. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.altindag.server.config; + +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.servlet.server.ServletWebServerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class ServerConfig { + + @Bean + public ServletWebServerFactory servletContainer(SSLConnectorCustomizer sslConnectorCustomizer) { + TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); + tomcat.addConnectorCustomizers(sslConnectorCustomizer); + return tomcat; + } + +} diff --git a/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/TomcatSSLContext.java b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/TomcatSSLContext.java new file mode 100644 index 0000000..16eac4c --- /dev/null +++ b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/config/TomcatSSLContext.java @@ -0,0 +1,75 @@ +/* + * Copyright 2022 Thunderberry. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.altindag.server.config; + +import nl.altindag.ssl.SSLFactory; +import org.apache.tomcat.util.net.SSLContext; + +import javax.net.ssl.*; +import java.security.SecureRandom; +import java.security.cert.X509Certificate; + +public final class TomcatSSLContext implements SSLContext { + + private final SSLFactory sslFactory; + + public TomcatSSLContext(SSLFactory sslFactory) { + this.sslFactory = sslFactory; + } + + @Override + public void init(KeyManager[] kms, TrustManager[] tms, SecureRandom sr) { + // not needed to initialize as it is already initialized + } + + @Override + public void destroy() { + + } + + @Override + public SSLSessionContext getServerSessionContext() { + return sslFactory.getSslContext().getServerSessionContext(); + } + + @Override + public SSLEngine createSSLEngine() { + return sslFactory.getSSLEngine(); + } + + @Override + public SSLServerSocketFactory getServerSocketFactory() { + return sslFactory.getSslServerSocketFactory(); + } + + @Override + public SSLParameters getSupportedSSLParameters() { + return sslFactory.getSslParameters(); + } + + @Override + public X509Certificate[] getCertificateChain(String alias) { + return sslFactory.getKeyManager() + .map(keyManager -> keyManager.getCertificateChain(alias)) + .orElseThrow(); + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return sslFactory.getTrustedCertificates().toArray(new X509Certificate[0]); + } + +} diff --git a/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/controller/HelloWorldController.java b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/controller/HelloWorldController.java new file mode 100644 index 0000000..204ce62 --- /dev/null +++ b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/controller/HelloWorldController.java @@ -0,0 +1,31 @@ +/* + * Copyright 2022 Thunderberry. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.altindag.server.controller; + +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class HelloWorldController { + + @GetMapping(value = "/api/hello", produces = MediaType.TEXT_PLAIN_VALUE) + public ResponseEntity hello() { + return ResponseEntity.ok("Hello"); + } + +} diff --git a/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/service/FileBasedSslUpdateService.java b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/service/FileBasedSslUpdateService.java new file mode 100644 index 0000000..70491fc --- /dev/null +++ b/instant-ssl-reloading-with-spring-tomcat/src/main/java/nl/altindag/server/service/FileBasedSslUpdateService.java @@ -0,0 +1,84 @@ +/* + * Copyright 2022 Thunderberry. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package nl.altindag.server.service; + +import nl.altindag.ssl.SSLFactory; +import nl.altindag.ssl.util.SSLFactoryUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.time.Instant; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; + +@Service +public class FileBasedSslUpdateService { + + private static final Logger LOGGER = LoggerFactory.getLogger(FileBasedSslUpdateService.class); + + private static final Path identityPath = Path.of("/path/to/your/identity.jks"); + private static final Path trustStorePath = Path.of("/path/to/your/truststore.jks"); + private static final char[] identityPassword = "secret".toCharArray(); + private static final char[] trustStorePassword = "secret".toCharArray(); + + private ZonedDateTime lastModifiedTimeIdentityStore = ZonedDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC); + private ZonedDateTime lastModifiedTimeTrustStore = ZonedDateTime.ofInstant(Instant.EPOCH, ZoneOffset.UTC); + + private final SSLFactory baseSslFactory; + + public FileBasedSslUpdateService(SSLFactory baseSslFactory) { + this.baseSslFactory = baseSslFactory; + } + + /** + * Checks every 10 seconds if the keystore files have been updated. + * If the files have been updated the service will read the content and update the ssl material + * within the existing ssl configuration. + */ + @Scheduled(cron = "*/10 * * * * *") + private void updateSslMaterial() throws IOException { + if (Files.exists(identityPath) && Files.exists(trustStorePath)) { + BasicFileAttributes identityAttributes = Files.readAttributes(identityPath, BasicFileAttributes.class); + BasicFileAttributes trustStoreAttributes = Files.readAttributes(trustStorePath, BasicFileAttributes.class); + + boolean identityUpdated = lastModifiedTimeIdentityStore.isBefore(ZonedDateTime.ofInstant(identityAttributes.lastModifiedTime().toInstant(), ZoneOffset.UTC)); + boolean trustStoreUpdated = lastModifiedTimeTrustStore.isBefore(ZonedDateTime.ofInstant(trustStoreAttributes.lastModifiedTime().toInstant(), ZoneOffset.UTC)); + + if (identityUpdated && trustStoreUpdated) { + LOGGER.info("Keystore files have been changed. Trying to read the file content and preparing to update the ssl material"); + + SSLFactory updatedSslFactory = SSLFactory.builder() + .withIdentityMaterial(identityPath, identityPassword) + .withTrustMaterial(trustStorePath, trustStorePassword) + .build(); + + SSLFactoryUtils.reload(baseSslFactory, updatedSslFactory); + + lastModifiedTimeIdentityStore = ZonedDateTime.ofInstant(identityAttributes.lastModifiedTime().toInstant(), ZoneOffset.UTC); + lastModifiedTimeTrustStore = ZonedDateTime.ofInstant(trustStoreAttributes.lastModifiedTime().toInstant(), ZoneOffset.UTC); + + LOGGER.info("Updating ssl material finished"); + } + } + } + +} diff --git a/instant-ssl-reloading-with-spring-tomcat/src/main/resources/application.yml b/instant-ssl-reloading-with-spring-tomcat/src/main/resources/application.yml new file mode 100644 index 0000000..c795d4b --- /dev/null +++ b/instant-ssl-reloading-with-spring-tomcat/src/main/resources/application.yml @@ -0,0 +1,9 @@ +server: + port: 8443 + +ssl: + client-auth: true + keystore-path: identity.jks + keystore-password: secret + truststore-path: truststore.jks + truststore-password: secret diff --git a/instant-ssl-reloading-with-spring-tomcat/src/main/resources/identity.jks b/instant-ssl-reloading-with-spring-tomcat/src/main/resources/identity.jks new file mode 100644 index 0000000000000000000000000000000000000000..0b23ff6e57d56539b6dbe5d165d5c3ea42910e88 GIT binary patch literal 3703 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 B)x7`! literal 0 HcmV?d00001 diff --git a/instant-ssl-reloading-with-spring-tomcat/src/main/resources/truststore.jks b/instant-ssl-reloading-with-spring-tomcat/src/main/resources/truststore.jks new file mode 100644 index 0000000000000000000000000000000000000000..8f898eea349446e8b24d2e2af68415825b155a20 GIT binary patch literal 1170 zcmV;D1a12;f&`8N0Ru3C1V;u5Duzgg_YDCD0ic2eI0S+OG%$h$Fff7yECvZGhDe6@ z4FLxRpn?P)FoFab0s#Opf&>x<2`Yw2hW8Bt2LUiC1_~;MNQUwBF~seys0aaJ=?AO6}L{F4^ZtLzJuAuC>}jhlRTer+9I z?5INFuQ6meX|jIPnS{<9Xh$Y4*5QV*=|3tkmKG`41Jn9>^~VHcdty@dIzZ%31^DWqj--&x*8X70sQ}8O0_X)MQn{VDFjlkaaSeb*T%6E} z1?P(V!KQ1-wXEO+DX|(7yziF)O`E`0wotbt9vYJ=wp}c@rlCo!|Fv+EPB#k(@2-e1 zy^m8R(*+w!5vr1RsM%y`BFQuCiXyrjcByhfrz)5UFC=<~wH?_Yyl)5n+Xj)H?^TKr zG&_K(yzfTQruw_$-aGF0$d0LcCXz}tP^)ob{zjMj$o-Rq8Ljfz+X&qLc}daBRjs1A>3CWwYC^?$4TuqeY&X~<~=kn_t|NM-*0}o!-LE!y#_o$Mvx|K zAs6!2+&ZwTn}2xho04t?x(gh`cW5)2iUjH23rY2hMg%xX(&>UHm4)`~x^QHcd%~`J z8vRfZoI@ECHG+clKyu`4>9g-qb;Uml^YH7?p!uB*5IDJJAB09(COWve5s*gTfq#qn zA6K@^JW_oUrFp_-hLudon@zD~+BOD7U*|PR{MTKPhB5-d5+7~Av0V*dJmbfgKi=(o zw&<$s5&cBhcc@DaWjSrWO`1H|_hy7Ea^E51;TBS(SptO?iVeo{fc`wHQs2wP_rS!i z9HYpu$h^)m(d<6Tla84xj=DKU3D_kvR)iAFqANMDm!s%PVL42}G2t+Q*+jE?TTg%T zr^G9Tf|MI!%~*kYBzV_c0F;J9Im@WuiMeC?kD-T}F3X!y8k207+tm>G0;Nc6y8 zV-*=E0ers(gf>R6{GacRHNq~Kp3{d3i)`Ihztb_mT-4gh$5c%tAoc#v-(8wwxjFQA zd13gl)hlx{*GV#SF_h@YqsAutIB1uG5%0vZJX1Qa)4dC7_FeTNi}cMQ04 kXNvqY9ijvj|G3ig&f*S9k~Djm(iOpGI))Oz0s{etpbgh2l>h($ literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index eb9dfb6..403b8e7 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ console-captor-examples instant-ssl-reloading-with-spring-jetty-database instant-server-ssl-reloading-with-quarkus + instant-ssl-reloading-with-spring-tomcat @@ -57,9 +58,10 @@ UTF-8 2022 - 8.1.6 + 8.3.1 2.9.0 2.7.5 + 10.1.19 1.9.9.1 2.14.1 2.0.6