Skip to content

Commit a2975d2

Browse files
committed
HADOOP-16524. Automatic keystore reloading for HttpServer2
Reapply of issue reverted first because it caused yarn failures and then again because the commit message was incorrectly formatted.
1 parent 5183aae commit a2975d2

File tree

10 files changed

+715
-197
lines changed

10 files changed

+715
-197
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/http/HttpServer2.java

Lines changed: 57 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,17 @@
2727
import java.net.MalformedURLException;
2828
import java.net.URI;
2929
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;
3531
import java.util.List;
32+
import java.util.ArrayList;
3633
import java.util.Map;
34+
import java.util.HashMap;
35+
import java.util.Collections;
36+
import java.util.Optional;
3737
import java.util.Properties;
38+
import java.util.Enumeration;
39+
import java.util.Arrays;
40+
import java.util.Timer;
3841
import java.util.regex.Matcher;
3942
import java.util.regex.Pattern;
4043

@@ -75,6 +78,8 @@
7578
import org.apache.hadoop.security.authentication.server.PseudoAuthenticationHandler;
7679
import org.apache.hadoop.security.authentication.util.SignerSecretProvider;
7780
import org.apache.hadoop.security.authorize.AccessControlList;
81+
import org.apache.hadoop.security.ssl.FileBasedKeyStoresFactory;
82+
import org.apache.hadoop.security.ssl.FileMonitoringTimerTask;
7883
import org.apache.hadoop.security.ssl.SSLFactory;
7984
import org.apache.hadoop.util.ReflectionUtils;
8085
import org.apache.hadoop.util.Shell;
@@ -186,6 +191,7 @@ public final class HttpServer2 implements FilterContainer {
186191
static final String STATE_DESCRIPTION_ALIVE = " - alive";
187192
static final String STATE_DESCRIPTION_NOT_LIVE = " - not live";
188193
private final SignerSecretProvider secretProvider;
194+
private final Optional<java.util.Timer> configurationChangeMonitor;
189195
private XFrameOption xFrameOption;
190196
private boolean xFrameOptionIsEnabled;
191197
public static final String HTTP_HEADER_PREFIX = "hadoop.http.header.";
@@ -244,6 +250,8 @@ public static class Builder {
244250

245251
private boolean sniHostCheckEnabled;
246252

253+
private Optional<Timer> configurationChangeMonitor = Optional.empty();
254+
247255
public Builder setName(String name){
248256
this.name = name;
249257
return this;
@@ -574,12 +582,45 @@ private ServerConnector createHttpsChannelConnector(
574582
}
575583

576584
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+
577595
conn.addFirstConnectionFactory(new SslConnectionFactory(sslContextFactory,
578596
HttpVersion.HTTP_1_1.asString()));
579597

580598
return conn;
581599
}
582600

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+
583624
private void setEnabledProtocols(SslContextFactory sslContextFactory) {
584625
String enabledProtocols = conf.get(SSLFactory.SSL_ENABLED_PROTOCOLS_KEY,
585626
SSLFactory.SSL_ENABLED_PROTOCOLS_DEFAULT);
@@ -622,6 +663,7 @@ private HttpServer2(final Builder b) throws IOException {
622663
this.webAppContext = createWebAppContext(b, adminsAcl, appDir);
623664
this.xFrameOptionIsEnabled = b.xFrameEnabled;
624665
this.xFrameOption = b.xFrameOption;
666+
this.configurationChangeMonitor = b.configurationChangeMonitor;
625667

626668
try {
627669
this.secretProvider =
@@ -1420,6 +1462,16 @@ void openListeners() throws Exception {
14201462
*/
14211463
public void stop() throws Exception {
14221464
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+
}
14231475
for (ServerConnector c : listeners) {
14241476
try {
14251477
c.close();

0 commit comments

Comments
 (0)