當您的應用需要訪問Tair (Redis OSS-compatible)執行個體時,您可以將執行個體帳號口令儲存在KMS的憑據中(即執行個體憑據),業務應用通過整合阿里雲SDK、KMS SDK或憑據SDK向KMS動態擷取帳號口令。您還可以為憑據配置輪轉,以減少帳號口令的泄露風險。
功能介紹
在KMS託管Tair (Redis OSS-compatible)執行個體的帳號口令時,應用程式將無需配置待用資料庫帳號口令。管理員在KMS建立執行個體憑據,應用程式調用GetSecretValue介面擷取執行個體帳號和口令資訊,用於訪問執行個體。
例如您在KMS中自訂的執行個體憑證為username時,KMS最終會在執行個體中建立username、username_clone帳號,實現雙帳號託管,並使用該帳號訪問執行個體,相比較單帳號,雙帳號託管情境下應用程式的可用性、安全性更高。您可以在KMS控制台設定帳號輪轉策略,預設情況下KMS每24小時會進行帳號輪轉,即使用不同的帳號登入執行個體,提高安全性。更多資訊請參見Redis/Tair憑據。
請勿在Tair (Redis OSS-compatible)控制台中修改或刪除由KMS建立的帳號口令,以避免業務失敗。
使用限制
不支援在控制台修改KMS建立的託管型帳號的密碼,您可以前往KMS控制台通過手動輪轉或配置自動輪轉策略更換該密碼,更多資訊請參見輪轉Redis/Tair憑據。
不支援在控制台刪除KMS建立的託管型帳號,如需刪除,請前往KMS控制台執行刪除操作,更多資訊請參見刪除Redis憑據。
不支援在控制台修改KMS建立的託管型帳號的備忘資訊。
前提條件
已建立ECS執行個體,用於串連執行個體執行個體。本樣本ECS的作業系統為Alibaba Cloud Linux 3.2104 LTS 64位,同時已安裝JAVA 1.8.0。
若使用RAM使用者(子帳號)或RAM角色管理執行個體憑據,您需要為該角色授予系統權限原則AliyunKMSSecretAdminAccess。具體操作請參見授權許可權。
操作步驟
建立並啟用KMS。具體操作請參見建立和啟用KMS。
建立KMS時需選擇VPC,請選擇與ECS相同的VPC網路。
若您已建立KMS,請在KMS中添加ECS的VPC網路,具體操作請參見配置VPC。
建立應用存取點。具體操作請參見建立應用存取點。
建立後,瀏覽量會自動下載ClientKey資訊,包含應用身份憑證內容(ClientKeyContent,JSON檔案)和憑證口令(ClientKeyPassword),請妥善儲存。
下載KMS的CA認證,您可以在KMS控制台的執行個體管理頁面進行下載,更多資訊請參見擷取KMS的CA認證。
建立一個使用者主要金鑰。具體操作請參見密鑰管理快速入門。
建立Tair (Redis OSS-compatible)執行個體憑據。具體操作請參見建立Redis/Tair憑據。
編寫Java測試代碼。
在專案中添加Maven依賴,從Maven倉庫中自動下載Java安裝包。同時還可以將專案依賴打進作業JAR包,需增加下述<build>。
<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>
編寫主代碼KMSJedisTest.java。
說明為了防止每次建立串連都訪問KMS擷取密碼,本樣本中增加了一個緩衝credentialCacheTime(預設為600s),在緩衝周期內,會返回緩衝的密碼值,而不去訪問 KMS,您可以通過setCredentialCacheTime介面來調整緩衝的時間,建議不要低於10分鐘。
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)); // 設定緩衝時間,防止頻繁請求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); } } } }
編寫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 } }
將整個專案打進JAR包,命令為
mvn package
。
在ECS中,通過Java SDK串連執行個體。
本樣本的文法如下:
java -jar <kms-redis-jar-with-dependencies.jar> <kmsEndpoint> <clientKeyFilePath> <clientKeyPass> <caCertPath> <secretName> <redisHost>
參數說明:
kms-redis-jar-with-dependencies.jar:JAR包,請使用尾碼為jar-with-dependencies的JAR包。
kmsEndpoint:KMS的VPC地址,您可以在KMS執行個體詳情頁擷取。
clientKeyFilePath:存取點應用身份憑證內容,為步驟2下載的JSON檔案。
clientKeyPass:存取點憑證口令,內容在步驟2下載的TXT檔案中。
caCertPath:KMS執行個體的CA認證,為步驟3下載的PEM檔案。
secretName:步驟5中建立執行個體憑據的名稱。
redisHost:執行個體的VPC串連地址與連接埠號碼,例如r-bp1g727yrai5yh****.redis.rds.aliyuncs.com:6379。
樣本:
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:6379
預期返回如下,表示已成功串連:
0 OK 1 OK 2 OK 3 OK 4 OK
您可以在KMS控制台執行立即輪轉憑據測試,具體操作請參見輪轉Redis/Tair憑據。
輪轉表示KMS將使用另一個帳號(username或username_clone)訪問執行個體。
此時,ECS的串連若仍正常,則表示當前可以實現密碼滾動功能。
... 30 OK 31 OK 32 OK 33 OK
在執行個體執行主從切換(HA)測試,觀察用戶端情況。
預期返回如下,表示在HA時串連閃斷,KMS執行個體更新憑據並成功重新串連:
138 OK 139 redis.clients.jedis.exceptions.JedisConnectionException: Unexpected end of stream. OK 142 OK 143 OK