blob: 3e8241956d6fd90bd3f83d45c28c8090366d9e86 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.drill.exec.rpc.user.security;
import com.typesafe.config.ConfigValueFactory;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import junit.framework.TestCase;
import org.apache.drill.common.config.DrillConfig;
import org.apache.drill.common.config.DrillProperties;
import org.apache.drill.exec.ExecConstants;
import org.apache.drill.test.BaseTestQuery;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import java.io.File;
import java.io.FileOutputStream;
import java.net.InetAddress;
import java.security.KeyStore;
import java.util.Properties;
import static junit.framework.TestCase.fail;
import static org.junit.Assert.assertEquals;
public class TestUserBitSSL extends BaseTestQuery {
private static DrillConfig newConfig;
private static Properties initProps; // initial client properties
private static ClassLoader classLoader;
private static String ksPath;
private static String tsPath;
private static String emptyTSPath;
private static String unknownKsPath;
@BeforeClass
public static void setupTest() throws Exception {
// Create a new DrillConfig
classLoader = TestUserBitSSL.class.getClassLoader();
ksPath = new File(classLoader.getResource("ssl/keystore.ks").getFile()).getAbsolutePath();
unknownKsPath = new File(classLoader.getResource("ssl/unknownkeystore.ks").getFile()).getAbsolutePath();
tsPath = new File(classLoader.getResource("ssl/truststore.ks").getFile()).getAbsolutePath();
emptyTSPath = new File(classLoader.getResource("ssl/emptytruststore.ks").getFile()).getAbsolutePath();
newConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.SSL_USE_HADOOP_CONF,
ConfigValueFactory.fromAnyRef(false))
.withValue(ExecConstants.USER_SSL_ENABLED,
ConfigValueFactory.fromAnyRef(true))
.withValue(ExecConstants.SSL_KEYSTORE_TYPE,
ConfigValueFactory.fromAnyRef("JKS"))
.withValue(ExecConstants.SSL_KEYSTORE_PATH,
ConfigValueFactory.fromAnyRef(ksPath))
.withValue(ExecConstants.SSL_KEYSTORE_PASSWORD,
ConfigValueFactory.fromAnyRef("drill123"))
.withValue(ExecConstants.SSL_KEY_PASSWORD,
ConfigValueFactory.fromAnyRef("drill123"))
.withValue(ExecConstants.SSL_TRUSTSTORE_TYPE,
ConfigValueFactory.fromAnyRef("JKS"))
.withValue(ExecConstants.SSL_TRUSTSTORE_PATH,
ConfigValueFactory.fromAnyRef(tsPath))
.withValue(ExecConstants.SSL_TRUSTSTORE_PASSWORD,
ConfigValueFactory.fromAnyRef("drill123"))
.withValue(ExecConstants.SSL_PROTOCOL,
ConfigValueFactory.fromAnyRef("TLSv1.3")));
initProps = new Properties();
initProps.setProperty(DrillProperties.ENABLE_TLS, "true");
initProps.setProperty(DrillProperties.TRUSTSTORE_PATH, tsPath);
initProps.setProperty(DrillProperties.TRUSTSTORE_PASSWORD, "drill123");
initProps.setProperty(DrillProperties.DISABLE_HOST_VERIFICATION, "true");
// Start an SSL enabled cluster
updateTestCluster(1, newConfig, initProps);
}
@AfterClass
public static void cleanTest() throws Exception {
DrillConfig restoreConfig =
new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties()));
updateTestCluster(1, restoreConfig);
}
@Test
public void testSSLConnection() throws Exception {
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.ENABLE_TLS, "true");
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PATH, tsPath);
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PASSWORD, "drill123");
connectionProps.setProperty(DrillProperties.DISABLE_HOST_VERIFICATION, "true");
try {
updateClient(connectionProps);
} catch (Exception e) {
TestCase.fail( new StringBuilder()
.append("SSL Connection failed with exception [" )
.append( e.getMessage() )
.append("]")
.toString());
}
}
@Test
public void testSSLConnectionWithKeystore() throws Exception {
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.ENABLE_TLS, "true");
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PATH, ksPath);
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PASSWORD, "drill123");
connectionProps.setProperty(DrillProperties.DISABLE_HOST_VERIFICATION, "true");
try {
updateClient(connectionProps);
} catch (Exception e) {
TestCase.fail( new StringBuilder()
.append("SSL Connection failed with exception [" )
.append( e.getMessage() )
.append("]")
.toString());
}
}
@Test
public void testSSLConnectionFailBadTrustStore() throws Exception {
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.ENABLE_TLS, "true");
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PATH, ""); // NO truststore
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PASSWORD, "drill123");
connectionProps.setProperty(DrillProperties.DISABLE_HOST_VERIFICATION, "true");
boolean failureCaught = false;
try {
updateClient(connectionProps);
} catch (Exception e) {
failureCaught = true;
}
assertEquals(failureCaught, true);
}
@Test
public void testSSLConnectionFailBadPassword() throws Exception {
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.ENABLE_TLS, "true");
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PATH, tsPath);
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PASSWORD, "bad_password");
connectionProps.setProperty(DrillProperties.DISABLE_HOST_VERIFICATION, "true");
boolean failureCaught = false;
try {
updateClient(connectionProps);
} catch (Exception e) {
failureCaught = true;
}
assertEquals(failureCaught, true);
}
@Test
public void testSSLConnectionFailEmptyTrustStore() throws Exception {
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.ENABLE_TLS, "true");
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PATH, emptyTSPath);
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PASSWORD, "drill123");
connectionProps.setProperty(DrillProperties.DISABLE_HOST_VERIFICATION, "true");
boolean failureCaught = false;
try {
updateClient(connectionProps);
} catch (Exception e) {
failureCaught = true;
}
assertEquals(failureCaught, true);
}
@Test
public void testSSLQuery() throws Exception {
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.ENABLE_TLS, "true");
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PATH, tsPath);
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PASSWORD, "drill123");
connectionProps.setProperty(DrillProperties.DISABLE_HOST_VERIFICATION, "true");
try {
updateClient(connectionProps);
} catch (Exception e) {
TestCase.fail( new StringBuilder()
.append("SSL Connection failed with exception [" )
.append( e.getMessage() )
.append("]")
.toString());
}
test("SELECT * FROM cp.`region.json`");
}
@Ignore("This test fails in some cases where the host name may be set up inconsistently.")
@Test
public void testClientConfigHostnameVerification() {
String password = "test_password";
String trustStoreFileName = "drillTestTrustStore";
String keyStoreFileName = "drillTestKeyStore";
KeyStore ts, ks;
File tempFile1, tempFile2;
String trustStorePath;
String keyStorePath;
try {
String fqdn = InetAddress.getLocalHost().getHostName();
SelfSignedCertificate certificate = new SelfSignedCertificate(fqdn);
tempFile1 = File.createTempFile(trustStoreFileName, ".ks");
tempFile1.deleteOnExit();
trustStorePath = tempFile1.getAbsolutePath();
//generate a truststore.
ts = KeyStore.getInstance(KeyStore.getDefaultType());
ts.load(null, password.toCharArray());
ts.setCertificateEntry("drillTest", certificate.cert());
// Store away the truststore.
try (FileOutputStream fos1 = new FileOutputStream(tempFile1);) {
ts.store(fos1, password.toCharArray());
} catch (Exception e) {
fail(e.getMessage());
}
tempFile2 = File.createTempFile(keyStoreFileName, ".ks");
tempFile2.deleteOnExit();
keyStorePath = tempFile2.getAbsolutePath();
//generate a keystore.
ts = KeyStore.getInstance(KeyStore.getDefaultType());
ts.load(null, password.toCharArray());
ts.setKeyEntry("drillTest", certificate.key(), password.toCharArray(), new java.security.cert.Certificate[]{certificate.cert()});
// Store away the keystore.
try (FileOutputStream fos2 = new FileOutputStream(tempFile2);) {
ts.store(fos2, password.toCharArray());
} catch (Exception e) {
fail(e.getMessage());
}
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.ENABLE_TLS, "true");
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PATH, trustStorePath);
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PASSWORD, password);
connectionProps.setProperty(DrillProperties.DISABLE_HOST_VERIFICATION, "false");
DrillConfig sslConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_SSL_ENABLED, ConfigValueFactory.fromAnyRef(true))
.withValue(ExecConstants.SSL_KEYSTORE_TYPE, ConfigValueFactory.fromAnyRef("JKS"))
.withValue(ExecConstants.SSL_KEYSTORE_PATH, ConfigValueFactory.fromAnyRef(keyStorePath))
.withValue(ExecConstants.SSL_KEYSTORE_PASSWORD, ConfigValueFactory.fromAnyRef("test_password"))
.withValue(ExecConstants.SSL_PROTOCOL, ConfigValueFactory.fromAnyRef("TLSv1.3")));
updateTestCluster(1, sslConfig, connectionProps);
} catch (Exception e) {
fail(e.getMessage());
}
//reset cluster
updateTestCluster(1, newConfig, initProps);
}
@Test
public void testClientConfigHostNameVerificationFail() throws Exception {
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.ENABLE_TLS, "true");
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PATH, tsPath);
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PASSWORD, "password");
connectionProps.setProperty(DrillProperties.DISABLE_HOST_VERIFICATION, "false");
boolean failureCaught = false;
try {
updateClient(connectionProps);
} catch (Exception e) {
failureCaught = true;
}
assertEquals(failureCaught, true);
}
@Test
public void testClientConfigCertificateVerification() {
// Fail if certificate is not valid
boolean failureCaught = false;
try {
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.ENABLE_TLS, "true");
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PATH, tsPath);
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PASSWORD, "drill123");
connectionProps.setProperty(DrillProperties.DISABLE_HOST_VERIFICATION, "true");
//connectionProps.setProperty(DrillProperties.DISABLE_CERT_VERIFICATION, "true");
DrillConfig sslConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_SSL_ENABLED, ConfigValueFactory.fromAnyRef(true))
.withValue(ExecConstants.SSL_KEYSTORE_TYPE, ConfigValueFactory.fromAnyRef("JKS"))
.withValue(ExecConstants.SSL_KEYSTORE_PATH, ConfigValueFactory.fromAnyRef(unknownKsPath))
.withValue(ExecConstants.SSL_KEYSTORE_PASSWORD, ConfigValueFactory.fromAnyRef("drill123"))
.withValue(ExecConstants.SSL_PROTOCOL, ConfigValueFactory.fromAnyRef("TLSv1.3")));
updateTestCluster(1, sslConfig, connectionProps);
} catch (Exception e) {
failureCaught = true;
}
//reset cluster
updateTestCluster(1, newConfig, initProps);
assertEquals(failureCaught, true);
}
@Test
public void testClientConfigNoCertificateVerification() {
// Pass if certificate is not valid, but mode is insecure.
try {
final Properties connectionProps = new Properties();
connectionProps.setProperty(DrillProperties.ENABLE_TLS, "true");
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PATH, tsPath);
connectionProps.setProperty(DrillProperties.TRUSTSTORE_PASSWORD, "drill123");
connectionProps.setProperty(DrillProperties.DISABLE_HOST_VERIFICATION, "true");
connectionProps.setProperty(DrillProperties.DISABLE_CERT_VERIFICATION, "true");
DrillConfig sslConfig = new DrillConfig(DrillConfig.create(cloneDefaultTestConfigProperties())
.withValue(ExecConstants.USER_SSL_ENABLED, ConfigValueFactory.fromAnyRef(true))
.withValue(ExecConstants.SSL_KEYSTORE_TYPE, ConfigValueFactory.fromAnyRef("JKS"))
.withValue(ExecConstants.SSL_KEYSTORE_PATH, ConfigValueFactory.fromAnyRef(unknownKsPath))
.withValue(ExecConstants.SSL_KEYSTORE_PASSWORD, ConfigValueFactory.fromAnyRef("drill123"))
.withValue(ExecConstants.SSL_PROTOCOL, ConfigValueFactory.fromAnyRef("TLSv1.3")));
updateTestCluster(1, sslConfig, connectionProps);
} catch (Exception e) {
fail(e.getMessage());
}
//reset cluster
updateTestCluster(1, newConfig, initProps);
}
}