|
27 | 27 | import java.net.MalformedURLException; |
28 | 28 | import java.net.URI; |
29 | 29 | import java.net.URL; |
30 | | -import java.util.Arrays; |
31 | | -import java.util.ArrayList; |
32 | | -import java.util.Collections; |
33 | | -import java.util.Enumeration; |
34 | | -import java.util.HashMap; |
| 30 | +import java.nio.file.Paths; |
35 | 31 | import java.util.List; |
| 32 | +import java.util.ArrayList; |
36 | 33 | import java.util.Map; |
| 34 | +import java.util.HashMap; |
| 35 | +import java.util.Collections; |
| 36 | +import java.util.Optional; |
37 | 37 | import java.util.Properties; |
| 38 | +import java.util.Enumeration; |
| 39 | +import java.util.Arrays; |
| 40 | +import java.util.Timer; |
38 | 41 | import java.util.regex.Matcher; |
39 | 42 | import java.util.regex.Pattern; |
40 | 43 |
|
|
75 | 78 | import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHandler; |
76 | 79 | import org.apache.hadoop.security.authentication.util.SignerSecretProvider; |
77 | 80 | import org.apache.hadoop.security.authorize.AccessControlList; |
| 81 | +import org.apache.hadoop.security.ssl.FileBasedKeyStoresFactory; |
| 82 | +import org.apache.hadoop.security.ssl.FileMonitoringTimerTask; |
78 | 83 | import org.apache.hadoop.security.ssl.SSLFactory; |
79 | 84 | import org.apache.hadoop.util.ReflectionUtils; |
80 | 85 | import org.apache.hadoop.util.Shell; |
@@ -186,6 +191,7 @@ public final class HttpServer2 implements FilterContainer { |
186 | 191 | static final String STATE_DESCRIPTION_ALIVE = " - alive"; |
187 | 192 | static final String STATE_DESCRIPTION_NOT_LIVE = " - not live"; |
188 | 193 | private final SignerSecretProvider secretProvider; |
| 194 | + private final Optional<java.util.Timer> configurationChangeMonitor; |
189 | 195 | private XFrameOption xFrameOption; |
190 | 196 | private boolean xFrameOptionIsEnabled; |
191 | 197 | public static final String HTTP_HEADER_PREFIX = "hadoop.http.header."; |
@@ -244,6 +250,8 @@ public static class Builder { |
244 | 250 |
|
245 | 251 | private boolean sniHostCheckEnabled; |
246 | 252 |
|
| 253 | + private Optional<Timer> configurationChangeMonitor = Optional.empty(); |
| 254 | + |
247 | 255 | public Builder setName(String name){ |
248 | 256 | this.name = name; |
249 | 257 | return this; |
@@ -574,12 +582,45 @@ private ServerConnector createHttpsChannelConnector( |
574 | 582 | } |
575 | 583 |
|
576 | 584 | setEnabledProtocols(sslContextFactory); |
| 585 | + |
| 586 | + long storesReloadInterval = |
| 587 | + conf.getLong(FileBasedKeyStoresFactory.SSL_STORES_RELOAD_INTERVAL_TPL_KEY, |
| 588 | + FileBasedKeyStoresFactory.DEFAULT_SSL_STORES_RELOAD_INTERVAL); |
| 589 | + |
| 590 | + if (storesReloadInterval > 0) { |
| 591 | + this.configurationChangeMonitor = Optional.of( |
| 592 | + this.makeConfigurationChangeMonitor(storesReloadInterval, sslContextFactory)); |
| 593 | + } |
| 594 | + |
577 | 595 | conn.addFirstConnectionFactory(new SslConnectionFactory(sslContextFactory, |
578 | 596 | HttpVersion.HTTP_1_1.asString())); |
579 | 597 |
|
580 | 598 | return conn; |
581 | 599 | } |
582 | 600 |
|
| 601 | + private Timer makeConfigurationChangeMonitor(long reloadInterval, |
| 602 | + SslContextFactory.Server sslContextFactory) { |
| 603 | + java.util.Timer timer = new java.util.Timer(FileBasedKeyStoresFactory.SSL_MONITORING_THREAD_NAME, true); |
| 604 | + // |
| 605 | + // The Jetty SSLContextFactory provides a 'reload' method which will reload both |
| 606 | + // truststore and keystore certificates. |
| 607 | + // |
| 608 | + timer.schedule(new FileMonitoringTimerTask( |
| 609 | + Paths.get(keyStore), |
| 610 | + path -> { |
| 611 | + LOG.info("Reloading certificates from store keystore " + keyStore); |
| 612 | + try { |
| 613 | + sslContextFactory.reload(factory -> { }); |
| 614 | + } catch (Exception ex) { |
| 615 | + LOG.error("Failed to reload SSL keystore certificates", ex); |
| 616 | + } |
| 617 | + },null), |
| 618 | + reloadInterval, |
| 619 | + reloadInterval |
| 620 | + ); |
| 621 | + return timer; |
| 622 | + } |
| 623 | + |
583 | 624 | private void setEnabledProtocols(SslContextFactory sslContextFactory) { |
584 | 625 | String enabledProtocols = conf.get(SSLFactory.SSL_ENABLED_PROTOCOLS_KEY, |
585 | 626 | SSLFactory.SSL_ENABLED_PROTOCOLS_DEFAULT); |
@@ -622,6 +663,7 @@ private HttpServer2(final Builder b) throws IOException { |
622 | 663 | this.webAppContext = createWebAppContext(b, adminsAcl, appDir); |
623 | 664 | this.xFrameOptionIsEnabled = b.xFrameEnabled; |
624 | 665 | this.xFrameOption = b.xFrameOption; |
| 666 | + this.configurationChangeMonitor = b.configurationChangeMonitor; |
625 | 667 |
|
626 | 668 | try { |
627 | 669 | this.secretProvider = |
@@ -1420,6 +1462,16 @@ void openListeners() throws Exception { |
1420 | 1462 | */ |
1421 | 1463 | public void stop() throws Exception { |
1422 | 1464 | MultiException exception = null; |
| 1465 | + if (this.configurationChangeMonitor.isPresent()) { |
| 1466 | + try { |
| 1467 | + this.configurationChangeMonitor.get().cancel(); |
| 1468 | + } catch (Exception e) { |
| 1469 | + LOG.error( |
| 1470 | + "Error while canceling configuration monitoring timer for webapp" |
| 1471 | + + webAppContext.getDisplayName(), e); |
| 1472 | + exception = addMultiException(exception, e); |
| 1473 | + } |
| 1474 | + } |
1423 | 1475 | for (ServerConnector c : listeners) { |
1424 | 1476 | try { |
1425 | 1477 | c.close(); |
|
0 commit comments