resolved conflicts after merge

This commit is contained in:
dashevchenko 2024-01-18 14:39:14 +02:00
parent b33d1f9c69
commit 9af96ad5be
11 changed files with 380 additions and 56 deletions

View File

@ -641,12 +641,34 @@ redis:
password: "${REDIS_PASSWORD:}"
# ssl config
ssl:
enabled: "${TB_REDIS_SSL_ENABLED:false}"
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:}"
# Enable/disable secure connection
enabled: "${REDIS_SSL_ENABLED:false}"
# Server SSL credentials
credentials:
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${TB_REDIS_SSL_CREDENTIALS_TYPE:pem}"
# PEM server credentials
pem:
# Path redis server (CA) certificate
cert_file: "${TB_REDIS_SSL_PEM_CERT:}"
# Path to user certificate file (optional)
user_cert_file: "${TB_REDIS_SSL_PEM_KEY:}"
# Path to user private key file (optional)
user_key_file: "${TB_REDIS_SSL_PEM_KEY_PASSWORD:}"
# Keystore server credentials
keystore:
# Type of the trust store (JKS or PKCS12)
truststore_type: "${TB_REDIS_SSL_KEY_STORE_TYPE:JKS}"
# The location of the trust store file
truststore_location: "${TB_REDIS_SSL_TRUSTSTORE_LOCATION:}"
# The password of trust store file if specified
truststore_password: "${TB_REDIS_SSL_TRUSTSTORE_PASSWORD:}"
# Type of the key store (JKS or PKCS12)
keystore_type: "${TB_REDIS_SSL_KEY_STORE_TYPE:JKS}"
# The location of the key store file. This is optional for the client and can be used for two-way authentication for the client
keystore_location: "${TB_REDIS_SSL_KEYSTORE_LOCATION:}"
# The store password for the key store file. This is optional for the client and only needed if ssl.keystore.location is configured. Key store password is not supported for PEM format
keystore_password: "${TB_REDIS_SSL_KEYSTORE_PASSWORD:}"
# pool config
pool_config:
# Maximum number of connections that can be allocated by the connection pool

View File

