001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.rest;
019
020import static org.junit.Assert.assertEquals;
021import static org.junit.Assert.assertTrue;
022
023import java.io.File;
024import java.security.KeyPair;
025import java.security.cert.X509Certificate;
026import java.util.Optional;
027import org.apache.hadoop.conf.Configuration;
028import org.apache.hadoop.hbase.HBaseClassTestRule;
029import org.apache.hadoop.hbase.HBaseTestingUtility;
030import org.apache.hadoop.hbase.http.ssl.KeyStoreTestUtil;
031import org.apache.hadoop.hbase.rest.client.Client;
032import org.apache.hadoop.hbase.rest.client.Cluster;
033import org.apache.hadoop.hbase.rest.client.Response;
034import org.apache.hadoop.hbase.testclassification.MediumTests;
035import org.apache.hadoop.hbase.testclassification.RestTests;
036import org.junit.After;
037import org.junit.AfterClass;
038import org.junit.Before;
039import org.junit.BeforeClass;
040import org.junit.ClassRule;
041import org.junit.Test;
042import org.junit.experimental.categories.Category;
043
044@Category({ RestTests.class, MediumTests.class})
045public class TestRESTServerSSL {
046
047  @ClassRule
048  public static final HBaseClassTestRule CLASS_RULE =
049      HBaseClassTestRule.forClass(TestRESTServerSSL.class);
050
051  private static final String KEY_STORE_PASSWORD = "myKSPassword";
052  private static final String TRUST_STORE_PASSWORD = "myTSPassword";
053
054  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
055  private static final HBaseRESTTestingUtility REST_TEST_UTIL = new HBaseRESTTestingUtility();
056  private static Client sslClient;
057  private static File keyDir;
058  private Configuration conf;
059
060  @BeforeClass
061  public static void beforeClass() throws Exception {
062    keyDir = initKeystoreDir();
063    KeyPair keyPair = KeyStoreTestUtil.generateKeyPair("RSA");
064    X509Certificate serverCertificate = KeyStoreTestUtil.generateCertificate(
065      "CN=localhost, O=server", keyPair, 30, "SHA1withRSA");
066
067    generateTrustStore("jks", serverCertificate);
068    generateTrustStore("jceks", serverCertificate);
069    generateTrustStore("pkcs12", serverCertificate);
070
071    generateKeyStore("jks", keyPair, serverCertificate);
072    generateKeyStore("jceks", keyPair, serverCertificate);
073    generateKeyStore("pkcs12", keyPair, serverCertificate);
074
075    TEST_UTIL.startMiniCluster();
076  }
077
078  @AfterClass
079  public static void afterClass() throws Exception {
080    // this will also delete the generated test keystore / teststore files,
081    // as we were placing them under the dataTestDir used by the minicluster
082    TEST_UTIL.shutdownMiniCluster();
083  }
084
085  @Before
086  public void beforeEachTest() {
087    conf = new Configuration(TEST_UTIL.getConfiguration());
088    conf.set(Constants.REST_SSL_ENABLED, "true");
089    conf.set(Constants.REST_SSL_KEYSTORE_KEYPASSWORD, KEY_STORE_PASSWORD);
090    conf.set(Constants.REST_SSL_KEYSTORE_PASSWORD, KEY_STORE_PASSWORD);
091    conf.set(Constants.REST_SSL_TRUSTSTORE_PASSWORD, TRUST_STORE_PASSWORD);
092  }
093
094  @After
095  public void tearDownAfterTest() {
096    REST_TEST_UTIL.shutdownServletContainer();
097  }
098
099  @Test
100  public void testSslConnection() throws Exception {
101    startRESTServerWithDefaultKeystoreType();
102
103    Response response = sslClient.get("/version", Constants.MIMETYPE_TEXT);
104    assertEquals(200, response.getCode());
105  }
106
107  @Test(expected = org.apache.http.client.ClientProtocolException.class)
108  public void testNonSslClientDenied() throws Exception {
109    startRESTServerWithDefaultKeystoreType();
110
111    Cluster localCluster = new Cluster().add("localhost", REST_TEST_UTIL.getServletPort());
112    Client nonSslClient = new Client(localCluster, false);
113
114    nonSslClient.get("/version");
115  }
116
117  @Test
118  public void testSslConnectionUsingKeystoreFormatJKS() throws Exception {
119    startRESTServer("jks");
120
121    Response response = sslClient.get("/version", Constants.MIMETYPE_TEXT);
122    assertEquals(200, response.getCode());
123  }
124
125  @Test
126  public void testSslConnectionUsingKeystoreFormatJCEKS() throws Exception {
127    startRESTServer("jceks");
128
129    Response response = sslClient.get("/version", Constants.MIMETYPE_TEXT);
130    assertEquals(200, response.getCode());
131  }
132
133  @Test
134  public void testSslConnectionUsingKeystoreFormatPKCS12() throws Exception {
135    startRESTServer("pkcs12");
136
137    Response response = sslClient.get("/version", Constants.MIMETYPE_TEXT);
138    assertEquals(200, response.getCode());
139  }
140
141
142
143  private static File initKeystoreDir() {
144    String dataTestDir = TEST_UTIL.getDataTestDir().toString();
145    File keystoreDir = new File(dataTestDir, TestRESTServerSSL.class.getSimpleName() + "_keys");
146    keystoreDir.mkdirs();
147    return keystoreDir;
148  }
149
150  private static void generateKeyStore(String keyStoreType, KeyPair keyPair,
151    X509Certificate serverCertificate) throws Exception {
152    String keyStorePath = getKeystoreFilePath(keyStoreType);
153    KeyStoreTestUtil.createKeyStore(keyStorePath, KEY_STORE_PASSWORD, KEY_STORE_PASSWORD,
154      "serverKS", keyPair.getPrivate(), serverCertificate, keyStoreType);
155  }
156
157  private static void generateTrustStore(String trustStoreType, X509Certificate serverCertificate)
158    throws Exception {
159    String trustStorePath = getTruststoreFilePath(trustStoreType);
160    KeyStoreTestUtil.createTrustStore(trustStorePath, TRUST_STORE_PASSWORD, "serverTS",
161      serverCertificate, trustStoreType);
162  }
163
164  private static String getKeystoreFilePath(String keyStoreType) {
165    return String.format("%s/serverKS.%s", keyDir.getAbsolutePath(), keyStoreType);
166  }
167
168  private static String getTruststoreFilePath(String trustStoreType) {
169    return String.format("%s/serverTS.%s", keyDir.getAbsolutePath(), trustStoreType);
170  }
171
172  private void startRESTServerWithDefaultKeystoreType() throws Exception {
173    conf.set(Constants.REST_SSL_KEYSTORE_STORE, getKeystoreFilePath("jks"));
174    conf.set(Constants.REST_SSL_TRUSTSTORE_STORE, getTruststoreFilePath("jks"));
175
176    REST_TEST_UTIL.startServletContainer(conf);
177    Cluster localCluster = new Cluster().add("localhost", REST_TEST_UTIL.getServletPort());
178    sslClient = new Client(localCluster, getTruststoreFilePath("jks"),
179      Optional.of(TRUST_STORE_PASSWORD), Optional.empty());
180  }
181
182  private void startRESTServer(String storeType) throws Exception {
183    conf.set(Constants.REST_SSL_KEYSTORE_TYPE, storeType);
184    conf.set(Constants.REST_SSL_KEYSTORE_STORE, getKeystoreFilePath(storeType));
185
186    conf.set(Constants.REST_SSL_TRUSTSTORE_STORE, getTruststoreFilePath(storeType));
187    conf.set(Constants.REST_SSL_TRUSTSTORE_TYPE, storeType);
188
189    REST_TEST_UTIL.startServletContainer(conf);
190    Cluster localCluster = new Cluster().add("localhost", REST_TEST_UTIL.getServletPort());
191    sslClient = new Client(localCluster, getTruststoreFilePath(storeType),
192                           Optional.of(TRUST_STORE_PASSWORD), Optional.of(storeType));
193  }
194
195}