Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SslOptions with Supplier of SSLParameters #4033

Draft
wants to merge 36 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
23ef9c3
Add SslOptions from Lettuce library
sazzad16 Sep 26, 2024
375e1ca
Add InsecureTrustManagerFactory from Netty library
sazzad16 Sep 27, 2024
ed26f37
Separate SSL socket creation in DefaultJedisSocketFactory
sazzad16 Sep 29, 2024
382fd81
Add SslOptions in JedisClientConfig
sazzad16 Sep 29, 2024
363d493
Add SslHostnameVerifyMode
sazzad16 Sep 30, 2024
261bf30
Allow system default key and trust managers and insecure trust manager
sazzad16 Sep 30, 2024
2ca615e
fix jdoc
sazzad16 Sep 30, 2024
8da45c3
Address review comments
sazzad16 Oct 1, 2024
e9444d9
Revert back to verification mode names in Lettuce,
sazzad16 Oct 10, 2024
ae97c00
Rename insecureTrust to noTruststoreVerification;
sazzad16 Oct 10, 2024
04fd034
Merge branch 'master' into ssl-options
sazzad16 Oct 10, 2024
09fee5e
SslVerifyMode is renamed back
sazzad16 Nov 10, 2024
ea3a164
Merge branch 'master' into ssl-options
sazzad16 Nov 10, 2024
499dfed
Remove InsecureTrustManagerFactory
sazzad16 Nov 10, 2024
b196739
Disable ALL existing SSL tests
sazzad16 Nov 18, 2024
1566a2a
JedisTest with SslOptions
sazzad16 Nov 18, 2024
06e2587
SSLACLJedisTest with SslOptions
sazzad16 Nov 18, 2024
4b27241
SSLOptionsJedisSentinelPoolTest with SslOptions
sazzad16 Nov 18, 2024
caa0b34
SSLJedisClusterTest with SslOptions
sazzad16 Nov 18, 2024
11bb446
TODO comment to enable existing SSL tests
sazzad16 Nov 18, 2024
ff88451
TODO command to enable existing SSL tests in csc package
sazzad16 Nov 18, 2024
315d73d
Enable existing SSL tests without impacting new ones
sazzad16 Nov 18, 2024
00d6dce
Missing enable existing SSL tests without impacting new ones
sazzad16 Nov 18, 2024
f56aec3
Keep only Builder pattern constructor for DefaultJedisClientConfig
sazzad16 Nov 24, 2024
06371b5
Limit HostnameVerifier only for legacy ssl config
sazzad16 Nov 24, 2024
bcce93b
Remove unused codes from SSLOptionsJedisTest
sazzad16 Nov 24, 2024
1fa23f2
Increase code reuse for LocalhostVerifier
sazzad16 Nov 24, 2024
a64a510
Individual JavaDoc for each SslVerifyMode
sazzad16 Nov 24, 2024
63aa76b
Custom SocketFactory won't be supported with SslOptions
sazzad16 Nov 25, 2024
10bc2dc
Deprecate DefaultJedisClientConfig.copyConfig()
sazzad16 Nov 25, 2024
a0591c8
Add option to set SSLContext protocol
sazzad16 Nov 25, 2024
309d477
Remove options to set KeyManager and TrustManager algorithms
sazzad16 Nov 25, 2024
42596bd
Add File checkers
sazzad16 Nov 25, 2024
1a44be1
minor user/password change
sazzad16 Nov 27, 2024
46c031e
minor update javadoc
sazzad16 Nov 28, 2024
f3a4b01
Receive Supplier of SSLParameters in SslOptions
sazzad16 Nov 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 71 additions & 34 deletions src/main/java/redis/clients/jedis/DefaultJedisClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public final class DefaultJedisClientConfig implements JedisClientConfig {
private final boolean ssl;
private final SSLSocketFactory sslSocketFactory;
private final SSLParameters sslParameters;
private final SslOptions sslOptions;
private final HostnameVerifier hostnameVerifier;

private final HostAndPortMapper hostAndPortMapper;
Expand All @@ -28,25 +29,22 @@ public final class DefaultJedisClientConfig implements JedisClientConfig {

private final boolean readOnlyForRedisClusterReplicas;

private DefaultJedisClientConfig(RedisProtocol protocol, int connectionTimeoutMillis, int soTimeoutMillis,
int blockingSocketTimeoutMillis, Supplier<RedisCredentials> credentialsProvider, int database,
String clientName, boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters,
HostnameVerifier hostnameVerifier, HostAndPortMapper hostAndPortMapper,
ClientSetInfoConfig clientSetInfoConfig, boolean readOnlyForRedisClusterReplicas) {
this.redisProtocol = protocol;
this.connectionTimeoutMillis = connectionTimeoutMillis;
this.socketTimeoutMillis = soTimeoutMillis;
this.blockingSocketTimeoutMillis = blockingSocketTimeoutMillis;
this.credentialsProvider = credentialsProvider;
this.database = database;
this.clientName = clientName;
this.ssl = ssl;
this.sslSocketFactory = sslSocketFactory;
this.sslParameters = sslParameters;
this.hostnameVerifier = hostnameVerifier;
this.hostAndPortMapper = hostAndPortMapper;
this.clientSetInfoConfig = clientSetInfoConfig;
this.readOnlyForRedisClusterReplicas = readOnlyForRedisClusterReplicas;
private DefaultJedisClientConfig(DefaultJedisClientConfig.Builder builder) {
this.redisProtocol = builder.redisProtocol;
this.connectionTimeoutMillis = builder.connectionTimeoutMillis;
this.socketTimeoutMillis = builder.socketTimeoutMillis;
this.blockingSocketTimeoutMillis = builder.blockingSocketTimeoutMillis;
this.credentialsProvider = builder.credentialsProvider;
this.database = builder.database;
this.clientName = builder.clientName;
this.ssl = builder.ssl;
this.sslSocketFactory = builder.sslSocketFactory;
this.sslParameters = builder.sslParameters;
this.sslOptions = builder.sslOptions;
this.hostnameVerifier = builder.hostnameVerifier;
this.hostAndPortMapper = builder.hostAndPortMapper;
this.clientSetInfoConfig = builder.clientSetInfoConfig;
this.readOnlyForRedisClusterReplicas = builder.readOnlyForRedisClusterReplicas;
}

@Override
Expand Down Expand Up @@ -110,6 +108,11 @@ public SSLParameters getSslParameters() {
return sslParameters;
}

@Override
public SslOptions getSslOptions() {
return sslOptions;
}

@Override
public HostnameVerifier getHostnameVerifier() {
return hostnameVerifier;
Expand Down Expand Up @@ -151,6 +154,7 @@ public static class Builder {
private boolean ssl = false;
private SSLSocketFactory sslSocketFactory = null;
private SSLParameters sslParameters = null;
private SslOptions sslOptions = null;
private HostnameVerifier hostnameVerifier = null;

private HostAndPortMapper hostAndPortMapper = null;
Expand All @@ -168,15 +172,13 @@ public DefaultJedisClientConfig build() {
new DefaultRedisCredentials(user, password));
}

return new DefaultJedisClientConfig(redisProtocol, connectionTimeoutMillis, socketTimeoutMillis,
blockingSocketTimeoutMillis, credentialsProvider, database, clientName, ssl,
sslSocketFactory, sslParameters, hostnameVerifier, hostAndPortMapper, clientSetInfoConfig,
readOnlyForRedisClusterReplicas);
return new DefaultJedisClientConfig(this);
}

/**
* Shortcut to {@link redis.clients.jedis.DefaultJedisClientConfig.Builder#protocol(RedisProtocol)} with
* {@link RedisProtocol#RESP3}.
* @return this
*/
public Builder resp3() {
return protocol(RedisProtocol.RESP3);
Expand Down Expand Up @@ -253,6 +255,11 @@ public Builder sslParameters(SSLParameters sslParameters) {
return this;
}

public Builder sslOptions(SslOptions sslOptions) {
this.sslOptions = sslOptions;
return this;
}

public Builder hostnameVerifier(HostnameVerifier hostnameVerifier) {
this.hostnameVerifier = hostnameVerifier;
return this;
Expand All @@ -278,19 +285,49 @@ public static DefaultJedisClientConfig create(int connectionTimeoutMillis, int s
int blockingSocketTimeoutMillis, String user, String password, int database, String clientName,
boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters,
HostnameVerifier hostnameVerifier, HostAndPortMapper hostAndPortMapper) {
return new DefaultJedisClientConfig(null,
connectionTimeoutMillis, soTimeoutMillis, blockingSocketTimeoutMillis,
new DefaultRedisCredentialsProvider(new DefaultRedisCredentials(user, password)), database,
clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier, hostAndPortMapper, null,
false);
Builder builder = builder();
builder.connectionTimeoutMillis(connectionTimeoutMillis).socketTimeoutMillis(soTimeoutMillis)
.blockingSocketTimeoutMillis(blockingSocketTimeoutMillis);
if (user != null || password != null) {
// deliberately not handling 'user != null && password == null' here
builder.credentials(new DefaultRedisCredentials(user, password));
}
builder.database(database).clientName(clientName);
builder.ssl(ssl).sslSocketFactory(sslSocketFactory).sslParameters(sslParameters).hostnameVerifier(hostnameVerifier);
builder.hostAndPortMapper(hostAndPortMapper);
return builder.build();
}

@Deprecated
public static DefaultJedisClientConfig copyConfig(JedisClientConfig copy) {
return new DefaultJedisClientConfig(copy.getRedisProtocol(),
copy.getConnectionTimeoutMillis(), copy.getSocketTimeoutMillis(),
copy.getBlockingSocketTimeoutMillis(), copy.getCredentialsProvider(),
copy.getDatabase(), copy.getClientName(), copy.isSsl(), copy.getSslSocketFactory(),
copy.getSslParameters(), copy.getHostnameVerifier(), copy.getHostAndPortMapper(),
copy.getClientSetInfoConfig(), copy.isReadOnlyForRedisClusterReplicas());
Builder builder = builder();
builder.protocol(copy.getRedisProtocol());
builder.connectionTimeoutMillis(copy.getConnectionTimeoutMillis());
builder.socketTimeoutMillis(copy.getSocketTimeoutMillis());
builder.blockingSocketTimeoutMillis(copy.getBlockingSocketTimeoutMillis());

Supplier<RedisCredentials> credentialsProvider = copy.getCredentialsProvider();
if (credentialsProvider != null) {
builder.credentialsProvider(credentialsProvider);
} else {
builder.user(copy.getUser());
builder.password(copy.getPassword());
}

builder.database(copy.getDatabase());
builder.clientName(copy.getClientName());

builder.ssl(copy.isSsl());
builder.sslSocketFactory(copy.getSslSocketFactory());
builder.sslParameters(copy.getSslParameters());
builder.hostnameVerifier(copy.getHostnameVerifier());
builder.sslOptions(copy.getSslOptions());
builder.hostAndPortMapper(copy.getHostAndPortMapper());

builder.clientSetInfoConfig(copy.getClientSetInfoConfig());
if (copy.isReadOnlyForRedisClusterReplicas()) {
builder.readOnlyForRedisClusterReplicas();
}
return builder.build();
}
}
72 changes: 53 additions & 19 deletions src/main/java/redis/clients/jedis/DefaultJedisSocketFactory.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package redis.clients.jedis;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
Expand All @@ -24,6 +27,7 @@ public class DefaultJedisSocketFactory implements JedisSocketFactory {
private int socketTimeout = Protocol.DEFAULT_TIMEOUT;
private boolean ssl = false;
private SSLSocketFactory sslSocketFactory = null;
private SslOptions sslOptions = null;
private SSLParameters sslParameters = null;
private HostnameVerifier hostnameVerifier = null;
private HostAndPortMapper hostAndPortMapper = null;
Expand All @@ -49,6 +53,7 @@ public DefaultJedisSocketFactory(HostAndPort hostAndPort, JedisClientConfig conf
this.ssl = config.isSsl();
this.sslSocketFactory = config.getSslSocketFactory();
this.sslParameters = config.getSslParameters();
this.sslOptions = config.getSslOptions();
this.hostnameVerifier = config.getHostnameVerifier();
this.hostAndPortMapper = config.getHostAndPortMapper();
}
Expand Down Expand Up @@ -89,25 +94,8 @@ public Socket createSocket() throws JedisConnectionException {
socket = connectToFirstSuccessfulHost(_hostAndPort);
socket.setSoTimeout(socketTimeout);

if (ssl) {
SSLSocketFactory _sslSocketFactory = this.sslSocketFactory;
if (null == _sslSocketFactory) {
_sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
}
Socket plainSocket = socket;
socket = _sslSocketFactory.createSocket(socket, _hostAndPort.getHost(), _hostAndPort.getPort(), true);

if (null != sslParameters) {
((SSLSocket) socket).setSSLParameters(sslParameters);
}
socket = new SSLSocketWrapper((SSLSocket) socket, plainSocket);

if (null != hostnameVerifier
&& !hostnameVerifier.verify(_hostAndPort.getHost(), ((SSLSocket) socket).getSession())) {
String message = String.format(
"The connection to '%s' failed ssl/tls hostname verification.", _hostAndPort.getHost());
throw new JedisConnectionException(message);
}
if (ssl || sslOptions != null) {
socket = createSslSocket(_hostAndPort, socket);
}

return socket;
Expand All @@ -122,6 +110,52 @@ public Socket createSocket() throws JedisConnectionException {
}
}

/**
* ssl enable check is done before this.
*/
private Socket createSslSocket(HostAndPort _hostAndPort, Socket socket) throws IOException, GeneralSecurityException {

Socket plainSocket = socket;

SSLSocketFactory _sslSocketFactory;
SSLParameters _sslParameters;

if (sslOptions != null) {

SSLContext _sslContext = sslOptions.createSslContext();
_sslSocketFactory = _sslContext.getSocketFactory();

_sslParameters = sslOptions.getSslParameters();

} else {

_sslSocketFactory = this.sslSocketFactory;
_sslParameters = this.sslParameters;
}

if (_sslSocketFactory == null) {
_sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
}

SSLSocket sslSocket = (SSLSocket) _sslSocketFactory.createSocket(socket,
_hostAndPort.getHost(), _hostAndPort.getPort(), true);

if (_sslParameters != null) {
sslSocket.setSSLParameters(_sslParameters);
}

if (sslOptions == null) {
// limiting HostnameVerifier only for legacy ssl config
if (hostnameVerifier != null && !hostnameVerifier.verify(_hostAndPort.getHost(), sslSocket.getSession())) {
String message = String.format("The connection to '%s' failed ssl/tls hostname verification.",
_hostAndPort.getHost());
throw new JedisConnectionException(message);
}
}

return new SSLSocketWrapper(sslSocket, plainSocket);
}

public void updateHostAndPort(HostAndPort hostAndPort) {
this.hostAndPort = hostAndPort;
}
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/redis/clients/jedis/JedisClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ default String getPassword() {
return null;
}

// TODO: return null
default Supplier<RedisCredentials> getCredentialsProvider() {
return new DefaultRedisCredentialsProvider(
new DefaultRedisCredentials(getUser(), getPassword()));
Expand Down Expand Up @@ -76,6 +77,16 @@ default HostnameVerifier getHostnameVerifier() {
return null;
}

/**
* {@link JedisClientConfig#isSsl()}, {@link JedisClientConfig#getSslSocketFactory()},
* {@link JedisClientConfig#getSslParameters()} and {@link JedisClientConfig#getHostnameVerifier()} will be ignored if
* {@link JedisClientConfig#getSslOptions() this} is set.
* @return ssl options
*/
default SslOptions getSslOptions() {
return null;
}

default HostAndPortMapper getHostAndPortMapper() {
return null;
}
Expand Down
Loading
Loading