This is an automated email from the ASF dual-hosted git repository.
meszibalu pushed a commit to branch branch-2.4
in repository https://gitbox.apache.org/repos/asf/hbase.git
The following commit(s) were added to refs/heads/branch-2.4 by this push:
new 1d0d70e HBASE-23303 Add default security headers if SSL is enabled
(#4128)
1d0d70e is described below
commit 1d0d70e641f6d8b58d039b5a5a9c79295ef237bd
Author: Andor Molnár <[email protected]>
AuthorDate: Wed Mar 2 15:25:57 2022 +0100
HBASE-23303 Add default security headers if SSL is enabled (#4128)
Signed-off-by: Balazs Meszaros <[email protected]>
---
.../main/java/org/apache/hadoop/hbase/http/HttpServer.java | 4 +++-
.../apache/hadoop/hbase/http/SecurityHeadersFilter.java | 12 +++++-------
.../org/apache/hadoop/hbase/http/TestSSLHttpServer.java | 14 ++++++++++++++
.../main/java/org/apache/hadoop/hbase/rest/RESTServer.java | 9 ++++++---
.../org/apache/hadoop/hbase/rest/TestRESTServerSSL.java | 8 ++++++--
5 files changed, 34 insertions(+), 13 deletions(-)
diff --git
a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
index d534e15..884780c 100644
--- a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
+++ b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/HttpServer.java
@@ -626,9 +626,11 @@ public class HttpServer implements FilterContainer {
ClickjackingPreventionFilter.class.getName(),
ClickjackingPreventionFilter.getDefaultParameters(conf));
+ HttpConfig httpConfig = new HttpConfig(conf);
+
addGlobalFilter("securityheaders",
SecurityHeadersFilter.class.getName(),
- SecurityHeadersFilter.getDefaultParameters(conf));
+ SecurityHeadersFilter.getDefaultParameters(conf,
httpConfig.isSecure()));
// But security needs to be enabled prior to adding the other servlets
if (authenticationEnabled) {
diff --git
a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/SecurityHeadersFilter.java
b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/SecurityHeadersFilter.java
index b83fef1..f00f2a1 100644
---
a/hbase-http/src/main/java/org/apache/hadoop/hbase/http/SecurityHeadersFilter.java
+++
b/hbase-http/src/main/java/org/apache/hadoop/hbase/http/SecurityHeadersFilter.java
@@ -39,8 +39,8 @@ import org.slf4j.LoggerFactory;
public class SecurityHeadersFilter implements Filter {
private static final Logger LOG =
LoggerFactory.getLogger(SecurityHeadersFilter.class);
- private static final String DEFAULT_HSTS = "";
- private static final String DEFAULT_CSP = "";
+ private static final String DEFAULT_HSTS =
"max-age=63072000;includeSubDomains;preload";
+ private static final String DEFAULT_CSP = "default-src https: data:
'unsafe-inline' 'unsafe-eval'";
private FilterConfig filterConfig;
@Override
@@ -70,12 +70,10 @@ public class SecurityHeadersFilter implements Filter {
public void destroy() {
}
- public static Map<String, String> getDefaultParameters(Configuration conf) {
+ public static Map<String, String> getDefaultParameters(Configuration conf,
boolean isSecure) {
Map<String, String> params = new HashMap<>();
- params.put("hsts", conf.get("hbase.http.filter.hsts.value",
- DEFAULT_HSTS));
- params.put("csp", conf.get("hbase.http.filter.csp.value",
- DEFAULT_CSP));
+ params.put("hsts", conf.get("hbase.http.filter.hsts.value", isSecure ?
DEFAULT_HSTS : ""));
+ params.put("csp", conf.get("hbase.http.filter.csp.value", isSecure ?
DEFAULT_CSP : ""));
return params;
}
}
diff --git
a/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestSSLHttpServer.java
b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestSSLHttpServer.java
index 382118d..2b72793 100644
---
a/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestSSLHttpServer.java
+++
b/hbase-http/src/test/java/org/apache/hadoop/hbase/http/TestSSLHttpServer.java
@@ -19,9 +19,11 @@ package org.apache.hadoop.hbase.http;
import java.io.ByteArrayOutputStream;
import java.io.File;
+import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
+import java.security.GeneralSecurityException;
import javax.net.ssl.HttpsURLConnection;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileUtil;
@@ -73,6 +75,7 @@ public class TestSSLHttpServer extends
HttpServerFunctionalTest {
serverConf = HTU.getConfiguration();
serverConf.setInt(HttpServer.HTTP_MAX_THREADS, TestHttpServer.MAX_THREADS);
+ serverConf.setBoolean(ServerConfigurationKeys.HBASE_SSL_ENABLED_KEY, true);
keystoresDir = new File(HTU.getDataTestDir("keystore").toString());
keystoresDir.mkdirs();
@@ -122,6 +125,17 @@ public class TestSSLHttpServer extends
HttpServerFunctionalTest {
"/echo?a=b&c<=d&e=>")));
}
+ @Test
+ public void testSecurityHeaders() throws IOException,
GeneralSecurityException {
+ HttpsURLConnection conn = (HttpsURLConnection) baseUrl.openConnection();
+ conn.setSSLSocketFactory(clientSslFactory.createSSLSocketFactory());
+ assertEquals(HttpsURLConnection.HTTP_OK, conn.getResponseCode());
+ assertEquals("max-age=63072000;includeSubDomains;preload",
+ conn.getHeaderField("Strict-Transport-Security"));
+ assertEquals("default-src https: data: 'unsafe-inline' 'unsafe-eval'",
+ conn.getHeaderField("Content-Security-Policy"));
+ }
+
private static String readOut(URL url) throws Exception {
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(clientSslFactory.createSSLSocketFactory());
diff --git
a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
index 40dfa90..1ff59c24 100644
--- a/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
+++ b/hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/RESTServer.java
@@ -148,11 +148,12 @@ public class RESTServer implements Constants {
ctxHandler.addFilter(holder, PATH_SPEC_ANY,
EnumSet.allOf(DispatcherType.class));
}
- private void addSecurityHeadersFilter(ServletContextHandler ctxHandler,
Configuration conf) {
+ private void addSecurityHeadersFilter(ServletContextHandler ctxHandler,
+ Configuration conf, boolean isSecure) {
FilterHolder holder = new FilterHolder();
holder.setName("securityheaders");
holder.setClassName(SecurityHeadersFilter.class.getName());
- holder.setInitParameters(SecurityHeadersFilter.getDefaultParameters(conf));
+ holder.setInitParameters(SecurityHeadersFilter.getDefaultParameters(conf,
isSecure));
ctxHandler.addFilter(holder, PATH_SPEC_ANY,
EnumSet.allOf(DispatcherType.class));
}
@@ -307,7 +308,9 @@ public class RESTServer implements Constants {
httpConfig.setSendDateHeader(false);
ServerConnector serverConnector;
+ boolean isSecure = false;
if (conf.getBoolean(REST_SSL_ENABLED, false)) {
+ isSecure = true;
HttpConfiguration httpsConfig = new HttpConfiguration(httpConfig);
httpsConfig.addCustomizer(new SecureRequestCustomizer());
@@ -395,7 +398,7 @@ public class RESTServer implements Constants {
}
addCSRFFilter(ctxHandler, conf);
addClickjackingPreventionFilter(ctxHandler, conf);
- addSecurityHeadersFilter(ctxHandler, conf);
+ addSecurityHeadersFilter(ctxHandler, conf, isSecure);
HttpServerUtil.constrainHttpMethods(ctxHandler, servlet.getConfiguration()
.getBoolean(REST_HTTP_ALLOW_OPTIONS_METHOD,
REST_HTTP_ALLOW_OPTIONS_METHOD_DEFAULT));
diff --git
a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestRESTServerSSL.java
b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestRESTServerSSL.java
index ea13360..1a55996 100644
---
a/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestRESTServerSSL.java
+++
b/hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestRESTServerSSL.java
@@ -18,8 +18,6 @@
package org.apache.hadoop.hbase.rest;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
import java.io.File;
import java.security.KeyPair;
import java.security.cert.X509Certificate;
@@ -107,6 +105,12 @@ public class TestRESTServerSSL {
Response response = sslClient.get("/version", Constants.MIMETYPE_TEXT);
assertEquals(200, response.getCode());
+
+ // Default security headers
+ assertEquals("max-age=63072000;includeSubDomains;preload",
+ response.getHeader("Strict-Transport-Security"));
+ assertEquals("default-src https: data: 'unsafe-inline' 'unsafe-eval'",
+ response.getHeader("Content-Security-Policy"));
}
@Test(expected = org.apache.http.client.ClientProtocolException.class)