@ -1,5 +1,5 @@
/**
* Copyright © 2016-2023 The Thingsboard Authors
* Copyright © 2016-2024 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.
@ -16,20 +16,20 @@
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 {
public class RedisKeystoreCredentialsConfig {
private boolean enabled;
private String type;
private String truststoreType;
private String truststoreLocation;
private String truststorePassword;
private String keystoreType;
private String keystoreLocation;
private String keystorePassword;

View File

@ -0,0 +1,28 @@
/**
* Copyright © 2016-2024 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;
@Data
public class RedisPemCredentialsConfig {
private String certFile;
private String userCertFile;
private String userKeyFile;
}

View File

@ -0,0 +1,33 @@
/**
* Copyright © 2016-2024 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.credentials")
@Data
public class RedisSslCredentialsConfiguration {
private String type;
private RedisKeystoreCredentialsConfig keystore;
private RedisPemCredentialsConfig pem;
}

View File

@ -33,6 +33,7 @@ import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.format.support.DefaultFormattingConversionService;
import org.springframework.util.Assert;
import org.thingsboard.common.util.SslUtil;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.EntityId;
import redis.clients.jedis.JedisPoolConfig;
@ -43,6 +44,11 @@ import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.CertPath;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
@ -100,7 +106,7 @@ public abstract class TBRedisCacheConfiguration {
}
@Autowired
private TbRedisSslCredentialsConfiguration redisSslCredentials;
private RedisSslCredentialsConfiguration redisSslCredentials;
protected abstract JedisConnectionFactory loadFactory();
@ -164,30 +170,93 @@ public abstract class TBRedisCacheConfiguration {
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);
}
KeyManagerFactory keyManagerFactory = createAndInitKeyManagerFactory();
TrustManagerFactory trustManagerFactory = createAndInitTrustManagerFactory();
sslContext.init(keyManagerFactory == null ? null : keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
return sslContext.getSocketFactory();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private TrustManagerFactory createAndInitTrustManagerFactory() throws Exception {
String type = redisSslCredentials.getType();
if ("pem".equals(type)) {
RedisPemCredentialsConfig pemCredentials = redisSslCredentials.getPem();
List<X509Certificate> caCerts = SslUtil.readCertFileByPath(pemCredentials.getCertFile());
KeyStore caKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
caKeyStore.load(null, null);
for (X509Certificate caCert : caCerts) {
caKeyStore.setCertificateEntry("redis-caCert-cert-" + caCert.getSubjectX500Principal().getName(), caCert);
}
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
trustManagerFactory.init(caKeyStore);
return trustManagerFactory;
} else if ("keystore".equals(type)) {
RedisKeystoreCredentialsConfig keystore = redisSslCredentials.getKeystore();
KeyStore trustStore = KeyStore.getInstance(keystore.getKeystoreType());
trustStore.load(new FileInputStream(keystore.getTruststoreLocation()), keystore.getTruststorePassword().toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
trustManagerFactory.init(trustStore);
return trustManagerFactory;
} else {
throw new RuntimeException(type + ": Invalid SSL credentials configuration. None of the PEM or KEYSTORE configurations can be used!");
}
}
private KeyManagerFactory createAndInitKeyManagerFactory() throws Exception {
String type = redisSslCredentials.getType();
if ("pem".equals(type)) {
RedisPemCredentialsConfig pemCredentials = redisSslCredentials.getPem();
return getKeyManagerFactory(pemCredentials);
} else if ("keystore".equals(type)) {
RedisKeystoreCredentialsConfig keystore = redisSslCredentials.getKeystore();
return getKeyManagerFactory(keystore);
} else {
throw new RuntimeException(type + ": Invalid SSL credentials configuration. None of the PEM or KEYSTORE configurations can be used!");
}
}
private KeyManagerFactory getKeyManagerFactory(RedisPemCredentialsConfig pemCredentials) throws Exception {
if (pemCredentials.getUserCertFile().isBlank() || pemCredentials.getUserKeyFile().isBlank()) {
return null;
}
List<X509Certificate> certificates = SslUtil.readCertFileByPath(pemCredentials.getCertFile());
PrivateKey privateKey = SslUtil.readPrivateKeyByFilePath(pemCredentials.getUserKeyFile(), null);
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
List<X509Certificate> unique = certificates.stream().distinct().toList();
for (X509Certificate cert : unique) {
keyStore.setCertificateEntry("redis-cert" + cert.getSubjectX500Principal().getName(), cert);
}
if (privateKey != null) {
CertificateFactory factory = CertificateFactory.getInstance("X.509");
CertPath certPath = factory.generateCertPath(certificates);
List<? extends Certificate> path = certPath.getCertificates();
Certificate[] x509Certificates = path.toArray(new Certificate[0]);
keyStore.setKeyEntry("redis-private-key", privateKey, null, x509Certificates);
}
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
kmf.init(keyStore, null);
return kmf;
}
private KeyManagerFactory getKeyManagerFactory(RedisKeystoreCredentialsConfig keystore) throws Exception {
if (keystore.getKeystoreLocation().isBlank() || keystore.getKeystoreLocation().isBlank()) {
return null;
}
KeyStore keyStore = KeyStore.getInstance(keystore.getKeystoreType());
keyStore.load(new FileInputStream(keystore.getKeystoreLocation()), keystore.getKeystorePassword().toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("PKIX");
kmf.init(keyStore, keystore.getKeystorePassword().toCharArray());
return kmf;
}
}

View File

@ -29,12 +29,17 @@ import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
import org.bouncycastle.operator.InputDecryptorProvider;
import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
import org.bouncycastle.pkcs.PKCSException;
import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder;
import org.thingsboard.server.common.data.StringUtils;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
@ -57,9 +62,18 @@ public class SslUtil {
@SneakyThrows
public static List<X509Certificate> readCertFile(String fileContent) {
return readCertFile(new StringReader(fileContent));
}
@SneakyThrows
public static List<X509Certificate> readCertFileByPath(String filePath) {
return readCertFile( new FileReader(filePath));
}
private static List<X509Certificate> readCertFile(Reader reader) throws IOException, CertificateException {
List<X509Certificate> certificates = new ArrayList<>();
JcaX509CertificateConverter certConverter = new JcaX509CertificateConverter();
try (PEMParser pemParser = new PEMParser(new StringReader(fileContent))) {
try (PEMParser pemParser = new PEMParser(reader)) {
Object object;
while ((object = pemParser.readObject()) != null) {
if (object instanceof X509CertificateHolder) {
@ -73,12 +87,27 @@ public class SslUtil {
@SneakyThrows
public static PrivateKey readPrivateKey(String fileContent, String passStr) {
char[] password = StringUtils.isEmpty(passStr) ? EMPTY_PASS : passStr.toCharArray();
if (StringUtils.isNotEmpty(fileContent)) {
StringReader reader = new StringReader(fileContent);
return readPrivateKey(reader, passStr);
}
return null;
}
@SneakyThrows
public static PrivateKey readPrivateKeyByFilePath(String filePath, String passStr) {
if (StringUtils.isNotEmpty(filePath)) {
FileReader fileReader = new FileReader(filePath);
return readPrivateKey(fileReader, passStr);
}
return null;
}
private static PrivateKey readPrivateKey(Reader reader, String passStr) throws IOException, PKCSException {
char[] password = StringUtils.isEmpty(passStr) ? EMPTY_PASS : passStr.toCharArray();
PrivateKey privateKey = null;
JcaPEMKeyConverter keyConverter = new JcaPEMKeyConverter();
if (StringUtils.isNotEmpty(fileContent)) {
try (PEMParser pemParser = new PEMParser(new StringReader(fileContent))) {
try (PEMParser pemParser = new PEMParser(reader)) {
Object object;
while ((object = pemParser.readObject()) != null) {
if (object instanceof PEMEncryptedKeyPair) {
@ -98,8 +127,6 @@ public class SslUtil {
}
}
}
}
return privateKey;
}
}

View File

@ -94,6 +94,35 @@ redis:
db: "${REDIS_DB:0}"
# db password
password: "${REDIS_PASSWORD:}"
ssl:
# Enable/disable secure connection
enabled: "${REDIS_SSL_ENABLED:false}"
# Server SSL credentials
credentials:
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${TB_REDIS_SSL_CREDENTIALS_TYPE:pem}"
# PEM server credentials
pem:
# Path redis server (CA) certificate
cert_file: "${TB_REDIS_SSL_PEM_CERT:}"
# Path to user certificate file
user_cert_file: "${TB_REDIS_SSL_PEM_KEY:}"
# Path to user private key file
user_key_file: "${TB_REDIS_SSL_PEM_KEY_PASSWORD:}"
# Keystore server credentials
keystore:
# Type of the trust store (JKS or PKCS12)
truststore_type: "${TB_REDIS_SSL_KEY_STORE_TYPE:JKS}"
# The location of the trust store file
truststore_location: "${TB_REDIS_SSL_TRUSTSTORE_LOCATION:}"
# The password of trust store file if specified
truststore_password: "${TB_REDIS_SSL_TRUSTSTORE_PASSWORD:}"
# Type of the key store (JKS or PKCS12)
keystore_type: "${TB_REDIS_SSL_KEY_STORE_TYPE:JKS}"
# The location of the key store file. This is optional for the client and can be used for two-way authentication for the client
keystore_location: "${TB_REDIS_SSL_KEYSTORE_LOCATION:}"
# The store password for the key store file. This is optional for the client and only needed if ssl.keystore.location is configured. Key store password is not supported for PEM format
keystore_password: "${TB_REDIS_SSL_KEYSTORE_PASSWORD:}"
# pool config
pool_config:
# Maximum number of connections that can be allocated by the connection pool

View File

@ -127,6 +127,35 @@ redis:
db: "${REDIS_DB:0}"
# db password
password: "${REDIS_PASSWORD:}"
ssl:
# Enable/disable secure connection
enabled: "${REDIS_SSL_ENABLED:false}"
# Server SSL credentials
credentials:
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${TB_REDIS_SSL_CREDENTIALS_TYPE:pem}"
# PEM server credentials
pem:
# Path redis server (CA) certificate
cert_file: "${TB_REDIS_SSL_PEM_CERT:}"
# Path to user certificate file
user_cert_file: "${TB_REDIS_SSL_PEM_KEY:}"
# Path to user private key file
user_key_file: "${TB_REDIS_SSL_PEM_KEY_PASSWORD:}"
# Keystore server credentials
keystore:
# Type of the trust store (JKS or PKCS12)
truststore_type: "${TB_REDIS_SSL_KEY_STORE_TYPE:JKS}"
# The location of the trust store file
truststore_location: "${TB_REDIS_SSL_TRUSTSTORE_LOCATION:}"
# The password of trust store file if specified
truststore_password: "${TB_REDIS_SSL_TRUSTSTORE_PASSWORD:}"
# Type of the key store (JKS or PKCS12)
keystore_type: "${TB_REDIS_SSL_KEY_STORE_TYPE:JKS}"
# The location of the key store file. This is optional for the client and can be used for two-way authentication for the client
keystore_location: "${TB_REDIS_SSL_KEYSTORE_LOCATION:}"
# The store password for the key store file. This is optional for the client and only needed if ssl.keystore.location is configured. Key store password is not supported for PEM format
keystore_password: "${TB_REDIS_SSL_KEYSTORE_PASSWORD:}"
# pool config
pool_config:
# Maximum number of connections that can be allocated by the connection pool

View File

@ -94,6 +94,35 @@ redis:
db: "${REDIS_DB:0}"
# db password
password: "${REDIS_PASSWORD:}"
ssl:
# Enable/disable secure connection
enabled: "${REDIS_SSL_ENABLED:false}"
# Server SSL credentials
credentials:
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${TB_REDIS_SSL_CREDENTIALS_TYPE:pem}"
# PEM server credentials
pem:
# Path redis server (CA) certificate
cert_file: "${TB_REDIS_SSL_PEM_CERT:}"
# Path to user certificate file
user_cert_file: "${TB_REDIS_SSL_PEM_KEY:}"
# Path to user private key file
user_key_file: "${TB_REDIS_SSL_PEM_KEY_PASSWORD:}"
# Keystore server credentials
keystore:
# Type of the trust store (JKS or PKCS12)
truststore_type: "${TB_REDIS_SSL_KEY_STORE_TYPE:JKS}"
# The location of the trust store file
truststore_location: "${TB_REDIS_SSL_TRUSTSTORE_LOCATION:}"
# The password of trust store file if specified
truststore_password: "${TB_REDIS_SSL_TRUSTSTORE_PASSWORD:}"
# Type of the key store (JKS or PKCS12)
keystore_type: "${TB_REDIS_SSL_KEY_STORE_TYPE:JKS}"
# The location of the key store file. This is optional for the client and can be used for two-way authentication for the client
keystore_location: "${TB_REDIS_SSL_KEYSTORE_LOCATION:}"
# The store password for the key store file. This is optional for the client and only needed if ssl.keystore.location is configured. Key store password is not supported for PEM format
keystore_password: "${TB_REDIS_SSL_KEYSTORE_PASSWORD:}"
# pool config
pool_config:
# Maximum number of connections that can be allocated by the connection pool

View File

@ -95,6 +95,35 @@ redis:
db: "${REDIS_DB:0}"
# db password
password: "${REDIS_PASSWORD:}"
ssl:
# Enable/disable secure connection
enabled: "${REDIS_SSL_ENABLED:false}"
# Server SSL credentials
credentials:
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${TB_REDIS_SSL_CREDENTIALS_TYPE:pem}"
# PEM server credentials
pem:
# Path redis server (CA) certificate
cert_file: "${TB_REDIS_SSL_PEM_CERT:}"
# Path to user certificate file
user_cert_file: "${TB_REDIS_SSL_PEM_KEY:}"
# Path to user private key file
user_key_file: "${TB_REDIS_SSL_PEM_KEY_PASSWORD:}"
# Keystore server credentials
keystore:
# Type of the trust store (JKS or PKCS12)
truststore_type: "${TB_REDIS_SSL_KEY_STORE_TYPE:JKS}"
# The location of the trust store file
truststore_location: "${TB_REDIS_SSL_TRUSTSTORE_LOCATION:}"
# The password of trust store file if specified
truststore_password: "${TB_REDIS_SSL_TRUSTSTORE_PASSWORD:}"
# Type of the key store (JKS or PKCS12)
keystore_type: "${TB_REDIS_SSL_KEY_STORE_TYPE:JKS}"
# The location of the key store file. This is optional for the client and can be used for two-way authentication for the client
keystore_location: "${TB_REDIS_SSL_KEYSTORE_LOCATION:}"
# The store password for the key store file. This is optional for the client and only needed if ssl.keystore.location is configured. Key store password is not supported for PEM format
keystore_password: "${TB_REDIS_SSL_KEYSTORE_PASSWORD:}"
# pool config
pool_config:
# Maximum number of connections that can be allocated by the connection pool

View File

@ -94,6 +94,35 @@ redis:
db: "${REDIS_DB:0}"
# db password
password: "${REDIS_PASSWORD:}"
ssl:
# Enable/disable secure connection
enabled: "${REDIS_SSL_ENABLED:false}"
# Server SSL credentials
credentials:
# Server credentials type (PEM - pem certificate file; KEYSTORE - java keystore)
type: "${TB_REDIS_SSL_CREDENTIALS_TYPE:pem}"
# PEM server credentials
pem:
# Path redis server (CA) certificate
cert_file: "${TB_REDIS_SSL_PEM_CERT:}"
# Path to user certificate file
user_cert_file: "${TB_REDIS_SSL_PEM_KEY:}"
# Path to user private key file
user_key_file: "${TB_REDIS_SSL_PEM_KEY_PASSWORD:}"
# Keystore server credentials
keystore:
# Type of the trust store (JKS or PKCS12)
truststore_type: "${TB_REDIS_SSL_KEY_STORE_TYPE:JKS}"
# The location of the trust store file
truststore_location: "${TB_REDIS_SSL_TRUSTSTORE_LOCATION:}"
# The password of trust store file if specified
truststore_password: "${TB_REDIS_SSL_TRUSTSTORE_PASSWORD:}"
# Type of the key store (JKS or PKCS12)
keystore_type: "${TB_REDIS_SSL_KEY_STORE_TYPE:JKS}"
# The location of the key store file. This is optional for the client and can be used for two-way authentication for the client
keystore_location: "${TB_REDIS_SSL_KEYSTORE_LOCATION:}"
# The store password for the key store file. This is optional for the client and only needed if ssl.keystore.location is configured. Key store password is not supported for PEM format
keystore_password: "${TB_REDIS_SSL_KEYSTORE_PASSWORD:}"
# pool config
pool_config:
# Maximum number of connections that can be allocated by the connection pool