2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org
.apache
.hadoop
.hbase
.rest
;
20 import static org
.junit
.Assert
.assertEquals
;
22 import java
.lang
.reflect
.Method
;
23 import java
.security
.KeyPair
;
24 import java
.security
.cert
.X509Certificate
;
25 import java
.util
.Optional
;
26 import org
.apache
.hadoop
.conf
.Configuration
;
27 import org
.apache
.hadoop
.hbase
.HBaseClassTestRule
;
28 import org
.apache
.hadoop
.hbase
.HBaseTestingUtil
;
29 import org
.apache
.hadoop
.hbase
.http
.ssl
.KeyStoreTestUtil
;
30 import org
.apache
.hadoop
.hbase
.rest
.client
.Client
;
31 import org
.apache
.hadoop
.hbase
.rest
.client
.Cluster
;
32 import org
.apache
.hadoop
.hbase
.rest
.client
.Response
;
33 import org
.apache
.hadoop
.hbase
.testclassification
.MediumTests
;
34 import org
.apache
.hadoop
.hbase
.testclassification
.RestTests
;
35 import org
.junit
.After
;
36 import org
.junit
.AfterClass
;
37 import org
.junit
.Before
;
38 import org
.junit
.BeforeClass
;
39 import org
.junit
.ClassRule
;
40 import org
.junit
.Test
;
41 import org
.junit
.experimental
.categories
.Category
;
42 import org
.slf4j
.Logger
;
43 import org
.slf4j
.LoggerFactory
;
45 @Category({ RestTests
.class, MediumTests
.class})
46 public class TestRESTServerSSL
{
49 public static final HBaseClassTestRule CLASS_RULE
=
50 HBaseClassTestRule
.forClass(TestRESTServerSSL
.class);
52 private static final Logger LOG
= LoggerFactory
.getLogger(TestRESTServerSSL
.class);
54 private static final String KEY_STORE_PASSWORD
= "myKSPassword";
55 private static final String TRUST_STORE_PASSWORD
= "myTSPassword";
57 private static final HBaseTestingUtil TEST_UTIL
= new HBaseTestingUtil();
58 private static final HBaseRESTTestingUtility REST_TEST_UTIL
= new HBaseRESTTestingUtility();
59 private static Client sslClient
;
60 private static File keyDir
;
61 private Configuration conf
;
63 // Workaround for jdk8 292 bug. See https://github.com/bcgit/bc-java/issues/941
64 // Below is a workaround described in above URL. Issue fingered first in comments in
65 // HBASE-25920 Support Hadoop 3.3.1
66 private static void initializeAlgorithmId() {
68 Class
<?
> algoId
= Class
.forName("sun.security.x509.AlgorithmId");
69 Method method
= algoId
.getMethod("get", String
.class);
70 method
.setAccessible(true);
71 method
.invoke(null, "PBEWithSHA1AndDESede");
72 } catch (Exception e
) {
73 LOG
.warn("failed to initialize AlgorithmId", e
);
78 public static void beforeClass() throws Exception
{
79 initializeAlgorithmId();
80 keyDir
= initKeystoreDir();
81 KeyPair keyPair
= KeyStoreTestUtil
.generateKeyPair("RSA");
82 X509Certificate serverCertificate
= KeyStoreTestUtil
.generateCertificate(
83 "CN=localhost, O=server", keyPair
, 30, "SHA1withRSA");
85 generateTrustStore("jks", serverCertificate
);
86 generateTrustStore("jceks", serverCertificate
);
87 generateTrustStore("pkcs12", serverCertificate
);
89 generateKeyStore("jks", keyPair
, serverCertificate
);
90 generateKeyStore("jceks", keyPair
, serverCertificate
);
91 generateKeyStore("pkcs12", keyPair
, serverCertificate
);
93 TEST_UTIL
.startMiniCluster();
97 public static void afterClass() throws Exception
{
98 // this will also delete the generated test keystore / teststore files,
99 // as we were placing them under the dataTestDir used by the minicluster
100 TEST_UTIL
.shutdownMiniCluster();
104 public void beforeEachTest() {
105 conf
= new Configuration(TEST_UTIL
.getConfiguration());
106 conf
.set(Constants
.REST_SSL_ENABLED
, "true");
107 conf
.set(Constants
.REST_SSL_KEYSTORE_KEYPASSWORD
, KEY_STORE_PASSWORD
);
108 conf
.set(Constants
.REST_SSL_KEYSTORE_PASSWORD
, KEY_STORE_PASSWORD
);
109 conf
.set(Constants
.REST_SSL_TRUSTSTORE_PASSWORD
, TRUST_STORE_PASSWORD
);
113 public void tearDownAfterTest() {
114 REST_TEST_UTIL
.shutdownServletContainer();
118 public void testSslConnection() throws Exception
{
119 startRESTServerWithDefaultKeystoreType();
121 Response response
= sslClient
.get("/version", Constants
.MIMETYPE_TEXT
);
122 assertEquals(200, response
.getCode());
124 // Default security headers
125 assertEquals("max-age=63072000;includeSubDomains;preload",
126 response
.getHeader("Strict-Transport-Security"));
127 assertEquals("default-src https: data: 'unsafe-inline' 'unsafe-eval'",
128 response
.getHeader("Content-Security-Policy"));
131 @Test(expected
= org
.apache
.http
.client
.ClientProtocolException
.class)
132 public void testNonSslClientDenied() throws Exception
{
133 startRESTServerWithDefaultKeystoreType();
135 Cluster localCluster
= new Cluster().add("localhost", REST_TEST_UTIL
.getServletPort());
136 Client nonSslClient
= new Client(localCluster
, false);
138 nonSslClient
.get("/version");
142 public void testSslConnectionUsingKeystoreFormatJKS() throws Exception
{
143 startRESTServer("jks");
145 Response response
= sslClient
.get("/version", Constants
.MIMETYPE_TEXT
);
146 assertEquals(200, response
.getCode());
150 public void testSslConnectionUsingKeystoreFormatJCEKS() throws Exception
{
151 startRESTServer("jceks");
153 Response response
= sslClient
.get("/version", Constants
.MIMETYPE_TEXT
);
154 assertEquals(200, response
.getCode());
158 public void testSslConnectionUsingKeystoreFormatPKCS12() throws Exception
{
159 startRESTServer("pkcs12");
161 Response response
= sslClient
.get("/version", Constants
.MIMETYPE_TEXT
);
162 assertEquals(200, response
.getCode());
167 private static File
initKeystoreDir() {
168 String dataTestDir
= TEST_UTIL
.getDataTestDir().toString();
169 File keystoreDir
= new File(dataTestDir
, TestRESTServerSSL
.class.getSimpleName() + "_keys");
170 keystoreDir
.mkdirs();
174 private static void generateKeyStore(String keyStoreType
, KeyPair keyPair
,
175 X509Certificate serverCertificate
) throws Exception
{
176 String keyStorePath
= getKeystoreFilePath(keyStoreType
);
177 KeyStoreTestUtil
.createKeyStore(keyStorePath
, KEY_STORE_PASSWORD
, KEY_STORE_PASSWORD
,
178 "serverKS", keyPair
.getPrivate(), serverCertificate
, keyStoreType
);
181 private static void generateTrustStore(String trustStoreType
, X509Certificate serverCertificate
)
183 String trustStorePath
= getTruststoreFilePath(trustStoreType
);
184 KeyStoreTestUtil
.createTrustStore(trustStorePath
, TRUST_STORE_PASSWORD
, "serverTS",
185 serverCertificate
, trustStoreType
);
188 private static String
getKeystoreFilePath(String keyStoreType
) {
189 return String
.format("%s/serverKS.%s", keyDir
.getAbsolutePath(), keyStoreType
);
192 private static String
getTruststoreFilePath(String trustStoreType
) {
193 return String
.format("%s/serverTS.%s", keyDir
.getAbsolutePath(), trustStoreType
);
196 private void startRESTServerWithDefaultKeystoreType() throws Exception
{
197 conf
.set(Constants
.REST_SSL_KEYSTORE_STORE
, getKeystoreFilePath("jks"));
198 conf
.set(Constants
.REST_SSL_TRUSTSTORE_STORE
, getTruststoreFilePath("jks"));
200 REST_TEST_UTIL
.startServletContainer(conf
);
201 Cluster localCluster
= new Cluster().add("localhost", REST_TEST_UTIL
.getServletPort());
202 sslClient
= new Client(localCluster
, getTruststoreFilePath("jks"),
203 Optional
.of(TRUST_STORE_PASSWORD
), Optional
.empty());
206 private void startRESTServer(String storeType
) throws Exception
{
207 conf
.set(Constants
.REST_SSL_KEYSTORE_TYPE
, storeType
);
208 conf
.set(Constants
.REST_SSL_KEYSTORE_STORE
, getKeystoreFilePath(storeType
));
210 conf
.set(Constants
.REST_SSL_TRUSTSTORE_STORE
, getTruststoreFilePath(storeType
));
211 conf
.set(Constants
.REST_SSL_TRUSTSTORE_TYPE
, storeType
);
213 REST_TEST_UTIL
.startServletContainer(conf
);
214 Cluster localCluster
= new Cluster().add("localhost", REST_TEST_UTIL
.getServletPort());
215 sslClient
= new Client(localCluster
, getTruststoreFilePath(storeType
),
216 Optional
.of(TRUST_STORE_PASSWORD
), Optional
.of(storeType
));