|
35 | 35 | import java.util.HashMap; |
36 | 36 | import java.util.List; |
37 | 37 | import java.util.Map; |
| 38 | +import java.util.Optional; |
| 39 | +import java.util.Timer; |
38 | 40 | import java.util.stream.Collectors; |
39 | 41 | import javax.servlet.Filter; |
40 | 42 | import javax.servlet.FilterChain; |
|
62 | 64 | import org.apache.hadoop.security.authentication.server.AuthenticationFilter; |
63 | 65 | import org.apache.hadoop.security.authorize.AccessControlList; |
64 | 66 | import org.apache.hadoop.security.authorize.ProxyUsers; |
| 67 | +import org.apache.hadoop.security.ssl.FileBasedKeyStoresFactory; |
| 68 | +import org.apache.hadoop.security.ssl.FileMonitoringTimerTask; |
65 | 69 | import org.apache.hadoop.util.Shell; |
66 | 70 | import org.apache.hadoop.util.StringUtils; |
67 | 71 | import org.apache.yetus.audience.InterfaceAudience; |
@@ -179,6 +183,7 @@ public class HttpServer implements FilterContainer { |
179 | 183 | .build(); |
180 | 184 |
|
181 | 185 | private final AccessControlList adminsAcl; |
| 186 | + private final Optional<Timer> configurationChangeMonitor; |
182 | 187 |
|
183 | 188 | protected final Server webServer; |
184 | 189 | protected String appDir; |
@@ -244,6 +249,8 @@ public static class Builder { |
244 | 249 | private String kerberosNameRulesKey; |
245 | 250 | private String signatureSecretFileKey; |
246 | 251 |
|
| 252 | + private Optional<Timer> configurationChangeMonitor = Optional.empty(); |
| 253 | + |
247 | 254 | /** |
248 | 255 | * @see #setAppDir(String) |
249 | 256 | * @deprecated Since 0.99.0. Use builder pattern via {@link #setAppDir(String)} instead. |
@@ -466,6 +473,15 @@ public HttpServer build() throws IOException { |
466 | 473 | LOG.debug("Excluded SSL Cipher List:" + excludeCiphers); |
467 | 474 | } |
468 | 475 |
|
| 476 | + long storesReloadInterval = |
| 477 | + conf.getLong(FileBasedKeyStoresFactory.SSL_STORES_RELOAD_INTERVAL_TPL_KEY, |
| 478 | + FileBasedKeyStoresFactory.DEFAULT_SSL_STORES_RELOAD_INTERVAL); |
| 479 | + |
| 480 | + if (storesReloadInterval > 0 && (keyStore != null || trustStore != null)) { |
| 481 | + this.configurationChangeMonitor = |
| 482 | + Optional.of(this.makeConfigurationChangeMonitor(storesReloadInterval, sslCtxFactory)); |
| 483 | + } |
| 484 | + |
469 | 485 | listener = new ServerConnector(server.webServer, |
470 | 486 | new SslConnectionFactory(sslCtxFactory, HttpVersion.HTTP_1_1.toString()), |
471 | 487 | new HttpConnectionFactory(httpsConfig)); |
@@ -496,6 +512,29 @@ public HttpServer build() throws IOException { |
496 | 512 |
|
497 | 513 | } |
498 | 514 |
|
| 515 | + private Timer makeConfigurationChangeMonitor(long reloadInterval, |
| 516 | + SslContextFactory.Server sslContextFactory) { |
| 517 | + Timer timer = new Timer("SSL Certificates Store Monitor", true); |
| 518 | + // |
| 519 | + // The Jetty SSLContextFactory provides a 'reload' method which will reload both |
| 520 | + // truststore and keystore certificates. |
| 521 | + // |
| 522 | + timer.schedule(new FileMonitoringTimerTask( |
| 523 | + Paths.get(keyStore), |
| 524 | + path -> { |
| 525 | + LOG.info("Reloading certificates from store keystore " + keyStore); |
| 526 | + try { |
| 527 | + sslContextFactory.reload(factory -> { }); |
| 528 | + } catch (Exception ex) { |
| 529 | + LOG.error("Failed to reload SSL keystore certificates", ex); |
| 530 | + } |
| 531 | + },null), |
| 532 | + reloadInterval, |
| 533 | + reloadInterval |
| 534 | + ); |
| 535 | + return timer; |
| 536 | + } |
| 537 | + |
499 | 538 | } |
500 | 539 |
|
501 | 540 | /** |
@@ -602,6 +641,7 @@ private HttpServer(final Builder b) throws IOException { |
602 | 641 | this.authenticationEnabled = b.securityEnabled; |
603 | 642 | initializeWebServer(b.name, b.hostName, b.conf, b.pathSpecs, b); |
604 | 643 | this.webServer.setHandler(buildGzipHandler(this.webServer.getHandler())); |
| 644 | + this.configurationChangeMonitor = b.configurationChangeMonitor; |
605 | 645 | } |
606 | 646 |
|
607 | 647 | private void initializeWebServer(String name, String hostName, Configuration conf, |
@@ -1291,6 +1331,15 @@ void openListeners() throws Exception { |
1291 | 1331 | */ |
1292 | 1332 | public void stop() throws Exception { |
1293 | 1333 | MultiException exception = null; |
| 1334 | + if (this.configurationChangeMonitor.isPresent()) { |
| 1335 | + try { |
| 1336 | + this.configurationChangeMonitor.get().cancel(); |
| 1337 | + } catch (Exception e) { |
| 1338 | + LOG.error("Error while canceling configuration monitoring timer for webapp" |
| 1339 | + + webAppContext.getDisplayName(), e); |
| 1340 | + exception = addMultiException(exception, e); |
| 1341 | + } |
| 1342 | + } |
1294 | 1343 | for (ListenerInfo li : listeners) { |
1295 | 1344 | if (!li.isManaged) { |
1296 | 1345 | continue; |
|
0 commit comments