question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Lettuce can't connect to Redis Cluster + SSL but can connect to same Redis server + SSL if treated as Standalone node.

See original GitHub issue

Bug Report

Current Behavior

I’ve an Azure Cache for Redis - Premium and Cluster enabled. I’ve been trying to connect to that Redis using spring-boot-starter-data-redis (spring boot version: 2.3.4.RELEASE, Java version: 11) and using lettuce client but Lettuce is throwing SSL exception when I am treating my Redis as a Redis Cluster but connects just fine when using it as a Standalone Redis server.

Stack trace
java.lang.IllegalStateException: Failed to execute CommandLineRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    ...
Caused by: org.springframework.data.redis.RedisConnectionFailureException: Redis connection failed; nested exception is io.lettuce.core.RedisConnectionException: Unable to connect to [RedisURI [host='<redacted>.redis.cache.windows.net', port=6380]]
    at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:66) ~[spring-data-redis-2.3.4.RELEASE.jar:2.3.4.RELEASE]
    ...
Caused by: io.lettuce.core.RedisConnectionException: Unable to connect to [RedisURI [host='<redacted>.redis.cache.windows.net', port=6380]]
    at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:78) ~[lettuce-core-5.3.4.RELEASE.jar:5.3.4.RELEASE]
    ...
Caused by: javax.net.ssl.SSLHandshakeException: No subject alternative names matching IP address <redacted> found
    ...
Caused by: java.security.cert.CertificateException: No subject alternative names matching IP address <redacted> found
    at java.base/sun.security.util.HostnameChecker.matchIP(HostnameChecker.java:165) ~[na:na]
    ...

Input Code

Input Code
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.*;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.StringRedisTemplate;


@Configuration
class LettuceConfig {

    @Bean
    StringRedisTemplate getStringRedisTemplate(final RedisProperties redisProperties) {
        return new StringRedisTemplate(getRedisConnectionFactory(redisProperties));
    }

    @Bean
    RedisConnectionFactory getRedisConnectionFactory(final RedisProperties redisProperties) {
    
        final RedisNode redisNode = RedisNode.newRedisNode()
                .listeningAt(redisProperties.getHost(), redisProperties.getPort())
                .build();

        // Connecting as a Redis Cluster
        final RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
        redisClusterConfiguration.addClusterNode(redisNode);
        redisClusterConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));

        // Connecting as a Standalone Redis server
        final RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(redisProperties.getHost());
        redisStandaloneConfiguration.setPort(redisProperties.getPort());
        redisStandaloneConfiguration.setPassword(RedisPassword.of(redisProperties.getPassword()));

        final LettuceClientConfiguration.LettuceClientConfigurationBuilder lettuceClientConfigurationBuilder =
                LettuceClientConfiguration.builder()
                .clientName(redisProperties.getClientName())
                .commandTimeout(redisProperties.getTimeout());

        if (redisProperties.isSsl()) {
            lettuceClientConfigurationBuilder.useSsl();
        }

        final LettuceClientConfiguration lettuceClientConfiguration = lettuceClientConfigurationBuilder.build();

        return new LettuceConnectionFactory(redisClusterConfiguration, lettuceClientConfiguration);
    }
}

@SpringBootApplication
public class LettuceClusterApplication implements CommandLineRunner {

    private final StringRedisTemplate stringRedisTemplate;

    @Autowired
    public LettuceClusterApplication(final StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    public static void main(String[] args) {
        SpringApplication.run(LettuceClusterApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println(stringRedisTemplate.hasKey("abc"));
    }
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.3.4.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.example</groupId>
	<artifactId>lettuce-cluster-connect</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>lettuce-cluster-connect</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>11</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-redis</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

</project>

My application.properties file:

spring.redis.host = <redacted>.redis.cache.windows.net
spring.redis.port = 6380
spring.redis.password = <redacted>
spring.redis.ssl = true
spring.redis.clientName = ${HOSTNAME}
spring.redis.timeout = 100000

Expected behavior/code

Lettuce should’ve been able to connect to Redis Cluster just fine and run the command just like it did when treating it as a standalone Redis node.

Environment

  • Lettuce version(s): 5.3.4.RELEASE (bundled under spring-boot-starter-data-redis)
  • Redis version: 4.0.14

Additional context

Found a similar GitHub issue: https://github.com/lettuce-io/lettuce-core/issues/246 and the documentation link: https://lettuce.io/core/release/reference/#ssl which both basically states that this issue is fixed with Lettuce version > 4.2. Quoting the relevant part of the documentation:

Lettuce supports SSL connections since version 3.1 on Redis Standalone connections and since version 4.2 on Redis Cluster.

Link to StackOverflow question: https://stackoverflow.com/q/64287424/3160529

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Reactions:2
  • Comments:9 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
mp911decommented, Oct 13, 2020

The linked ticket #246 and #209 are about general SSL support for Redis Cluster.

This report is related to the fact that the Redis certificate doesn’t contain Redis cluster IP addresses. The Redis cluster topology is based on IP addresses, not hostnames (see https://redis.io/commands/cluster-nodes). Enabling SSL also enables SSL certificate validation. No subject alternative names matching IP address basically means that the supplied SSL certificate doesn’t list the target IP address.

There are a couple of things you can do about:

  1. If the Redis IP addresses can be mapped onto a hostname, then please configure a MappingSocketAddressResolver through ClientResources to map IP addresses into hostnames that are listed in the SSL certificate
  2. Reach out to the Azure support team so that they fix the SSL certificates and include all cluster IP addresses
  3. (least favorable option) Disable SSL certificate validation. That can be done with RedisURI.setVerifyPeer(false) when using Lettuce directly. For Spring, you should be able to register a LettuceClientConfigurationBuilder customizer to invoke LettuceSslClientConfigurationBuilder.disablePeerVerification().
0reactions
ilr39commented, Mar 4, 2021
Read more comments on GitHub >

github_iconTop Results From Across the Web

java - Lettuce can't connect to Redis Cluster using SSL but ...
Lettuce can't connect to Redis Cluster using SSL but can connect to same Redis server using SSL by treating it as a Standalone...
Read more >
Lettuce Reference Guide
RedisURI supports Redis Standalone, Redis Sentinel and Redis Cluster with plain, SSL, TLS and unix domain socket connections.
Read more >
Spring Data Redis
The Spring Data Redis project applies core Spring concepts to the development of solutions by using a key-value style data store.
Read more >
Amazon Elasticache FAQs - Amazon Web Service
Amazon ElastiCache is a web service that makes it easy to deploy and run Memcached or Redis protocol-compliant server nodes in the cloud....
Read more >
Configuration | StackExchange.Redis
When connecting to Redis version 6 or above with an ACL configured, your ACL user needs to at least have permissions to run...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found