From 24d0e50a0c9a3e35df2b22b43f4d0420872fae06 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Fri, 10 Jul 2020 12:18:18 +1000 Subject: [PATCH 01/12] Issue #5019 - add class to do SSL keystore hot-reload in new module Signed-off-by: Lachlan Roberts --- jetty-ssl-reload/pom.xml | 28 ++++ .../jetty/ssl/reload/SslKeyStoreScanner.java | 146 ++++++++++++++++++ pom.xml | 1 + 3 files changed, 175 insertions(+) create mode 100644 jetty-ssl-reload/pom.xml create mode 100644 jetty-ssl-reload/src/main/java/org/eclipse/jetty/ssl/reload/SslKeyStoreScanner.java diff --git a/jetty-ssl-reload/pom.xml b/jetty-ssl-reload/pom.xml new file mode 100644 index 000000000000..198cffa8b32b --- /dev/null +++ b/jetty-ssl-reload/pom.xml @@ -0,0 +1,28 @@ + + + org.eclipse.jetty + jetty-project + 9.4.31-SNAPSHOT + + 4.0.0 + jetty-ssl-reload + Jetty :: SSL Reload + + + ${project.groupId}.ssl.reload + + + + + org.eclipse.jetty + jetty-util + ${project.version} + + + org.eclipse.jetty + jetty-server + ${project.version} + test + + + diff --git a/jetty-ssl-reload/src/main/java/org/eclipse/jetty/ssl/reload/SslKeyStoreScanner.java b/jetty-ssl-reload/src/main/java/org/eclipse/jetty/ssl/reload/SslKeyStoreScanner.java new file mode 100644 index 000000000000..f96b1e0e64f5 --- /dev/null +++ b/jetty-ssl-reload/src/main/java/org/eclipse/jetty/ssl/reload/SslKeyStoreScanner.java @@ -0,0 +1,146 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.ssl.reload; + +import java.io.File; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.util.Scanner; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.annotation.ManagedOperation; +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +public class SslKeyStoreScanner extends AbstractLifeCycle implements Scanner.DiscreteListener +{ + private static final Logger LOG = Log.getLogger(SslKeyStoreScanner.class); + + private final SslContextFactory sslContextFactory; + private final File keystoreFile; + private final List files = new ArrayList<>(); + private Scanner _scanner; + private int _scanInterval = 1; + + public SslKeyStoreScanner(SslContextFactory sslContextFactory) + { + this.sslContextFactory = sslContextFactory; + this.keystoreFile = new File(URI.create(sslContextFactory.getKeyStorePath())); // getKeyStorePath is giving url instead of path? + if (!keystoreFile.exists()) + throw new IllegalArgumentException("keystore file does not exist"); + if (keystoreFile.isDirectory()) + throw new IllegalArgumentException("expected keystore file not directory"); + + File parentFile = keystoreFile.getParentFile(); + if (!parentFile.exists() || !parentFile.isDirectory()) + throw new IllegalArgumentException("error obtaining keystore dir"); + + files.add(parentFile); + if (LOG.isDebugEnabled()) + LOG.debug("created {}", this); + } + + @Override + protected void doStart() throws Exception + { + if (LOG.isDebugEnabled()) + LOG.debug(this.getClass().getSimpleName() + ".doStart()"); + + _scanner = new Scanner(); + _scanner.setScanDirs(files); + _scanner.setScanInterval(_scanInterval); + _scanner.setReportDirs(false); + _scanner.setReportExistingFilesOnStartup(false); + _scanner.setScanDepth(1); + _scanner.addListener(this); + _scanner.start(); + } + + @Override + protected void doStop() throws Exception + { + if (_scanner != null) + { + _scanner.stop(); + _scanner.removeListener(this); + _scanner = null; + } + } + + @Override + public void fileAdded(String filename) throws Exception + { + if (LOG.isDebugEnabled()) + LOG.debug("added {}", filename); + + if (keystoreFile.toPath().toString().equals(filename)) + reload(); + } + + @Override + public void fileChanged(String filename) throws Exception + { + if (LOG.isDebugEnabled()) + LOG.debug("changed {}", filename); + + if (keystoreFile.toPath().toString().equals(filename)) + reload(); + } + + @Override + public void fileRemoved(String filename) throws Exception + { + if (LOG.isDebugEnabled()) + LOG.debug("removed {}", filename); + + // TODO: do we want to do this? + if (keystoreFile.toPath().toString().equals(filename)) + reload(); + } + + @ManagedOperation(value = "Reload the SSL Keystore", impact = "ACTION") + public void reload() throws Exception + { + if (LOG.isDebugEnabled()) + LOG.debug("reloading keystore file {}", keystoreFile); + + try + { + sslContextFactory.reload(scf -> {}); + } + catch (Throwable t) + { + LOG.warn("Keystore Reload Failed", t); + } + } + + @ManagedAttribute("scanning interval to detect changes which need reloaded") + public int getScanInterval() + { + return _scanInterval; + } + + public void setScanInterval(int scanInterval) + { + _scanInterval = scanInterval; + } +} diff --git a/pom.xml b/pom.xml index ca03a78b8274..2bddbcb1b632 100644 --- a/pom.xml +++ b/pom.xml @@ -95,6 +95,7 @@ jetty-server jetty-xml jetty-security + jetty-ssl-reload jetty-openid jetty-servlet jetty-webapp From 13e034fc5f904f5a79aef21bbdf01016aac3293b Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Fri, 10 Jul 2020 12:19:28 +1000 Subject: [PATCH 02/12] Issue #5019 - add testing for the SslKeyStoreScanner Signed-off-by: Lachlan Roberts --- .../jetty/ssl/reload/KeystoreReloadTest.java | 212 ++++++++++++++++++ .../src/test/resources/badKeystore | Bin 0 -> 2457 bytes .../test/resources/jetty-logging.properties | 4 + .../src/test/resources/newKeystore | Bin 0 -> 2071 bytes .../src/test/resources/oldKeystore | Bin 0 -> 2303 bytes 5 files changed, 216 insertions(+) create mode 100644 jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java create mode 100644 jetty-ssl-reload/src/test/resources/badKeystore create mode 100644 jetty-ssl-reload/src/test/resources/jetty-logging.properties create mode 100644 jetty-ssl-reload/src/test/resources/newKeystore create mode 100644 jetty-ssl-reload/src/test/resources/oldKeystore diff --git a/jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java b/jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java new file mode 100644 index 000000000000..e5aeaf7c4a99 --- /dev/null +++ b/jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java @@ -0,0 +1,212 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.ssl.reload; + +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.SecureRandom; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.time.Duration; +import java.util.Calendar; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManager; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLHandshakeException; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.SecureRequestCustomizer; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.SslConnectionFactory; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.util.log.StacklessLogging; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class KeystoreReloadTest +{ + private static final int scanInterval = 1; + private Server server; + + public static void useKeystore(String keystore) throws Exception + { + Path keystoreDir = MavenTestingUtils.getTestResourcePath("keystoreDir"); + Path keystorePath = keystoreDir.resolve("keystore"); + if (Files.exists(keystorePath)) + Files.delete(keystorePath); + + if (keystore == null) + return; + + Files.copy(MavenTestingUtils.getTestResourceFile(keystore).toPath(), keystorePath); + keystorePath.toFile().deleteOnExit(); + } + + @BeforeEach + public void start() throws Exception + { + useKeystore("oldKeystore"); + + String keystore = MavenTestingUtils.getTestResourceFile("keystoreDir/keystore").getAbsolutePath(); + SslContextFactory sslContextFactory = new SslContextFactory.Server(); + sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStorePassword("storepwd"); + sslContextFactory.setKeyManagerPassword("keypwd"); + + server = new Server(); + SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()); + HttpConfiguration httpsConfig = new HttpConfiguration(); + httpsConfig.addCustomizer(new SecureRequestCustomizer()); + HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory(httpsConfig); + ServerConnector connector = new ServerConnector(server, sslConnectionFactory, httpConnectionFactory); + connector.setPort(8443); + server.addConnector(connector); + + // Configure Keystore Reload. + SslKeyStoreScanner keystoreScanner = new SslKeyStoreScanner(sslContextFactory); + keystoreScanner.setScanInterval(scanInterval); + server.addBean(keystoreScanner); + + server.start(); + } + + @AfterEach + public void stop() throws Exception + { + server.stop(); + } + + @Test + public void testKeystoreHotReload() throws Exception + { + URL serverUrl = server.getURI().toURL(); + + // Check the original certificate expiry. + X509Certificate cert1 = getCertificateFromUrl(serverUrl); + assertThat(getExpiryYear(cert1), is(2015)); + + // Switch to use newKeystore which has a later expiry date. + useKeystore("newKeystore"); + Thread.sleep(Duration.ofSeconds(scanInterval * 2).toMillis()); + + // The scanner should have detected the updated keystore, expiry should be renewed. + X509Certificate cert2 = getCertificateFromUrl(serverUrl); + assertThat(getExpiryYear(cert2), is(2020)); + } + + @Test + public void testReloadWithBadKeystore() throws Exception + { + URL serverUrl = server.getURI().toURL(); + + // Check the original certificate expiry. + X509Certificate cert1 = getCertificateFromUrl(serverUrl); + assertThat(getExpiryYear(cert1), is(2015)); + + // Switch to use badKeystore which has the incorrect passwords. + try (StacklessLogging ignored = new StacklessLogging(SslKeyStoreScanner.class)) + { + useKeystore("badKeystore"); + Thread.sleep(Duration.ofSeconds(scanInterval * 2).toMillis()); + } + + // The good keystore is removed, now the bad keystore now causes an SSL Handshake exception. + assertThrows(SSLHandshakeException.class, () -> getCertificateFromUrl(serverUrl)); + } + + @Test + public void testKeystoreRemoval() throws Exception + { + URL serverUrl = server.getURI().toURL(); + + // Check the original certificate expiry. + X509Certificate cert1 = getCertificateFromUrl(serverUrl); + assertThat(getExpiryYear(cert1), is(2015)); + + // Delete the keystore. + try (StacklessLogging ignored = new StacklessLogging(SslKeyStoreScanner.class)) + { + useKeystore(null); + Thread.sleep(Duration.ofSeconds(scanInterval * 2).toMillis()); + } + + // The good keystore is removed, having no keystore causes an SSL Handshake exception. + assertThrows(SSLHandshakeException.class, () -> getCertificateFromUrl(serverUrl)); + + // Switch to use keystore2 which has a later expiry date. + useKeystore("newKeystore"); + Thread.sleep(Duration.ofSeconds(scanInterval * 2).toMillis()); + X509Certificate cert2 = getCertificateFromUrl(serverUrl); + assertThat(getExpiryYear(cert2), is(2020)); + } + + public static int getExpiryYear(X509Certificate cert) + { + Calendar instance = Calendar.getInstance(); + instance.setTime(cert.getNotAfter()); + return instance.get(Calendar.YEAR); + } + + public static X509Certificate getCertificateFromUrl(URL serverUrl) throws Exception + { + SSLContext ctx = SSLContext.getInstance("TLS"); + ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom()); + SSLContext.setDefault(ctx); + + HttpsURLConnection connection = (HttpsURLConnection)serverUrl.openConnection(); + connection.setHostnameVerifier((a, b) -> true); + connection.connect(); + Certificate[] certs = connection.getServerCertificates(); + connection.disconnect(); + + assertThat(certs.length, is(1)); + return (X509Certificate)certs[0]; + } + + private static class DefaultTrustManager implements X509TrustManager + { + @Override + public void checkClientTrusted(X509Certificate[] arg0, String arg1) + { + } + + @Override + public void checkServerTrusted(X509Certificate[] arg0, String arg1) + { + } + + @Override + public X509Certificate[] getAcceptedIssuers() + { + return null; + } + } +} diff --git a/jetty-ssl-reload/src/test/resources/badKeystore b/jetty-ssl-reload/src/test/resources/badKeystore new file mode 100644 index 0000000000000000000000000000000000000000..568291d71eddf760f6456162653859a89a397b18 GIT binary patch literal 2457 zcmY*ZS5Om(7EQ;{OCo^{(xMP05^88mkfn$e#n4v;l`2I#f(1x45{NVrkRky@n$nS? z2nZrgiWDJ8?m{xc;}r%?SiC$dF%nGGrT%g>n9$K0N*#1d+2o zvQAQ|(*+0_(mIbI>Yxt_=ns}m!L{{NwO_^$zH}SVh!xHx zQd>nRIvH6L)P6>L@9sqDh|i7ctkIstm%M7YOl@W{VK2WsO~q5_-7xRf*vb)pz=O9f z^fJ{ru^~Srw>KG{QCr_y9@oyf4Cu*+GBgNwSe)$Rv)q5Q8pe4t2{F0Sof52SVPR?^PJ{M#Qi+;}yZN+O*lV_VM72B_Aq z0(2;kD`nv78n#^1ET{nOXDp%_yvg3za09q{U`@UI_JH?{73c7TI9{ zwvN7TI6aod=AM*pwb6E0rUMseVz$cA@0Z&7>tt3{rv-OhP=o&AG@ZKR32M-!@MVjV zJ$UU*0--!2g`~o1R+BgXtxNe;Rp93z>x~}8PsbTb?Ol`Kw=e>tf=o)`M{=K*{Oi@% zQA^tstW&47Ow{~Wx9!yu+{45yEKdvT=20SoN=0_{e{bMP8qqV86QghrN8PLqKM=ca z**(>`70-40)$lW{kiilHOSXM^4#a%LaE(kL5N?gC`;?3(eN4{wx@}1L#9cg&s}G$U z9?k42euWtqB3|qbQnagJ2jBx<6QE)&VC#^5{MR*m`jDqD4C*!@zp`dI=NH&+yR%{{ zL+>&9sV2@91U*j&D^G$~BFslRkb#AvPt zOVovIfa*p9sxjAWs?nm3jH#a?aMEw~$OBSfjM*FS8Dfg<@jEN`qo2wIe#yVx;pIGY zS?}grn93Lym2*8qG$m+fUy|=L=&^0TZ8_4aoi^rE_M=4g;$RLel`N50$ zip&nmi01)ZZ5?AK+(AgpRTYunmBJ`Qx-h6o_S3Qj9Hrx?Xlt4wot~Z=ndJJ}t*Vhy z>3iMV%_NZM9b4%pUo5w* zAt8y}@0ag$K66A6BybyH(m~G?Bwq29H#fQwF-ezVA~CwX)qcC9eW{u8l0B2sft$HY zk{+FvMf*J71+DV2B4G4M-3$y{Y6P(IKar?2>Q(S($p+fgh`}>WCbEv0E*8a1kFs_r8VenxtEL|X}ZsSg>@!s z=XxYhnj}=gn1Cv8)(m5lUKDcx8w|W09vvsbR-be#)6ay&1w+jZWTN76rXAn9o#f?| zHDYJwzAo|9rE_+vdbcD^X9>8V_`~om5ZHD{`hFbdL@iMYbNThY z-w+>*_ZH=kcJXxa=4X_zn~tMbzj;|SG$-bVwG|nJ499W;?%B^}c)2eb#%1TjsMlj3 zS8bm)Z8N>1o;oe{=5|XRLs8xQz1{|07_paDc>3&jtBsc#+Dz+}$&FpOYqq|Nop zS}^LUUMX)MkGJ{6}B_XOu-JX z^0!sO5el-$P5^EIoB;lZ5e&c`q7%UH5ODza!!G!*{wBZ|;Dnd_XAP-dpX8;w?eQ9Ge^1F zQ%^lwDa5uXG5_-3Ff!QYhalSQocNM}kUqDf+(d#sjs|eA@&3ZkR`Zu0xj|)CkLz%I*pHEu3p~KU=UH@at*T-C+ZQ6-98PZS*sMnEpM;{r zk6B~kqT)AvXJTCt7R+BNs2Wika`Wh4tS6K!!%rpgm3c{Ae= z%^$ij$6UfX!7f|VL0_ZN#?-WR>ju*Ad%`X5UFdbp9{kcDx@dQ^HWGO1?t{&w)UZ8IJnp%{j)m(YlOZWuQ`&T{ET$xY_xum@F#0TVM7R4bir|#OD=-i3@WE@S=b8{$$n7d1G4SY&bTD;I}-?Z z@OifVTEiUce@$tn#gb^^%mq7cU9h<@?3rcRe5p96Pb4)_U9hv|NJ^T$&(XBdJ=f|Ac>+J7>V)J5r9)<;=Ftew@@6 z+p}L~3}nn91P9v<&s#nzQQNEATvwDK_5{7K*@a{(>M)S`?@EWFN1o=?_62BMw&V=& z?5gS#SzOL4sH;)l&%GkQDX(*^CIA(J*xmDRv`xEL=@H_J*TzfY;V|eaUNA@$3V#+TzRAWgttEM>B`+s#2BpP6obG(D&s$JS8wG2 literal 0 HcmV?d00001 diff --git a/jetty-ssl-reload/src/test/resources/jetty-logging.properties b/jetty-ssl-reload/src/test/resources/jetty-logging.properties new file mode 100644 index 000000000000..6af29424dc12 --- /dev/null +++ b/jetty-ssl-reload/src/test/resources/jetty-logging.properties @@ -0,0 +1,4 @@ +org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog +org.eclipse.jetty.deploy.LEVEL=WARN +org.eclipse.jetty.util.Scanner=WARN +org.eclipse.jetty.ssl.reload.LEVEL=DEBUG diff --git a/jetty-ssl-reload/src/test/resources/newKeystore b/jetty-ssl-reload/src/test/resources/newKeystore new file mode 100644 index 0000000000000000000000000000000000000000..133fc4ead39773e902330933d5dcb1313e8a12e1 GIT binary patch literal 2071 zcmV+y2HH0004VGm@@~ z000F5FoFd9Fb)O^D+U1s0V)C!0RaU71cC(U{wI^t3YE`=z}E|&JEa+8N6rw|yD3y4 zAN*HyF5vS<;(7i3@TH+4$ET8=hax^i-@`A~Ex%nWK~s=()Z)S`0>S-HdMt2S!dugs zHSi;)A=gNO6XJA%w4$OD8Npr{g-nb{=kEP+v`_Vo0!T{0$u;ESx2}3^@unzW;6Js= zPU8^-crX&@&`ifO%4-%DNK|n9a8^ATViZyTF^mbk_)=Ac8u4#2`;-QPlD|IZ%&s+k zrhwJ_O}-_L;t$K|dKx)7@y}}XmGUIDBjsgt-&-{-ks&fDyY)acP*^CXTWLqW!mIfl zQcSl_kh^^Zaw{n~E_$9v2c98@ICWD!ly699aqre)kTGw`nbW9w2yVUZfIaRtQrIoM z&FY5GL!-UAY2q!Hb5*S`l!=3&?ldM#Q)~qX<}HLe0AHVjgNkb6lcNAdB%NSOG~xFo zXR@HCIh@>jCW_10eM>C{(qF*E!oe`tP?2Om!fsIU?m+CK5C!j6`4~-N{Zs-F(+4Cv z()#C!*v5|pu!tMiSOdFK#}E+V)*->CFh;s8_H4DOqj-aQoSKM?A>(>u>1W<&6&75woCz?5TtS%rL(RjfPA{5*-^Bz` z&B$PCo2A*i9yI2)aDV!e;~$MdJFt_gNb=wo8tw&uAv9!1Z!@r189|@T%z`~?oXEvn zLB;x4ob&{2@9Z1ZZ`(`NOeADLyw>(4D7u)=H22Gnd*fs4~Nz~N`$HNnTT4uXHUEllS$7r-w;aD0q;UuMFF z;veO^nF+b1Qlx%Kd$F^+q{?$9Owf8&{W(1Uc^okiTZdZ;sODd{ZSj@oAjsCHD3@$LOTH5Suv^fT<4sWJXlAGa(nUrwN zN-!k-GWbkER{+|N?X8K%I*JL+g`7FBGyq+5#A4~o!8umpVg__yU|L!xIr-Bbbx2cO z`VAG1Wn6M@2PvWiH{(}DMGQQ@&7}>y@!We_`k<01f2h9*-?FuR#%PMEf?#J)ZmlVY z@d>Ov*MP4lU&as*%mbFd=ij>n=oiHVcT@!s5-qAnf1Et1r)s7pe-v#1pc|NWZ5oVi zyLUC;kzg*fIv=`R*4h9V4tOPQ>()((4QkO{BXOg00GlRWE@l|%pSRBCY+i{7Yw3j5sQjOEzOh(b6Ejo~ z&A98fLQgHX)*kOnf19D@m!GB9C+#P*{kd}Q0ytdFp5dv99h%1KeLoGx2U&AqOaOXD z4){lXQrr%}C{d3>4pP!SF;!Q+QFL(az%-6X25%!V*=f_#TT%e^`mvjJkt#@UgPL$_ zFmQ=|nS8BaCvA!i@wue)1r~%qtFBKaXQ%WyX|(t?<0+GQ0000100mesH842<00P4> zf&#xVf&r(X0|Eg80t9m|*Ca3v1_>&LNQUyR6n?!E9Zya?f@^W5SBggm)v#!;oZ1CJO$_Fzcg29R>qc9S#H*1QaUvR}e>W zuhYx_Sz4e_D!#?={6H`b1_>&LNQUjflyiJJgX&|NRC{VF3;m+~d# zlTCCrz*zlxK-D14CB?qkV-XZBVEB^o+clQU^v0vpJ-7H@@t}8z4mh5y8Iry2GIM+{ z7Lt*Ey=$+QpSbWMGu33S{s?U06!c4pM!aglWxFVrQ>&1K@O!lyH5#W?6)Z2NgG&S7 z>o^%(^7(;D5=5E8!b6Q0T4gg)=z#Kv8q--$dnylevZcMy$N54AT;Ptp7N3{4?FF^9 zwMsLbbPI6aVt%@+&%y Bv6TP- literal 0 HcmV?d00001 diff --git a/jetty-ssl-reload/src/test/resources/oldKeystore b/jetty-ssl-reload/src/test/resources/oldKeystore new file mode 100644 index 0000000000000000000000000000000000000000..8325b026098e23fd9b3cd6607fc55c63c247a45a GIT binary patch literal 2303 zcmd6o`8O2&9>-^~WNgDUjg);~hG9mAFtYCkL*kKb7$XcCF^Y#tJocq-M4s%8L?IPQ zkwnZB4Ur`#VQeX)XyU4S@43%8_aC@Fe13Sp&-ZiA=e$4P^ZM+r?ydp=0MLE`e-&?N z5S2>XSB_Cq-_!sAUI2>D!$pbkK{WV)JV14j7!b${;Gy$y3XMHRSvnI2ZR_;7RXOj? zZTnjLn_TM4qXB5*%~{dvx>08>g}hP|nu{UhWZR5EM10RNMMIOeRsoO!o-gsSnOolO zWc-psgp1--%-S-(oD**_YzRb9FJ_rRn)5B@*0Zl1&x5C592D?t3cp>lE1CA4G*SEx z0BaDd7VPuDVyfbwT<^&WsdcV&O7ZNoMfeN^UI7a5cgF5(&|U8!J!Q zB+bQZYo4nwy+6fnc}MdMx}-Uu(zb`g9^t!_u2$EgPXJB%AI3b*xcS6t)@-C<2X4DJ zSeSxYZiY$}S0mnb?45MEAj9x~r{3r;?a+cqD1A$PRx`S#uE&8k9^A)f`T#m6rOk%{q;3RQy~jP-!2UMZ{yH*!|AK2*%d z#ZrD`>-&TgZui)hF>&idC8NQF-@*1))0MVqBX6eFqoM)+-a%qP3X*SfA%%c-yA1x( zvXfV~qnIe16DV`Uzz)yXQ$p$HbnR_?rBEw@Y@g^Y7}6IK$3GvN#|k_b4wklCvP4 zg;`qAJ1Q`Qr5Y@&5A2mQZr{CoDQkQ_3cpsmXzR3ui>MZAdff}r4A&5MJpbWr>^I+G zvcKh4^Gs3aHk&Aas|lUhoB5?xW3V1sRG-m7Ik?GIs%fur0I;B^lTvFBmxt~V;W(9V z7*eA{WM{V4fl=EQ$84`TMq+H>)hAJ zpNj&Cd3TlX+LQ{oj#qRWxRv+Fd37s^TO0S>VG`Jw<`Em3RKHgZec7TfTz!4aK1G6H zU+A~%H&V=LJK}=P4JKVakQpo5Uzd~qu#-1Lp6aiAsjJ1hn6DsfBKG>g(tx=VM$GxD z&$E8Sy3qp%wp3%Y=jl@$Tb8I#xb8z6F0z8paLFlCGfc+uLYulm#ZLaGRsl@Uir0U1 zB5Bv4G37rx?OJ@eOGt|Z*pjHZ>t-1h*X^e!zgweE!_o*R4E^LNGqKzVxd`JJfZVfPwI zQgZ>qt}DScoTXu&=~|@QF!Fxa_ajnfnCt7>7P-CAaUhcL@p4X!jPGnFEx82rI(<;T z`3E}h+mr9Lq@;E%@7ayaFQSYe)&_}Ohz`7YwCk%?rqgweGrC4XA|1j# zes~SxE&XSbZ3`(+&vT%zVEdmW_M2u14FZO3J9)#~YL z_pg8^EauZ~w1B1A>O99?D#=5>Mg{jBeaq^Lp5$Qw4Hxp(NjwgWdHzx zTTygy3yKaZEdzsqAP^6)TZ;lpm`^}G)h6|;Fc2uX56t8Y9q2wW!R|bKFn;%lGZB>7 zi2pbT{>S0}ucM4o{MiM8NjXxYsj_DNG+A>>#MziIDmj8Ai>K1GWF4qPEgcb*(9d^* zFkZW#wAPVCN&M7cnAk6RYXt>{kt5FrX;Gp{C~1VSjvh)+7p0?v#-h+(za&-%`)~RG zH7p${|MxKWFAbOu6x}~TKnREq1OifoG*q$YuM+js#yC;rVoaQ>v%c&CT576`vAv8K zG7pIFh5JMVPF6)f#NVkQ-Cbk$Pek89XVu;lDVub{2#P+UI_f?6YGXX#d4UO+Moz)W zvAZuM@c07mxLC36Zx{=q+>*EQK@`34Q&H?Bigt)Dvf0$sMbY_^k{?p>5dK9D^hloMqN!dVGR{yr`e~Neq}Z?| zD>o|%4Y@SrNAa48X%VRSlNfaVBWQ6`aC)Ki&FJ!0o`iBZY z&m;=DbncZd;o2$fvTxE*TzMMVkqoAl8{rUA?ISDx*}oYQkh4>u%xdW zXL1QHr#;?=xuQ~d&b=v@>^{hhxlAD_>9alq%%G!r)#vOelxM-iB=GM684XBU|kmX zlG3}aCnG_K(9mYVbjy`DH$S<@^uCwiIp$sK=iUTik<2(Mh+ Date: Fri, 10 Jul 2020 12:20:12 +1000 Subject: [PATCH 03/12] Issue #5019 - add jetty module files and xml to use ssl-reload with start.jar Signed-off-by: Lachlan Roberts --- .../src/main/config/etc/jetty-ssl-reload.xml | 13 +++++++++++++ .../src/main/config/modules/ssl-reload.mod | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 jetty-ssl-reload/src/main/config/etc/jetty-ssl-reload.xml create mode 100644 jetty-ssl-reload/src/main/config/modules/ssl-reload.mod diff --git a/jetty-ssl-reload/src/main/config/etc/jetty-ssl-reload.xml b/jetty-ssl-reload/src/main/config/etc/jetty-ssl-reload.xml new file mode 100644 index 000000000000..c9c15382f92c --- /dev/null +++ b/jetty-ssl-reload/src/main/config/etc/jetty-ssl-reload.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/jetty-ssl-reload/src/main/config/modules/ssl-reload.mod b/jetty-ssl-reload/src/main/config/modules/ssl-reload.mod new file mode 100644 index 000000000000..14fdd4579d28 --- /dev/null +++ b/jetty-ssl-reload/src/main/config/modules/ssl-reload.mod @@ -0,0 +1,17 @@ +DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html + +[description] +Enables the SSL keystore to be reloaded after any changes are detected on the file system. + +[depend] +ssl + +[lib] +lib/jetty-ssl-reload-${jetty.version}.jar + +[xml] +etc/jetty-ssl-reload.xml + +[ini-template] +# Monitored directory scan period (seconds) +# jetty.ssl.reload.scanInterval=1 \ No newline at end of file From 80f09e1f712641d8457bcf693930fe1dbfe571d1 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Fri, 10 Jul 2020 18:39:19 +1000 Subject: [PATCH 04/12] fix issues with the ssl-reload module Signed-off-by: Lachlan Roberts --- jetty-bom/pom.xml | 5 +++++ jetty-home/pom.xml | 5 +++++ jetty-ssl-reload/src/main/config/etc/jetty-ssl-reload.xml | 2 +- jetty-ssl-reload/src/main/config/modules/ssl-reload.mod | 4 ++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/jetty-bom/pom.xml b/jetty-bom/pom.xml index e9e87d39d8fc..fce0b3898ea1 100644 --- a/jetty-bom/pom.xml +++ b/jetty-bom/pom.xml @@ -344,6 +344,11 @@ jetty-webapp 9.4.31-SNAPSHOT + + org.eclipse.jetty + jetty-ssl-reload + 9.4.31-SNAPSHOT + org.eclipse.jetty.websocket javax-websocket-client-impl diff --git a/jetty-home/pom.xml b/jetty-home/pom.xml index 44a984c40acd..98b792cbb804 100644 --- a/jetty-home/pom.xml +++ b/jetty-home/pom.xml @@ -768,6 +768,11 @@ jetty-nosql ${project.version} + + org.eclipse.jetty + jetty-ssl-reload + ${project.version} + diff --git a/jetty-ssl-reload/src/main/config/etc/jetty-ssl-reload.xml b/jetty-ssl-reload/src/main/config/etc/jetty-ssl-reload.xml index c9c15382f92c..9e3c771abf9d 100644 --- a/jetty-ssl-reload/src/main/config/etc/jetty-ssl-reload.xml +++ b/jetty-ssl-reload/src/main/config/etc/jetty-ssl-reload.xml @@ -4,7 +4,7 @@ - + diff --git a/jetty-ssl-reload/src/main/config/modules/ssl-reload.mod b/jetty-ssl-reload/src/main/config/modules/ssl-reload.mod index 14fdd4579d28..e77bec8259fd 100644 --- a/jetty-ssl-reload/src/main/config/modules/ssl-reload.mod +++ b/jetty-ssl-reload/src/main/config/modules/ssl-reload.mod @@ -3,6 +3,10 @@ DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-m [description] Enables the SSL keystore to be reloaded after any changes are detected on the file system. +[tags] +connector +ssl + [depend] ssl From dc91f690be247863aab17536779497d0420f0699 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Mon, 13 Jul 2020 11:33:51 +1000 Subject: [PATCH 05/12] use WorkDirExtension in KeystoreReloadTest to fix failing tests on jenkins Signed-off-by: Lachlan Roberts --- .../jetty/ssl/reload/KeystoreReloadTest.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java b/jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java index e5aeaf7c4a99..16ca7a54a3db 100644 --- a/jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java +++ b/jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java @@ -41,43 +41,51 @@ import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.log.StacklessLogging; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertThrows; +@ExtendWith(WorkDirExtension.class) public class KeystoreReloadTest { private static final int scanInterval = 1; + public WorkDir testdir; private Server server; - public static void useKeystore(String keystore) throws Exception + public String useKeystore(String keystore) throws Exception { - Path keystoreDir = MavenTestingUtils.getTestResourcePath("keystoreDir"); + Path keystoreDir = testdir.getEmptyPathDir(); Path keystorePath = keystoreDir.resolve("keystore"); if (Files.exists(keystorePath)) Files.delete(keystorePath); if (keystore == null) - return; + return null; Files.copy(MavenTestingUtils.getTestResourceFile(keystore).toPath(), keystorePath); keystorePath.toFile().deleteOnExit(); + + if (!Files.exists(keystorePath)) + throw new IllegalStateException("keystore file was not created"); + + return keystorePath.toAbsolutePath().toString(); } @BeforeEach public void start() throws Exception { - useKeystore("oldKeystore"); - - String keystore = MavenTestingUtils.getTestResourceFile("keystoreDir/keystore").getAbsolutePath(); + String keystorePath = useKeystore("oldKeystore"); SslContextFactory sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystore); + sslContextFactory.setKeyStorePath(keystorePath); sslContextFactory.setKeyStorePassword("storepwd"); sslContextFactory.setKeyManagerPassword("keypwd"); From 0705d8ea77390cb9ab8dae4e728a6a3802c4bfe4 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Mon, 13 Jul 2020 13:35:47 +1000 Subject: [PATCH 06/12] generalize the exception type tested in KeystoreReloadTest Signed-off-by: Lachlan Roberts --- .../org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java b/jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java index 16ca7a54a3db..caf595388529 100644 --- a/jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java +++ b/jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java @@ -29,7 +29,6 @@ import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.KeyManager; import javax.net.ssl.SSLContext; -import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; @@ -146,8 +145,8 @@ public void testReloadWithBadKeystore() throws Exception Thread.sleep(Duration.ofSeconds(scanInterval * 2).toMillis()); } - // The good keystore is removed, now the bad keystore now causes an SSL Handshake exception. - assertThrows(SSLHandshakeException.class, () -> getCertificateFromUrl(serverUrl)); + // The good keystore is removed, now the bad keystore now causes an exception. + assertThrows(Throwable.class, () -> getCertificateFromUrl(serverUrl)); } @Test @@ -166,8 +165,8 @@ public void testKeystoreRemoval() throws Exception Thread.sleep(Duration.ofSeconds(scanInterval * 2).toMillis()); } - // The good keystore is removed, having no keystore causes an SSL Handshake exception. - assertThrows(SSLHandshakeException.class, () -> getCertificateFromUrl(serverUrl)); + // The good keystore is removed, having no keystore causes an exception. + assertThrows(Throwable.class, () -> getCertificateFromUrl(serverUrl)); // Switch to use keystore2 which has a later expiry date. useKeystore("newKeystore"); From 62ee077b7967a1f6ecc9c5d66827c463eea16e3f Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Mon, 13 Jul 2020 23:11:09 +1000 Subject: [PATCH 07/12] move code of ssl-reload module to jetty-util, move module files to jetty-server Signed-off-by: Lachlan Roberts --- jetty-bom/pom.xml | 5 ---- jetty-home/pom.xml | 5 ---- .../src/main/config/etc/jetty-ssl-reload.xml | 2 +- .../src/main/config/modules/ssl-reload.mod | 0 jetty-ssl-reload/pom.xml | 28 ------------------ .../test/resources/jetty-logging.properties | 4 --- .../jetty/util/ssl}/SslKeyStoreScanner.java | 3 +- pom.xml | 1 - .../jetty/test}/KeystoreReloadTest.java | 3 +- .../src/test/resources/badKeystore | Bin .../src/test/resources/newKeystore | Bin .../src/test/resources/oldKeystore | Bin 12 files changed, 4 insertions(+), 47 deletions(-) rename {jetty-ssl-reload => jetty-server}/src/main/config/etc/jetty-ssl-reload.xml (82%) rename {jetty-ssl-reload => jetty-server}/src/main/config/modules/ssl-reload.mod (100%) delete mode 100644 jetty-ssl-reload/pom.xml delete mode 100644 jetty-ssl-reload/src/test/resources/jetty-logging.properties rename {jetty-ssl-reload/src/main/java/org/eclipse/jetty/ssl/reload => jetty-util/src/main/java/org/eclipse/jetty/util/ssl}/SslKeyStoreScanner.java (98%) rename {jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload => tests/test-integration/src/test/java/org/eclipse/jetty/test}/KeystoreReloadTest.java (98%) rename {jetty-ssl-reload => tests/test-integration}/src/test/resources/badKeystore (100%) rename {jetty-ssl-reload => tests/test-integration}/src/test/resources/newKeystore (100%) rename {jetty-ssl-reload => tests/test-integration}/src/test/resources/oldKeystore (100%) diff --git a/jetty-bom/pom.xml b/jetty-bom/pom.xml index fce0b3898ea1..e9e87d39d8fc 100644 --- a/jetty-bom/pom.xml +++ b/jetty-bom/pom.xml @@ -344,11 +344,6 @@ jetty-webapp 9.4.31-SNAPSHOT - - org.eclipse.jetty - jetty-ssl-reload - 9.4.31-SNAPSHOT - org.eclipse.jetty.websocket javax-websocket-client-impl diff --git a/jetty-home/pom.xml b/jetty-home/pom.xml index 98b792cbb804..44a984c40acd 100644 --- a/jetty-home/pom.xml +++ b/jetty-home/pom.xml @@ -768,11 +768,6 @@ jetty-nosql ${project.version} - - org.eclipse.jetty - jetty-ssl-reload - ${project.version} - diff --git a/jetty-ssl-reload/src/main/config/etc/jetty-ssl-reload.xml b/jetty-server/src/main/config/etc/jetty-ssl-reload.xml similarity index 82% rename from jetty-ssl-reload/src/main/config/etc/jetty-ssl-reload.xml rename to jetty-server/src/main/config/etc/jetty-ssl-reload.xml index 9e3c771abf9d..7520ee853783 100644 --- a/jetty-ssl-reload/src/main/config/etc/jetty-ssl-reload.xml +++ b/jetty-server/src/main/config/etc/jetty-ssl-reload.xml @@ -4,7 +4,7 @@ - + diff --git a/jetty-ssl-reload/src/main/config/modules/ssl-reload.mod b/jetty-server/src/main/config/modules/ssl-reload.mod similarity index 100% rename from jetty-ssl-reload/src/main/config/modules/ssl-reload.mod rename to jetty-server/src/main/config/modules/ssl-reload.mod diff --git a/jetty-ssl-reload/pom.xml b/jetty-ssl-reload/pom.xml deleted file mode 100644 index 198cffa8b32b..000000000000 --- a/jetty-ssl-reload/pom.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - org.eclipse.jetty - jetty-project - 9.4.31-SNAPSHOT - - 4.0.0 - jetty-ssl-reload - Jetty :: SSL Reload - - - ${project.groupId}.ssl.reload - - - - - org.eclipse.jetty - jetty-util - ${project.version} - - - org.eclipse.jetty - jetty-server - ${project.version} - test - - - diff --git a/jetty-ssl-reload/src/test/resources/jetty-logging.properties b/jetty-ssl-reload/src/test/resources/jetty-logging.properties deleted file mode 100644 index 6af29424dc12..000000000000 --- a/jetty-ssl-reload/src/test/resources/jetty-logging.properties +++ /dev/null @@ -1,4 +0,0 @@ -org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.deploy.LEVEL=WARN -org.eclipse.jetty.util.Scanner=WARN -org.eclipse.jetty.ssl.reload.LEVEL=DEBUG diff --git a/jetty-ssl-reload/src/main/java/org/eclipse/jetty/ssl/reload/SslKeyStoreScanner.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslKeyStoreScanner.java similarity index 98% rename from jetty-ssl-reload/src/main/java/org/eclipse/jetty/ssl/reload/SslKeyStoreScanner.java rename to jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslKeyStoreScanner.java index f96b1e0e64f5..b6224151fff0 100644 --- a/jetty-ssl-reload/src/main/java/org/eclipse/jetty/ssl/reload/SslKeyStoreScanner.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslKeyStoreScanner.java @@ -16,7 +16,7 @@ // ======================================================================== // -package org.eclipse.jetty.ssl.reload; +package org.eclipse.jetty.util.ssl; import java.io.File; import java.net.URI; @@ -29,7 +29,6 @@ import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.util.ssl.SslContextFactory; public class SslKeyStoreScanner extends AbstractLifeCycle implements Scanner.DiscreteListener { diff --git a/pom.xml b/pom.xml index 2bddbcb1b632..ca03a78b8274 100644 --- a/pom.xml +++ b/pom.xml @@ -95,7 +95,6 @@ jetty-server jetty-xml jetty-security - jetty-ssl-reload jetty-openid jetty-servlet jetty-webapp diff --git a/jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/KeystoreReloadTest.java similarity index 98% rename from jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java rename to tests/test-integration/src/test/java/org/eclipse/jetty/test/KeystoreReloadTest.java index caf595388529..6197aeaf829d 100644 --- a/jetty-ssl-reload/src/test/java/org/eclipse/jetty/ssl/reload/KeystoreReloadTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/KeystoreReloadTest.java @@ -16,7 +16,7 @@ // ======================================================================== // -package org.eclipse.jetty.ssl.reload; +package org.eclipse.jetty.test; import java.net.URL; import java.nio.file.Files; @@ -44,6 +44,7 @@ import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.log.StacklessLogging; import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.ssl.SslKeyStoreScanner; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/jetty-ssl-reload/src/test/resources/badKeystore b/tests/test-integration/src/test/resources/badKeystore similarity index 100% rename from jetty-ssl-reload/src/test/resources/badKeystore rename to tests/test-integration/src/test/resources/badKeystore diff --git a/jetty-ssl-reload/src/test/resources/newKeystore b/tests/test-integration/src/test/resources/newKeystore similarity index 100% rename from jetty-ssl-reload/src/test/resources/newKeystore rename to tests/test-integration/src/test/resources/newKeystore diff --git a/jetty-ssl-reload/src/test/resources/oldKeystore b/tests/test-integration/src/test/resources/oldKeystore similarity index 100% rename from jetty-ssl-reload/src/test/resources/oldKeystore rename to tests/test-integration/src/test/resources/oldKeystore From b03643961d9d7be6ed566c42aadb9bd6428a3cfa Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Tue, 14 Jul 2020 00:10:11 +1000 Subject: [PATCH 08/12] add tests in KeystoreScannerTest for changes with symlinks Signed-off-by: Lachlan Roberts --- .../jetty/test/KeystoreReloadTest.java | 134 +++++++++++++----- .../test/resources/jetty-logging.properties | 1 + 2 files changed, 103 insertions(+), 32 deletions(-) diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/KeystoreReloadTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/KeystoreReloadTest.java index 6197aeaf829d..6ce107718c80 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/KeystoreReloadTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/KeystoreReloadTest.java @@ -60,34 +60,35 @@ public class KeystoreReloadTest private static final int scanInterval = 1; public WorkDir testdir; private Server server; + private Path keystoreDir; - public String useKeystore(String keystore) throws Exception + @BeforeEach + public void before() { - Path keystoreDir = testdir.getEmptyPathDir(); - Path keystorePath = keystoreDir.resolve("keystore"); - if (Files.exists(keystorePath)) - Files.delete(keystorePath); - - if (keystore == null) - return null; - - Files.copy(MavenTestingUtils.getTestResourceFile(keystore).toPath(), keystorePath); - keystorePath.toFile().deleteOnExit(); - - if (!Files.exists(keystorePath)) - throw new IllegalStateException("keystore file was not created"); + keystoreDir = testdir.getEmptyPathDir(); + } - return keystorePath.toAbsolutePath().toString(); + @FunctionalInterface + public interface Configuration + { + void configure(SslContextFactory sslContextFactory) throws Exception; } - @BeforeEach public void start() throws Exception { - String keystorePath = useKeystore("oldKeystore"); + start(sslContextFactory -> + { + String keystorePath = useKeystore("oldKeystore").toString(); + sslContextFactory.setKeyStorePath(keystorePath); + sslContextFactory.setKeyStorePassword("storepwd"); + sslContextFactory.setKeyManagerPassword("keypwd"); + }); + } + + public void start(Configuration configuration) throws Exception + { SslContextFactory sslContextFactory = new SslContextFactory.Server(); - sslContextFactory.setKeyStorePath(keystorePath); - sslContextFactory.setKeyStorePassword("storepwd"); - sslContextFactory.setKeyManagerPassword("keypwd"); + configuration.configure(sslContextFactory); server = new Server(); SslConnectionFactory sslConnectionFactory = new SslConnectionFactory(sslContextFactory, HttpVersion.HTTP_1_1.asString()); @@ -95,7 +96,6 @@ public void start() throws Exception httpsConfig.addCustomizer(new SecureRequestCustomizer()); HttpConnectionFactory httpConnectionFactory = new HttpConnectionFactory(httpsConfig); ServerConnector connector = new ServerConnector(server, sslConnectionFactory, httpConnectionFactory); - connector.setPort(8443); server.addConnector(connector); // Configure Keystore Reload. @@ -115,10 +115,10 @@ public void stop() throws Exception @Test public void testKeystoreHotReload() throws Exception { - URL serverUrl = server.getURI().toURL(); + start(); // Check the original certificate expiry. - X509Certificate cert1 = getCertificateFromUrl(serverUrl); + X509Certificate cert1 = getCertificateFromServer(); assertThat(getExpiryYear(cert1), is(2015)); // Switch to use newKeystore which has a later expiry date. @@ -126,17 +126,17 @@ public void testKeystoreHotReload() throws Exception Thread.sleep(Duration.ofSeconds(scanInterval * 2).toMillis()); // The scanner should have detected the updated keystore, expiry should be renewed. - X509Certificate cert2 = getCertificateFromUrl(serverUrl); + X509Certificate cert2 = getCertificateFromServer(); assertThat(getExpiryYear(cert2), is(2020)); } @Test public void testReloadWithBadKeystore() throws Exception { - URL serverUrl = server.getURI().toURL(); + start(); // Check the original certificate expiry. - X509Certificate cert1 = getCertificateFromUrl(serverUrl); + X509Certificate cert1 = getCertificateFromServer(); assertThat(getExpiryYear(cert1), is(2015)); // Switch to use badKeystore which has the incorrect passwords. @@ -147,16 +147,16 @@ public void testReloadWithBadKeystore() throws Exception } // The good keystore is removed, now the bad keystore now causes an exception. - assertThrows(Throwable.class, () -> getCertificateFromUrl(serverUrl)); + assertThrows(Throwable.class, () -> getCertificateFromServer()); } @Test public void testKeystoreRemoval() throws Exception { - URL serverUrl = server.getURI().toURL(); + start(); // Check the original certificate expiry. - X509Certificate cert1 = getCertificateFromUrl(serverUrl); + X509Certificate cert1 = getCertificateFromServer(); assertThat(getExpiryYear(cert1), is(2015)); // Delete the keystore. @@ -167,15 +167,84 @@ public void testKeystoreRemoval() throws Exception } // The good keystore is removed, having no keystore causes an exception. - assertThrows(Throwable.class, () -> getCertificateFromUrl(serverUrl)); + assertThrows(Throwable.class, () -> getCertificateFromServer()); // Switch to use keystore2 which has a later expiry date. useKeystore("newKeystore"); Thread.sleep(Duration.ofSeconds(scanInterval * 2).toMillis()); - X509Certificate cert2 = getCertificateFromUrl(serverUrl); + X509Certificate cert2 = getCertificateFromServer(); assertThat(getExpiryYear(cert2), is(2020)); } + @Test + public void testReloadChangingSymbolicLink() throws Exception + { + Path keystorePath = keystoreDir.resolve("symlinkKeystore"); + start(sslContextFactory -> + { + Files.createSymbolicLink(keystorePath, useKeystore("oldKeystore")); + sslContextFactory.setKeyStorePath(keystorePath.toString()); + sslContextFactory.setKeyStorePassword("storepwd"); + sslContextFactory.setKeyManagerPassword("keypwd"); + }); + + // Check the original certificate expiry. + X509Certificate cert1 = getCertificateFromServer(); + assertThat(getExpiryYear(cert1), is(2015)); + + // Change the symlink to point to the newKeystore file location which has a later expiry date. + Files.delete(keystorePath); + Files.createSymbolicLink(keystorePath, useKeystore("newKeystore")); + Thread.sleep(Duration.ofSeconds(scanInterval * 2).toMillis()); + + // The scanner should have detected the updated keystore, expiry should be renewed. + X509Certificate cert2 = getCertificateFromServer(); + assertThat(getExpiryYear(cert2), is(2020)); + } + + @Test + public void testReloadChangingTargetOfSymbolicLink() throws Exception + { + start(sslContextFactory -> + { + Path keystorePath = keystoreDir.resolve("symlinkKeystore"); + Files.createSymbolicLink(keystorePath, useKeystore("oldKeystore")); + sslContextFactory.setKeyStorePath(keystorePath.toString()); + sslContextFactory.setKeyStorePassword("storepwd"); + sslContextFactory.setKeyManagerPassword("keypwd"); + }); + + // Check the original certificate expiry. + X509Certificate cert1 = getCertificateFromServer(); + assertThat(getExpiryYear(cert1), is(2015)); + + // Change the target file of the symlink to the newKeystore which has a later expiry date. + useKeystore("newKeystore"); + Thread.sleep(Duration.ofSeconds(scanInterval * 2).toMillis()); + + // The scanner should have detected the updated keystore, expiry should be renewed. + X509Certificate cert2 = getCertificateFromServer(); + assertThat(getExpiryYear(cert2), is(2020)); + } + + public Path useKeystore(String keystore) throws Exception + { + Path keystorePath = keystoreDir.resolve("keystore"); + if (Files.exists(keystorePath)) + Files.delete(keystorePath); + + if (keystore == null) + return null; + + Files.copy(MavenTestingUtils.getTestResourceFile(keystore).toPath(), keystorePath); + keystorePath.toFile().deleteOnExit(); + + if (!Files.exists(keystorePath)) + throw new IllegalStateException("keystore file was not created"); + + return keystorePath.toAbsolutePath(); + } + public static int getExpiryYear(X509Certificate cert) { Calendar instance = Calendar.getInstance(); @@ -183,8 +252,9 @@ public static int getExpiryYear(X509Certificate cert) return instance.get(Calendar.YEAR); } - public static X509Certificate getCertificateFromUrl(URL serverUrl) throws Exception + public X509Certificate getCertificateFromServer() throws Exception { + URL serverUrl = server.getURI().toURL(); SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom()); SSLContext.setDefault(ctx); diff --git a/tests/test-integration/src/test/resources/jetty-logging.properties b/tests/test-integration/src/test/resources/jetty-logging.properties index 8f0c83cbb6a8..faa733ccad58 100644 --- a/tests/test-integration/src/test/resources/jetty-logging.properties +++ b/tests/test-integration/src/test/resources/jetty-logging.properties @@ -2,3 +2,4 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog #org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog #org.eclipse.jetty.LEVEL=DEBUG #org.eclipse.jetty.websocket.LEVEL=DEBUG +#org.eclipse.jetty.util.ssl.SslKeyStoreScanner.LEVEL=DEBUG From a83844df32f08a18ba2e78a76f98205a611d1254 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Tue, 14 Jul 2020 16:10:07 +1000 Subject: [PATCH 09/12] changes from review Signed-off-by: Lachlan Roberts --- ...eload.xml => jetty-ssl-context-reload.xml} | 5 +- .../src/main/config/modules/ssl-reload.mod | 7 +- ...StoreScanner.java => KeyStoreScanner.java} | 75 +++++++------------ ...loadTest.java => KeyStoreScannerTest.java} | 10 +-- .../test/resources/jetty-logging.properties | 2 +- 5 files changed, 38 insertions(+), 61 deletions(-) rename jetty-server/src/main/config/etc/{jetty-ssl-reload.xml => jetty-ssl-context-reload.xml} (74%) rename jetty-util/src/main/java/org/eclipse/jetty/util/ssl/{SslKeyStoreScanner.java => KeyStoreScanner.java} (60%) rename tests/test-integration/src/test/java/org/eclipse/jetty/test/{KeystoreReloadTest.java => KeyStoreScannerTest.java} (96%) diff --git a/jetty-server/src/main/config/etc/jetty-ssl-reload.xml b/jetty-server/src/main/config/etc/jetty-ssl-context-reload.xml similarity index 74% rename from jetty-server/src/main/config/etc/jetty-ssl-reload.xml rename to jetty-server/src/main/config/etc/jetty-ssl-context-reload.xml index 7520ee853783..267bda744bc5 100644 --- a/jetty-server/src/main/config/etc/jetty-ssl-reload.xml +++ b/jetty-server/src/main/config/etc/jetty-ssl-context-reload.xml @@ -1,12 +1,11 @@ - - + - + diff --git a/jetty-server/src/main/config/modules/ssl-reload.mod b/jetty-server/src/main/config/modules/ssl-reload.mod index e77bec8259fd..68eb6c950123 100644 --- a/jetty-server/src/main/config/modules/ssl-reload.mod +++ b/jetty-server/src/main/config/modules/ssl-reload.mod @@ -10,12 +10,9 @@ ssl [depend] ssl -[lib] -lib/jetty-ssl-reload-${jetty.version}.jar - [xml] -etc/jetty-ssl-reload.xml +etc/jetty-ssl-context-reload.xml [ini-template] # Monitored directory scan period (seconds) -# jetty.ssl.reload.scanInterval=1 \ No newline at end of file +# jetty.sslContext.reload.scanInterval=1 \ No newline at end of file diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslKeyStoreScanner.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/KeyStoreScanner.java similarity index 60% rename from jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslKeyStoreScanner.java rename to jetty-util/src/main/java/org/eclipse/jetty/util/ssl/KeyStoreScanner.java index b6224151fff0..2e311dd6b69f 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslKeyStoreScanner.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/KeyStoreScanner.java @@ -19,74 +19,56 @@ package org.eclipse.jetty.util.ssl; import java.io.File; -import java.net.URI; -import java.util.ArrayList; -import java.util.List; +import java.io.IOException; +import java.util.Collections; import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedOperation; -import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.component.ContainerLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -public class SslKeyStoreScanner extends AbstractLifeCycle implements Scanner.DiscreteListener +public class KeyStoreScanner extends ContainerLifeCycle implements Scanner.DiscreteListener { - private static final Logger LOG = Log.getLogger(SslKeyStoreScanner.class); + private static final Logger LOG = Log.getLogger(KeyStoreScanner.class); private final SslContextFactory sslContextFactory; private final File keystoreFile; - private final List files = new ArrayList<>(); - private Scanner _scanner; - private int _scanInterval = 1; + private final Scanner _scanner; - public SslKeyStoreScanner(SslContextFactory sslContextFactory) + public KeyStoreScanner(SslContextFactory sslContextFactory) { this.sslContextFactory = sslContextFactory; - this.keystoreFile = new File(URI.create(sslContextFactory.getKeyStorePath())); // getKeyStorePath is giving url instead of path? - if (!keystoreFile.exists()) - throw new IllegalArgumentException("keystore file does not exist"); - if (keystoreFile.isDirectory()) - throw new IllegalArgumentException("expected keystore file not directory"); + try + { + keystoreFile = sslContextFactory.getKeyStoreResource().getFile(); + if (keystoreFile == null || !keystoreFile.exists()) + throw new IllegalArgumentException("keystore file does not exist"); + if (keystoreFile.isDirectory()) + throw new IllegalArgumentException("expected keystore file not directory"); + } + catch (IOException e) + { + throw new IllegalArgumentException("could not obtain keystore file", e); + } File parentFile = keystoreFile.getParentFile(); if (!parentFile.exists() || !parentFile.isDirectory()) throw new IllegalArgumentException("error obtaining keystore dir"); - files.add(parentFile); - if (LOG.isDebugEnabled()) - LOG.debug("created {}", this); - } - - @Override - protected void doStart() throws Exception - { - if (LOG.isDebugEnabled()) - LOG.debug(this.getClass().getSimpleName() + ".doStart()"); - _scanner = new Scanner(); - _scanner.setScanDirs(files); - _scanner.setScanInterval(_scanInterval); + _scanner.setScanDirs(Collections.singletonList(parentFile)); + _scanner.setScanInterval(1); _scanner.setReportDirs(false); _scanner.setReportExistingFilesOnStartup(false); _scanner.setScanDepth(1); _scanner.addListener(this); - _scanner.start(); - } - - @Override - protected void doStop() throws Exception - { - if (_scanner != null) - { - _scanner.stop(); - _scanner.removeListener(this); - _scanner = null; - } + addBean(_scanner); } @Override - public void fileAdded(String filename) throws Exception + public void fileAdded(String filename) { if (LOG.isDebugEnabled()) LOG.debug("added {}", filename); @@ -96,7 +78,7 @@ public void fileAdded(String filename) throws Exception } @Override - public void fileChanged(String filename) throws Exception + public void fileChanged(String filename) { if (LOG.isDebugEnabled()) LOG.debug("changed {}", filename); @@ -106,18 +88,17 @@ public void fileChanged(String filename) throws Exception } @Override - public void fileRemoved(String filename) throws Exception + public void fileRemoved(String filename) { if (LOG.isDebugEnabled()) LOG.debug("removed {}", filename); - // TODO: do we want to do this? if (keystoreFile.toPath().toString().equals(filename)) reload(); } @ManagedOperation(value = "Reload the SSL Keystore", impact = "ACTION") - public void reload() throws Exception + public void reload() { if (LOG.isDebugEnabled()) LOG.debug("reloading keystore file {}", keystoreFile); @@ -135,11 +116,11 @@ public void reload() throws Exception @ManagedAttribute("scanning interval to detect changes which need reloaded") public int getScanInterval() { - return _scanInterval; + return _scanner.getScanInterval(); } public void setScanInterval(int scanInterval) { - _scanInterval = scanInterval; + _scanner.setScanInterval(scanInterval); } } diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/KeystoreReloadTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/KeyStoreScannerTest.java similarity index 96% rename from tests/test-integration/src/test/java/org/eclipse/jetty/test/KeystoreReloadTest.java rename to tests/test-integration/src/test/java/org/eclipse/jetty/test/KeyStoreScannerTest.java index 6ce107718c80..1b54fdefae73 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/KeystoreReloadTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/KeyStoreScannerTest.java @@ -43,8 +43,8 @@ import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.log.StacklessLogging; +import org.eclipse.jetty.util.ssl.KeyStoreScanner; import org.eclipse.jetty.util.ssl.SslContextFactory; -import org.eclipse.jetty.util.ssl.SslKeyStoreScanner; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -55,7 +55,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; @ExtendWith(WorkDirExtension.class) -public class KeystoreReloadTest +public class KeyStoreScannerTest { private static final int scanInterval = 1; public WorkDir testdir; @@ -99,7 +99,7 @@ public void start(Configuration configuration) throws Exception server.addConnector(connector); // Configure Keystore Reload. - SslKeyStoreScanner keystoreScanner = new SslKeyStoreScanner(sslContextFactory); + KeyStoreScanner keystoreScanner = new KeyStoreScanner(sslContextFactory); keystoreScanner.setScanInterval(scanInterval); server.addBean(keystoreScanner); @@ -140,7 +140,7 @@ public void testReloadWithBadKeystore() throws Exception assertThat(getExpiryYear(cert1), is(2015)); // Switch to use badKeystore which has the incorrect passwords. - try (StacklessLogging ignored = new StacklessLogging(SslKeyStoreScanner.class)) + try (StacklessLogging ignored = new StacklessLogging(KeyStoreScanner.class)) { useKeystore("badKeystore"); Thread.sleep(Duration.ofSeconds(scanInterval * 2).toMillis()); @@ -160,7 +160,7 @@ public void testKeystoreRemoval() throws Exception assertThat(getExpiryYear(cert1), is(2015)); // Delete the keystore. - try (StacklessLogging ignored = new StacklessLogging(SslKeyStoreScanner.class)) + try (StacklessLogging ignored = new StacklessLogging(KeyStoreScanner.class)) { useKeystore(null); Thread.sleep(Duration.ofSeconds(scanInterval * 2).toMillis()); diff --git a/tests/test-integration/src/test/resources/jetty-logging.properties b/tests/test-integration/src/test/resources/jetty-logging.properties index faa733ccad58..d608f24d0e00 100644 --- a/tests/test-integration/src/test/resources/jetty-logging.properties +++ b/tests/test-integration/src/test/resources/jetty-logging.properties @@ -2,4 +2,4 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog #org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog #org.eclipse.jetty.LEVEL=DEBUG #org.eclipse.jetty.websocket.LEVEL=DEBUG -#org.eclipse.jetty.util.ssl.SslKeyStoreScanner.LEVEL=DEBUG +#org.eclipse.jetty.util.ssl.KeyStoreScanner.LEVEL=DEBUG From 2541f1f648f86767f9e95d1d002a5ca57fe99e21 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 15 Jul 2020 11:20:57 +1000 Subject: [PATCH 10/12] add javadoc for KeyStoreScanner class Signed-off-by: Lachlan Roberts --- .../src/main/config/etc/jetty-ssl-context-reload.xml | 2 +- jetty-server/src/main/config/modules/ssl-reload.mod | 2 +- .../java/org/eclipse/jetty/util/ssl/KeyStoreScanner.java | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/jetty-server/src/main/config/etc/jetty-ssl-context-reload.xml b/jetty-server/src/main/config/etc/jetty-ssl-context-reload.xml index 267bda744bc5..46346359ed74 100644 --- a/jetty-server/src/main/config/etc/jetty-ssl-context-reload.xml +++ b/jetty-server/src/main/config/etc/jetty-ssl-context-reload.xml @@ -3,7 +3,7 @@ - + diff --git a/jetty-server/src/main/config/modules/ssl-reload.mod b/jetty-server/src/main/config/modules/ssl-reload.mod index 68eb6c950123..acddb16a4c7f 100644 --- a/jetty-server/src/main/config/modules/ssl-reload.mod +++ b/jetty-server/src/main/config/modules/ssl-reload.mod @@ -1,4 +1,4 @@ -DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html +# DO NOT EDIT - See: https://www.eclipse.org/jetty/documentation/current/startup-modules.html [description] Enables the SSL keystore to be reloaded after any changes are detected on the file system. diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/KeyStoreScanner.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/KeyStoreScanner.java index 2e311dd6b69f..3c4197552d4b 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/KeyStoreScanner.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/KeyStoreScanner.java @@ -21,6 +21,7 @@ import java.io.File; import java.io.IOException; import java.util.Collections; +import java.util.function.Consumer; import org.eclipse.jetty.util.Scanner; import org.eclipse.jetty.util.annotation.ManagedAttribute; @@ -29,6 +30,12 @@ import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; +/** + *

