Armazene as credenciais de instância do Tair (Redis OSS-compatible) no KMS para que sua aplicação recupere senhas dinamicamente via SDK, eliminando a necessidade de credenciais estáticas incorporadas ao código. Ative também a rotação automática de credenciais para reduzir o risco de vazamento.
Como funciona
Com credenciais de instância gerenciadas pelo KMS para instâncias do Tair (Redis OSS-compatible), sua aplicação não precisa mais de senhas estáticas. Um administrador crie uma credencial de instância no KMS, e a aplicação chama a operação GetSecretValue para recuperar a conta e a senha em tempo de execução.
Por exemplo, se o nome da credencial for username, o KMS crie as contas username e username_clone na instância. Essa abordagem de conta dupla oferece maior disponibilidade e segurança. Defina uma política de rotação no console do KMS. Por padrão, o KMS rotaciona as contas a cada 24 horas, alternando qual conta está ativa. Credenciais Redis/Tair.
Não modifique nem exclua senhas de contas gerenciadas pelo KMS no console do Tair (Redis OSS-compatible). Isso evita interrupções no serviço.
Limitações
Não é possível alterar a senha de uma conta gerenciada pelo KMS no console do Tair. Faça a rotação manualmente ou configure a rotação automática no console do KMS. Rotacionar credenciais Redis/Tair.
Não é possível exclua uma conta gerenciada pelo KMS no console do Tair. Exclua-a no console do KMS. Excluir credenciais Redis.
Não é possível modifique a descrição de uma conta gerenciada pelo KMS no console do Tair.
Pré-requisitos
Uma instância ECS capaz de se conectar à instância Tair. Este tópico utiliza Alibaba Cloud Linux 3.2104 LTS 64-bit com Java 1.8.0.
Caso utilize um usuário RAM ou uma função RAM para gerencie credenciais de instância, anexe a política de sistema AliyunKMSSecretAdminAccess ao usuário ou à função. Conceder permissões.
Procedimento
-
Crie e ative uma instância KMS. Criar e ativar uma instância KMS.
Selecione a mesma vpc da instância ECS ao criar a instância KMS.
Se você já possui uma instância KMS, adicione a vpc da instância ECS a ela. Configure uma vpc.
-
Crie um application access point (AAP). Criar um application access point.
Após a criação, o navegador baixa o application identity credential content (ClientKeyContent, a JSON file) e o credential security token (ClientKeyPassword). Armazene-os em local seguro.
Baixe o certificado CA da instância KMS na página Instance Management. Obter o certificado CA de uma instância KMS.
Crie uma customer master key (CMK). Introdução ao gerenciamento de chaves.
Crie uma credencial para a instância do Tair (Redis OSS-compatible). Criar uma credencial Redis/Tair.
-
Escreva o código de teste em Java.
-
Adicione as seguintes dependências Maven. A seção <build> empacota todas as dependências em um único JAR.
<dependencies> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>5.1.0</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>alibabacloud-dkms-gcs-sdk</artifactId> <version>0.5.2</version> </dependency> <dependency> <groupId>com.aliyun</groupId> <artifactId>tea</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.10</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.9</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-assembly-plugin</artifactId> <version>3.3.0</version> <configuration> <archive> <manifest> <mainClass> com.aliyun.KMSJedisTest </mainClass> </manifest> </archive> <descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration> <executions> <execution> <id>assemble-all</id> <phase>package</phase> <goals> <goal>single</goal> </goals> </execution> </executions> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build> -
Escreva o código principal em KMSJedisTest.java.
NotaEste exemplo armazena credenciais em cache por 600 segundos (configurável via setCredentialCacheTime) para evitar chamadas ao KMS a cada nova conexão. Recomenda-se um tempo de cache de pelo menos 10 minutos.
package com.aliyun; import java.time.Duration; import redis.clients.jedis.DefaultJedisClientConfig; import redis.clients.jedis.HostAndPort; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; public class KMSJedisTest { public static void main(String[] args) throws Exception { if (args.length < 2) { System.out.println( "Please input kmsEndpoint, clientKeyFilePath, clientKeyPass, caCertPath, secretName, redisHost"); return; } String endpoint = args[0]; String clientKeyFilePath = args[1]; String clientKeyPass = args[2]; String caCertPath = args[3]; String secretName = args[4]; KMSRedisCredentialsProvider kmsRedisCredentialsProvider = new KMSRedisCredentialsProvider(endpoint, clientKeyFilePath, clientKeyPass, caCertPath, secretName); kmsRedisCredentialsProvider.setCredentialCacheTime(Duration.ofSeconds(10)); // Set the cache duration to prevent frequent requests to KMS. String redisHost = args[5]; JedisPool jedisPool = new JedisPool(HostAndPort.from(redisHost), DefaultJedisClientConfig.builder().credentialsProvider(kmsRedisCredentialsProvider).build()); for (int i = 0; i < Integer.MAX_VALUE; i++) { Thread.sleep(1000); try (Jedis jedis = jedisPool.getResource()) { System.out.println(jedis.set("" + i, "" + i)); System.out.println(jedis.get("" + i)); } catch (Exception e) { System.out.println(e); } } } } -
Escreva o código para KMSRedisCredentialsProvider.java.
package com.aliyun; import java.time.Duration; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import org.json.JSONObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.DefaultRedisCredentials; import redis.clients.jedis.RedisCredentials; import redis.clients.jedis.RedisCredentialsProvider; import com.aliyun.dkms.gcs.openapi.models.Config; import com.aliyun.dkms.gcs.sdk.Client; import com.aliyun.dkms.gcs.sdk.models.*; public class KMSRedisCredentialsProvider implements RedisCredentialsProvider { private static final Logger logger = LoggerFactory.getLogger(KMSRedisCredentialsProvider.class); private final String endpoint; private final String clientKeyFilePath; private final String clientKeyPass; private final String caCertPath; private final String secretName; private static Client client = null; // credential cache time private Duration credentialCacheTime = Duration.ofSeconds(600); private DefaultRedisCredentials cachedCredentials = null; private LocalDateTime credentialsExpiration = null; public KMSRedisCredentialsProvider(String endpoint, String clientKeyFilePath, String clientKeyPass, String caCertPath, String secretName) { this.endpoint = endpoint; this.clientKeyFilePath = clientKeyFilePath; this.clientKeyPass = clientKeyPass; this.caCertPath = caCertPath; this.secretName = secretName; createClientInstance(endpoint, clientKeyFilePath, clientKeyPass, caCertPath); } public void setCredentialCacheTime(Duration credentialCacheTime) { this.credentialCacheTime = credentialCacheTime; } private static synchronized void createClientInstance(String endpoint, String clientKeyFilePath, String clientKeyPass, String caCertPath) { if (client == null) { try { client = new Client(new Config() .setProtocol("https") .setEndpoint(endpoint) .setCaFilePath(caCertPath) .setClientKeyFile(clientKeyFilePath) .setPassword(clientKeyPass)); } catch (Exception e) { logger.error("Init kms client failed", e); throw new RuntimeException(e); } } } @Override public RedisCredentials get() { try { LocalDateTime now = LocalDateTime.now(); // Check cache if (cachedCredentials != null && now.isBefore(credentialsExpiration)) { return cachedCredentials; } GetSecretValueRequest request = new GetSecretValueRequest().setSecretName(secretName); GetSecretValueResponse getSecretValueResponse = client.getSecretValue(request); logger.debug("Now: " + now.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + ", getSecretValueRequest: " + request); String secretData = getSecretValueResponse.getSecretData(); JSONObject secretObject = new JSONObject(secretData); if (secretObject.get("AccountName") == null || secretObject.get("AccountPassword") == null) { throw new IllegalArgumentException("secretData must contain AccountName and AccountPassword"); } cachedCredentials = new DefaultRedisCredentials(secretObject.get("AccountName").toString(), secretObject.get("AccountPassword").toString()); credentialsExpiration = now.plusSeconds(credentialCacheTime.getSeconds()); return cachedCredentials; } catch (Exception e) { logger.error("get secret failed", e); throw new RuntimeException(e); } } @Override public void prepare() { // do nothing } @Override public void cleanUp() { // do nothing } } Empacote o projeto inteiro em um arquivo JAR executando o seguinte comando:
mvn package.
-
-
Execute o JAR na instância ECS para conectar-se ao Tair.
Sintaxe:
java -jar <kms-redis-jar-with-dependencies.jar> <kmsEndpoint> <clientKeyFilePath> <clientKeyPass> <caCertPath> <secretName> <redisHost>Parâmetros:
kms-redis-jar-with-dependencies.jar: O JAR empacotado com todas as dependências.
kmsEndpoint: O endpoint vpc da instância KMS, disponível na página de detalhes da instância.
clientKeyFilePath: O arquivo JSON de credencial de identidade do AAP baixado no Passo 2.
clientKeyPass: O token de segurança da credencial (arquivo TXT) baixado no Passo 2.
caCertPath: O certificado CA da instância KMS (arquivo PEM) baixado no Passo 3.
secretName: O nome da credencial de instância criada no Passo 5.
redisHost: O endereço de conexão vpc e a porta, por exemplo, r-bp1g727yrai5yh.redis.rds.aliyuncs.com:6379.
Exemplo:
java -jar kms-redis-samples-1.0-SNAPSHOT-jar-with-dependencies.jar kst-hzz6674e7fbw21x9x****.cryptoservice.kms.aliyuncs.com /root/clientKey_KAAP.6432ddc6-f23a-4d78-ac84-****4598206b.json 267d1****1cda4415058e1d72ec49e0a /root/PrivateKmsCA_kst-hzz6674e7fbw21x9x****.pem kms-redis r-bp1g727yrai5yh****.redis.rds.aliyuncs.com:6379Saída esperada em caso de conexão bem-sucedida:
0 OK 1 OK 2 OK 3 OK 4 OK -
Teste a rotação imediata de credenciais no console do KMS. Rotacionar credenciais Redis/Tair.
A rotação alterna a conta ativa entre username e username_clone.
Se a conexão da instância ECS permanecer estável, a rotação está funcionando corretamente.
... 30 OK 31 OK 32 OK 33 OK -
Execute um teste de switchover de alta disponibilidade (master-replica) na instância e monitore o cliente.
A saída a seguir mostra uma desconexão transitória durante o switchover de alta disponibilidade, seguida de uma reconexão bem-sucedida após o KMS atualizar a credencial.
138 OK 139 redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream. OK 142 OK 143 OK