added ssl support for redis

This commit is contained in:
dashevchenko 2023-09-11 16:57:28 +03:00
parent 1bcea1a3fc
commit b2b5573a61
6 changed files with 142 additions and 24 deletions

View File

@ -548,6 +548,14 @@ redis:
db: "${REDIS_DB:0}"
# db password
password: "${REDIS_PASSWORD:}"
# ssl config
ssl:
enabled: "${TB_REDIS_SSL_ENABLED:true}"
truststoreLocation: "${TB_REDIS_SSL_TRUSTSTORE_LOCATION:}"
truststorePassword: "${TB_REDIS_SSL_TRUSTSTORE_PASSWORD:}"
# client authentication could be optional and depends on redis server configuration
keystoreLocation: "${TB_REDIS_SSL_KEYSTORE_LOCATION:}"
keystorePassword: "${TB_REDIS_SSL_KEYSTORE_PASSWORD:}"
# pool config
pool_config:
maxTotal: "${REDIS_POOL_CONFIG_MAX_TOTAL:128}"

View File

@ -16,6 +16,8 @@
package org.thingsboard.server.cache;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cache.CacheManager;
@ -35,6 +37,12 @@ import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.EntityId;
import redis.clients.jedis.JedisPoolConfig;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
@ -44,6 +52,7 @@ import java.util.List;
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
@EnableCaching
@Data
@Slf4j
public abstract class TBRedisCacheConfiguration {
private static final String COMMA = ",";
@ -90,6 +99,9 @@ public abstract class TBRedisCacheConfiguration {
return loadFactory();
}
@Autowired
private TbRedisSslCredentialsConfiguration redisSslCredentials;
protected abstract JedisConnectionFactory loadFactory();
/**
@ -149,4 +161,33 @@ public abstract class TBRedisCacheConfiguration {
}
return result;
}
protected SSLSocketFactory createSslSocketFactory() {
try {
KeyStore trustStore = KeyStore.getInstance("jks");
trustStore.load(new FileInputStream(redisSslCredentials.getTruststoreLocation()), redisSslCredentials.getTruststorePassword().toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
trustManagerFactory.init(trustStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
// client authentication is optional
if (redisSslCredentials.getKeystoreLocation() != null && redisSslCredentials.getKeystorePassword() != null) {
KeyStore keyStore = KeyStore.getInstance("pkcs12");
keyStore.load(new FileInputStream(redisSslCredentials.getKeystoreLocation()), redisSslCredentials.getKeystorePassword().toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("PKIX");
keyManagerFactory.init(keyStore, redisSslCredentials.getKeystorePassword().toCharArray());
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
} else {
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
}
return sslContext.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@ -20,6 +20,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
@Configuration
@ -39,15 +40,29 @@ public class TBRedisClusterConfiguration extends TBRedisCacheConfiguration {
@Value("${redis.password:}")
private String password;
@Value("${redis.ssl.enabled:}")
private boolean useSsl;
public JedisConnectionFactory loadFactory() {
RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();
clusterConfiguration.setClusterNodes(getNodes(clusterNodes));
clusterConfiguration.setMaxRedirects(maxRedirects);
clusterConfiguration.setPassword(password);
if (useDefaultPoolConfig) {
return new JedisConnectionFactory(clusterConfiguration);
} else {
return new JedisConnectionFactory(clusterConfiguration, buildPoolConfig());
return new JedisConnectionFactory(clusterConfiguration, buildClientConfig());
}
private JedisClientConfiguration buildClientConfig() {
JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfigurationBuilder = JedisClientConfiguration.builder();
if (!useDefaultPoolConfig) {
jedisClientConfigurationBuilder
.usePooling()
.poolConfig(buildPoolConfig());
}
if (useSsl) {
jedisClientConfigurationBuilder
.useSsl()
.sslSocketFactory(createSslSocketFactory());
}
return jedisClientConfigurationBuilder.build();
}
}

View File

@ -20,6 +20,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
@Configuration
@ -42,6 +43,9 @@ public class TBRedisSentinelConfiguration extends TBRedisCacheConfiguration {
@Value("${redis.db:}")
private Integer database;
@Value("${redis.ssl.enabled:}")
private boolean useSsl;
@Value("${redis.password:}")
private String password;
@ -52,11 +56,21 @@ public class TBRedisSentinelConfiguration extends TBRedisCacheConfiguration {
redisSentinelConfiguration.setSentinelPassword(sentinelPassword);
redisSentinelConfiguration.setPassword(password);
redisSentinelConfiguration.setDatabase(database);
if (useDefaultPoolConfig) {
return new JedisConnectionFactory(redisSentinelConfiguration);
} else {
return new JedisConnectionFactory(redisSentinelConfiguration, buildPoolConfig());
}
return new JedisConnectionFactory(redisSentinelConfiguration, buildClientConfig());
}
private JedisClientConfiguration buildClientConfig() {
JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfigurationBuilder = JedisClientConfiguration.builder();
if (!useDefaultPoolConfig) {
jedisClientConfigurationBuilder
.usePooling()
.poolConfig(buildPoolConfig());
}
if (useSsl) {
jedisClientConfigurationBuilder
.useSsl()
.sslSocketFactory(createSslSocketFactory());
}
return jedisClientConfigurationBuilder.build();
}
}

View File

@ -57,32 +57,36 @@ public class TBRedisStandaloneConfiguration extends TBRedisCacheConfiguration {
@Value("${redis.password:}")
private String password;
@Value("${redis.ssl.enabled:}")
private boolean useSsl;
public JedisConnectionFactory loadFactory() {
RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration();
standaloneConfiguration.setHostName(host);
standaloneConfiguration.setPort(port);
standaloneConfiguration.setDatabase(db);
standaloneConfiguration.setPassword(password);
if (useDefaultClientConfig) {
return new JedisConnectionFactory(standaloneConfiguration);
} else {
return new JedisConnectionFactory(standaloneConfiguration, buildClientConfig());
}
return new JedisConnectionFactory(standaloneConfiguration, buildClientConfig());
}
private JedisClientConfiguration buildClientConfig() {
if (usePoolConfig) {
return JedisClientConfiguration.builder()
JedisClientConfiguration.JedisClientConfigurationBuilder jedisClientConfigurationBuilder = JedisClientConfiguration.builder();
if (!useDefaultClientConfig) {
jedisClientConfigurationBuilder
.clientName(clientName)
.connectTimeout(Duration.ofMillis(connectTimeout))
.readTimeout(Duration.ofMillis(readTimeout))
.usePooling().poolConfig(buildPoolConfig())
.build();
} else {
return JedisClientConfiguration.builder()
.clientName(clientName)
.connectTimeout(Duration.ofMillis(connectTimeout))
.readTimeout(Duration.ofMillis(readTimeout)).build();
.readTimeout(Duration.ofMillis(readTimeout));
}
if (useSsl) {
jedisClientConfigurationBuilder
.useSsl()
.sslSocketFactory(createSslSocketFactory());
}
if (usePoolConfig) {
jedisClientConfigurationBuilder
.usePooling()
.poolConfig(buildPoolConfig());
}
return jedisClientConfigurationBuilder.build();
}
}

View File

@ -0,0 +1,36 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.cache;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "redis.ssl")
@Data
public class TbRedisSslCredentialsConfiguration {
private boolean enabled;
private String truststoreLocation;
private String truststorePassword;
private String keystoreLocation;
private String keystorePassword;
}