The {@link KeyStoreScanner} is used to monitor the KeyStore file used by the {@link SslContextFactory}. + * It will reload the {@link SslContextFactory} if it detects that the KeyStore file has been modified.

+ *

If the TrustStore file needs to be changed, then this should be done before touching the KeyStore file, + * the {@link SslContextFactory#reload(Consumer)} will only occur after the KeyStore file has been modified.

+ */ public class KeyStoreScanner extends ContainerLifeCycle implements Scanner.DiscreteListener { private static final Logger LOG = Log.getLogger(KeyStoreScanner.class); From c40ba6922259b7c5c19e71dea2bfc6335c96982b Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 15 Jul 2020 15:35:22 +1000 Subject: [PATCH 11/12] add null checks in SslContextFactory for _factory, which could be null if reload failed Signed-off-by: Lachlan Roberts --- .../org/eclipse/jetty/util/ssl/SslContextFactory.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java index 9cce77971098..9366e77e5229 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java @@ -1131,6 +1131,9 @@ public SSLContext getSslContext() synchronized (this) { + if (_factory == null) + throw new IllegalStateException("reload failed SslContext unavailable"); + return _factory._context; } } @@ -1532,6 +1535,9 @@ public KeyStore getKeyStore() synchronized (this) { + if (_factory == null) + throw new IllegalStateException("reload failed KeyStore unavailable"); + return _factory._keyStore; } } @@ -1553,6 +1559,9 @@ public KeyStore getTrustStore() synchronized (this) { + if (_factory == null) + throw new IllegalStateException("reload failed TrustStore unavailable"); + return _factory._trustStore; } } From 0f7d99c3bfecf6f67b95f64c031880a0b4832078 Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 15 Jul 2020 19:01:15 +1000 Subject: [PATCH 12/12] add documentation for ssl-reload, change exception message in SslContextFactory Signed-off-by: Lachlan Roberts --- .../configuring/connectors/configuring-ssl.adoc | 12 ++++++++++++ .../eclipse/jetty/util/ssl/SslContextFactory.java | 6 +++--- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-ssl.adoc b/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-ssl.adoc index 555d72e73c20..6e9e65e9b1d4 100644 --- a/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-ssl.adoc +++ b/jetty-documentation/src/main/asciidoc/configuring/connectors/configuring-ssl.adoc @@ -989,3 +989,15 @@ As a reminder, when configuring your includes/excludes, *excludes always win*. Dumps can be configured as part of the `jetty.xml` configuration for your server. Please see the documentation on the link:#jetty-dump-tool[Jetty Dump Tool] for more information. + + +==== SslContextFactory KeyStore Reload + +Jetty can be configured to monitor the directory of the KeyStore file specified in the SslContextFactory, and reload the +SslContextFactory if any changes are detected to the KeyStore file. + +If changes need to be done to other file such as the TrustStore file, this must be done before the change to the Keystore +file which will then trigger the `SslContextFactory` reload. + +With the Jetty distribution this feature can be used by simply activating the `ssl-reload` startup module. +For embedded usage the `KeyStoreScanner` should be created given the `SslContextFactory` and added as a bean on the Server. diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java index 9366e77e5229..8776e4182051 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/ssl/SslContextFactory.java @@ -1132,7 +1132,7 @@ public SSLContext getSslContext() synchronized (this) { if (_factory == null) - throw new IllegalStateException("reload failed SslContext unavailable"); + throw new IllegalStateException("SslContextFactory reload failed"); return _factory._context; } @@ -1536,7 +1536,7 @@ public KeyStore getKeyStore() synchronized (this) { if (_factory == null) - throw new IllegalStateException("reload failed KeyStore unavailable"); + throw new IllegalStateException("SslContextFactory reload failed"); return _factory._keyStore; } @@ -1560,7 +1560,7 @@ public KeyStore getTrustStore() synchronized (this) { if (_factory == null) - throw new IllegalStateException("reload failed TrustStore unavailable"); + throw new IllegalStateException("SslContextFactory reload failed"); return _factory._trustStore; }