GEODE-9542: Enable SSL client authentication for Radish#6826
Conversation
- When Geode's ssl-require-authentication is enabled, Redis clients must authenticate with a valid certificate.
| FileWatchingX509ExtendedTrustManager.newFileWatchingTrustManager(sslConfigForServer)); | ||
| } | ||
|
|
||
| SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(keyManagerFactory); |
There was a problem hiding this comment.
The javadoc for SslContextBuilder.forServer() states that the keyManagerFactory must be non-null, but it's possible that we get to this point with a null keyManagerFactory if sslConfigForServer.getKeystore() returns null. Is it actually possible for sslConfigForServer.getKeystore() to return null? If not, that check should probably be removed.
There was a problem hiding this comment.
Good catch. I cannot think why no keystore would be provided and it seems like a misconfiguration. You need a keystore if you want to use SSL/TLS. To that end I think it's OK to throw an exception in that case. WDYT?
| assertThatThrownBy(jedis::ping).satisfiesAnyOf( | ||
| e -> assertThat(e.getMessage()).contains("SSLException"), | ||
| e -> assertThat(e.getMessage()).contains("SSLHandshakeException")); |
There was a problem hiding this comment.
These assertions could be combined to a single one:
assertThatThrownBy(jedis::ping).isInstanceOf(SSLException.class);
Since SSLHandshakeException extends SSLException. Alternately, you could use satisfiesAnyOf() with assertions on the message body rather than exception name, but this might be trickier if you don't know all the different messages that might be expected in exceptions here.
There was a problem hiding this comment.
I've removed this block since exceptions are now just coming from the createClient call.
There was a problem hiding this comment.
And... now I've added it back since it seems that the createClient can also sometime fail (I have a feeling it's platform or JDK specific). But if the client is created, then this ping will definitely fail.
There was a problem hiding this comment.
The top level exception is always JedisConnectionException and the ones here are somewhere in the stack but not even the root cause. That's why I'm just looking at the message since that always contains one of these. I also have a strong suspicion that different plaforms and/or JDK produce slightly different messages or exception stacks. This seems to be one way to cover all bases.
...patible-with-redis/src/main/java/org/apache/geode/redis/internal/netty/NettyRedisServer.java
Outdated
Show resolved
Hide resolved
| } | ||
|
|
||
| @Test | ||
| public void givenMutualAuthentication_clientErrorsWithoutKeystore() throws Exception { |
There was a problem hiding this comment.
I may just be misunderstanding, but this test name implies mutual authentication, but the boolean mutualAuthentication argument passed to the createClient() method is false.
There was a problem hiding this comment.
So the givenMutualAuthentication bit is supposed to refer to how the server is configured. Passing false to createClient in order to not set it up to perform mutual auth and thus error. Feel free to suggest some more expressive test method name or perhaps rename the parameters.
There was a problem hiding this comment.
Maybe the test name could be "givenMutualAuthenticationOnServer..."? Not super important though.
...apis-compatible-with-redis/src/distributedTest/java/org/apache/geode/redis/SSLDUnitTest.java
Outdated
Show resolved
Hide resolved
...patible-with-redis/src/main/java/org/apache/geode/redis/internal/netty/NettyRedisServer.java
Outdated
Show resolved
Hide resolved
...patible-with-redis/src/main/java/org/apache/geode/redis/internal/netty/NettyRedisServer.java
Outdated
Show resolved
Hide resolved
upthewaterspout
left a comment
There was a problem hiding this comment.
Overall, looks great! Looks like you also added support for watching the keystore and truststore files.
A few comments, inline.
...patible-with-redis/src/main/java/org/apache/geode/redis/internal/netty/NettyRedisServer.java
Outdated
Show resolved
Hide resolved
...apis-compatible-with-redis/src/distributedTest/java/org/apache/geode/redis/SSLDUnitTest.java
Show resolved
Hide resolved
...apis-compatible-with-redis/src/distributedTest/java/org/apache/geode/redis/SSLDUnitTest.java
Outdated
Show resolved
Hide resolved
...apis-compatible-with-redis/src/distributedTest/java/org/apache/geode/redis/SSLDUnitTest.java
Outdated
Show resolved
Hide resolved
...apis-compatible-with-redis/src/distributedTest/java/org/apache/geode/redis/SSLDUnitTest.java
Show resolved
Hide resolved
|
|
||
| if (!sslConfigForServer.isAnyProtocols()) { | ||
| sslContextBuilder.protocols( | ||
| Arrays.asList(sslConfigForServer.getProtocolsAsStringArray())); |
There was a problem hiding this comment.
I wonder if we should add some tests that our redis server is honoring the ciphers and protocols that the user is specifying.
There was a problem hiding this comment.
I started down that road but I'm not sure how to best go about it. I started playing with some code I found that probes the port and tries to set various SSL options, but it seems a bit fragile since the JDKs are always adjusting their supported security. Any other ideas?
There was a problem hiding this comment.
I was able to verify manually, but that doesn't help.
There was a problem hiding this comment.
If it helps, I see lots of other tests checked in that are picking explicit protocols - eg RestAPIsWithSSLDUnitTest.
I wonder if netty's use of the protocol list exactly matches ours, for example. We allow "any" to show up in the list, and we also order the list with the highest priority protocol first.
There was a problem hiding this comment.
So I added some test code, that I found, that probes a SSL-enabled server and informs what protocols, ciphers and certificates the server has. Originally it was just a standalone-app so I massaged it a bit for this use.
There was a problem hiding this comment.
Ok. I also be fine with a test that just ensures the client can't connect with mismatched protocols, etc. to see that setting the protocol works. But if you like this TestSslServer approach better I'm ok with that.
| @@ -0,0 +1,1505 @@ | |||
| /* | |||
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license | |||
There was a problem hiding this comment.
I don't think this file should have the apache license header, it should probably just have the original MIT license header.
|
|
||
| if (!sslConfigForServer.isAnyProtocols()) { | ||
| sslContextBuilder.protocols( | ||
| Arrays.asList(sslConfigForServer.getProtocolsAsStringArray())); |
There was a problem hiding this comment.
Ok. I also be fine with a test that just ensures the client can't connect with mismatched protocols, etc. to see that setting the protocol works. But if you like this TestSslServer approach better I'm ok with that.
authenticate with a valid certificate.
Thank you for submitting a contribution to Apache Geode.
In order to streamline the review of the contribution we ask you
to ensure the following steps have been taken:
For all changes:
Is there a JIRA ticket associated with this PR? Is it referenced in the commit message?
Has your PR been rebased against the latest commit within the target branch (typically
develop)?Is your initial contribution a single, squashed commit?
Does
gradlew buildrun cleanly?Have you written or updated unit tests to verify your changes?
If adding new dependencies to the code, are these dependencies licensed in a way that is compatible for inclusion under ASF 2.0?
Note:
Please ensure that once the PR is submitted, check Concourse for build issues and
submit an update to your PR as soon as possible. If you need help, please send an
email to dev@geode.apache.org.