/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ package org.voltdb; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.Map; import org.junit.After; import org.junit.Test; import org.voltdb.VoltDB.Configuration; import org.voltdb.client.Client; import org.voltdb.client.ClientConfig; import org.voltdb.client.ClientFactory; import org.voltdb.client.ClientResponse; import org.voltdb.compiler.VoltProjectBuilder; import org.voltdb.regressionsuites.LocalCluster; import org.voltdb.utils.MiscUtils; import com.google_voltpatches.common.collect.ImmutableMap; import junit.framework.TestCase; public class TestSSL extends TestCase { private LocalCluster m_cluster; private ServerThread m_server; private Client m_client; private Client m_admin; private static final String KEYSTORE_RESOURCE = "keystore"; private static final String KEYSTORE_PASSWD = "password"; private static final String KEYSTORE_PASSWD_OBFUSCATED = "OBF:1v2j1uum1xtv1zej1zer1xtn1uvk1v1v"; private static final String KEYSTORE_SYSPROP = "javax.net.ssl.keyStore"; private static final String KEYSTORE_PASSWD_SYSPROP = "javax.net.ssl.keyStorePassword"; private static final String TRUSTSTORE_SYSPROP = "javax.net.ssl.trustStore"; private static final String TRUSTSTORE_PASSWD_SYSPROP = "javax.net.ssl.trustStorePassword"; private static final String SSL_PROPS_FILE = "ssl-config"; private static final String SSL_PROPS_FILE_INVALID = "ssl-config-invalid"; static VoltProjectBuilder getBuilderForTest() throws IOException { VoltProjectBuilder builder = new VoltProjectBuilder(); builder.addLiteralSchema("CREATE TABLE T(A1 INTEGER NOT NULL, A2 INTEGER, PRIMARY KEY(A1));"); builder.addPartitionInfo("T", "A1"); builder.addStmtProcedure("InsertA", "INSERT INTO T VALUES(?,?);", "T.A1: 0"); builder.addStmtProcedure("CountA", "SELECT COUNT(*) FROM T"); builder.addStmtProcedure("SelectA", "SELECT * FROM T"); builder.setSslEnabled(true); builder.setSslExternal(true); return builder; } @Override @After public void tearDown() throws Exception { if (m_admin != null) { m_admin.close(); } if (m_client != null) { m_client.close(); } if (m_server != null) { m_server.shutdown(); m_server.join(); m_server = null; } if (m_cluster != null) { m_cluster.shutDown(); m_cluster = null; } } private void startLocalCluster(String keyStorePath, String keyStorePasswd, String certStorePath, String certStorePasswd) throws Exception { VoltProjectBuilder builder = getBuilderForTest(); if (keyStorePath != null) { builder.setKeyStoreInfo(keyStorePath, keyStorePasswd); } if (certStorePath != null) { builder.setCertStoreInfo(certStorePath, certStorePasswd); } System.setProperty("io.netty.leakDetection.level", "PARANOID"); Map<String,String> env = ImmutableMap.of("io.netty.leakDetection.level","PARANOID"); m_cluster = new LocalCluster("ssl.jar", 2, 2, 1, BackendTarget.NATIVE_EE_JNI, LocalCluster.FailureState.ALL_RUNNING, false, true, env); boolean success = m_cluster.compile(builder); assertTrue(success); MiscUtils.copyFile(builder.getPathToDeployment(), Configuration.getPathToCatalogForTest("ssl.xml")); m_cluster.setHasLocalServer(false); m_cluster.startUp(); } private void startServerThread(String keyStorePath, String keyStorePasswd, String certStorePath, String certStorePasswd) throws Exception { VoltProjectBuilder builder = getBuilderForTest(); if (keyStorePath != null) { String keystore = getResourcePath(keyStorePath); builder.setKeyStoreInfo(keystore, keyStorePasswd); } if (certStorePath != null) { String certstore = getResourcePath(certStorePath); builder.setCertStoreInfo(certstore, certStorePasswd); } boolean success = builder.compile(Configuration.getPathToCatalogForTest("ssl.jar")); assertTrue(success); VoltDB.Configuration config = new VoltDB.Configuration(); config.m_pathToCatalog = config.setPathToCatalogForTest("ssl.jar"); config.m_pathToDeployment = builder.getPathToDeployment(); m_server = new ServerThread(config); m_server.start(); m_server.waitForInitialization(); } private void checkAdminAndClient(int adminPort, int clientPort, String sslPropsFile) throws Exception { ClientConfig clientConfig = new ClientConfig("", "", null); clientConfig.setTrustStoreConfigFromPropertyFile(getResourcePath(sslPropsFile)); clientConfig.enableSSL(); //Add long loopy test for (int i = 0; i < 100; i++) { m_admin = ClientFactory.createClient(clientConfig); m_admin.createConnection("localhost", adminPort); m_admin.close(); m_client = ClientFactory.createClient(clientConfig); m_client.createConnection("localhost", clientPort); m_client.close(); } m_admin = ClientFactory.createClient(clientConfig); m_admin.createConnection("localhost", adminPort); m_admin.callProcedure("@Pause"); VoltTable[] results = m_admin.callProcedure("@SystemInformation").getResults(); checkSystemInformationClusterState(results[0], "Paused"); m_client = ClientFactory.createClient(clientConfig); m_client.createConnection("localhost", clientPort); results = m_client.callProcedure("CountA").getResults(); assertEquals(0, results[0].asScalarLong()); } private String getResourcePath(String resource) { URL res = this.getClass().getResource(resource); return res == null ? resource : res.getPath(); } @Test public void ntestServerThreadDefaultPortDeployment() throws Exception { startServerThread(KEYSTORE_RESOURCE, KEYSTORE_PASSWD, KEYSTORE_RESOURCE, KEYSTORE_PASSWD); checkAdminAndClient(m_server.m_config.m_adminPort, m_server.m_config.m_port, SSL_PROPS_FILE); } @Test public void ntestServerThreadObfuscatedPassword() throws Exception { startServerThread(KEYSTORE_RESOURCE, KEYSTORE_PASSWD_OBFUSCATED, KEYSTORE_RESOURCE, KEYSTORE_PASSWD_OBFUSCATED); checkAdminAndClient(m_server.m_config.m_adminPort, m_server.m_config.m_port, SSL_PROPS_FILE); } @Test public void ntestServerThreadKeystoreCertStoreInDeployment() throws Exception { System.setProperty(KEYSTORE_SYSPROP, getResourcePath(KEYSTORE_RESOURCE)); System.setProperty(KEYSTORE_PASSWD_SYSPROP, KEYSTORE_PASSWD); System.setProperty(TRUSTSTORE_SYSPROP, getResourcePath(KEYSTORE_RESOURCE)); System.setProperty(TRUSTSTORE_PASSWD_SYSPROP, KEYSTORE_PASSWD); startServerThread("invalid", "invalid", null, null); checkAdminAndClient(m_server.m_config.m_adminPort, m_server.m_config.m_port, SSL_PROPS_FILE_INVALID); } @Test public void testLocalClusterDefaultPortDeployment() throws Exception { startLocalCluster(KEYSTORE_RESOURCE, KEYSTORE_PASSWD, KEYSTORE_RESOURCE, KEYSTORE_PASSWD); checkAdminAndClient(m_cluster.adminPort(0), m_cluster.port(0), SSL_PROPS_FILE); } @Test public void ntestLocalCLusterObfuscatedPassword() throws Exception { startLocalCluster(KEYSTORE_RESOURCE, KEYSTORE_PASSWD_OBFUSCATED, KEYSTORE_RESOURCE, KEYSTORE_PASSWD_OBFUSCATED); checkAdminAndClient(m_cluster.adminPort(0), m_cluster.port(0), SSL_PROPS_FILE); } @Test public void ntestLocalClusterRejoin() throws Exception { org.voltdb.utils.VoltFile.resetSubrootForThisProcess(); VoltProjectBuilder builder = getBuilderForTest(); builder.setKeyStoreInfo(getResourcePath(KEYSTORE_RESOURCE), KEYSTORE_PASSWD); builder.setCertStoreInfo(getResourcePath(KEYSTORE_RESOURCE), KEYSTORE_PASSWD); int sitesPerHost = 2; int hostCount = 3; int kFactor = 2; m_cluster = new LocalCluster("sslRejoin.jar", sitesPerHost, hostCount, kFactor, BackendTarget.NATIVE_EE_JNI, false); m_cluster.setMaxHeap(1400); m_cluster.overrideAnyRequestForValgrind(); m_cluster.setHasLocalServer(false); boolean success = m_cluster.compile(builder); assertTrue(success); MiscUtils.copyFile(builder.getPathToDeployment(), Configuration.getPathToCatalogForTest("sslRejoin.xml")); m_cluster.startUp(); ClientConfig clientConfig = new ClientConfig("", "", null); clientConfig.setTrustStoreConfigFromPropertyFile(getResourcePath(SSL_PROPS_FILE)); clientConfig.enableSSL(); ClientResponse response; Client client; client = ClientFactory.createClient(clientConfig); client.createConnection("localhost", m_cluster.port(0)); response = client.callProcedure("InsertA",1,1); assertEquals(ClientResponse.SUCCESS, response.getStatus()); response = client.callProcedure("CountA"); assertEquals(ClientResponse.SUCCESS, response.getStatus()); assertEquals(1,response.getResults()[0].asScalarLong()); client.drain(); client.close(); client = ClientFactory.createClient(clientConfig); client.createConnection("localhost", m_cluster.port(1)); response = client.callProcedure("InsertA",2,2); assertEquals(ClientResponse.SUCCESS, response.getStatus()); response = client.callProcedure("CountA"); assertEquals(ClientResponse.SUCCESS, response.getStatus()); assertEquals(2,response.getResults()[0].asScalarLong()); client.drain(); client.close(); m_cluster.killSingleHost(0); Thread.sleep(100); VoltDB.Configuration config = new VoltDB.Configuration(m_cluster.portGenerator); config.m_startAction = m_cluster.isNewCli() ? StartAction.PROBE : StartAction.REJOIN; config.m_pathToCatalog = Configuration.getPathToCatalogForTest("sslRejoin.jar"); if (m_cluster.isNewCli()) { config.m_voltdbRoot = new File(m_cluster.getServerSpecificRoot("0")); config.m_forceVoltdbCreate = false; config.m_hostCount = hostCount; } config.m_pathToDeployment = Configuration.getPathToCatalogForTest("sslRejoin.xml"); config.m_leader = ":" + m_cluster.internalPort(1); config.m_coordinators = m_cluster.coordinators(1); config.m_isRejoinTest = true; m_cluster.setPortsFromConfig(0, config); m_server = new ServerThread(config); m_server.start(); m_server.waitForRejoin(); Thread.sleep(5000); System.out.println("ServerThread joined"); client = ClientFactory.createClient(clientConfig); System.out.println("Try connect to port:" + m_cluster.port(0)); client.createConnection("localhost", m_cluster.port(0)); System.out.println("Start Proc"); response = client.callProcedure("InsertA",3,3); assertEquals(ClientResponse.SUCCESS, response.getStatus()); response = client.callProcedure("CountA"); assertEquals(ClientResponse.SUCCESS, response.getStatus()); assertEquals(3,response.getResults()[0].asScalarLong()); client.drain(); client.close(); client = ClientFactory.createClient(clientConfig); System.out.println("Try connect to port:" + m_cluster.port(2)); client.createConnection("localhost", m_cluster.port(2)); System.out.println("Start Proc"); response = client.callProcedure("InsertA",4,4); assertEquals(ClientResponse.SUCCESS, response.getStatus()); response = client.callProcedure("CountA"); assertEquals(ClientResponse.SUCCESS, response.getStatus()); assertEquals(4,response.getResults()[0].asScalarLong()); client.drain(); client.close(); m_server.shutdown(); m_cluster.shutDown(); } void checkSystemInformationClusterState(VoltTable sysinfo, String state) { for (int i = 0; i < sysinfo.getRowCount(); i++) { sysinfo.advanceRow(); if (sysinfo.get("KEY", VoltType.STRING).equals("CLUSTERSTATE")) { assertTrue(state.equalsIgnoreCase((String) sysinfo.get("VALUE", VoltType.STRING))); return; } } fail("Failed to find CLUSTERSTATE key in SystemInformation results"); } }