Skip to content

Commit

Permalink
Add SSL support to JedisCluster (#1550)
Browse files Browse the repository at this point in the history
* Add SSL support to JedisCluster

* Remove unused imports from SSLJedisClusterTest

* Add JedisClusterPortMap interface for mapping between plaintext and SSL ports.

* Fix casing in JedisClusterPortMap

* Add new constructor for JedisClusterInfoCache instead of modifying existing one to fix backwards compatibility issue

* Undo unintended formatting changes.

* Improve name of test case in SSLJedisClusterTest

* Fix logic for closing JedisCluster objects in SSLJedisClusterTest

* Add option to specify mapping between redis and ssl hostnames

* Remove unneeded public access modifiers
  • Loading branch information
joroda authored and marcosnils committed Aug 1, 2018
1 parent fbc2270 commit 3d03825
Show file tree
Hide file tree
Showing 9 changed files with 432 additions and 14 deletions.
15 changes: 15 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,21 @@ pid = /tmp/stunnel.pid
[redis]
accept = 127.0.0.1:6390
connect = 127.0.0.1:6379
[redis_cluster_1]
accept = 127.0.0.1:8379
connect = 127.0.0.1:7379
[redis_cluster_2]
accept = 127.0.0.1:8380
connect = 127.0.001:7380
[redis_cluster_3]
accept = 127.0.0.1:8381
connect = 127.0.001:7381
[redis_cluster_4]
accept = 127.0.0.1:8382
connect = 127.0.0.1:7382
[redis_cluster_5]
accept = 127.0.0.1:8383
connect = 127.0.0.1:7383
endef

export REDIS1_CONF
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/redis/clients/jedis/BinaryJedisCluster.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
import java.util.Set;
import redis.clients.util.JedisClusterHashTagUtil;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocketFactory;

public class BinaryJedisCluster implements BasicCommands, BinaryJedisClusterCommands,
MultiKeyBinaryJedisClusterCommands, JedisClusterBinaryScriptingCommands, Closeable {

Expand Down Expand Up @@ -60,6 +64,19 @@ public BinaryJedisCluster(Set<HostAndPort> jedisClusterNode, int connectionTimeo
connectionTimeout, soTimeout, password, clientName);
this.maxAttempts = maxAttempts;
}

public BinaryJedisCluster(Set<HostAndPort> jedisClusterNode, int connectionTimeout, int soTimeout, int maxAttempts, String password, String clientName, GenericObjectPoolConfig poolConfig,
boolean ssl) {
this(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, clientName, poolConfig, ssl, null, null, null, null);
}

public BinaryJedisCluster(Set<HostAndPort> jedisClusterNode, int connectionTimeout, int soTimeout, int maxAttempts, String password, String clientName, GenericObjectPoolConfig poolConfig,
boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters,
HostnameVerifier hostnameVerifier, JedisClusterHostAndPortMap hostAndPortMap) {
this.connectionHandler = new JedisSlotBasedConnectionHandler(jedisClusterNode, poolConfig,
connectionTimeout, soTimeout, password, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier, hostAndPortMap);
this.maxAttempts = maxAttempts;
}

@Override
public void close() throws IOException {
Expand Down
34 changes: 33 additions & 1 deletion src/main/java/redis/clients/jedis/JedisCluster.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
import java.util.Map.Entry;
import java.util.Set;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocketFactory;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import redis.clients.util.JedisClusterHashTagUtil;

Expand Down Expand Up @@ -64,6 +68,20 @@ public JedisCluster(HostAndPort node, int connectionTimeout, int soTimeout,
int maxAttempts, String password, String clientName, final GenericObjectPoolConfig poolConfig) {
this(Collections.singleton(node), connectionTimeout, soTimeout, maxAttempts, password, clientName, poolConfig);
}

public JedisCluster(HostAndPort node, int connectionTimeout, int soTimeout,
int maxAttempts, String password, String clientName, final GenericObjectPoolConfig poolConfig,
boolean ssl) {
super(Collections.singleton(node), connectionTimeout, soTimeout, maxAttempts, password, clientName, poolConfig, ssl);
}

public JedisCluster(HostAndPort node, int connectionTimeout, int soTimeout,
int maxAttempts, String password, String clientName, final GenericObjectPoolConfig poolConfig,
boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters,
HostnameVerifier hostnameVerifier, JedisClusterHostAndPortMap hostAndPortMap) {
super(Collections.singleton(node), connectionTimeout, soTimeout, maxAttempts, password, clientName, poolConfig,
ssl, sslSocketFactory, sslParameters, hostnameVerifier, hostAndPortMap);
}

public JedisCluster(Set<HostAndPort> nodes) {
this(nodes, DEFAULT_TIMEOUT);
Expand Down Expand Up @@ -103,7 +121,21 @@ public JedisCluster(Set<HostAndPort> jedisClusterNode, int connectionTimeout, in
public JedisCluster(Set<HostAndPort> jedisClusterNode, int connectionTimeout, int soTimeout,
int maxAttempts, String password, String clientName, final GenericObjectPoolConfig poolConfig) {
super(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, clientName, poolConfig);
}
}

public JedisCluster(Set<HostAndPort> jedisClusterNode, int connectionTimeout, int soTimeout,
int maxAttempts, String password, String clientName, final GenericObjectPoolConfig poolConfig,
boolean ssl) {
super(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, clientName, poolConfig, ssl);
}

public JedisCluster(Set<HostAndPort> jedisClusterNode, int connectionTimeout, int soTimeout,
int maxAttempts, String password, String clientName, final GenericObjectPoolConfig poolConfig,
boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters,
HostnameVerifier hostnameVerifier, JedisClusterHostAndPortMap hostAndPortMap) {
super(jedisClusterNode, connectionTimeout, soTimeout, maxAttempts, password, clientName, poolConfig,
ssl, sslSocketFactory, sslParameters, hostnameVerifier, hostAndPortMap);
}

@Override
public String set(final String key, final String value) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import java.util.Map;
import java.util.Set;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocketFactory;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import redis.clients.jedis.exceptions.JedisConnectionException;
Expand All @@ -18,9 +22,17 @@ public JedisClusterConnectionHandler(Set<HostAndPort> nodes,

public JedisClusterConnectionHandler(Set<HostAndPort> nodes,
final GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout, String password, String clientName) {
this.cache = new JedisClusterInfoCache(poolConfig, connectionTimeout, soTimeout, password, clientName);
initializeSlotsCache(nodes, poolConfig, connectionTimeout, soTimeout, password, clientName);
}
this(nodes, poolConfig, connectionTimeout, soTimeout, password, clientName, false, null, null, null, null);
}

public JedisClusterConnectionHandler(Set<HostAndPort> nodes,
final GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout, String password, String clientName,
boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters,
HostnameVerifier hostnameVerifier, JedisClusterHostAndPortMap portMap) {
this.cache = new JedisClusterInfoCache(poolConfig, connectionTimeout, soTimeout, password, clientName,
ssl, sslSocketFactory, sslParameters, hostnameVerifier, portMap);
initializeSlotsCache(nodes, poolConfig, connectionTimeout, soTimeout, password, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier);
}

abstract Jedis getConnection();

Expand All @@ -34,12 +46,12 @@ public Map<String, JedisPool> getNodes() {
return cache.getNodes();
}

private void initializeSlotsCache(Set<HostAndPort> startNodes, GenericObjectPoolConfig poolConfig,
int connectionTimeout, int soTimeout, String password, String clientName) {
private void initializeSlotsCache(Set<HostAndPort> startNodes, GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout, String password, String clientName,
boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier) {
for (HostAndPort hostAndPort : startNodes) {
Jedis jedis = null;
try {
jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort(), connectionTimeout, soTimeout);
jedis = new Jedis(hostAndPort.getHost(), hostAndPort.getPort(), connectionTimeout, soTimeout, ssl, sslSocketFactory, sslParameters, hostnameVerifier);
if (password != null) {
jedis.auth(password);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package redis.clients.jedis;

public interface JedisClusterHostAndPortMap {
HostAndPort getSSLHostAndPort(String host, int port);
}
34 changes: 30 additions & 4 deletions src/main/java/redis/clients/jedis/JedisClusterInfoCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,37 @@ public class JedisClusterInfoCache {
private String password;
private String clientName;

private boolean ssl;
private SSLSocketFactory sslSocketFactory;
private SSLParameters sslParameters;
private HostnameVerifier hostnameVerifier;
private JedisClusterHostAndPortMap hostAndPortMap;

private static final int MASTER_NODE_INDEX = 2;

public JedisClusterInfoCache(final GenericObjectPoolConfig poolConfig, int timeout) {
this(poolConfig, timeout, timeout, null, null);
}

public JedisClusterInfoCache(final GenericObjectPoolConfig poolConfig,
final int connectionTimeout, final int soTimeout, final String password, final String clientName) {
this(poolConfig, connectionTimeout, soTimeout, password, clientName, false, null, null, null, null);
}

public JedisClusterInfoCache(final GenericObjectPoolConfig poolConfig,
final int connectionTimeout, final int soTimeout, final String password, final String clientName,
boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters,
HostnameVerifier hostnameVerifier, JedisClusterHostAndPortMap hostAndPortMap) {
this.poolConfig = poolConfig;
this.connectionTimeout = connectionTimeout;
this.soTimeout = soTimeout;
this.password = password;
this.clientName = clientName;
this.ssl = ssl;
this.sslSocketFactory = sslSocketFactory;
this.sslParameters = sslParameters;
this.hostnameVerifier = hostnameVerifier;
this.hostAndPortMap = hostAndPortMap;
}

public void discoverClusterNodesAndSlots(Jedis jedis) {
Expand Down Expand Up @@ -147,8 +165,15 @@ private void discoverClusterSlots(Jedis jedis) {
}

private HostAndPort generateHostAndPort(List<Object> hostInfos) {
return new HostAndPort(SafeEncoder.encode((byte[]) hostInfos.get(0)),
((Long) hostInfos.get(1)).intValue());
String host = SafeEncoder.encode((byte[]) hostInfos.get(0));
int port = ((Long) hostInfos.get(1)).intValue();
if (ssl && hostAndPortMap != null) {
HostAndPort hostAndPort = hostAndPortMap.getSSLHostAndPort(host, port);
if (hostAndPortMap != null) {
return hostAndPort;
}
}
return new HostAndPort(host, port);
}

public JedisPool setupNodeIfNotExist(HostAndPort node) {
Expand All @@ -159,7 +184,8 @@ public JedisPool setupNodeIfNotExist(HostAndPort node) {
if (existingPool != null) return existingPool;

JedisPool nodePool = new JedisPool(poolConfig, node.getHost(), node.getPort(),
connectionTimeout, soTimeout, password, 0, clientName, false, null, null, null);
connectionTimeout, soTimeout, password, 0, clientName,
ssl, sslSocketFactory, sslParameters, hostnameVerifier);
nodes.put(nodeKey, nodePool);
return nodePool;
} finally {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocketFactory;

import redis.clients.jedis.exceptions.JedisException;
import redis.clients.jedis.exceptions.JedisNoReachableClusterNodeException;

Expand All @@ -27,6 +31,11 @@ public JedisSlotBasedConnectionHandler(Set<HostAndPort> nodes, GenericObjectPool
public JedisSlotBasedConnectionHandler(Set<HostAndPort> nodes, GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout, String password, String clientName) {
super(nodes, poolConfig, connectionTimeout, soTimeout, password, clientName);
}

public JedisSlotBasedConnectionHandler(Set<HostAndPort> nodes, GenericObjectPoolConfig poolConfig, int connectionTimeout, int soTimeout, String password, String clientName,
boolean ssl, SSLSocketFactory sslSocketFactory, SSLParameters sslParameters, HostnameVerifier hostnameVerifier, JedisClusterHostAndPortMap portMap) {
super(nodes, poolConfig, connectionTimeout, soTimeout, password, clientName, ssl, sslSocketFactory, sslParameters, hostnameVerifier, portMap);
}

@Override
public Jedis getConnection() {
Expand Down
Loading

0 comments on commit 3d03825

Please sign in to comment.