Skip to content

Commit 263ac91

Browse files
committed
HBASE-28911: Automatic SSL keystore reloading for HttpServer
implement the same as HADOOP-16524
1 parent 402aa92 commit 263ac91

File tree

1 file changed

+49
-0
lines changed

1 file changed

+49
-0
lines changed

hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import java.util.HashMap;
3636
import java.util.List;
3737
import java.util.Map;
38+
import java.util.Optional;
39+
import java.util.Timer;
3840
import java.util.stream.Collectors;
3941
import javax.servlet.Filter;
4042
import javax.servlet.FilterChain;
@@ -62,6 +64,8 @@
6264
import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
6365
import org.apache.hadoop.security.authorize.AccessControlList;
6466
import org.apache.hadoop.security.authorize.ProxyUsers;
67+
import org.apache.hadoop.security.ssl.FileBasedKeyStoresFactory;
68+
import org.apache.hadoop.security.ssl.FileMonitoringTimerTask;
6569
import org.apache.hadoop.util.Shell;
6670
import org.apache.hadoop.util.StringUtils;
6771
import org.apache.yetus.audience.InterfaceAudience;
@@ -179,6 +183,7 @@ public class HttpServer implements FilterContainer {
179183
.build();
180184

181185
private final AccessControlList adminsAcl;
186+
private final Optional<Timer> configurationChangeMonitor;
182187

183188
protected final Server webServer;
184189
protected String appDir;
@@ -244,6 +249,8 @@ public static class Builder {
244249
private String kerberosNameRulesKey;
245250
private String signatureSecretFileKey;
246251

252+
private Optional<Timer> configurationChangeMonitor = Optional.empty();
253+
247254
/**
248255
* @see #setAppDir(String)
249256
* @deprecated Since 0.99.0. Use builder pattern via {@link #setAppDir(String)} instead.
@@ -466,6 +473,15 @@ public HttpServer build() throws IOException {
466473
LOG.debug("Excluded SSL Cipher List:" + excludeCiphers);
467474
}
468475

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+
469485
listener = new ServerConnector(server.webServer,
470486
new SslConnectionFactory(sslCtxFactory, HttpVersion.HTTP_1_1.toString()),
471487
new HttpConnectionFactory(httpsConfig));
@@ -496,6 +512,29 @@ public HttpServer build() throws IOException {
496512

497513
}
498514

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+
499538
}
500539

501540
/**
@@ -602,6 +641,7 @@ private HttpServer(final Builder b) throws IOException {
602641
this.authenticationEnabled = b.securityEnabled;
603642
initializeWebServer(b.name, b.hostName, b.conf, b.pathSpecs, b);
604643
this.webServer.setHandler(buildGzipHandler(this.webServer.getHandler()));
644+
this.configurationChangeMonitor = b.configurationChangeMonitor;
605645
}
606646

607647
private void initializeWebServer(String name, String hostName, Configuration conf,
@@ -1291,6 +1331,15 @@ void openListeners() throws Exception {
12911331
*/
12921332
public void stop() throws Exception {
12931333
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+
}
12941343
for (ListenerInfo li : listeners) {
12951344
if (!li.isManaged) {
12961345
continue;

0 commit comments

Comments
 (0)