blob: 8e1e8fb6311f2dcbdc7d408be126c0eff1370ddb [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.tinkerpop.gremlin.server;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.tinkerpop.gremlin.driver.Client;
import org.apache.tinkerpop.gremlin.driver.Cluster;
import org.apache.tinkerpop.gremlin.driver.exception.NoHostAvailableException;
import org.junit.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.TimeoutException;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class GremlinServerSslIntegrateTest extends AbstractGremlinServerIntegrationTest {
private static final String PEM_SERVER_KEY = "src/test/resources/server.key.pk8";
private static final String PEM_SERVER_CRT = "src/test/resources/server.crt";
private static final String PEM_CLIENT_KEY = "src/test/resources/client.key.pk8";
private static final String PEM_CLIENT_CRT = "src/test/resources/client.crt";
/**
* Configure specific Gremlin Server settings for specific tests.
*/
@Override
public Settings overrideSettings(final Settings settings) {
final String nameOfTest = name.getMethodName();
switch (nameOfTest) {
case "shouldEnableSsl":
case "shouldEnableSslButFailIfClientConnectsWithoutIt":
settings.ssl = new Settings.SslSettings();
settings.ssl.enabled = true;
settings.ssl.keyStore = JKS_SERVER_KEY;
settings.ssl.keyStorePassword = KEY_PASS;
settings.ssl.keyStoreType = KEYSTORE_TYPE_JKS;
break;
case "shouldEnableSslWithSslContextProgrammaticallySpecified":
settings.ssl = new Settings.SslSettings();
settings.ssl.enabled = true;
settings.ssl.overrideSslContext(createServerSslContext());
break;
case "shouldEnableSslAndClientCertificateAuthWithLegacyPem":
case "shouldEnableSslAndClientCertificateAuthAndFailWithoutCertWithLegacyPem":
settings.ssl = new Settings.SslSettings();
settings.ssl.enabled = true;
settings.ssl.needClientAuth = ClientAuth.REQUIRE;
settings.ssl.keyCertChainFile = PEM_SERVER_CRT;
settings.ssl.keyFile = PEM_SERVER_KEY;
settings.ssl.keyPassword = KEY_PASS;
// Trust the client
settings.ssl.trustCertChainFile = PEM_CLIENT_CRT;
break;
// Trust the client
case "shouldEnableSslAndClientCertificateAuthAndFailWithoutTrustedClientCertWithLegacyPem":
settings.ssl = new Settings.SslSettings();
settings.ssl.enabled = true;
settings.ssl.needClientAuth = ClientAuth.REQUIRE;
settings.ssl.keyCertChainFile = PEM_SERVER_CRT;
settings.ssl.keyFile = PEM_SERVER_KEY;
settings.ssl.keyPassword = KEY_PASS;
// Trust ONLY the server cert
settings.ssl.trustCertChainFile = PEM_SERVER_CRT;
break;
case "shouldEnableSslAndClientCertificateAuthWithPkcs12":
settings.ssl = new Settings.SslSettings();
settings.ssl.enabled = true;
settings.ssl.needClientAuth = ClientAuth.REQUIRE;
settings.ssl.keyStore = P12_SERVER_KEY;
settings.ssl.keyStorePassword = KEY_PASS;
settings.ssl.keyStoreType = KEYSTORE_TYPE_PKCS12;
settings.ssl.trustStore = P12_SERVER_TRUST;
settings.ssl.trustStorePassword = KEY_PASS;
break;
case "shouldEnableSslAndClientCertificateAuth":
case "shouldEnableSslAndClientCertificateAuthAndFailWithoutCert":
settings.ssl = new Settings.SslSettings();
settings.ssl.enabled = true;
settings.ssl.needClientAuth = ClientAuth.REQUIRE;
settings.ssl.keyStore = JKS_SERVER_KEY;
settings.ssl.keyStorePassword = KEY_PASS;
settings.ssl.keyStoreType = KEYSTORE_TYPE_JKS;
settings.ssl.trustStore = JKS_SERVER_TRUST;
settings.ssl.trustStorePassword = KEY_PASS;
break;
case "shouldEnableSslAndClientCertificateAuthAndFailWithoutTrustedClientCert":
settings.ssl = new Settings.SslSettings();
settings.ssl.enabled = true;
settings.ssl.needClientAuth = ClientAuth.REQUIRE;
settings.ssl.keyStore = JKS_SERVER_KEY;
settings.ssl.keyStorePassword = KEY_PASS;
settings.ssl.keyStoreType = KEYSTORE_TYPE_JKS;
break;
case "shouldEnableSslAndFailIfProtocolsDontMatch":
settings.ssl = new Settings.SslSettings();
settings.ssl.enabled = true;
settings.ssl.keyStore = JKS_SERVER_KEY;
settings.ssl.keyStorePassword = KEY_PASS;
settings.ssl.keyStoreType = KEYSTORE_TYPE_JKS;
settings.ssl.sslEnabledProtocols = Collections.singletonList("TLSv1.1");
break;
case "shouldEnableSslAndFailIfCiphersDontMatch":
settings.ssl = new Settings.SslSettings();
settings.ssl.enabled = true;
settings.ssl.keyStore = JKS_SERVER_KEY;
settings.ssl.keyStorePassword = KEY_PASS;
settings.ssl.keyStoreType = KEYSTORE_TYPE_JKS;
settings.ssl.sslCipherSuites = Collections.singletonList("TLS_DHE_RSA_WITH_AES_128_CBC_SHA");
break;
case "shouldEnableSslAndClientCertificateAuthWithDifferentStoreType":
case "shouldEnableSslAndClientCertificateAuthAndFailWithIncorrectKeyStoreType":
case "shouldEnableSslAndClientCertificateAuthAndFailWithIncorrectTrustStoreType":
settings.ssl = new Settings.SslSettings();
settings.ssl.enabled = true;
settings.ssl.needClientAuth = ClientAuth.REQUIRE;
settings.ssl.keyStore = JKS_SERVER_KEY;
settings.ssl.keyStorePassword = KEY_PASS;
settings.ssl.keyStoreType = KEYSTORE_TYPE_JKS;
settings.ssl.trustStore = P12_SERVER_TRUST;
settings.ssl.trustStorePassword = KEY_PASS;
settings.ssl.trustStoreType = TRUSTSTORE_TYPE_PKCS12;
break;
}
return settings;
}
private static SslContext createServerSslContext() {
final SslProvider provider = SslProvider.JDK;
try {
// this is not good for production - just testing
final SelfSignedCertificate ssc = new SelfSignedCertificate();
return SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).sslProvider(provider).build();
} catch (Exception ce) {
throw new RuntimeException("Couldn't setup self-signed certificate for test");
}
}
@Test
public void shouldEnableSsl() {
final Cluster cluster = TestClientFactory.build().enableSsl(true).keyStore(JKS_SERVER_KEY).keyStorePassword(KEY_PASS).sslSkipCertValidation(true).create();
final Client client = cluster.connect();
try {
// this should return "nothing" - there should be no exception
assertEquals("test", client.submit("'test'").one().getString());
} finally {
cluster.close();
}
}
@Test
public void shouldEnableSslWithSslContextProgrammaticallySpecified() throws Exception {
// just for testing - this is not good for production use
final SslContextBuilder builder = SslContextBuilder.forClient();
builder.trustManager(InsecureTrustManagerFactory.INSTANCE);
builder.sslProvider(SslProvider.JDK);
final Cluster cluster = TestClientFactory.build().enableSsl(true).sslContext(builder.build()).create();
final Client client = cluster.connect();
try {
// this should return "nothing" - there should be no exception
assertEquals("test", client.submit("'test'").one().getString());
} finally {
cluster.close();
}
}
@Test
public void shouldEnableSslButFailIfClientConnectsWithoutIt() {
final Cluster cluster = TestClientFactory.build().enableSsl(false).create();
final Client client = cluster.connect();
try {
client.submit("'test'").one();
fail("Should throw exception because ssl is enabled on the server but not on client");
} catch(Exception x) {
final Throwable root = ExceptionUtils.getRootCause(x);
assertThat(root, instanceOf(NoHostAvailableException.class));
} finally {
cluster.close();
}
}
@Test
public void shouldEnableSslAndClientCertificateAuthWithLegacyPem() {
final Cluster cluster = TestClientFactory.build().enableSsl(true)
.keyCertChainFile(PEM_CLIENT_CRT).keyFile(PEM_CLIENT_KEY)
.keyPassword(KEY_PASS).trustCertificateChainFile(PEM_SERVER_CRT).create();
final Client client = cluster.connect();
try {
assertEquals("test", client.submit("'test'").one().getString());
} finally {
cluster.close();
}
}
@Test
public void shouldEnableSslAndClientCertificateAuthAndFailWithoutCertWithLegacyPem() {
final Cluster cluster = TestClientFactory.build().enableSsl(true).keyStore(JKS_SERVER_KEY).keyStorePassword(KEY_PASS).sslSkipCertValidation(true).create();
final Client client = cluster.connect();
try {
client.submit("'test'").one();
fail("Should throw exception because ssl client auth is enabled on the server but client does not have a cert");
} catch(Exception x) {
final Throwable root = ExceptionUtils.getRootCause(x);
assertThat(root, instanceOf(NoHostAvailableException.class));
} finally {
cluster.close();
}
}
@Test
public void shouldEnableSslAndClientCertificateAuthAndFailWithoutTrustedClientCertWithLegacyPem() {
final Cluster cluster = TestClientFactory.build().enableSsl(true)
.keyCertChainFile(PEM_CLIENT_CRT).keyFile(PEM_CLIENT_KEY)
.keyPassword(KEY_PASS).trustCertificateChainFile(PEM_SERVER_CRT).create();
final Client client = cluster.connect();
try {
client.submit("'test'").one();
fail("Should throw exception because ssl client auth is enabled on the server but does not trust client's cert");
} catch(Exception x) {
final Throwable root = ExceptionUtils.getRootCause(x);
assertThat(root, instanceOf(NoHostAvailableException.class));
} finally {
cluster.close();
}
}
@Test
public void shouldEnableSslAndClientCertificateAuthWithPkcs12() {
final Cluster cluster = TestClientFactory.build().enableSsl(true).keyStore(P12_CLIENT_KEY).keyStorePassword(KEY_PASS)
.keyStoreType(KEYSTORE_TYPE_PKCS12).trustStore(P12_CLIENT_TRUST).trustStorePassword(KEY_PASS).create();
final Client client = cluster.connect();
try {
assertEquals("test", client.submit("'test'").one().getString());
} finally {
cluster.close();
}
}
@Test
public void shouldEnableSslAndClientCertificateAuth() {
final Cluster cluster = TestClientFactory.build().enableSsl(true).keyStore(JKS_CLIENT_KEY).keyStorePassword(KEY_PASS)
.keyStoreType(KEYSTORE_TYPE_JKS).trustStore(JKS_CLIENT_TRUST).trustStorePassword(KEY_PASS).create();
final Client client = cluster.connect();
try {
assertEquals("test", client.submit("'test'").one().getString());
} finally {
cluster.close();
}
}
@Test
public void shouldEnableSslAndClientCertificateAuthAndFailWithoutCert() {
final Cluster cluster = TestClientFactory.build().enableSsl(true).keyStore(JKS_SERVER_KEY).keyStorePassword(KEY_PASS)
.keyStoreType(KEYSTORE_TYPE_JKS).sslSkipCertValidation(true).create();
final Client client = cluster.connect();
try {
client.submit("'test'").one();
fail("Should throw exception because ssl client auth is enabled on the server but client does not have a cert");
} catch (Exception x) {
final Throwable root = ExceptionUtils.getRootCause(x);
assertThat(root, instanceOf(NoHostAvailableException.class));
} finally {
cluster.close();
}
}
@Test
public void shouldEnableSslAndClientCertificateAuthAndFailWithoutTrustedClientCert() {
final Cluster cluster = TestClientFactory.build().enableSsl(true).keyStore(JKS_CLIENT_KEY).keyStorePassword(KEY_PASS)
.keyStoreType(KEYSTORE_TYPE_JKS).trustStore(JKS_CLIENT_TRUST).trustStorePassword(KEY_PASS).create();
final Client client = cluster.connect();
try {
client.submit("'test'").one();
fail("Should throw exception because ssl client auth is enabled on the server but does not trust client's cert");
} catch (Exception x) {
final Throwable root = ExceptionUtils.getRootCause(x);
assertThat(root, instanceOf(NoHostAvailableException.class));
} finally {
cluster.close();
}
}
@Test
public void shouldEnableSslAndFailIfProtocolsDontMatch() {
final Cluster cluster = TestClientFactory.build().enableSsl(true).keyStore(JKS_SERVER_KEY).keyStorePassword(KEY_PASS)
.sslSkipCertValidation(true).sslEnabledProtocols(Arrays.asList("TLSv1.2")).create();
final Client client = cluster.connect();
try {
client.submit("'test'").one();
fail("Should throw exception because ssl client requires TLSv1.2 whereas server supports only TLSv1.1");
} catch (Exception x) {
final Throwable root = ExceptionUtils.getRootCause(x);
assertThat(root, instanceOf(NoHostAvailableException.class));
} finally {
cluster.close();
}
}
@Test
public void shouldEnableSslAndFailIfCiphersDontMatch() {
final Cluster cluster = TestClientFactory.build().enableSsl(true).keyStore(JKS_SERVER_KEY).keyStorePassword(KEY_PASS)
.sslSkipCertValidation(true).sslCipherSuites(Arrays.asList("SSL_RSA_WITH_RC4_128_SHA")).create();
final Client client = cluster.connect();
try {
client.submit("'test'").one();
fail("Should throw exception because ssl client requires TLSv1.2 whereas server supports only TLSv1.1");
} catch (Exception x) {
final Throwable root = ExceptionUtils.getRootCause(x);
assertThat(root, instanceOf(NoHostAvailableException.class));
} finally {
cluster.close();
}
}
@Test
public void shouldEnableSslAndClientCertificateAuthWithDifferentStoreType() {
final Cluster cluster = TestClientFactory.build().enableSsl(true)
.keyStore(JKS_CLIENT_KEY).keyStorePassword(KEY_PASS).keyStoreType(KEYSTORE_TYPE_JKS)
.trustStore(P12_CLIENT_TRUST).trustStorePassword(KEY_PASS).trustStoreType(TRUSTSTORE_TYPE_PKCS12)
.create();
final Client client = cluster.connect();
try {
assertEquals("test", client.submit("'test'").one().getString());
} finally {
cluster.close();
}
final Cluster cluster2 = TestClientFactory.build().enableSsl(true)
.keyStore(P12_CLIENT_KEY).keyStorePassword(KEY_PASS).keyStoreType(KEYSTORE_TYPE_PKCS12)
.trustStore(JKS_CLIENT_TRUST).trustStorePassword(KEY_PASS).trustStoreType(TRUSTSTORE_TYPE_JKS)
.create();
final Client client2 = cluster2.connect();
try {
assertEquals("test", client2.submit("'test'").one().getString());
} finally {
cluster2.close();
}
}
@Test
public void shouldEnableSslAndClientCertificateAuthAndFailWithIncorrectKeyStoreType() {
final Cluster cluster = TestClientFactory.build().enableSsl(true)
.keyStore(JKS_CLIENT_KEY).keyStorePassword(KEY_PASS).keyStoreType(KEYSTORE_TYPE_PKCS12)
.trustStore(P12_CLIENT_TRUST).trustStorePassword(KEY_PASS).trustStoreType(TRUSTSTORE_TYPE_PKCS12)
.create();
final Client client = cluster.connect();
try {
String res = client.submit("'test'").one().getString();
fail("Should throw exception because incorrect keyStoreType is specified");
} catch (Exception x) {
final Throwable root = ExceptionUtils.getRootCause(x);
assertThat(root, instanceOf(NoHostAvailableException.class));
} finally {
cluster.close();
}
}
@Test
public void shouldEnableSslAndClientCertificateAuthAndFailWithIncorrectTrustStoreType() {
final Cluster cluster = TestClientFactory.build().enableSsl(true)
.keyStore(P12_CLIENT_KEY).keyStorePassword(KEY_PASS).keyStoreType(KEYSTORE_TYPE_PKCS12)
.trustStore(JKS_CLIENT_TRUST).trustStorePassword(KEY_PASS).trustStoreType(TRUSTSTORE_TYPE_PKCS12)
.create();
final Client client = cluster.connect();
try {
client.submit("'test'").one();
fail("Should throw exception because incorrect trustStoreType is specified");
} catch (Exception x) {
final Throwable root = ExceptionUtils.getRootCause(x);
assertThat(root, instanceOf(NoHostAvailableException.class));
} finally {
cluster.close();
}
}
}