diff --git a/src/test/java/redis/clients/jedis/SSLACLJedisTest.java b/src/test/java/redis/clients/jedis/SSLACLJedisTest.java
deleted file mode 100644
index 296ff552d5..0000000000
--- a/src/test/java/redis/clients/jedis/SSLACLJedisTest.java
+++ /dev/null
@@ -1,102 +0,0 @@
-package redis.clients.jedis;
-
-import io.redis.test.annotations.ConditionalOnEnv;
-import io.redis.test.annotations.SinceRedisVersion;
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.List;
-
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Tag;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
-import redis.clients.jedis.util.EnvCondition;
-import redis.clients.jedis.util.RedisVersionCondition;
-import redis.clients.jedis.util.TestEnvUtil;
-import redis.clients.jedis.util.TlsUtil;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * This test class is a copy of {@link SSLJedisTest}.
- *
- * This test is only executed when the server/cluster is Redis 6. or more.
- */
-@SinceRedisVersion(value = "6.0.0", message = "Not running ACL test on this version of Redis")
-@Tag("integration")
-@ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_SOURCE, enabled = false)
-public class SSLACLJedisTest {
-
- protected static EndpointConfig endpoint;
-
- protected static EndpointConfig endpointWithDefaultUser;
-
- @RegisterExtension
- public static EnvCondition envCondition = new EnvCondition();
-
- @RegisterExtension
- public static RedisVersionCondition versionCondition = new RedisVersionCondition(
- () -> Endpoints.getRedisEndpoint("standalone0-acl-tls"));
-
- private static final String trustStoreName = SSLACLJedisTest.class.getSimpleName();
-
- @BeforeAll
- public static void prepare() {
- endpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls");
- endpointWithDefaultUser = Endpoints.getRedisEndpoint("standalone0-tls");
- List trustedCertLocation = Arrays.asList(endpoint.getCertificatesLocation(),
- endpointWithDefaultUser.getCertificatesLocation());
- Path trustStorePath = TlsUtil.createAndSaveTestTruststore(trustStoreName, trustedCertLocation,
- "changeit");
-
- TlsUtil.setCustomTrustStore(trustStorePath, "changeit");
- }
-
- @AfterAll
- public static void teardownTrustStore() {
- TlsUtil.restoreOriginalTrustStore();
- }
-
- @Test
- public void connectWithSsl() {
- try (Jedis jedis = new Jedis(endpoint.getHost(), endpoint.getPort(), true)) {
- jedis.auth(endpoint.getUsername(), endpoint.getPassword());
- assertEquals("PONG", jedis.ping());
- }
- }
-
- @Test
- public void connectWithConfig() {
- try (Jedis jedis = new Jedis(endpoint.getHostAndPort(),
- DefaultJedisClientConfig.builder().ssl(true).build())) {
- jedis.auth(endpoint.getUsername(), endpoint.getPassword());
- assertEquals("PONG", jedis.ping());
- }
- }
-
- @Test
- public void connectWithUrl() {
- // The "rediss" scheme instructs jedis to open a SSL/TLS connection.
- try (Jedis jedis = new Jedis(
- endpointWithDefaultUser.getURIBuilder().defaultCredentials().build().toString())) {
- assertEquals("PONG", jedis.ping());
- }
- try (Jedis jedis = new Jedis(
- endpoint.getURIBuilder().defaultCredentials().build().toString())) {
- assertEquals("PONG", jedis.ping());
- }
- }
-
- @Test
- public void connectWithUri() {
- // The "rediss" scheme instructs jedis to open a SSL/TLS connection.
- try (Jedis jedis = new Jedis(
- endpointWithDefaultUser.getURIBuilder().defaultCredentials().build())) {
- assertEquals("PONG", jedis.ping());
- }
- try (Jedis jedis = new Jedis(endpoint.getURIBuilder().defaultCredentials().build())) {
- assertEquals("PONG", jedis.ping());
- }
- }
-}
diff --git a/src/test/java/redis/clients/jedis/SSLACLRedisClusterClientTest.java b/src/test/java/redis/clients/jedis/SSLACLRedisClusterClientTest.java
deleted file mode 100644
index 3d2e4d2c64..0000000000
--- a/src/test/java/redis/clients/jedis/SSLACLRedisClusterClientTest.java
+++ /dev/null
@@ -1,328 +0,0 @@
-package redis.clients.jedis;
-
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.anyOf;
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-import static redis.clients.jedis.util.TlsUtil.*;
-
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLParameters;
-
-import io.redis.test.annotations.SinceRedisVersion;
-import io.redis.test.utils.RedisVersion;
-
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.Tag;
-import redis.clients.jedis.util.RedisVersionUtil;
-import redis.clients.jedis.util.TlsUtil;
-import redis.clients.jedis.exceptions.JedisClusterOperationException;
-
-@SinceRedisVersion(value = "7.0.0", message = "Redis 6.2.x returns non-tls port in CLUSTER SLOTS command. Enable for 6.2.x after tests are fixed.")
-@Tag("integration")
-public class SSLACLRedisClusterClientTest extends RedisClusterClientTestBase {
-
- private static final int DEFAULT_REDIRECTIONS = 5;
- private static final ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig();
-
- protected static EndpointConfig tlsEndpoint;
-
- // legacy test env bootstrap uses stunnel causing redis server to report non-tls port instead tls one containerised
- // test env enables tls directly on Redis nodes and in this case tls_port is correctly reported
- // TODO : remove stunnel from legacy env
- // static int tlsPortOffset = 0;
- private final HostAndPortMapper hostAndPortMap = (hostAndPort) -> {
- String host = hostAndPort.getHost();
- int port = hostAndPort.getPort();
-
- if ("127.0.0.1".equals(host)) {
- host = "localhost";
- }
- return new HostAndPort(host, port);
- };
-
- // don't map IP addresses so that we try to connect with host 127.0.0.1
- private final HostAndPortMapper portMap = (hostAndPort) -> {
- if ("localhost".equals(hostAndPort.getHost())) {
- return hostAndPort;
- }
- return new HostAndPort(hostAndPort.getHost(), hostAndPort.getPort() /* + tlsPortOffset */);
- };
-
- private static final String trustStoreName = SSLACLRedisClusterClientTest.class.getSimpleName();
-
- @BeforeAll
- public static void prepare() {
- tlsEndpoint = Endpoints.getRedisEndpoint("cluster-unbound-tls");
- List trustedCertLocation = Collections.singletonList(tlsEndpoint.getCertificatesLocation());
- Path trustStorePath = TlsUtil.createAndSaveTestTruststore(trustStoreName, trustedCertLocation,"changeit");
-
- TlsUtil.setCustomTrustStore(trustStorePath, "changeit");
- }
-
- @AfterAll
- public static void teardownTrustStore() {
- TlsUtil.restoreOriginalTrustStore();
- }
-
- @Test
- public void testSSLDiscoverNodesAutomatically() {
- DefaultJedisClientConfig config = DefaultJedisClientConfig.builder()
- .user("default").password(tlsEndpoint.getPassword()).ssl(true)
- .hostAndPortMapper(hostAndPortMap).build();
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(config)
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- Map clusterNodes = jc.getClusterNodes();
- assertEquals(3, clusterNodes.size());
-
- /**
- * In versions prior to Redis 7.x, Redis does not natively support automatic port switching between TLS and
- * non-TLS ports for CLUSTER SLOTS. When using Redis 6.2.16 in a cluster mode with TLS, CLUSTER command returns
- * the regular (non-TLS) port rather than the TLS port.
- */
- if (RedisVersionUtil.getRedisVersion(jc.getConnectionFromSlot(0)).isLessThanOrEqualTo(RedisVersion.V7_0_0)) {
- assertTrue(clusterNodes.containsKey(nodeInfo1.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo2.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo3.toString()));
- } else {
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
- }
- jc.get("foo");
- }
-
- try (RedisClusterClient jc2 = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(config)
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- Map clusterNodes = jc2.getClusterNodes();
- assertEquals(3, clusterNodes.size());
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
- jc2.get("foo");
- }
- }
-
- @Test
- public void testSSLWithoutPortMap() {
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword()).ssl(true).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- Map clusterNodes = jc.getClusterNodes();
- assertEquals(3, clusterNodes.size());
- /**
- * In versions prior to Redis 7.x, Redis does not natively support automatic port switching between TLS and
- * non-TLS ports for CLUSTER SLOTS. When using Redis 6.2.16 in a cluster mode with TLS, CLUSTER command returns
- * the regular (non-TLS) port rather than the TLS port.
- */
- if (RedisVersionUtil.getRedisVersion(jc.getConnectionFromSlot(0)).isLessThanOrEqualTo(RedisVersion.V7_0_0)) {
- assertTrue(clusterNodes.containsKey(nodeInfo1.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo2.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo3.toString()));
- } else {
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
- }
- }
- }
-
- @Test
- public void connectByIpAddress() {
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword()).ssl(true)
- .hostAndPortMapper(hostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc.get("foo");
- }
- }
-
- @Test
- public void connectToNodesFailsWithSSLParametersAndNoHostMapping() {
- final SSLParameters sslParameters = new SSLParameters();
- sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(new HostAndPort("localhost", tlsEndpoint.getPort())))
- .clientConfig(DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword()).ssl(true)
- .sslParameters(sslParameters).hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc.get("foo");
- fail("It should fail after all cluster attempts.");
-// } catch (JedisClusterMaxAttemptsException e) {
- } catch (JedisClusterOperationException e) {
- // initial connection to localhost works, but subsequent connections to nodes use 127.0.0.1
- // and fail hostname verification
- assertThat(e.getMessage(), anyOf(
- containsString("No more cluster attempts left."),
- containsString("Cluster retry deadline exceeded.")
- ));
- }
- }
-
- @Test
- public void connectToNodesSucceedsWithSSLParametersAndHostMapping() {
- final SSLParameters sslParameters = new SSLParameters();
- sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword()).ssl(true)
- .sslParameters(sslParameters).hostAndPortMapper(hostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc.get("foo");
- }
- }
-
- @Test
- public void connectByIpAddressFailsWithSSLParameters() {
- final SSLParameters sslParameters = new SSLParameters();
- sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword()).ssl(true)
- .sslParameters(sslParameters).hostAndPortMapper(hostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
-// jc.get("key");
-// Assert.fail("There should be no reachable node in cluster.");
-//// } catch (JedisNoReachableClusterNodeException e) {
- } catch (JedisClusterOperationException e) {
-// assertEquals("No reachable node in cluster.", e.getMessage());
- assertEquals("Could not initialize cluster slots cache.", e.getMessage());
- }
- }
-
- @Test
- public void connectWithCustomHostNameVerifier() {
- HostnameVerifier hostnameVerifier = new TlsUtil.BasicHostnameVerifier();
- HostnameVerifier localhostVerifier = new TlsUtil.LocalhostVerifier();
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(new HostAndPort("localhost", tlsEndpoint.getPort())))
- .clientConfig(DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword()).ssl(true)
- .hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc.get("foo");
- fail("It should fail after all cluster attempts.");
-// } catch (JedisClusterMaxAttemptsException e) {
- } catch (JedisClusterOperationException e) {
- // initial connection made with 'localhost' but subsequent connections to nodes use 127.0.0.1
- // which causes custom hostname verification to fail
- assertThat(e.getMessage(), anyOf(
- containsString("No more cluster attempts left."),
- containsString("Cluster retry deadline exceeded.")
- ));
- }
-
- try (RedisClusterClient jc2 = RedisClusterClient.builder()
- .nodes(Collections.singleton(new HostAndPort("127.0.0.1", tlsEndpoint.getPort())))
- .clientConfig(DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword()).ssl(true)
- .hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
-// jc2.get("key");
-// Assert.fail("There should be no reachable node in cluster.");
-//// } catch (JedisNoReachableClusterNodeException e) {
- } catch (JedisClusterOperationException e) {
- // JedisNoReachableClusterNodeException exception occurs from not being able to connect since
- // the socket factory fails the hostname verification
-// assertEquals("No reachable node in cluster.", e.getMessage());
- assertEquals("Could not initialize cluster slots cache.", e.getMessage());
- }
-
- try (RedisClusterClient jc3 = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword()).ssl(true)
- .hostnameVerifier(localhostVerifier).hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc3.get("foo");
- }
- }
-
- @Test
- public void connectWithCustomSocketFactory() {
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword()).ssl(true)
- .sslSocketFactory(sslSocketFactoryForEnv(tlsEndpoint.getCertificatesLocation()))
- .hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- assertEquals(3, jc.getClusterNodes().size());
- }
- }
-
- @Test
- public void connectWithEmptyTrustStore() throws Exception {
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword()).ssl(true)
- .sslSocketFactory(createTrustNoOneSslSocketFactory()).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
-// jc.get("key");
-// Assert.fail("There should be no reachable node in cluster.");
-//// } catch (JedisNoReachableClusterNodeException e) {
- } catch (JedisClusterOperationException e) {
-// assertEquals("No reachable node in cluster.", e.getMessage());
- assertEquals("Could not initialize cluster slots cache.", e.getMessage());
- }
- }
-
- @Test
- public void defaultHostAndPortUsedIfMapReturnsNull() {
- HostAndPortMapper nullHostAndPortMap = (HostAndPort hostAndPort) -> null;
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(new HostAndPort("localhost", nodeInfo1.getPort())))
- .clientConfig(DefaultJedisClientConfig.builder().user("default").password(endpoint.getPassword()).ssl(false)
- .hostAndPortMapper(nullHostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- Map clusterNodes = jc.getClusterNodes();
- assertEquals(3, clusterNodes.size());
- assertTrue(clusterNodes.containsKey(nodeInfo1.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo2.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo3.toString()));
- }
- }
-}
diff --git a/src/test/java/redis/clients/jedis/SSLJedisSentinelPoolTest.java b/src/test/java/redis/clients/jedis/SSLJedisSentinelPoolTest.java
deleted file mode 100644
index 060b5df797..0000000000
--- a/src/test/java/redis/clients/jedis/SSLJedisSentinelPoolTest.java
+++ /dev/null
@@ -1,112 +0,0 @@
-package redis.clients.jedis;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Tag;
-import org.junit.jupiter.api.Test;
-import redis.clients.jedis.util.TlsUtil;
-
-@Tag("integration")
-public class SSLJedisSentinelPoolTest {
-
- private static EndpointConfig sentinel;
-
- private static final String MASTER_NAME = "aclmaster";
-
- private static Set sentinels = new HashSet<>();
-
- private static final HostAndPortMapper SSL_PORT_MAPPER = (HostAndPort hap)
- -> new HostAndPort(hap.getHost(), hap.getPort() + 10000);
-
- private static final HostAndPortMapper SSL_PORT_MAPPER_PRIMARY = (HostAndPort hap)
- -> new HostAndPort(hap.getHost(), hap.getPort() + 11);
-
- private static final GenericObjectPoolConfig POOL_CONFIG = new GenericObjectPoolConfig<>();
- private static final String trustStoreName = SSLJedisSentinelPoolTest.class.getSimpleName();
-
- @BeforeAll
- public static void prepare() {
- sentinel = Endpoints.getRedisEndpoint("sentinel-standalone0");
- List trustedCertLocation = Collections.singletonList(Paths.get("redis1-2-5-8-sentinel/work/tls"));
- Path trustStorePath = TlsUtil.createAndSaveTestTruststore(trustStoreName, trustedCertLocation,"changeit");
- TlsUtil.setCustomTrustStore(trustStorePath, "changeit");
-
- sentinels.add(sentinel.getHostAndPort());
- }
-
- @AfterAll
- public static void teardownTrustStore() {
- TlsUtil.restoreOriginalTrustStore();
- }
-
- @Test
- public void sentinelWithoutSslConnectsToRedisWithSsl() {
-
- DefaultJedisClientConfig masterConfig = Endpoints.getRedisEndpoint("standalone0-acl-tls")
- .getClientConfigBuilder().clientName("master-client").hostAndPortMapper(SSL_PORT_MAPPER_PRIMARY)
- .build();
-
- DefaultJedisClientConfig sentinelConfig = sentinel.getClientConfigBuilder()
- .clientName("sentinel-client").build();
-
- try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig,
- sentinelConfig)) {
- pool.getResource().close();
- }
-
- try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
- masterConfig, sentinelConfig)) {
- pool.getResource().close();
- }
- }
-
- @Test
- public void sentinelWithSslConnectsToRedisWithoutSsl() {
-
- DefaultJedisClientConfig masterConfig = Endpoints.getRedisEndpoint("standalone0-acl")
- .getClientConfigBuilder().clientName("master-client").build();
-
- DefaultJedisClientConfig sentinelConfig = Endpoints.getRedisEndpoint(
- "sentinel-standalone0-tls").getClientConfigBuilder().clientName("sentinel-client")
- .hostAndPortMapper(SSL_PORT_MAPPER).build();
-
- try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) {
- pool.getResource().close();
- }
-
- try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
- masterConfig, sentinelConfig)) {
- pool.getResource().close();
- }
- }
-
- @Test
- public void sentinelWithSslConnectsToRedisWithSsl() {
-
- DefaultJedisClientConfig masterConfig = Endpoints.getRedisEndpoint("standalone0-acl-tls")
- .getClientConfigBuilder().clientName("master-client").hostAndPortMapper(SSL_PORT_MAPPER_PRIMARY)
- .build();
-
- DefaultJedisClientConfig sentinelConfig = Endpoints.getRedisEndpoint(
- "sentinel-standalone0-tls").getClientConfigBuilder().clientName("sentinel-client")
- .hostAndPortMapper(SSL_PORT_MAPPER).build();
-
- try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) {
- pool.getResource().close();
- }
-
- try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
- masterConfig, sentinelConfig)) {
- pool.getResource().close();
- }
- }
-
-}
diff --git a/src/test/java/redis/clients/jedis/SSLOptionsJedisSentinelPoolTest.java b/src/test/java/redis/clients/jedis/SSLOptionsJedisSentinelPoolTest.java
deleted file mode 100644
index 5c5bbae4ab..0000000000
--- a/src/test/java/redis/clients/jedis/SSLOptionsJedisSentinelPoolTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package redis.clients.jedis;
-
-import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Tag;
-import org.junit.jupiter.api.Test;
-import redis.clients.jedis.util.TlsUtil;
-
-@Tag("integration")
-public class SSLOptionsJedisSentinelPoolTest {
-
- private static EndpointConfig sentinel;
-
- private static final String MASTER_NAME = "aclmaster";
-
- private static Set sentinels = new HashSet<>();
-
- private static final HostAndPortMapper SSL_PORT_MAPPER = (HostAndPort hap)
- -> new HostAndPort(hap.getHost(), hap.getPort() + 10000);
-
- private static final HostAndPortMapper SSL_PORT_MAPPER_PRIMARY = (HostAndPort hap)
- -> new HostAndPort(hap.getHost(), hap.getPort() + 11);
-
- private static final GenericObjectPoolConfig POOL_CONFIG = new GenericObjectPoolConfig<>();
-
- private static final String trustStoreName = SSLOptionsJedisSentinelPoolTest.class.getSimpleName();
- private static Path trustStorePath;
- private static SslOptions sslOptions;
-
- @BeforeAll
- public static void prepare() {
- sentinel = Endpoints.getRedisEndpoint("sentinel-standalone0");
- List trustedCertLocation = Collections.singletonList(Paths.get("redis1-2-5-8-sentinel/work/tls"));
- trustStorePath = TlsUtil.createAndSaveTestTruststore(trustStoreName, trustedCertLocation,"changeit");
- sslOptions = SslOptions.builder()
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks")
- .sslVerifyMode(SslVerifyMode.CA).build();
-
- sentinels.add(sentinel.getHostAndPort());
- }
-
- @Test
- public void sentinelWithoutSslConnectsToRedisWithSsl() {
-
- DefaultJedisClientConfig masterConfig = Endpoints.getRedisEndpoint("standalone0-acl-tls")
- .getClientConfigBuilder().clientName("master-client").sslOptions(sslOptions)
- .hostAndPortMapper(SSL_PORT_MAPPER_PRIMARY).build();
-
- DefaultJedisClientConfig sentinelConfig = sentinel.getClientConfigBuilder()
- .clientName("sentinel-client").build();
-
- try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) {
- pool.getResource().close();
- }
-
- try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
- masterConfig, sentinelConfig)) {
- pool.getResource().close();
- }
- }
-
- @Test
- public void sentinelWithSslConnectsToRedisWithoutSsl() {
-
- DefaultJedisClientConfig masterConfig = Endpoints.getRedisEndpoint("standalone0-acl")
- .getClientConfigBuilder().clientName("master-client").build();
-
- DefaultJedisClientConfig sentinelConfig = Endpoints.getRedisEndpoint(
- "sentinel-standalone0-tls").getClientConfigBuilder().sslOptions(sslOptions)
- .hostAndPortMapper(SSL_PORT_MAPPER).build();
-
- try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) {
- pool.getResource().close();
- }
-
- try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
- masterConfig, sentinelConfig)) {
- pool.getResource().close();
- }
- }
-
- @Test
- public void sentinelWithSslConnectsToRedisWithSsl() {
-
- DefaultJedisClientConfig masterConfig = Endpoints.getRedisEndpoint("standalone0-acl-tls")
- .getClientConfigBuilder().clientName("master-client").sslOptions(sslOptions)
- .hostAndPortMapper(SSL_PORT_MAPPER_PRIMARY).build();
-
- DefaultJedisClientConfig sentinelConfig = Endpoints.getRedisEndpoint(
- "sentinel-standalone0-tls").getClientConfigBuilder().clientName("sentinel-client")
- .sslOptions(sslOptions).hostAndPortMapper(SSL_PORT_MAPPER).build();
-
- try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig, sentinelConfig)) {
- pool.getResource().close();
- }
-
- try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
- masterConfig, sentinelConfig)) {
- pool.getResource().close();
- }
- }
-
-}
diff --git a/src/test/java/redis/clients/jedis/SSLOptionsJedisTest.java b/src/test/java/redis/clients/jedis/SSLOptionsJedisTest.java
deleted file mode 100644
index bd1c012e10..0000000000
--- a/src/test/java/redis/clients/jedis/SSLOptionsJedisTest.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package redis.clients.jedis;
-
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.Tag;
-import redis.clients.jedis.util.TlsUtil;
-
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-@Tag("integration")
-public class SSLOptionsJedisTest {
-
- protected static EndpointConfig endpoint;
-
- protected static EndpointConfig aclEndpoint;
-
- private static final String trustStoreName = SSLOptionsJedisTest.class.getSimpleName();
- private static Path trustStorePath;
- @BeforeAll
- public static void prepare() {
- endpoint = Endpoints.getRedisEndpoint("standalone0-tls");
- aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls");
- List trustedCertLocation = Arrays.asList(endpoint.getCertificatesLocation(),aclEndpoint.getCertificatesLocation());
- trustStorePath = TlsUtil.createAndSaveTestTruststore(trustStoreName, trustedCertLocation,"changeit");
- }
-
- @Test
- public void connectWithSsl() {
- try (Jedis jedis = new Jedis(endpoint.getHostAndPort(),
- DefaultJedisClientConfig.builder()
- .sslOptions(SslOptions.builder()
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks")
- .build()).build())) {
- jedis.auth(endpoint.getPassword());
- assertEquals("PONG", jedis.ping());
- }
- }
-
- @Test
- public void connectWithClientConfig() {
- try (Jedis jedis = new Jedis(endpoint.getHostAndPort(),
- endpoint.getClientConfigBuilder()
- .sslOptions(SslOptions.builder()
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks")
- .build()).build())) {
- assertEquals("PONG", jedis.ping());
- }
- }
-
- @Test
- public void connectWithSslInsecure() {
- try (Jedis jedis = new Jedis(endpoint.getHostAndPort(),
- endpoint.getClientConfigBuilder()
- .sslOptions(SslOptions.builder()
- .sslVerifyMode(SslVerifyMode.INSECURE)
- .build()).build())) {
- assertEquals("PONG", jedis.ping());
- }
- }
-
- @Test
- public void connectWithSslContextProtocol() {
- try (Jedis jedis = new Jedis(endpoint.getHostAndPort(),
- endpoint.getClientConfigBuilder()
- .sslOptions(SslOptions.builder()
- .sslProtocol("SSL")
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks")
- .build()).build())) {
- assertEquals("PONG", jedis.ping());
- }
- }
-
- @Test
- public void connectWithAcl() {
- try (Jedis jedis = new Jedis(aclEndpoint.getHostAndPort(),
- aclEndpoint.getClientConfigBuilder()
- .sslOptions(SslOptions.builder()
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks")
- .build()).build())) {
- assertEquals("PONG", jedis.ping());
- }
- }
-}
diff --git a/src/test/java/redis/clients/jedis/SSLOptionsRedisClientTest.java b/src/test/java/redis/clients/jedis/SSLOptionsRedisClientTest.java
deleted file mode 100644
index 2522b0f7c6..0000000000
--- a/src/test/java/redis/clients/jedis/SSLOptionsRedisClientTest.java
+++ /dev/null
@@ -1,89 +0,0 @@
-package redis.clients.jedis;
-
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.Tag;
-import redis.clients.jedis.util.TlsUtil;
-
-import java.nio.file.Path;
-import java.util.Arrays;
-import java.util.List;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-@Tag("integration")
-public class SSLOptionsRedisClientTest {
-
- protected static EndpointConfig endpoint;
-
- protected static EndpointConfig aclEndpoint;
-
- private static final String trustStoreName = SSLACLJedisTest.class.getSimpleName();
- private static Path trustStorePath;
-
- @BeforeAll
- public static void prepare() {
- endpoint = Endpoints.getRedisEndpoint("standalone0-tls");
- aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls");
- List trustedCertLocation = Arrays.asList(endpoint.getCertificatesLocation(),
- aclEndpoint.getCertificatesLocation());
- trustStorePath = TlsUtil.createAndSaveTestTruststore(trustStoreName, trustedCertLocation,
- "changeit");
- }
-
- @Test
- public void connectWithClientConfig() {
- try (RedisClient jedis = RedisClient.builder()
- .hostAndPort(endpoint.getHostAndPort())
- .clientConfig(endpoint.getClientConfigBuilder()
- .sslOptions(SslOptions.builder()
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks")
- .build()).build())
- .build()) {
- assertEquals("PONG", jedis.ping());
- }
- }
-
- @Test
- public void connectWithSslInsecure() {
- try (RedisClient jedis = RedisClient.builder()
- .hostAndPort(endpoint.getHostAndPort())
- .clientConfig(endpoint.getClientConfigBuilder()
- .sslOptions(SslOptions.builder()
- .sslVerifyMode(SslVerifyMode.INSECURE)
- .build()).build())
- .build()) {
- assertEquals("PONG", jedis.ping());
- }
- }
-
- @Test
- public void connectWithSslContextProtocol() {
- try (RedisClient jedis = RedisClient.builder()
- .hostAndPort(endpoint.getHostAndPort())
- .clientConfig(endpoint.getClientConfigBuilder()
- .sslOptions(SslOptions.builder()
- .sslProtocol("SSL")
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks")
- .build()).build())
- .build()) {
- assertEquals("PONG", jedis.ping());
- }
- }
-
- @Test
- public void connectWithAcl() {
- try (RedisClient jedis = RedisClient.builder()
- .hostAndPort(aclEndpoint.getHostAndPort())
- .clientConfig(aclEndpoint.getClientConfigBuilder()
- .sslOptions(SslOptions.builder()
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks")
- .build()).build())
- .build()) {
- assertEquals("PONG", jedis.ping());
- }
- }
-}
diff --git a/src/test/java/redis/clients/jedis/SSLOptionsRedisClusterClientTest.java b/src/test/java/redis/clients/jedis/SSLOptionsRedisClusterClientTest.java
deleted file mode 100644
index 79ef4238cf..0000000000
--- a/src/test/java/redis/clients/jedis/SSLOptionsRedisClusterClientTest.java
+++ /dev/null
@@ -1,260 +0,0 @@
-package redis.clients.jedis;
-
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLParameters;
-
-import io.redis.test.annotations.SinceRedisVersion;
-import io.redis.test.utils.RedisVersion;
-
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.Tag;
-import redis.clients.jedis.exceptions.JedisClusterOperationException;
-import redis.clients.jedis.util.RedisVersionUtil;
-import redis.clients.jedis.util.TlsUtil;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.anyOf;
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
-@SinceRedisVersion(value = "7.0.0", message = "Redis 6.2.x returns non-tls port in CLUSTER SLOTS command. Enable for 6.2.x after test is fixed.")
-@Tag("integration")
-public class SSLOptionsRedisClusterClientTest extends RedisClusterClientTestBase {
-
- private static final int DEFAULT_REDIRECTIONS = 5;
- private static final ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig();
-
- protected static EndpointConfig tlsEndpoint;
-
- private final HostAndPortMapper hostAndPortMap = (HostAndPort hostAndPort) -> {
- String host = hostAndPort.getHost();
- int port = hostAndPort.getPort();
- if (host.equals("127.0.0.1")) {
- host = "localhost";
- }
- return new HostAndPort(host, port);
- };
-
- // don't map IP addresses so that we try to connect with host 127.0.0.1
- private final HostAndPortMapper portMap = (HostAndPort hostAndPort) -> {
- if ("localhost".equals(hostAndPort.getHost())) {
- return hostAndPort;
- }
- return new HostAndPort(hostAndPort.getHost(), hostAndPort.getPort());
- };
-
- private static final String trustStoreName = SSLOptionsRedisClusterClientTest.class.getSimpleName();
- private static Path trustStorePath;
-
- @BeforeAll
- public static void prepare() {
- tlsEndpoint = Endpoints.getRedisEndpoint("cluster-unbound-tls");
- List trustedCertLocation = Collections.singletonList(tlsEndpoint.getCertificatesLocation());
- trustStorePath = TlsUtil.createAndSaveTestTruststore(trustStoreName, trustedCertLocation,"changeit");
- }
-
- @Test
- public void testSSLDiscoverNodesAutomatically() {
- try (RedisClusterClient jc2 = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
- .sslOptions(SslOptions.builder()
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks").build())
- .hostAndPortMapper(hostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- Map clusterNodes = jc2.getClusterNodes();
- assertEquals(3, clusterNodes.size());
- /*
- * In versions prior to Redis 7.x, Redis does not natively support automatic port switching between TLS and non-TLS ports for CLUSTER SLOTS.
- * When using Redis 6.2.16 in a cluster mode with TLS, CLUSTER command returns the regular (non-TLS) port rather than the TLS port.
- */
- if (RedisVersionUtil.getRedisVersion(jc2.getConnectionFromSlot(0)).isLessThanOrEqualTo(RedisVersion.V7_0_0)) {
- assertTrue(clusterNodes.containsKey(nodeInfo1.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo2.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo3.toString()));
- } else {
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
- }
- jc2.get("foo");
- }
- }
-
- @Test
- public void testSSLWithoutPortMap() {
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
- .sslOptions(SslOptions.builder()
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks")
- .sslVerifyMode(SslVerifyMode.CA).build())
- .build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- Map clusterNodes = jc.getClusterNodes();
- assertEquals(3, clusterNodes.size());
- /**
- * In versions prior to Redis 7.x, Redis does not natively support automatic port switching between TLS and non-TLS ports for CLUSTER SLOTS.
- * When using Redis 6.2.16 in a cluster mode with TLS, CLUSTER command returns the regular (non-TLS) port rather than the TLS port.
- */
- if (RedisVersionUtil.getRedisVersion(jc.getConnectionFromSlot(0)).isLessThanOrEqualTo(RedisVersion.V7_0_0)) {
- assertTrue(clusterNodes.containsKey(nodeInfo1.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo2.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo3.toString()));
- } else {
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
- }
- }
- }
-
- @Test
- public void connectByIpAddress() {
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
- .sslOptions(SslOptions.builder()
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks").build())
- .hostAndPortMapper(hostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc.get("foo");
- }
- }
-
- @Test
- public void connectToNodesFailsWithSSLParametersAndNoHostMapping() {
- final SSLParameters sslParameters = new SSLParameters();
- sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(new HostAndPort("localhost", tlsEndpoint.getPort())))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
- .sslOptions(SslOptions.builder()
- .sslParameters(sslParameters)
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks").build())
- .hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc.get("foo");
- fail("It should fail after all cluster attempts.");
- } catch (JedisClusterOperationException e) {
- // initial connection to localhost works, but subsequent connections to nodes use 127.0.0.1
- // and fail hostname verification
- assertThat(e.getMessage(), anyOf(
- containsString("No more cluster attempts left."),
- containsString("Cluster retry deadline exceeded.")
- ));
- }
- }
-
- @Test
- public void connectToNodesSucceedsWithSSLParametersAndHostMapping() {
- final SSLParameters sslParameters = new SSLParameters();
- sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
- .sslOptions(SslOptions.builder()
- .sslParameters(sslParameters)
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks").build())
- .hostAndPortMapper(hostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc.get("foo");
- }
- }
-
- @Test
- public void connectByIpAddressFailsWithSSLParameters() {
- final SSLParameters sslParameters = new SSLParameters();
- sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
- .sslOptions(SslOptions.builder()
- .sslParameters(sslParameters)
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks").build())
- .hostAndPortMapper(hostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- } catch (JedisClusterOperationException e) {
- assertEquals("Could not initialize cluster slots cache.", e.getMessage());
- }
- }
-
- @Test
- public void connectWithCustomHostNameVerifier() {
- HostnameVerifier hostnameVerifier = new TlsUtil.BasicHostnameVerifier();
- HostnameVerifier localhostVerifier = new TlsUtil.LocalhostVerifier();
-
- SslOptions sslOptions = SslOptions.builder()
- .truststore(trustStorePath.toFile())
- .trustStoreType("jceks")
- .sslVerifyMode(SslVerifyMode.CA).build();
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(new HostAndPort("localhost", tlsEndpoint.getPort())))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).sslOptions(sslOptions)
- .hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc.get("foo");
- fail("It should fail after all cluster attempts.");
- } catch (JedisClusterOperationException e) {
- // initial connection made with 'localhost' but subsequent connections to nodes use 127.0.0.1
- // which causes custom hostname verification to fail
- assertThat(e.getMessage(), anyOf(
- containsString("No more cluster attempts left."),
- containsString("Cluster retry deadline exceeded.")
- ));
- }
-
- try (RedisClusterClient jc2 = RedisClusterClient.builder()
- .nodes(Collections.singleton(new HostAndPort("127.0.0.1", tlsEndpoint.getPort())))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).sslOptions(sslOptions)
- .hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- } catch (JedisClusterOperationException e) {
- assertEquals("Could not initialize cluster slots cache.", e.getMessage());
- }
-
- try (RedisClusterClient jc3 = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).sslOptions(sslOptions)
- .hostnameVerifier(localhostVerifier).hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc3.get("foo");
- }
- }
-
-}
diff --git a/src/test/java/redis/clients/jedis/SSLRedisClusterClientTest.java b/src/test/java/redis/clients/jedis/SSLRedisClusterClientTest.java
deleted file mode 100644
index 213d5389b0..0000000000
--- a/src/test/java/redis/clients/jedis/SSLRedisClusterClientTest.java
+++ /dev/null
@@ -1,317 +0,0 @@
-package redis.clients.jedis;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.anyOf;
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-import static redis.clients.jedis.util.TlsUtil.*;
-
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.SSLParameters;
-
-import io.redis.test.annotations.SinceRedisVersion;
-import io.redis.test.utils.RedisVersion;
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.Tag;
-import redis.clients.jedis.util.RedisVersionUtil;
-import redis.clients.jedis.util.TlsUtil;
-
-import redis.clients.jedis.exceptions.JedisClusterOperationException;
-
-@SinceRedisVersion(value = "7.0.0", message = "Redis 6.2.x returns non-tls port in CLUSTER SLOTS command. Enable for 6.2.x after test is fixed.")
-@Tag("integration")
-public class SSLRedisClusterClientTest extends RedisClusterClientTestBase {
-
- private static final int DEFAULT_REDIRECTIONS = 5;
- private static final ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig();
-
- protected static EndpointConfig tlsEndpoint;
-
- private final HostAndPortMapper hostAndPortMap = (HostAndPort hostAndPort) -> {
- String host = hostAndPort.getHost();
- int port = hostAndPort.getPort();
- if (host.equals("127.0.0.1")) {
- host = "localhost";
- }
- return new HostAndPort(host, port);
- };
-
- // don't map IP addresses so that we try to connect with host 127.0.0.1
- private final HostAndPortMapper portMap = (HostAndPort hostAndPort) -> {
- if ("localhost".equals(hostAndPort.getHost())) {
- return hostAndPort;
- }
- return new HostAndPort(hostAndPort.getHost(), hostAndPort.getPort());
- };
-
- private static final String trustStoreName = SSLRedisClusterClientTest.class.getSimpleName();
-
- @BeforeAll
- public static void prepare() {
- tlsEndpoint = Endpoints.getRedisEndpoint("cluster-unbound-tls");
- List trustedCertLocation = Collections.singletonList(tlsEndpoint.getCertificatesLocation());
- Path trustStorePath = TlsUtil.createAndSaveTestTruststore(trustStoreName, trustedCertLocation,"changeit");
-
- TlsUtil.setCustomTrustStore(trustStorePath, "changeit");
- }
-
- @AfterAll
- public static void teardownTrustStore() {
- TlsUtil.restoreOriginalTrustStore();
- }
-
- @Test
- public void testSSLDiscoverNodesAutomatically() {
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true)
- .hostAndPortMapper(hostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- Map clusterNodes = jc.getClusterNodes();
- assertEquals(3, clusterNodes.size());
- /**
- * In versions prior to Redis 7.x, Redis does not natively support automatic port switching between TLS and
- * non-TLS ports for CLUSTER SLOTS. When using Redis 6.2.16 in a cluster mode with TLS, CLUSTER command returns
- * the regular (non-TLS) port rather than the TLS port.
- */
- if (RedisVersionUtil.getRedisVersion(jc.getConnectionFromSlot(0)).isLessThanOrEqualTo(RedisVersion.V7_0_0)) {
- assertTrue(clusterNodes.containsKey(nodeInfo1.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo2.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo3.toString()));
- } else {
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
- }
-
- jc.get("foo");
- }
-
- try (RedisClusterClient jc2 = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true)
- .hostAndPortMapper(hostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- Map clusterNodes = jc2.getClusterNodes();
- assertEquals(3, clusterNodes.size());
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
- jc2.get("foo");
- }
- }
-
- @Test
- public void testSSLWithoutPortMap() {
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- Map clusterNodes = jc.getClusterNodes();
- assertEquals(3, clusterNodes.size());
- /**
- * In versions prior to Redis 7.x, Redis does not natively support automatic port switching between TLS and
- * non-TLS ports for CLUSTER SLOTS. When using Redis 6.2.16 in a cluster mode with TLS, CLUSTER command returns
- * the regular (non-TLS) port rather than the TLS port.
- */
- if (RedisVersionUtil.getRedisVersion(jc.getConnectionFromSlot(0)).isLessThanOrEqualTo(RedisVersion.V7_0_0)) {
- assertTrue(clusterNodes.containsKey(nodeInfo1.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo2.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo3.toString()));
- } else {
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
- assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
- }
- }
- }
-
- @Test
- public void connectByIpAddress() {
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true)
- .hostAndPortMapper(hostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc.get("foo");
- }
- }
-
- @Test
- public void connectToNodesFailsWithSSLParametersAndNoHostMapping() {
- final SSLParameters sslParameters = new SSLParameters();
- sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(new HostAndPort("localhost", tlsEndpoint.getPort())))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true)
- .sslParameters(sslParameters).hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc.get("foo");
- fail("It should fail after all cluster attempts.");
- } catch (JedisClusterOperationException e) {
- // initial connection to localhost works, but subsequent connections to nodes use 127.0.0.1
- // and fail hostname verification
- assertThat(e.getMessage(), anyOf(
- containsString("No more cluster attempts left."),
- containsString("Cluster retry deadline exceeded.")
- ));
- }
- }
-
- @Test
- @SinceRedisVersion(value = "7.0.0", message = "Redis 6.2.x returns non-tls port in CLUSTER SLOTS command. Enable for 6.2.x after test is fixed.")
- public void connectToNodesSucceedsWithSSLParametersAndHostMapping() {
- final SSLParameters sslParameters = new SSLParameters();
- sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true)
- .sslParameters(sslParameters).hostAndPortMapper(hostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc.get("foo");
- }
- }
-
- @Test
- public void connectByIpAddressFailsWithSSLParameters() {
- final SSLParameters sslParameters = new SSLParameters();
- sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true)
- .sslParameters(sslParameters).hostAndPortMapper(hostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
-// jc.get("key");
-// Assert.fail("There should be no reachable node in cluster.");
-//// } catch (JedisNoReachableClusterNodeException e) {
- } catch (JedisClusterOperationException e) {
-// assertEquals("No reachable node in cluster.", e.getMessage());
- assertEquals("Could not initialize cluster slots cache.", e.getMessage());
- }
- }
-
- @Test
- public void connectWithCustomHostNameVerifier() {
- HostnameVerifier hostnameVerifier = new TlsUtil.BasicHostnameVerifier();
- HostnameVerifier localhostVerifier = new TlsUtil.LocalhostVerifier();
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(new HostAndPort("localhost", tlsEndpoint.getPort())))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true)
- .hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc.get("foo");
- fail("It should fail after all cluster attempts.");
-// } catch (JedisClusterMaxAttemptsException e) {
- } catch (JedisClusterOperationException e) {
- // initial connection made with 'localhost' but subsequent connections to nodes use 127.0.0.1
- // which causes custom hostname verification to fail
- assertThat(e.getMessage(), anyOf(
- containsString("No more cluster attempts left."),
- containsString("Cluster retry deadline exceeded.")
- ));
- }
-
- try (RedisClusterClient jc2 = RedisClusterClient.builder()
- .nodes(Collections.singleton(new HostAndPort("127.0.0.1", tlsEndpoint.getPort())))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true)
- .hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
-// jc2.get("foo");
-// Assert.fail("There should be no reachable node in cluster.");
-//// } catch (JedisNoReachableClusterNodeException e) {
- } catch (JedisClusterOperationException e) {
- // JedisNoReachableClusterNodeException exception occurs from not being able to connect
- // since the socket factory fails the hostname verification
-// assertEquals("No reachable node in cluster.", e.getMessage());
- assertEquals("Could not initialize cluster slots cache.", e.getMessage());
- }
-
- try (RedisClusterClient jc3 = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true)
- .hostnameVerifier(localhostVerifier).hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- jc3.get("foo");
- }
- }
-
- @Test
- public void connectWithCustomSocketFactory() throws Exception {
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true)
- .sslSocketFactory(sslSocketFactoryForEnv(tlsEndpoint.getCertificatesLocation()))
- .hostAndPortMapper(portMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- assertEquals(3, jc.getClusterNodes().size());
- }
- }
-
- @Test
- public void connectWithEmptyTrustStore() throws Exception {
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
- .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true)
- .sslSocketFactory(createTrustNoOneSslSocketFactory()).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
- } catch (JedisClusterOperationException e) {
- assertEquals("Could not initialize cluster slots cache.", e.getMessage());
- }
- }
-
- @Test
- public void defaultHostAndPortUsedIfMapReturnsNull() {
- HostAndPortMapper nullHostAndPortMap = (HostAndPort hostAndPort) -> null;
-
- try (RedisClusterClient jc = RedisClusterClient.builder()
- .nodes(Collections.singleton(new HostAndPort("localhost", nodeInfo1.getPort())))
- .clientConfig(DefaultJedisClientConfig.builder().password(endpoint.getPassword()).ssl(false)
- .hostAndPortMapper(nullHostAndPortMap).build())
- .maxAttempts(DEFAULT_REDIRECTIONS)
- .poolConfig(DEFAULT_POOL_CONFIG)
- .build()) {
-
- Map clusterNodes = jc.getClusterNodes();
- assertEquals(3, clusterNodes.size());
- assertTrue(clusterNodes.containsKey(nodeInfo1.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo2.toString()));
- assertTrue(clusterNodes.containsKey(nodeInfo3.toString()));
- }
- }
-}
diff --git a/src/test/java/redis/clients/jedis/RedisClusterClientTest.java b/src/test/java/redis/clients/jedis/UnboundRedisClusterClientTest.java
similarity index 99%
rename from src/test/java/redis/clients/jedis/RedisClusterClientTest.java
rename to src/test/java/redis/clients/jedis/UnboundRedisClusterClientTest.java
index 86a81de88b..3b46c25f42 100644
--- a/src/test/java/redis/clients/jedis/RedisClusterClientTest.java
+++ b/src/test/java/redis/clients/jedis/UnboundRedisClusterClientTest.java
@@ -42,7 +42,7 @@
import redis.clients.jedis.util.Pool;
@Tag("integration")
-public class RedisClusterClientTest extends RedisClusterClientTestBase {
+public class UnboundRedisClusterClientTest extends UnboundRedisClusterClientTestBase {
private static final int DEFAULT_TIMEOUT = 2000; //sec
private static final int DEFAULT_REDIRECTIONS = 5;
diff --git a/src/test/java/redis/clients/jedis/RedisClusterClientTestBase.java b/src/test/java/redis/clients/jedis/UnboundRedisClusterClientTestBase.java
similarity index 98%
rename from src/test/java/redis/clients/jedis/RedisClusterClientTestBase.java
rename to src/test/java/redis/clients/jedis/UnboundRedisClusterClientTestBase.java
index 5989c92155..8adc218c29 100644
--- a/src/test/java/redis/clients/jedis/RedisClusterClientTestBase.java
+++ b/src/test/java/redis/clients/jedis/UnboundRedisClusterClientTestBase.java
@@ -13,7 +13,7 @@
import redis.clients.jedis.util.JedisClusterTestUtil;
@Tag("integration")
-public abstract class RedisClusterClientTestBase {
+public abstract class UnboundRedisClusterClientTestBase {
protected static EndpointConfig endpoint;
diff --git a/src/test/java/redis/clients/jedis/commands/jedis/ClusterCommandsTest.java b/src/test/java/redis/clients/jedis/commands/jedis/ClusterCommandsTest.java
index 806259593d..78d4296d3d 100644
--- a/src/test/java/redis/clients/jedis/commands/jedis/ClusterCommandsTest.java
+++ b/src/test/java/redis/clients/jedis/commands/jedis/ClusterCommandsTest.java
@@ -252,7 +252,6 @@ public void clusterShards() {
assertNotNull(nodeInfo.getIp());
assertNull(nodeInfo.getHostname());
assertNotNull(nodeInfo.getPort());
- assertNotNull(nodeInfo.getTlsPort()); // currently we are always starting Redis server with `tls-port`
assertNotNull(nodeInfo.getRole());
assertNotNull(nodeInfo.getReplicationOffset());
assertNotNull(nodeInfo.getHealth());
diff --git a/src/test/java/redis/clients/jedis/tls/ACLJedisIT.java b/src/test/java/redis/clients/jedis/tls/ACLJedisIT.java
new file mode 100644
index 0000000000..04cd6c74b9
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/ACLJedisIT.java
@@ -0,0 +1,74 @@
+package redis.clients.jedis.tls;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import redis.clients.jedis.DefaultJedisClientConfig;
+import redis.clients.jedis.Jedis;
+
+/**
+ * SSL/TLS tests for {@link Jedis} with ACL authentication (username + password).
+ *
+ * This test class focuses on testing the ssl(true) flag approach (using system truststore) with ACL
+ * credentials.
+ */
+
+public class ACLJedisIT extends JedisTlsTestBase {
+ /**
+ * Tests SSL connection with explicit ACL credentials (username + password).
+ */
+ @Test
+ public void connectWithSsl() {
+ try (Jedis jedis = new Jedis(aclEndpoint.getHost(), aclEndpoint.getPort(), true)) {
+ jedis.auth(aclEndpoint.getUsername(), aclEndpoint.getPassword());
+ assertEquals("PONG", jedis.ping());
+ }
+ }
+
+ /**
+ * Tests SSL connection using DefaultJedisClientConfig with ACL credentials.
+ */
+ @Test
+ public void connectWithConfig() {
+ try (Jedis jedis = new Jedis(aclEndpoint.getHostAndPort(),
+ DefaultJedisClientConfig.builder().ssl(true).build())) {
+ jedis.auth(aclEndpoint.getUsername(), aclEndpoint.getPassword());
+ assertEquals("PONG", jedis.ping());
+ }
+ }
+
+ /**
+ * Tests SSL connection using URL with credentials.
+ */
+ @Test
+ public void connectWithUrl() {
+ // The "rediss" scheme instructs jedis to open a SSL/TLS connection.
+ // Test with default user endpoint
+ try (
+ Jedis jedis = new Jedis(endpoint.getURIBuilder().defaultCredentials().build().toString())) {
+ assertEquals("PONG", jedis.ping());
+ }
+ // Test with ACL user endpoint
+ try (Jedis jedis = new Jedis(
+ aclEndpoint.getURIBuilder().defaultCredentials().build().toString())) {
+ assertEquals("PONG", jedis.ping());
+ }
+ }
+
+ /**
+ * Tests SSL connection using URI with credentials.
+ */
+ @Test
+ public void connectWithUri() {
+ // The "rediss" scheme instructs jedis to open a SSL/TLS connection.
+ // Test with default user endpoint
+ try (Jedis jedis = new Jedis(endpoint.getURIBuilder().defaultCredentials().build())) {
+ assertEquals("PONG", jedis.ping());
+ }
+ // Test with ACL user endpoint
+ try (Jedis jedis = new Jedis(aclEndpoint.getURIBuilder().defaultCredentials().build())) {
+ assertEquals("PONG", jedis.ping());
+ }
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/tls/ACLRedisClientIT.java b/src/test/java/redis/clients/jedis/tls/ACLRedisClientIT.java
new file mode 100644
index 0000000000..67b0a7027c
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/ACLRedisClientIT.java
@@ -0,0 +1,82 @@
+package redis.clients.jedis.tls;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import redis.clients.jedis.DefaultJedisClientConfig;
+import redis.clients.jedis.RedisClient;
+
+/**
+ * SSL/TLS tests for {@link RedisClient} with ACL authentication (username + password).
+ *
+ * This test class focuses on testing the ssl(true) flag approach (using system truststore) with ACL
+ * credentials.
+ */
+
+public class ACLRedisClientIT extends RedisClientTlsTestBase {
+ /**
+ * Tests SSL connection with explicit ACL credentials (username + password).
+ */
+ @Test
+ public void connectWithSsl() {
+ try (
+ RedisClient client = RedisClient.builder()
+ .hostAndPort(aclEndpoint.getHost(), aclEndpoint.getPort())
+ .clientConfig(DefaultJedisClientConfig.builder().ssl(true)
+ .user(aclEndpoint.getUsername()).password(aclEndpoint.getPassword()).build())
+ .build()) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+
+ /**
+ * Tests SSL connection using endpoint's client config builder (credentials from endpoint).
+ */
+ @Test
+ public void connectWithConfig() {
+ try (
+ RedisClient client = RedisClient.builder().hostAndPort(aclEndpoint.getHostAndPort())
+ .clientConfig(DefaultJedisClientConfig.builder().ssl(true)
+ .user(aclEndpoint.getUsername()).password(aclEndpoint.getPassword()).build())
+ .build()) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+
+ /**
+ * Tests SSL connection using URL with credentials.
+ */
+ @Test
+ public void connectWithUrl() {
+ // The "rediss" scheme instructs jedis to open a SSL/TLS connection.
+ // Test with default user endpoint
+ try (RedisClient client = RedisClient
+ .create(endpoint.getURIBuilder().defaultCredentials().build().toString())) {
+ assertEquals("PONG", client.ping());
+ }
+ // Test with ACL user endpoint
+ try (RedisClient client = RedisClient
+ .create(aclEndpoint.getURIBuilder().defaultCredentials().build().toString())) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+
+ /**
+ * Tests SSL connection using URI with credentials.
+ */
+ @Test
+ public void connectWithUri() {
+ // The "rediss" scheme instructs jedis to open a SSL/TLS connection.
+ // Test with default user endpoint
+ try (RedisClient client = RedisClient
+ .create(endpoint.getURIBuilder().defaultCredentials().build())) {
+ assertEquals("PONG", client.ping());
+ }
+ // Test with ACL user endpoint
+ try (RedisClient client = RedisClient
+ .create(aclEndpoint.getURIBuilder().defaultCredentials().build())) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/tls/ACLRedisClusterClientIT.java b/src/test/java/redis/clients/jedis/tls/ACLRedisClusterClientIT.java
new file mode 100644
index 0000000000..d67338de44
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/ACLRedisClusterClientIT.java
@@ -0,0 +1,196 @@
+package redis.clients.jedis.tls;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import static redis.clients.jedis.util.TlsUtil.*;
+
+import java.util.Collections;
+import java.util.Map;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLParameters;
+
+import org.junit.jupiter.api.Test;
+import redis.clients.jedis.*;
+import redis.clients.jedis.util.TlsUtil;
+import redis.clients.jedis.exceptions.JedisClusterOperationException;
+
+/**
+ * SSL/TLS Redis Cluster tests with ACL authentication
+ */
+public class ACLRedisClusterClientIT extends RedisClusterTestBase {
+
+ private static final int DEFAULT_REDIRECTIONS = 5;
+ private static final ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig();
+
+ @Test
+ public void testSSLDiscoverNodesAutomatically() {
+ DefaultJedisClientConfig config = DefaultJedisClientConfig.builder().user("default")
+ .password(tlsEndpoint.getPassword()).ssl(true).hostAndPortMapper(hostAndPortMap).build();
+
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort())).clientConfig(config)
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ Map clusterNodes = jc.getClusterNodes();
+ assertEquals(6, clusterNodes.size());
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
+ jc.get("foo");
+ }
+ }
+
+ @Test
+ public void testSSLWithoutPortMap() {
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().user("default")
+ .password(tlsEndpoint.getPassword()).ssl(true).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ Map clusterNodes = jc.getClusterNodes();
+ assertEquals(6, clusterNodes.size());
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
+ jc.get("foo");
+ }
+ }
+
+ @Test
+ public void connectByIpAddress() {
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(
+ DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword())
+ .ssl(true).hostAndPortMapper(hostAndPortMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc.get("foo");
+ }
+ }
+
+ @Test
+ public void connectToNodesFailsWithSSLParametersAndNoHostMapping() {
+ final SSLParameters sslParameters = new SSLParameters();
+ sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
+
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(new HostAndPort("localhost", tlsEndpoint.getPort())))
+ .clientConfig(
+ DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword())
+ .ssl(true).sslParameters(sslParameters).hostAndPortMapper(portMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc.get("foo");
+ fail("It should fail after all cluster attempts.");
+ } catch (JedisClusterOperationException e) {
+ // initial connection to localhost works, but subsequent connections to nodes use 127.0.0.1
+ // and fail hostname verification
+ assertThat(e.getMessage(), anyOf(containsString("No more cluster attempts left."),
+ containsString("Cluster retry deadline exceeded.")));
+ }
+ }
+
+ @Test
+ public void connectToNodesSucceedsWithSSLParametersAndHostMapping() {
+ final SSLParameters sslParameters = new SSLParameters();
+ sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
+
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(
+ DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword())
+ .ssl(true).sslParameters(sslParameters).hostAndPortMapper(hostAndPortMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc.get("foo");
+ }
+ }
+
+ @Test
+ public void connectByIpAddressFailsWithSSLParameters() {
+ final SSLParameters sslParameters = new SSLParameters();
+ sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
+
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(
+ DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword())
+ .ssl(true).sslParameters(sslParameters).hostAndPortMapper(hostAndPortMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ } catch (JedisClusterOperationException e) {
+ assertEquals("Could not initialize cluster slots cache.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void connectWithCustomHostNameVerifier() {
+ HostnameVerifier hostnameVerifier = new TlsUtil.BasicHostnameVerifier();
+ HostnameVerifier localhostVerifier = new TlsUtil.LocalhostVerifier();
+
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(new HostAndPort("localhost", tlsEndpoint.getPort())))
+ .clientConfig(
+ DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword())
+ .ssl(true).hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc.get("foo");
+ fail("It should fail after all cluster attempts.");
+ } catch (JedisClusterOperationException e) {
+ // initial connection made with 'localhost' but subsequent connections to nodes use 127.0.0.1
+ // which causes custom hostname verification to fail
+ assertThat(e.getMessage(), anyOf(containsString("No more cluster attempts left."),
+ containsString("Cluster retry deadline exceeded.")));
+ }
+
+ try (RedisClusterClient jc2 = RedisClusterClient.builder()
+ .nodes(Collections.singleton(new HostAndPort("127.0.0.1", tlsEndpoint.getPort())))
+ .clientConfig(
+ DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword())
+ .ssl(true).hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ } catch (JedisClusterOperationException e) {
+ // JedisNoReachableClusterNodeException exception occurs from not being able to connect since
+ // the socket factory fails the hostname verification
+ assertEquals("Could not initialize cluster slots cache.", e.getMessage());
+ }
+
+ try (RedisClusterClient jc3 = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(
+ DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword())
+ .ssl(true).hostnameVerifier(localhostVerifier).hostAndPortMapper(portMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc3.get("foo");
+ }
+ }
+
+ @Test
+ public void connectWithCustomSocketFactory() {
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().user("default")
+ .password(tlsEndpoint.getPassword()).ssl(true)
+ .sslSocketFactory(sslSocketFactoryForEnv(tlsEndpoint.getCertificatesLocation()))
+ .hostAndPortMapper(portMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ assertEquals(6, jc.getClusterNodes().size());
+ jc.get("foo");
+ }
+ }
+
+ @Test
+ public void connectWithEmptyTrustStore() throws Exception {
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(
+ DefaultJedisClientConfig.builder().user("default").password(tlsEndpoint.getPassword())
+ .ssl(true).sslSocketFactory(createTrustNoOneSslSocketFactory()).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc.get("foo");
+ } catch (JedisClusterOperationException e) {
+ assertEquals("Could not initialize cluster slots cache.", e.getMessage());
+ }
+ }
+
+}
diff --git a/src/test/java/redis/clients/jedis/tls/ACLRedisSentinelClientIT.java b/src/test/java/redis/clients/jedis/tls/ACLRedisSentinelClientIT.java
new file mode 100644
index 0000000000..57dfe3f613
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/ACLRedisSentinelClientIT.java
@@ -0,0 +1,99 @@
+package redis.clients.jedis.tls;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import io.redis.test.annotations.ConditionalOnEnv;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import redis.clients.jedis.DefaultJedisClientConfig;
+import redis.clients.jedis.EndpointConfig;
+import redis.clients.jedis.Endpoints;
+import redis.clients.jedis.RedisSentinelClient;
+import redis.clients.jedis.util.EnvCondition;
+import redis.clients.jedis.util.RedisVersionCondition;
+import redis.clients.jedis.util.TestEnvUtil;
+
+/**
+ * SSL/TLS tests for {@link RedisSentinelClient} with ACL authentication (username + password).
+ *
+ * This test class focuses on testing the ssl(true) flag approach (using system truststore) rather
+ * than explicit SslOptions configuration.
+ */
+@ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_SOURCE, enabled = false)
+public class ACLRedisSentinelClientIT extends RedisSentinelTlsTestBase {
+
+ // Endpoint for master with ACL authentication
+ private static EndpointConfig aclEndpoint;
+
+ @RegisterExtension
+ public static EnvCondition envCondition = new EnvCondition();
+
+ @RegisterExtension
+ public static RedisVersionCondition versionCondition = new RedisVersionCondition(
+ () -> Endpoints.getRedisEndpoint("standalone0-acl-tls"));
+
+ @BeforeAll
+ public static void setUp() {
+ aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls");
+ }
+
+ /**
+ * Tests SSL connection with explicit ACL credentials (username + password).
+ */
+ @Test
+ public void connectWithSsl() {
+ DefaultJedisClientConfig masterConfig = DefaultJedisClientConfig.builder()
+ .clientName("master-client").ssl(true).user(aclEndpoint.getUsername())
+ .password(aclEndpoint.getPassword()).hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build();
+
+ DefaultJedisClientConfig sentinelConfig = Endpoints.getRedisEndpoint("sentinel-standalone0-tls")
+ .getClientConfigBuilder().clientName("sentinel-client").ssl(true)
+ .hostAndPortMapper(SENTINEL_SSL_PORT_MAPPER).build();
+
+ try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME)
+ .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig)
+ .build()) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+
+ /**
+ * Tests SSL connection using endpoint's client config builder (credentials from endpoint).
+ */
+ @Test
+ public void connectWithConfig() {
+ DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder()
+ .clientName("master-client").ssl(true).hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build();
+
+ DefaultJedisClientConfig sentinelConfig = Endpoints.getRedisEndpoint("sentinel-standalone0-tls")
+ .getClientConfigBuilder().clientName("sentinel-client").ssl(true)
+ .hostAndPortMapper(SENTINEL_SSL_PORT_MAPPER).build();
+
+ try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME)
+ .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig)
+ .build()) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+
+ /**
+ * Tests SSL connection using SslOptions with truststore configuration.
+ */
+ @Test
+ public void connectWithSslOptions() {
+ DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder()
+ .clientName("master-client").sslOptions(sslOptions)
+ .hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build();
+
+ DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithSsl(sslOptions);
+
+ try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME)
+ .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig)
+ .build()) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/SSLJedisTest.java b/src/test/java/redis/clients/jedis/tls/JedisIT.java
similarity index 56%
rename from src/test/java/redis/clients/jedis/SSLJedisTest.java
rename to src/test/java/redis/clients/jedis/tls/JedisIT.java
index d536709638..539aac0408 100644
--- a/src/test/java/redis/clients/jedis/SSLJedisTest.java
+++ b/src/test/java/redis/clients/jedis/tls/JedisIT.java
@@ -1,37 +1,19 @@
-package redis.clients.jedis;
-
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.List;
-
-import org.junit.jupiter.api.AfterAll;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.Tag;
-import redis.clients.jedis.util.TlsUtil;
+package redis.clients.jedis.tls;
import static org.junit.jupiter.api.Assertions.assertEquals;
-@Tag("integration")
-public class SSLJedisTest {
-
- protected static EndpointConfig endpoint;
-
- private static final String trustStoreName = SSLJedisTest.class.getSimpleName();
-
- @BeforeAll
- public static void prepare() {
- endpoint = Endpoints.getRedisEndpoint("standalone0-tls");
- List trustedCertLocation = Collections.singletonList(endpoint.getCertificatesLocation());
- Path trustStorePath = TlsUtil.createAndSaveTestTruststore(trustStoreName, trustedCertLocation,"changeit");
+import org.junit.jupiter.api.Test;
- TlsUtil.setCustomTrustStore(trustStorePath, "changeit");
- }
+import redis.clients.jedis.DefaultJedisClientConfig;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisClientConfig;
- @AfterAll
- public static void teardownTrustStore() {
- TlsUtil.restoreOriginalTrustStore();
- }
+/**
+ * SSL/TLS tests for {@link Jedis} with basic authentication (password-only, no ACL).
+ *
+ * Uses the system truststore (ssl=true flag) for SSL connections.
+ */
+public class JedisIT extends JedisTlsTestBase {
@Test
public void connectWithSsl() {
@@ -52,13 +34,12 @@ public void connectWithConfig() {
@Test
public void connectWithConfigInterface() {
- try (Jedis jedis = new Jedis(endpoint.getHostAndPort(),
- new JedisClientConfig() {
- @Override
- public boolean isSsl() {
- return true;
- }
- })) {
+ try (Jedis jedis = new Jedis(endpoint.getHostAndPort(), new JedisClientConfig() {
+ @Override
+ public boolean isSsl() {
+ return true;
+ }
+ })) {
jedis.auth(endpoint.getPassword());
assertEquals("PONG", jedis.ping());
}
@@ -87,5 +68,4 @@ public void connectWithUri() {
assertEquals("PONG", jedis.ping());
}
}
-
}
diff --git a/src/test/java/redis/clients/jedis/tls/JedisSentinelPoolIT.java b/src/test/java/redis/clients/jedis/tls/JedisSentinelPoolIT.java
new file mode 100644
index 0000000000..760ce91380
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/JedisSentinelPoolIT.java
@@ -0,0 +1,103 @@
+package redis.clients.jedis.tls;
+
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import redis.clients.jedis.DefaultJedisClientConfig;
+import redis.clients.jedis.EndpointConfig;
+import redis.clients.jedis.Endpoints;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisSentinelPool;
+
+/**
+ * SSL/TLS tests for {@link JedisSentinelPool} using system truststore (ssl=true flag).
+ *
+ * Tests various combinations of SSL on master and sentinel connections:
+ *
+ * - Sentinel without SSL, Redis master with SSL
+ * - Sentinel with SSL, Redis master without SSL
+ * - Both sentinel and Redis master with SSL
+ *
+ */
+public class JedisSentinelPoolIT extends RedisSentinelTlsTestBase {
+
+ private static final GenericObjectPoolConfig POOL_CONFIG = new GenericObjectPoolConfig<>();
+
+ // Endpoints for different SSL configurations
+ private static EndpointConfig aclTlsEndpoint;
+ private static EndpointConfig aclEndpoint;
+ private static EndpointConfig sentinelTlsEndpoint;
+
+ @BeforeAll
+ public static void setUp() {
+ aclTlsEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls");
+ aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl");
+ sentinelTlsEndpoint = Endpoints.getRedisEndpoint("sentinel-standalone0-tls");
+ }
+
+ /**
+ * Tests sentinel without SSL connecting to Redis master with SSL.
+ */
+ @Test
+ public void sentinelWithoutSslConnectsToRedisWithSsl() {
+ DefaultJedisClientConfig masterConfig = aclTlsEndpoint.getClientConfigBuilder()
+ .clientName("master-client").hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build();
+
+ DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithoutSsl();
+
+ try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig,
+ sentinelConfig)) {
+ pool.getResource().close();
+ }
+
+ try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
+ masterConfig, sentinelConfig)) {
+ pool.getResource().close();
+ }
+ }
+
+ /**
+ * Tests sentinel with SSL connecting to Redis master without SSL.
+ */
+ @Test
+ public void sentinelWithSslConnectsToRedisWithoutSsl() {
+ DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder()
+ .clientName("master-client").build();
+
+ DefaultJedisClientConfig sentinelConfig = sentinelTlsEndpoint.getClientConfigBuilder()
+ .clientName("sentinel-client").hostAndPortMapper(SENTINEL_SSL_PORT_MAPPER).build();
+
+ try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig,
+ sentinelConfig)) {
+ pool.getResource().close();
+ }
+
+ try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
+ masterConfig, sentinelConfig)) {
+ pool.getResource().close();
+ }
+ }
+
+ /**
+ * Tests both sentinel and Redis master with SSL.
+ */
+ @Test
+ public void sentinelWithSslConnectsToRedisWithSsl() {
+ DefaultJedisClientConfig masterConfig = aclTlsEndpoint.getClientConfigBuilder()
+ .clientName("master-client").hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build();
+
+ DefaultJedisClientConfig sentinelConfig = sentinelTlsEndpoint.getClientConfigBuilder()
+ .clientName("sentinel-client").hostAndPortMapper(SENTINEL_SSL_PORT_MAPPER).build();
+
+ try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig,
+ sentinelConfig)) {
+ pool.getResource().close();
+ }
+
+ try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
+ masterConfig, sentinelConfig)) {
+ pool.getResource().close();
+ }
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/tls/JedisTlsTestBase.java b/src/test/java/redis/clients/jedis/tls/JedisTlsTestBase.java
new file mode 100644
index 0000000000..6b8c3e5d99
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/JedisTlsTestBase.java
@@ -0,0 +1,61 @@
+package redis.clients.jedis.tls;
+
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.params.provider.Arguments;
+
+import redis.clients.jedis.EndpointConfig;
+import redis.clients.jedis.Endpoints;
+import redis.clients.jedis.SslOptions;
+import redis.clients.jedis.SslVerifyMode;
+import redis.clients.jedis.util.TlsUtil;
+
+/**
+ * Abstract base class for SSL/TLS tests for {@link redis.clients.jedis.Jedis}.
+ */
+public abstract class JedisTlsTestBase {
+
+ private static final String TRUSTSTORE_PASSWORD = "changeit";
+
+ protected static EndpointConfig endpoint;
+ protected static EndpointConfig aclEndpoint;
+ protected static Path trustStorePath;
+ protected static SslOptions sslOptions;
+
+ @BeforeAll
+ public static void setUpTrustStore() {
+ endpoint = Endpoints.getRedisEndpoint("standalone0-tls");
+ aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls");
+
+ List trustedCertLocation = Arrays.asList(endpoint.getCertificatesLocation(),
+ aclEndpoint.getCertificatesLocation());
+ trustStorePath = TlsUtil.createAndSaveTestTruststore(JedisTlsTestBase.class.getSimpleName(),
+ trustedCertLocation, TRUSTSTORE_PASSWORD);
+
+ TlsUtil.setCustomTrustStore(trustStorePath, TRUSTSTORE_PASSWORD);
+ sslOptions = createTruststoreSslOptions();
+ }
+
+ @AfterAll
+ public static void tearDownTrustStore() {
+ TlsUtil.restoreOriginalTrustStore();
+ }
+
+ protected static SslOptions createTruststoreSslOptions() {
+ return SslOptions.builder().truststore(trustStorePath.toFile()).trustStoreType("jceks")
+ .sslVerifyMode(SslVerifyMode.CA).build();
+ }
+
+ protected static Stream sslOptionsProvider() {
+ return Stream.of(Arguments.of("truststore", createTruststoreSslOptions()),
+ Arguments.of("insecure", SslOptions.builder().sslVerifyMode(SslVerifyMode.INSECURE).build()),
+ Arguments.of("ssl-protocol",
+ SslOptions.builder().sslProtocol("SSL").truststore(trustStorePath.toFile())
+ .trustStoreType("jceks").sslVerifyMode(SslVerifyMode.CA).build()));
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/tls/RedisClientIT.java b/src/test/java/redis/clients/jedis/tls/RedisClientIT.java
new file mode 100644
index 0000000000..897737264d
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/RedisClientIT.java
@@ -0,0 +1,63 @@
+package redis.clients.jedis.tls;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+import redis.clients.jedis.DefaultJedisClientConfig;
+import redis.clients.jedis.RedisClient;
+
+/**
+ * SSL/TLS tests for {@link RedisClient} with basic authentication (password-only, no ACL).
+ *
+ * Uses the system truststore (ssl=true flag) for SSL connections.
+ */
+public class RedisClientIT extends RedisClientTlsTestBase {
+
+ @Test
+ public void connectWithSsl() {
+ try (RedisClient client = RedisClient.builder()
+ .hostAndPort(endpoint.getHost(), endpoint.getPort())
+ .clientConfig(
+ DefaultJedisClientConfig.builder().ssl(true).password(endpoint.getPassword()).build())
+ .build()) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+
+ @Test
+ public void connectWithConfig() {
+ try (RedisClient client = RedisClient.builder().hostAndPort(endpoint.getHostAndPort())
+ .clientConfig(
+ DefaultJedisClientConfig.builder().ssl(true).password(endpoint.getPassword()).build())
+ .build()) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+
+ /**
+ * Tests opening a default SSL/TLS connection to redis using "rediss://" scheme url.
+ */
+ @Test
+ public void connectWithUrl() {
+ // The "rediss" scheme instructs jedis to open a SSL/TLS connection.
+ // URI includes credentials via defaultCredentials()
+ try (RedisClient client = RedisClient
+ .create(endpoint.getURIBuilder().defaultCredentials().build().toString())) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+
+ /**
+ * Tests opening a default SSL/TLS connection to redis.
+ */
+ @Test
+ public void connectWithUri() {
+ // The "rediss" scheme instructs jedis to open a SSL/TLS connection.
+ // URI includes credentials via defaultCredentials()
+ try (RedisClient client = RedisClient
+ .create(endpoint.getURIBuilder().defaultCredentials().build())) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/tls/RedisClientTlsTestBase.java b/src/test/java/redis/clients/jedis/tls/RedisClientTlsTestBase.java
new file mode 100644
index 0000000000..c90eb80b8b
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/RedisClientTlsTestBase.java
@@ -0,0 +1,61 @@
+package redis.clients.jedis.tls;
+
+import java.nio.file.Path;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.params.provider.Arguments;
+
+import redis.clients.jedis.EndpointConfig;
+import redis.clients.jedis.Endpoints;
+import redis.clients.jedis.SslOptions;
+import redis.clients.jedis.SslVerifyMode;
+import redis.clients.jedis.util.TlsUtil;
+
+/**
+ * Abstract base class for SSL/TLS tests for {@link redis.clients.jedis.RedisClient}.
+ */
+public abstract class RedisClientTlsTestBase {
+
+ private static final String TRUSTSTORE_PASSWORD = "changeit";
+
+ protected static EndpointConfig endpoint;
+ protected static EndpointConfig aclEndpoint;
+ protected static Path trustStorePath;
+ protected static SslOptions sslOptions;
+
+ @BeforeAll
+ public static void setUpTrustStore() {
+ endpoint = Endpoints.getRedisEndpoint("standalone0-tls");
+ aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls");
+
+ List trustedCertLocation = Arrays.asList(endpoint.getCertificatesLocation(),
+ aclEndpoint.getCertificatesLocation());
+ trustStorePath = TlsUtil.createAndSaveTestTruststore(
+ RedisClientTlsTestBase.class.getSimpleName(), trustedCertLocation, TRUSTSTORE_PASSWORD);
+
+ TlsUtil.setCustomTrustStore(trustStorePath, TRUSTSTORE_PASSWORD);
+ sslOptions = createTruststoreSslOptions();
+ }
+
+ @AfterAll
+ public static void tearDownTrustStore() {
+ TlsUtil.restoreOriginalTrustStore();
+ }
+
+ protected static SslOptions createTruststoreSslOptions() {
+ return SslOptions.builder().truststore(trustStorePath.toFile()).trustStoreType("jceks")
+ .sslVerifyMode(SslVerifyMode.CA).build();
+ }
+
+ protected static Stream sslOptionsProvider() {
+ return Stream.of(Arguments.of("truststore", createTruststoreSslOptions()),
+ Arguments.of("insecure", SslOptions.builder().sslVerifyMode(SslVerifyMode.INSECURE).build()),
+ Arguments.of("ssl-protocol",
+ SslOptions.builder().sslProtocol("SSL").truststore(trustStorePath.toFile())
+ .trustStoreType("jceks").sslVerifyMode(SslVerifyMode.CA).build()));
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/tls/RedisClusterClientIT.java b/src/test/java/redis/clients/jedis/tls/RedisClusterClientIT.java
new file mode 100644
index 0000000000..23706be076
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/RedisClusterClientIT.java
@@ -0,0 +1,187 @@
+package redis.clients.jedis.tls;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+import static redis.clients.jedis.util.TlsUtil.*;
+
+import java.util.Collections;
+import java.util.Map;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLParameters;
+
+import io.redis.test.annotations.SinceRedisVersion;
+import org.junit.jupiter.api.Test;
+import redis.clients.jedis.*;
+import redis.clients.jedis.util.TlsUtil;
+import redis.clients.jedis.exceptions.JedisClusterOperationException;
+
+public class RedisClusterClientIT extends RedisClusterTestBase {
+
+ private static final int DEFAULT_REDIRECTIONS = 3;
+ private static final ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig();
+
+ @Test
+ public void testSSLDiscoverNodesAutomatically() {
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(createSslOptions()).hostAndPortMapper(hostAndPortMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ Map clusterNodes = jc.getClusterNodes();
+ assertEquals(6, clusterNodes.size());
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
+ jc.get("foo");
+ }
+ }
+
+ @Test
+ public void testSSLWithoutPortMap() {
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(
+ DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ Map clusterNodes = jc.getClusterNodes();
+ assertEquals(6, clusterNodes.size());
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
+ jc.get("foo");
+ }
+ }
+
+ @Test
+ public void connectByIpAddress() {
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(createSslOptions()).hostAndPortMapper(hostAndPortMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc.get("foo");
+ }
+ }
+
+ @Test
+ public void connectToNodesFailsWithSSLParametersAndNoHostMapping() {
+ final SSLParameters sslParameters = new SSLParameters();
+ sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
+
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(new HostAndPort("localhost", tlsEndpoint.getPort())))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(createSslOptions()).sslParameters(sslParameters).hostAndPortMapper(portMap)
+ .build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc.get("foo");
+ fail("It should fail after all cluster attempts.");
+ } catch (JedisClusterOperationException e) {
+ // initial connection to localhost works, but subsequent connections to nodes use 127.0.0.1
+ // and fail hostname verification
+ assertThat(e.getMessage(), anyOf(containsString("No more cluster attempts left."),
+ containsString("Cluster retry deadline exceeded.")));
+ }
+ }
+
+ @Test
+ public void connectToNodesSucceedsWithSSLParametersAndHostMapping() {
+ final SSLParameters sslParameters = new SSLParameters();
+ sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
+
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(createSslOptions()).sslParameters(sslParameters)
+ .hostAndPortMapper(hostAndPortMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc.get("foo");
+ }
+ }
+
+ @Test
+ public void connectByIpAddressFailsWithSSLParameters() {
+ final SSLParameters sslParameters = new SSLParameters();
+ sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
+
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(createSslOptions()).sslParameters(sslParameters)
+ .hostAndPortMapper(hostAndPortMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ } catch (JedisClusterOperationException e) {
+ assertEquals("Could not initialize cluster slots cache.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void connectWithCustomHostNameVerifier() {
+ HostnameVerifier hostnameVerifier = new TlsUtil.BasicHostnameVerifier();
+ HostnameVerifier localhostVerifier = new TlsUtil.LocalhostVerifier();
+
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(new HostAndPort("localhost", tlsEndpoint.getPort())))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .ssl(true).hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc.get("foo");
+ fail("It should fail after all cluster attempts.");
+ } catch (JedisClusterOperationException e) {
+ // initial connection made with 'localhost' but subsequent connections to nodes use 127.0.0.1
+ // which causes custom hostname verification to fail
+ assertThat(e.getMessage(), anyOf(containsString("No more cluster attempts left."),
+ containsString("Cluster retry deadline exceeded.")));
+ }
+
+ try (RedisClusterClient jc2 = RedisClusterClient.builder()
+ .nodes(Collections.singleton(new HostAndPort("127.0.0.1", tlsEndpoint.getPort())))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .ssl(true).hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ } catch (JedisClusterOperationException e) {
+ // JedisNoReachableClusterNodeException exception occurs from not being able to connect
+ // since the socket factory fails the hostname verification
+ assertEquals("Could not initialize cluster slots cache.", e.getMessage());
+ }
+
+ try (RedisClusterClient jc3 = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .ssl(true).hostnameVerifier(localhostVerifier).hostAndPortMapper(portMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc3.get("foo");
+ }
+ }
+
+ @Test
+ public void connectWithCustomSocketFactory() throws Exception {
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(
+ DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword()).ssl(true)
+ .sslSocketFactory(sslSocketFactoryForEnv(tlsEndpoint.getCertificatesLocation()))
+ .hostAndPortMapper(portMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ assertEquals(6, jc.getClusterNodes().size());
+ jc.get("foo");
+ }
+ }
+
+ @Test
+ public void connectWithEmptyTrustStore() throws Exception {
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .ssl(true).sslSocketFactory(createTrustNoOneSslSocketFactory()).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ } catch (JedisClusterOperationException e) {
+ assertEquals("Could not initialize cluster slots cache.", e.getMessage());
+ }
+ }
+
+}
diff --git a/src/test/java/redis/clients/jedis/tls/RedisClusterTestBase.java b/src/test/java/redis/clients/jedis/tls/RedisClusterTestBase.java
new file mode 100644
index 0000000000..1d7249275d
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/RedisClusterTestBase.java
@@ -0,0 +1,120 @@
+package redis.clients.jedis.tls;
+
+import java.nio.file.Path;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+
+import io.redis.test.annotations.ConditionalOnEnv;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import redis.clients.jedis.DefaultJedisClientConfig;
+import redis.clients.jedis.EndpointConfig;
+import redis.clients.jedis.Endpoints;
+import redis.clients.jedis.HostAndPort;
+import redis.clients.jedis.HostAndPortMapper;
+import redis.clients.jedis.RedisClusterClient;
+import redis.clients.jedis.SslOptions;
+import redis.clients.jedis.util.*;
+
+/**
+ * Abstract base class for SSL/TLS Redis cluster tests.
+ *
+ * This class provides common setup and teardown for TLS-enabled Redis cluster tests, including
+ * truststore initialization and cluster client configuration.
+ *
+ * Uses the {@code cluster-stable-tls} endpoint for stable integration tests.
+ *
+ * Note: The {@link RedisVersionCondition} and {@link EnabledOnCommandCondition} extensions use the
+ * non-TLS {@code cluster-stable} endpoint for version/command checks because JUnit 5 extensions run
+ * before {@code @BeforeAll} methods where the truststore is configured.
+ */
+@ConditionalOnEnv(value = TestEnvUtil.ENV_OSS_SOURCE, enabled = false)
+public abstract class RedisClusterTestBase {
+
+ private static final String ENDPOINT_NAME = "cluster-stable-tls";
+ /**
+ * Non-TLS endpoint used for version and command checks. Extensions run before @BeforeAll, so we
+ * can't use TLS endpoints for these checks since the truststore isn't configured yet.
+ */
+ private static final String VERSION_CHECK_ENDPOINT_NAME = "cluster-stable";
+ private static final String TRUSTSTORE_PASSWORD = "changeit";
+
+ @RegisterExtension
+ public static EnvCondition envCondition = new EnvCondition();
+
+ @RegisterExtension
+ public EnabledOnCommandCondition enabledOnCommandCondition = new EnabledOnCommandCondition(
+ () -> Endpoints.getRedisEndpoint(VERSION_CHECK_ENDPOINT_NAME));
+
+ protected static EndpointConfig tlsEndpoint;
+ protected static Path trustStorePath;
+
+ protected RedisClusterClient cluster;
+
+ /**
+ * HostAndPortMapper that maps IP addresses (127.0.0.1) to localhost for hostname verification.
+ */
+ protected final HostAndPortMapper hostAndPortMap = (HostAndPort hostAndPort) -> {
+ String host = hostAndPort.getHost();
+ int port = hostAndPort.getPort();
+ if ("127.0.0.1".equals(host)) {
+ host = "localhost";
+ }
+ return new HostAndPort(host, port);
+ };
+
+ /**
+ * HostAndPortMapper that only maps localhost, leaving IP addresses unchanged. Useful for testing
+ * hostname verification failures.
+ */
+ protected final HostAndPortMapper portMap = (HostAndPort hostAndPort) -> {
+ if ("localhost".equals(hostAndPort.getHost())) {
+ return hostAndPort;
+ }
+ return new HostAndPort(hostAndPort.getHost(), hostAndPort.getPort());
+ };
+
+ @BeforeAll
+ public static void prepareEndpointAndTrustStore() {
+ tlsEndpoint = Endpoints.getRedisEndpoint(ENDPOINT_NAME);
+ List trustedCertLocation = Collections
+ .singletonList(tlsEndpoint.getCertificatesLocation());
+ trustStorePath = TlsUtil.createAndSaveTestTruststore(RedisClusterTestBase.class.getSimpleName(),
+ trustedCertLocation, TRUSTSTORE_PASSWORD);
+ TlsUtil.setCustomTrustStore(trustStorePath, TRUSTSTORE_PASSWORD);
+ }
+
+ @AfterAll
+ public static void teardownTrustStore() {
+ TlsUtil.restoreOriginalTrustStore();
+ }
+
+ @BeforeEach
+ public void setUp() {
+ SslOptions sslOptions = SslOptions.builder().truststore(trustStorePath.toFile())
+ .trustStoreType("jceks").build();
+
+ cluster = RedisClusterClient.builder().nodes(new HashSet<>(tlsEndpoint.getHostsAndPorts()))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(sslOptions).hostAndPortMapper(hostAndPortMap).build())
+ .build();
+ cluster.flushAll();
+ }
+
+ @AfterEach
+ public void tearDown() {
+ if (cluster != null) {
+ cluster.flushAll();
+ cluster.close();
+ }
+ }
+
+ protected static SslOptions createSslOptions() {
+ return SslOptions.builder().truststore(trustStorePath.toFile()).trustStoreType("jceks").build();
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/tls/RedisSentinelClientIT.java b/src/test/java/redis/clients/jedis/tls/RedisSentinelClientIT.java
new file mode 100644
index 0000000000..8137e75c8f
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/RedisSentinelClientIT.java
@@ -0,0 +1,55 @@
+package redis.clients.jedis.tls;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import redis.clients.jedis.DefaultJedisClientConfig;
+import redis.clients.jedis.EndpointConfig;
+import redis.clients.jedis.Endpoints;
+import redis.clients.jedis.RedisSentinelClient;
+import redis.clients.jedis.SslOptions;
+
+/**
+ * SSL/TLS tests for {@link RedisSentinelClient} with basic authentication (password-only, no ACL).
+ *
+ * Tests various SSL/TLS connection configurations including:
+ *
+ * - Basic SSL connection with truststore
+ * - Insecure SSL mode (no certificate verification)
+ * - Custom SSL protocol
+ *
+ *
+ * This test class uses the default user with password authentication instead of ACL user. The
+ * sentinel connection does not use SSL, only the master connection uses SSL.
+ */
+public class RedisSentinelClientIT extends RedisSentinelTlsTestBase {
+
+ // Endpoint for master with default user (password-only, no ACL)
+ private static EndpointConfig masterEndpoint;
+
+ @BeforeAll
+ public static void setUp() {
+ masterEndpoint = Endpoints.getRedisEndpoint("standalone0-tls");
+ }
+
+ @ParameterizedTest(name = "connectWithSsl_{0}")
+ @MethodSource("sslOptionsProvider")
+ void connectWithSsl(String testName, SslOptions ssl) {
+ DefaultJedisClientConfig masterConfig = DefaultJedisClientConfig.builder()
+ .clientName("master-client").sslOptions(ssl).password(masterEndpoint.getPassword())
+ .hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build();
+
+ // Sentinel requires authentication but does not use SSL
+ DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithoutSsl();
+
+ try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME)
+ .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig)
+ .build()) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+
+}
diff --git a/src/test/java/redis/clients/jedis/tls/RedisSentinelTlsTestBase.java b/src/test/java/redis/clients/jedis/tls/RedisSentinelTlsTestBase.java
new file mode 100644
index 0000000000..26dadb792f
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/RedisSentinelTlsTestBase.java
@@ -0,0 +1,85 @@
+package redis.clients.jedis.tls;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.params.provider.Arguments;
+
+import redis.clients.jedis.DefaultJedisClientConfig;
+import redis.clients.jedis.EndpointConfig;
+import redis.clients.jedis.Endpoints;
+import redis.clients.jedis.HostAndPort;
+import redis.clients.jedis.HostAndPortMapper;
+import redis.clients.jedis.SslOptions;
+import redis.clients.jedis.SslVerifyMode;
+import redis.clients.jedis.util.TlsUtil;
+
+/**
+ * Abstract base class for Redis Sentinel TLS integration tests.
+ */
+public abstract class RedisSentinelTlsTestBase {
+
+ protected static final String MASTER_NAME = "aclmaster";
+ private static final String TRUSTSTORE_PASSWORD = "changeit";
+ private static final String TRUSTSTORE_TYPE = "jceks";
+
+ protected static EndpointConfig sentinel;
+ protected static Set sentinels = new HashSet<>();
+ protected static Path trustStorePath;
+ protected static SslOptions sslOptions;
+
+ protected static final HostAndPortMapper SENTINEL_SSL_PORT_MAPPER = (
+ HostAndPort hap) -> new HostAndPort(hap.getHost(), hap.getPort() + 10000);
+
+ protected static final HostAndPortMapper PRIMARY_SSL_PORT_MAPPER = (
+ HostAndPort hap) -> new HostAndPort(hap.getHost(), hap.getPort() + 11);
+
+ @BeforeAll
+ public static void setupSentinelTls() {
+ sentinel = Endpoints.getRedisEndpoint("sentinel-standalone0");
+ sentinels.add(sentinel.getHostAndPort());
+
+ List trustedCertLocation = Collections
+ .singletonList(Paths.get("redis1-2-5-8-sentinel/work/tls"));
+ trustStorePath = TlsUtil.createAndSaveTestTruststore(
+ RedisSentinelTlsTestBase.class.getSimpleName(), trustedCertLocation, TRUSTSTORE_PASSWORD);
+ sslOptions = createTruststoreSslOptions();
+
+ TlsUtil.setCustomTrustStore(trustStorePath, TRUSTSTORE_PASSWORD);
+ }
+
+ @AfterAll
+ public static void teardownTrustStore() {
+ TlsUtil.restoreOriginalTrustStore();
+ }
+
+ protected static SslOptions createTruststoreSslOptions() {
+ return SslOptions.builder().truststore(trustStorePath.toFile()).trustStoreType(TRUSTSTORE_TYPE)
+ .sslVerifyMode(SslVerifyMode.CA).build();
+ }
+
+ protected static DefaultJedisClientConfig createSentinelConfigWithSsl(SslOptions ssl) {
+ return Endpoints.getRedisEndpoint("sentinel-standalone0-tls").getClientConfigBuilder()
+ .clientName("sentinel-client").sslOptions(ssl).hostAndPortMapper(SENTINEL_SSL_PORT_MAPPER)
+ .build();
+ }
+
+ protected static DefaultJedisClientConfig createSentinelConfigWithoutSsl() {
+ return sentinel.getClientConfigBuilder().clientName("sentinel-client").build();
+ }
+
+ protected static Stream sslOptionsProvider() {
+ return Stream.of(Arguments.of("truststore", createTruststoreSslOptions()),
+ Arguments.of("insecure", SslOptions.builder().sslVerifyMode(SslVerifyMode.INSECURE).build()),
+ Arguments.of("ssl-protocol",
+ SslOptions.builder().sslProtocol("SSL").truststore(trustStorePath.toFile())
+ .trustStoreType(TRUSTSTORE_TYPE).sslVerifyMode(SslVerifyMode.CA).build()));
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/tls/SSLOptionsJedisIT.java b/src/test/java/redis/clients/jedis/tls/SSLOptionsJedisIT.java
new file mode 100644
index 0000000000..74eb13b378
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/SSLOptionsJedisIT.java
@@ -0,0 +1,61 @@
+package redis.clients.jedis.tls;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import redis.clients.jedis.DefaultJedisClientConfig;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.SslOptions;
+
+/**
+ * SSL/TLS tests for {@link Jedis} using SslOptions builder pattern.
+ *
+ * Tests various SSL/TLS connection configurations including:
+ *
+ * - Basic SSL connection with truststore
+ * - Insecure SSL mode (no certificate verification)
+ * - Custom SSL protocol
+ * - ACL authentication over SSL
+ *
+ */
+public class SSLOptionsJedisIT extends JedisTlsTestBase {
+
+ /**
+ * Tests connecting to Redis with various SSL configurations using DefaultJedisClientConfig.
+ */
+ @ParameterizedTest(name = "connectWithSsl_{0}")
+ @MethodSource("sslOptionsProvider")
+ void connectWithSsl(String testName, SslOptions ssl) {
+ try (Jedis jedis = new Jedis(endpoint.getHostAndPort(),
+ DefaultJedisClientConfig.builder().sslOptions(ssl).build())) {
+ jedis.auth(endpoint.getPassword());
+ assertEquals("PONG", jedis.ping());
+ }
+ }
+
+ /**
+ * Tests connecting to Redis with various SSL configurations using endpoint's client config.
+ */
+ @ParameterizedTest(name = "connectWithClientConfig_{0}")
+ @MethodSource("sslOptionsProvider")
+ void connectWithClientConfig(String testName, SslOptions ssl) {
+ try (Jedis jedis = new Jedis(endpoint.getHostAndPort(),
+ endpoint.getClientConfigBuilder().sslOptions(ssl).build())) {
+ assertEquals("PONG", jedis.ping());
+ }
+ }
+
+ /**
+ * Tests ACL authentication over SSL.
+ */
+ @Test
+ public void connectWithAcl() {
+ try (Jedis jedis = new Jedis(aclEndpoint.getHostAndPort(),
+ aclEndpoint.getClientConfigBuilder().sslOptions(sslOptions).build())) {
+ assertEquals("PONG", jedis.ping());
+ }
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/tls/SSLOptionsJedisSentinelPoolIT.java b/src/test/java/redis/clients/jedis/tls/SSLOptionsJedisSentinelPoolIT.java
new file mode 100644
index 0000000000..b30dc9c867
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/SSLOptionsJedisSentinelPoolIT.java
@@ -0,0 +1,104 @@
+package redis.clients.jedis.tls;
+
+import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+import redis.clients.jedis.DefaultJedisClientConfig;
+import redis.clients.jedis.EndpointConfig;
+import redis.clients.jedis.Endpoints;
+import redis.clients.jedis.Jedis;
+import redis.clients.jedis.JedisSentinelPool;
+
+/**
+ * SSL/TLS tests for {@link JedisSentinelPool} using SslOptions builder pattern.
+ *
+ * Tests various combinations of SSL on master and sentinel connections:
+ *
+ * - Sentinel without SSL, Redis master with SSL (using SslOptions)
+ * - Sentinel with SSL (using SslOptions), Redis master without SSL
+ * - Both sentinel and Redis master with SSL (using SslOptions)
+ *
+ */
+public class SSLOptionsJedisSentinelPoolIT extends RedisSentinelTlsTestBase {
+
+ private static final GenericObjectPoolConfig POOL_CONFIG = new GenericObjectPoolConfig<>();
+
+ // Endpoints for different SSL configurations
+ private static EndpointConfig aclTlsEndpoint;
+ private static EndpointConfig aclEndpoint;
+ private static EndpointConfig sentinelTlsEndpoint;
+
+ @BeforeAll
+ public static void setUp() {
+ aclTlsEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls");
+ aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl");
+ sentinelTlsEndpoint = Endpoints.getRedisEndpoint("sentinel-standalone0-tls");
+ }
+
+ /**
+ * Tests sentinel without SSL connecting to Redis master with SSL using SslOptions.
+ */
+ @Test
+ public void sentinelWithoutSslConnectsToRedisWithSsl() {
+ DefaultJedisClientConfig masterConfig = aclTlsEndpoint.getClientConfigBuilder()
+ .clientName("master-client").sslOptions(sslOptions)
+ .hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build();
+
+ DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithoutSsl();
+
+ try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig,
+ sentinelConfig)) {
+ pool.getResource().close();
+ }
+
+ try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
+ masterConfig, sentinelConfig)) {
+ pool.getResource().close();
+ }
+ }
+
+ /**
+ * Tests sentinel with SSL (using SslOptions) connecting to Redis master without SSL.
+ */
+ @Test
+ public void sentinelWithSslConnectsToRedisWithoutSsl() {
+ DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder()
+ .clientName("master-client").build();
+
+ DefaultJedisClientConfig sentinelConfig = sentinelTlsEndpoint.getClientConfigBuilder()
+ .sslOptions(sslOptions).hostAndPortMapper(SENTINEL_SSL_PORT_MAPPER).build();
+
+ try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig,
+ sentinelConfig)) {
+ pool.getResource().close();
+ }
+
+ try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
+ masterConfig, sentinelConfig)) {
+ pool.getResource().close();
+ }
+ }
+
+ /**
+ * Tests both sentinel and Redis master with SSL using SslOptions.
+ */
+ @Test
+ public void sentinelWithSslConnectsToRedisWithSsl() {
+ DefaultJedisClientConfig masterConfig = aclTlsEndpoint.getClientConfigBuilder()
+ .clientName("master-client").sslOptions(sslOptions)
+ .hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build();
+
+ DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithSsl(sslOptions);
+
+ try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, masterConfig,
+ sentinelConfig)) {
+ pool.getResource().close();
+ }
+
+ try (JedisSentinelPool pool = new JedisSentinelPool(MASTER_NAME, sentinels, POOL_CONFIG,
+ masterConfig, sentinelConfig)) {
+ pool.getResource().close();
+ }
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/tls/SSLOptionsRedisClientIT.java b/src/test/java/redis/clients/jedis/tls/SSLOptionsRedisClientIT.java
new file mode 100644
index 0000000000..ae13a62710
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/SSLOptionsRedisClientIT.java
@@ -0,0 +1,45 @@
+package redis.clients.jedis.tls;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import redis.clients.jedis.RedisClient;
+import redis.clients.jedis.SslOptions;
+
+/**
+ * SSL/TLS tests for {@link RedisClient} using SslOptions builder pattern.
+ *
+ * Tests various SSL/TLS connection configurations including:
+ *
+ * - Basic SSL connection with truststore
+ * - Insecure SSL mode (no certificate verification)
+ * - Custom SSL protocol
+ * - ACL authentication over SSL
+ *
+ */
+public class SSLOptionsRedisClientIT extends RedisClientTlsTestBase {
+
+ @ParameterizedTest(name = "connectWithSsl_{0}")
+ @MethodSource("sslOptionsProvider")
+ void connectWithSsl(String testName, SslOptions ssl) {
+ try (RedisClient client = RedisClient.builder().hostAndPort(endpoint.getHostAndPort())
+ .clientConfig(endpoint.getClientConfigBuilder().sslOptions(ssl).build()).build()) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+
+ /**
+ * Tests ACL authentication over SSL.
+ */
+ @Test
+ public void connectWithAcl() {
+ try (RedisClient client = RedisClient.builder().hostAndPort(aclEndpoint.getHostAndPort())
+ .clientConfig(aclEndpoint.getClientConfigBuilder().sslOptions(sslOptions).build())
+ .build()) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+}
diff --git a/src/test/java/redis/clients/jedis/tls/SSLOptionsRedisClusterClientIT.java b/src/test/java/redis/clients/jedis/tls/SSLOptionsRedisClusterClientIT.java
new file mode 100644
index 0000000000..7ec79760b6
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/SSLOptionsRedisClusterClientIT.java
@@ -0,0 +1,175 @@
+package redis.clients.jedis.tls;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
+import java.util.Collections;
+import java.util.Map;
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLParameters;
+
+import org.junit.jupiter.api.Test;
+import redis.clients.jedis.*;
+import redis.clients.jedis.exceptions.JedisClusterOperationException;
+import redis.clients.jedis.util.TlsUtil;
+
+/**
+ * SSL/TLS Redis Cluster tests using SslOptions builder pattern.
+ */
+public class SSLOptionsRedisClusterClientIT extends RedisClusterTestBase {
+
+ private static final int DEFAULT_REDIRECTIONS = 5;
+ private static final ConnectionPoolConfig DEFAULT_POOL_CONFIG = new ConnectionPoolConfig();
+
+ @Test
+ public void testSSLDiscoverNodesAutomatically() {
+ try (RedisClusterClient jc2 = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(SslOptions.builder().truststore(trustStorePath.toFile())
+ .trustStoreType("jceks").build())
+ .hostAndPortMapper(hostAndPortMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ Map clusterNodes = jc2.getClusterNodes();
+ assertEquals(6, clusterNodes.size());
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
+ jc2.get("foo");
+ }
+ }
+
+ @Test
+ public void testSSLWithoutPortMap() {
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(SslOptions.builder().truststore(trustStorePath.toFile())
+ .trustStoreType("jceks").sslVerifyMode(SslVerifyMode.CA).build())
+ .build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ Map clusterNodes = jc.getClusterNodes();
+ assertEquals(6, clusterNodes.size());
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(0).toString()));
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(1).toString()));
+ assertTrue(clusterNodes.containsKey(tlsEndpoint.getHostAndPort(2).toString()));
+ jc.get("foo");
+ }
+ }
+
+ @Test
+ public void connectByIpAddress() {
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(SslOptions.builder().truststore(trustStorePath.toFile())
+ .trustStoreType("jceks").build())
+ .hostAndPortMapper(hostAndPortMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc.get("foo");
+ }
+ }
+
+ @Test
+ public void connectToNodesFailsWithSSLParametersAndNoHostMapping() {
+ final SSLParameters sslParameters = new SSLParameters();
+ sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
+
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(new HostAndPort("localhost", tlsEndpoint.getPort())))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(SslOptions.builder().sslParameters(sslParameters)
+ .truststore(trustStorePath.toFile()).trustStoreType("jceks").build())
+ .hostAndPortMapper(portMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc.get("foo");
+ fail("It should fail after all cluster attempts.");
+ } catch (JedisClusterOperationException e) {
+ // initial connection to localhost works, but subsequent connections to nodes use 127.0.0.1
+ // and fail hostname verification
+ assertThat(e.getMessage(), anyOf(containsString("No more cluster attempts left."),
+ containsString("Cluster retry deadline exceeded.")));
+ }
+ }
+
+ @Test
+ public void connectToNodesSucceedsWithSSLParametersAndHostMapping() {
+ final SSLParameters sslParameters = new SSLParameters();
+ sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
+
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(SslOptions.builder().sslParameters(sslParameters)
+ .truststore(trustStorePath.toFile()).trustStoreType("jceks").build())
+ .hostAndPortMapper(hostAndPortMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc.get("foo");
+ }
+ }
+
+ @Test
+ public void connectByIpAddressFailsWithSSLParameters() {
+ final SSLParameters sslParameters = new SSLParameters();
+ sslParameters.setEndpointIdentificationAlgorithm("HTTPS");
+
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(SslOptions.builder().sslParameters(sslParameters)
+ .truststore(trustStorePath.toFile()).trustStoreType("jceks").build())
+ .hostAndPortMapper(hostAndPortMap).build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ } catch (JedisClusterOperationException e) {
+ assertEquals("Could not initialize cluster slots cache.", e.getMessage());
+ }
+ }
+
+ @Test
+ public void connectWithCustomHostNameVerifier() {
+ HostnameVerifier hostnameVerifier = new TlsUtil.BasicHostnameVerifier();
+ HostnameVerifier localhostVerifier = new TlsUtil.LocalhostVerifier();
+
+ SslOptions sslOptions = SslOptions.builder().truststore(trustStorePath.toFile())
+ .trustStoreType("jceks").sslVerifyMode(SslVerifyMode.CA).build();
+
+ try (RedisClusterClient jc = RedisClusterClient.builder()
+ .nodes(Collections.singleton(new HostAndPort("localhost", tlsEndpoint.getPort())))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(sslOptions).hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap)
+ .build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc.get("foo");
+ fail("It should fail after all cluster attempts.");
+ } catch (JedisClusterOperationException e) {
+ // initial connection made with 'localhost' but subsequent connections to nodes use 127.0.0.1
+ // which causes custom hostname verification to fail
+ assertThat(e.getMessage(), anyOf(containsString("No more cluster attempts left."),
+ containsString("Cluster retry deadline exceeded.")));
+ }
+
+ try (RedisClusterClient jc2 = RedisClusterClient.builder()
+ .nodes(Collections.singleton(new HostAndPort("127.0.0.1", tlsEndpoint.getPort())))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(sslOptions).hostnameVerifier(hostnameVerifier).hostAndPortMapper(portMap)
+ .build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ } catch (JedisClusterOperationException e) {
+ assertEquals("Could not initialize cluster slots cache.", e.getMessage());
+ }
+
+ try (RedisClusterClient jc3 = RedisClusterClient.builder()
+ .nodes(Collections.singleton(tlsEndpoint.getHostAndPort()))
+ .clientConfig(DefaultJedisClientConfig.builder().password(tlsEndpoint.getPassword())
+ .sslOptions(sslOptions).hostnameVerifier(localhostVerifier).hostAndPortMapper(portMap)
+ .build())
+ .maxAttempts(DEFAULT_REDIRECTIONS).poolConfig(DEFAULT_POOL_CONFIG).build()) {
+ jc3.get("foo");
+ }
+ }
+
+}
diff --git a/src/test/java/redis/clients/jedis/tls/SSLOptionsRedisSentinelClientIT.java b/src/test/java/redis/clients/jedis/tls/SSLOptionsRedisSentinelClientIT.java
new file mode 100644
index 0000000000..88c6fc36ef
--- /dev/null
+++ b/src/test/java/redis/clients/jedis/tls/SSLOptionsRedisSentinelClientIT.java
@@ -0,0 +1,95 @@
+package redis.clients.jedis.tls;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import redis.clients.jedis.DefaultJedisClientConfig;
+import redis.clients.jedis.EndpointConfig;
+import redis.clients.jedis.Endpoints;
+import redis.clients.jedis.RedisSentinelClient;
+import redis.clients.jedis.SslOptions;
+
+/**
+ * SSL/TLS tests for RedisSentinelClient using SslOptions builder pattern.
+ *
+ * Tests various SSL/TLS connection configurations including:
+ *
+ * - Basic SSL connection with truststore
+ * - Insecure SSL mode (no certificate verification)
+ * - Custom SSL protocol
+ * - ACL authentication over SSL
+ *
+ *
+ * Both master and sentinel connections use SSL in these tests.
+ */
+public class SSLOptionsRedisSentinelClientIT extends RedisSentinelTlsTestBase {
+
+ // Endpoint for master with ACL authentication
+ private static EndpointConfig aclEndpoint;
+
+ @BeforeAll
+ public static void setUp() {
+ aclEndpoint = Endpoints.getRedisEndpoint("standalone0-acl-tls");
+ }
+
+ /**
+ * Tests connecting to Redis master and sentinel with various SSL configurations. Both master and
+ * sentinel connections use SSL.
+ */
+ @ParameterizedTest(name = "connectWithSsl_{0}")
+ @MethodSource("sslOptionsProvider")
+ void connectWithSsl(String testName, SslOptions ssl) {
+ DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder()
+ .clientName("master-client").sslOptions(ssl).hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER)
+ .build();
+
+ DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithSsl(ssl);
+
+ try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME)
+ .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig)
+ .build()) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+
+ /**
+ * Tests ACL authentication over SSL (same as truststore test but explicitly named).
+ */
+ @Test
+ public void connectWithAcl() {
+ DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder()
+ .clientName("master-client").sslOptions(sslOptions)
+ .hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build();
+
+ DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithSsl(sslOptions);
+
+ try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME)
+ .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig)
+ .build()) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+
+ /**
+ * Tests that sentinel without SSL can connect to Redis master with SSL.
+ */
+ @Test
+ public void sentinelWithoutSslConnectsToRedisWithSsl() {
+ DefaultJedisClientConfig masterConfig = aclEndpoint.getClientConfigBuilder()
+ .clientName("master-client").sslOptions(sslOptions)
+ .hostAndPortMapper(PRIMARY_SSL_PORT_MAPPER).build();
+
+ DefaultJedisClientConfig sentinelConfig = createSentinelConfigWithoutSsl();
+
+ try (RedisSentinelClient client = RedisSentinelClient.builder().masterName(MASTER_NAME)
+ .sentinels(sentinels).clientConfig(masterConfig).sentinelClientConfig(sentinelConfig)
+ .build()) {
+ assertEquals("PONG", client.ping());
+ }
+ }
+
+}
diff --git a/src/test/resources/endpoints.json b/src/test/resources/endpoints.json
index 480af9cb2e..fa5605c00d 100644
--- a/src/test/resources/endpoints.json
+++ b/src/test/resources/endpoints.json
@@ -157,16 +157,6 @@
"redis://127.0.0.1:7481"
]
},
- "cluster-unbound-tls": {
- "password": "cluster",
- "tls": true,
- "certificates_location": "cluster-unbound/work/tls",
- "endpoints": [
- "rediss://127.0.0.1:8379",
- "rediss://127.0.0.1:8380",
- "rediss://127.0.0.1:8381"
- ]
- },
"cluster-stable-tls": {
"password": "cluster",
"tls": true,
diff --git a/src/test/resources/env/cluster-unbound/config/node-7379-8379/redis.conf b/src/test/resources/env/cluster-unbound/config/node-7379-8379/redis.conf
index 35a794a99b..b285f173cd 100644
--- a/src/test/resources/env/cluster-unbound/config/node-7379-8379/redis.conf
+++ b/src/test/resources/env/cluster-unbound/config/node-7379-8379/redis.conf
@@ -1,8 +1,6 @@
bind 0.0.0.0
port 7379
-tls-port 8379
requirepass cluster
-tls-auth-clients no
cluster-node-timeout 150
save ""
appendonly no
diff --git a/src/test/resources/env/cluster-unbound/config/node-7380-8380/redis.conf b/src/test/resources/env/cluster-unbound/config/node-7380-8380/redis.conf
index c6129ed906..9694a663df 100644
--- a/src/test/resources/env/cluster-unbound/config/node-7380-8380/redis.conf
+++ b/src/test/resources/env/cluster-unbound/config/node-7380-8380/redis.conf
@@ -1,8 +1,6 @@
bind 0.0.0.0
port 7380
-tls-port 8380
requirepass cluster
-tls-auth-clients no
cluster-node-timeout 150
save ""
appendonly no
diff --git a/src/test/resources/env/cluster-unbound/config/node-7381-8381/redis.conf b/src/test/resources/env/cluster-unbound/config/node-7381-8381/redis.conf
index aed97aceba..9fae282cea 100644
--- a/src/test/resources/env/cluster-unbound/config/node-7381-8381/redis.conf
+++ b/src/test/resources/env/cluster-unbound/config/node-7381-8381/redis.conf
@@ -1,8 +1,6 @@
bind 0.0.0.0
port 7381
-tls-port 8381
requirepass cluster
-tls-auth-clients no
cluster-node-timeout 150
save ""
appendonly no
diff --git a/src/test/resources/env/cluster-unbound/config/node-7382-8382/redis.conf b/src/test/resources/env/cluster-unbound/config/node-7382-8382/redis.conf
index 41e8cd15c9..5b85b4ba4f 100644
--- a/src/test/resources/env/cluster-unbound/config/node-7382-8382/redis.conf
+++ b/src/test/resources/env/cluster-unbound/config/node-7382-8382/redis.conf
@@ -1,8 +1,6 @@
bind 0.0.0.0
requirepass cluster
port 7382
-tls-port 8382
-tls-auth-clients no
cluster-node-timeout 150
save ""
appendonly no
diff --git a/src/test/resources/env/cluster-unbound/config/node-7383-8383/redis.conf b/src/test/resources/env/cluster-unbound/config/node-7383-8383/redis.conf
index aa5669901f..5ec98c4027 100644
--- a/src/test/resources/env/cluster-unbound/config/node-7383-8383/redis.conf
+++ b/src/test/resources/env/cluster-unbound/config/node-7383-8383/redis.conf
@@ -1,8 +1,6 @@
bind 0.0.0.0
port 7383
-tls-port 8383
requirepass cluster
-tls-auth-clients no
cluster-node-timeout 150
save ""
appendonly no
diff --git a/src/test/resources/env/docker-compose.yml b/src/test/resources/env/docker-compose.yml
index dfca32b43f..89de19cad1 100644
--- a/src/test/resources/env/docker-compose.yml
+++ b/src/test/resources/env/docker-compose.yml
@@ -123,30 +123,33 @@ services:
<<: *client-libs-image
container_name: cluster-unbound-1
environment:
- - TLS_ENABLED=yes
- REDIS_PASSWORD=cluster
ports:
- "7379-7383:7379-7383"
- - "8379-8383:8379-8383"
volumes:
- ${REDIS_ENV_CONF_DIR}/cluster-unbound/config:/redis/config:r
- ${REDIS_ENV_WORK_DIR}/cluster-unbound/work:/redis/work:rw
+ #TLS endpoints of Cluster stable are used for TLS tests that do not require client authentication
cluster-stable:
sysctls:
- net.ipv6.conf.all.disable_ipv6=1
<<: *client-libs-image
container_name: cluster-stable-1
#network_mode: host
- command: --cluster-announce-ip 127.0.0.1 --cluster-node-timeout 150 --save ""
+ command: --cluster-announce-ip 127.0.0.1 --cluster-node-timeout 150 --tls-auth-clients no --save ""
environment:
- REDIS_CLUSTER=yes
- REDIS_PASSWORD=cluster
- PORT=7479
+ - TLS_PORT=8479
- NODES=6
- REPLICAS=1
+ - TLS_ENABLED=yes
+ - TLS_AUTH_CLIENTS_USER=off
ports:
- "7479-7484:7479-7484"
+ - "8479-8484:8479-8484"
volumes:
- ${REDIS_ENV_CONF_DIR}/cluster-stable/config:/redis/config:r
- ${REDIS_ENV_WORK_DIR}/cluster-stable/work:/redis/work:rw