Skip to content

GEODE-9542: Enable SSL client authentication for Radish#6826

Merged
jdeppe-pivotal merged 14 commits intoapache:developfrom
jdeppe-pivotal:feature/GEODE-9542-radish-client-ssl-auth
Sep 14, 2021
Merged

GEODE-9542: Enable SSL client authentication for Radish#6826
jdeppe-pivotal merged 14 commits intoapache:developfrom
jdeppe-pivotal:feature/GEODE-9542-radish-client-ssl-auth

Conversation

@jdeppe-pivotal
Copy link
Contributor

  • When Geode's ssl-require-authentication is enabled, Redis clients must
    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 build run 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.

- When Geode's ssl-require-authentication is enabled, Redis clients must
  authenticate with a valid certificate.
@jdeppe-pivotal jdeppe-pivotal added the redis Issues related to the geode-for-redis module label Sep 1, 2021
@jdeppe-pivotal jdeppe-pivotal changed the title GEODE-9542: Enable client authentication for Radish GEODE-9542: Enable SSL client authentication for Radish Sep 1, 2021
FileWatchingX509ExtendedTrustManager.newFileWatchingTrustManager(sslConfigForServer));
}

SslContextBuilder sslContextBuilder = SslContextBuilder.forServer(keyManagerFactory);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Comment on lines 90 to 92
assertThatThrownBy(jedis::ping).satisfiesAnyOf(
e -> assertThat(e.getMessage()).contains("SSLException"),
e -> assertThat(e.getMessage()).contains("SSLHandshakeException"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed this block since exceptions are now just coming from the createClient call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@jdeppe-pivotal jdeppe-pivotal marked this pull request as draft September 1, 2021 21:01
@jdeppe-pivotal jdeppe-pivotal marked this pull request as ready for review September 1, 2021 21:33
}

@Test
public void givenMutualAuthentication_clientErrorsWithoutKeystore() throws Exception {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may just be misunderstanding, but this test name implies mutual authentication, but the boolean mutualAuthentication argument passed to the createClient() method is false.

Copy link
Contributor Author

@jdeppe-pivotal jdeppe-pivotal Sep 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the test name could be "givenMutualAuthenticationOnServer..."? Not super important though.

Copy link
Contributor

@upthewaterspout upthewaterspout left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, looks great! Looks like you also added support for watching the keystore and truststore files.

A few comments, inline.


if (!sslConfigForServer.isAnyProtocols()) {
sslContextBuilder.protocols(
Arrays.asList(sslConfigForServer.getProtocolsAsStringArray()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should add some tests that our redis server is honoring the ciphers and protocols that the user is specifying.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to verify manually, but that doesn't help.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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()));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@jdeppe-pivotal jdeppe-pivotal merged commit 81153cf into apache:develop Sep 14, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

redis Issues related to the geode-for-redis module

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants