Compare commits

..

3 Commits
1.0.5 ... dev

Author SHA1 Message Date
Sunrisea 7a26e3940e
1.0.6 version release (#20) 2024-12-25 16:24:34 +08:00
Sunrisea 3ba4dc8e91
Support for KMS RAM access methods is provided (#19)
* refact AliyunConfigFilter, support the kms3.0 ram mode

* fix bug

* optimize logic

* support credentials for kms config encryption

* fix ram auth bug

* fix ram auth bug

* fix ram auth bug

* fix processor bug
2024-12-25 11:31:01 +08:00
KomachiSion 098c9c5a46 Fix log match credentials provider frequently. 2024-12-05 15:01:33 +08:00
18 changed files with 1782 additions and 1036 deletions

View File

@ -17,7 +17,7 @@
<inceptionYear>2018</inceptionYear>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client-mse-extension</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
<packaging>jar</packaging>
<developers>
@ -101,6 +101,12 @@
<version>1.3.7</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>kms20160120</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>com.alibaba.nacos</groupId>
<artifactId>nacos-client</artifactId>

View File

@ -1,58 +1,22 @@
package com.alibaba.nacos.client.aliyun;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.filter.AbstractConfigFilter;
import com.alibaba.nacos.api.config.filter.IConfigFilterChain;
import com.alibaba.nacos.api.config.filter.IConfigRequest;
import com.alibaba.nacos.api.config.filter.IConfigResponse;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.utils.StringUtils;
import com.aliyun.dkms.gcs.openapi.models.Config;
import com.aliyun.kms.KmsTransferAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.auth.AlibabaCloudCredentialsProvider;
import com.aliyuncs.auth.InstanceProfileCredentialsProvider;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.http.HttpClientConfig;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.kms.model.v20160120.DecryptRequest;
import com.aliyuncs.kms.model.v20160120.DescribeKeyRequest;
import com.aliyuncs.kms.model.v20160120.DescribeKeyResponse;
import com.aliyuncs.kms.model.v20160120.EncryptRequest;
import com.aliyuncs.kms.model.v20160120.GenerateDataKeyRequest;
import com.aliyuncs.kms.model.v20160120.GenerateDataKeyResponse;
import com.aliyuncs.kms.model.v20160120.SetDeletionProtectionRequest;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_KMS_AES_128_PREFIX;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_KMS_AES_256_PREFIX;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_PREFIX;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CONTENT;
import static com.alibaba.nacos.client.aliyun.AliyunConst.DATA_ID;
import static com.alibaba.nacos.client.aliyun.AliyunConst.ENCODE_UTF8;
import static com.alibaba.nacos.client.aliyun.AliyunConst.ENCRYPTED_DATA_KEY;
import static com.alibaba.nacos.client.aliyun.AliyunConst.GROUP;
import static com.alibaba.nacos.client.aliyun.AliyunConst.KEY_ID;
import static com.alibaba.nacos.client.aliyun.AliyunConst.KMS_REGION_ID;
import static com.alibaba.nacos.client.aliyun.AliyunConst.REGION_ID;
import static com.alibaba.nacos.client.aliyun.AliyunConst.STRING_VALUE_BLANK_ERROR_MSG_FORMAT;
/**
* the IConfigFilter of Aliyun.
@ -63,249 +27,27 @@ public class AliyunConfigFilter extends AbstractConfigFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(AliyunConfigFilter.class);
public static final int defaultRetryTimes = 3;
public static final int defaultRetryIntervalMilliseconds = 2 * 100;
public static final int defaultTimeoutMilliseconds = 3 * 1000;
private AliyunConst.KmsVersion kmsVersion;
private IAcsClient kmsClient;
private String keyId;
private final Set<String> addedKeys = new HashSet<String>();
private AsyncProcessor asyncProcessor;
private Exception localInitException;
private boolean isUseLocalCache;
private KmsLocalCache kmsLocalCache;
private boolean localCacheTestMode = false;
private KmsEncryptor kmsEncryptor;
@Override
public void init(Properties properties) {
LOGGER.info("init ConfigFilter: {}, for more information, please check: {}",
this.getFilterName(), AliyunConst.MSE_ENCRYPTED_CONFIG_USAGE_DOCUMENT_URL);
// get kms version, default using kms v1
String kv = properties.getProperty(AliyunConst.KMS_VERSION_KEY,
System.getProperty(AliyunConst.KMS_VERSION_KEY, System.getenv(AliyunConst.KMS_VERSION_KEY)));
if (StringUtils.isBlank(kv)) {
LOGGER.warn("kms version is not set, using kms v1 version.");
kmsVersion = AliyunConst.KmsVersion.Kmsv1;
} else {
kmsVersion = AliyunConst.KmsVersion.fromValue(kv);
if (kmsVersion == AliyunConst.KmsVersion.UNKNOWN_VERSION) {
LOGGER.warn("kms version is not supported, using kms v1 version.");
kmsVersion = AliyunConst.KmsVersion.Kmsv1;
} else {
LOGGER.info("using kms version {}.", kmsVersion.getValue());
}
}
//keyId corresponding to the id/alias of KMS's secret key, using mseServiceKeyId by default
if (kmsVersion == AliyunConst.KmsVersion.Kmsv1) {
keyId = AliyunConst.KMS_DEFAULT_KEY_ID_VALUE;
LOGGER.info("using default keyId {}.", keyId);
} else if(kmsVersion == AliyunConst.KmsVersion.Kmsv3) {
keyId = properties.getProperty(KEY_ID, System.getProperty(KEY_ID, System.getenv(KEY_ID)));
if (StringUtils.isBlank(keyId)) {
String errorMsg = "keyId is not set up yet, unable to encrypt the configuration.";
localInitException = new RuntimeException(errorMsg);
LOGGER.error(AliyunConst.formatHelpMessage(errorMsg), localInitException);
return;
} else {
LOGGER.info("using keyId {}.", keyId);
}
}
this.isUseLocalCache = KmsUtils.parsePropertyValue(properties, AliyunConst.NACOS_CONFIG_ENCRYPTION_KMS_LOCAL_CACHE_SWITCH,
AliyunConst.DEFAULT_KMS_LOCAL_CACHE_SWITCH);
if (this.isUseLocalCache()) {
this.localCacheTestMode = KmsUtils.parsePropertyValue(properties, AliyunConst.NACOS_CONFIG_ENCRYPTION_KMS_LOCAL_CACHE_TEST_MODE, false);
LOGGER.info("using kms encryption local cache.");
this.kmsLocalCache = new KmsLocalCache(properties);
}
try {
if (kmsVersion == AliyunConst.KmsVersion.Kmsv1) {
kmsClient = createKmsV1Client(properties);
} else if (kmsVersion == AliyunConst.KmsVersion.Kmsv3) {
kmsClient = createKmsV3Client(properties);
}
} catch (ClientException e) {
LOGGER.error(AliyunConst.formatHelpMessage("kms init failed."), e);
localInitException = e;
} catch (Exception e) {
LOGGER.error(AliyunConst.formatHelpMessage("create kms client failed."), e);
localInitException = e;
}
try {
asyncProcessor = new AsyncProcessor();
} catch (Exception e) {
LOGGER.error("init async processor failed.", e);
}
}
/**
* init kms v1 client, accessing the KMS service through a shared gateway.
*
* @date 2023/9/19
* @description
* @param properties
* @return com.aliyuncs.IAcsClient
* @throws
*/
private IAcsClient createKmsV1Client(Properties properties) {
String regionId = properties.getProperty(REGION_ID, System.getProperty(REGION_ID, System.getenv(REGION_ID)));
String kmsRegionId = properties.getProperty(KMS_REGION_ID, System.getProperty(KMS_REGION_ID, System.getenv(KMS_REGION_ID)));
if (StringUtils.isBlank(regionId)) {
regionId = kmsRegionId;
}
LOGGER.info("using regionId {}.", regionId);
if (StringUtils.isBlank(kmsRegionId)) {
kmsRegionId = regionId;
}
LOGGER.info("using kms regionId {}.", kmsRegionId);
if (StringUtils.isBlank(kmsRegionId) && StringUtils.isBlank(regionId)) {
String errorMsg = "region is not set up yet";
LOGGER.error(AliyunConst.formatHelpMessage(errorMsg));
localInitException = new RuntimeException(errorMsg);
return null;
}
String ramRoleName= properties.getProperty(PropertyKeyConst.RAM_ROLE_NAME,
System.getProperty(PropertyKeyConst.RAM_ROLE_NAME, System.getenv(PropertyKeyConst.RAM_ROLE_NAME)));
LOGGER.info("using ramRoleName {}.", ramRoleName);
String accessKey = properties.getProperty(PropertyKeyConst.ACCESS_KEY,
System.getProperty(PropertyKeyConst.ACCESS_KEY, System.getenv(PropertyKeyConst.ACCESS_KEY)));
LOGGER.info("using accessKey {}.", accessKey);
String secretKey = properties.getProperty(PropertyKeyConst.SECRET_KEY,
System.getProperty(PropertyKeyConst.SECRET_KEY, System.getenv(PropertyKeyConst.SECRET_KEY)));
// String kmsEndpoint = properties.getProperty(AliyunConst.KMS_ENDPOINT,
// System.getProperty(AliyunConst.KMS_ENDPOINT, System.getenv(AliyunConst.KMS_ENDPOINT)));
// if (!StringUtils.isBlank(kmsEndpoint)) {
// DefaultProfile.addEndpoint(regionId, "kms", kmsEndpoint);
// }
// LOGGER.info("using kmsEndpoint {}.", kmsEndpoint);
IClientProfile profile = null;
IAcsClient kmsClient = null;
if (!StringUtils.isBlank(ramRoleName)) {
profile = DefaultProfile.getProfile(regionId);
AlibabaCloudCredentialsProvider alibabaCloudCredentialsProvider = new InstanceProfileCredentialsProvider(
ramRoleName);
kmsClient = new KmsTransferAcsClient(profile, alibabaCloudCredentialsProvider);
LOGGER.info("successfully create kms client by using RAM role.");
} else {
profile = DefaultProfile.getProfile(regionId, accessKey, secretKey);
kmsClient = new KmsTransferAcsClient(profile);
LOGGER.info("successfully create kms client by using ak/sk.");
}
return kmsClient;
}
/**
* init kms v3 client, accessing the KMS service through the KMS instance gateway.
*
* @date 2023/9/19
* @description
* @param properties
* @return
* @throws
*/
private IAcsClient createKmsV3Client(Properties properties) throws ClientException {
Config config = new Config();
config.setProtocol("https");
IClientProfile profile = null;
String kmsClientKeyContent = properties.getProperty(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY,
System.getProperty(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, System.getenv(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY)));
if (!StringUtils.isBlank(kmsClientKeyContent)) {
LOGGER.info("using {}: {}.", AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, kmsClientKeyContent);
config.setClientKeyContent(kmsClientKeyContent);
} else {
String errorMsg = null;
LOGGER.info("{} is empty, will read from file.", AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY);
String kmsClientKeyFilePath = properties.getProperty(AliyunConst.KMS_CLIENT_KEY_FILE_PATH_KEY,
System.getProperty(AliyunConst.KMS_CLIENT_KEY_FILE_PATH_KEY, System.getenv(AliyunConst.KMS_CLIENT_KEY_FILE_PATH_KEY)));
if (!StringUtils.isBlank(kmsClientKeyFilePath)) {
String s = readFileToString(kmsClientKeyFilePath);
if (!StringUtils.isBlank(s)) {
LOGGER.info("using kmsClientKeyFilePath: {}.", kmsClientKeyFilePath);
config.setClientKeyFile(kmsClientKeyFilePath);
if(!StringUtils.isBlank(kmsClientKeyContent)||!StringUtils.isBlank(kmsClientKeyFilePath)){
LOGGER.info("kmsClientKeyContent or kmsClientKeyFilePath is set up, using ClientKey to connect KMS.");
this.kmsEncryptor = new ClientKeyKmsEncryptor(properties);
}else{
errorMsg = "both config from kmsClientKeyContent and kmsClientKeyFilePath is empty";
}
} else {
errorMsg = "kmsClientKeyFilePath is empty";
}
if (!StringUtils.isBlank(errorMsg)) {
localInitException = new RuntimeException(errorMsg);
return null;
LOGGER.info("kmsClientKeyContent and kmsClientKeyFilePath are not set up, using Ram to connect KMS.");
this.kmsEncryptor = new RamKmsEncryptor(properties);
}
}
String kmsEndpoint = properties.getProperty(AliyunConst.KMS_ENDPOINT,
System.getProperty(AliyunConst.KMS_ENDPOINT, System.getenv(AliyunConst.KMS_ENDPOINT)));
if (StringUtils.isBlank(kmsEndpoint)) {
String errorMsg = String.format("%s is empty", AliyunConst.KMS_ENDPOINT);
localInitException = new RuntimeException(errorMsg);
return null;
} else {
LOGGER.info("using kmsEndpoint: {}.", kmsEndpoint);
config.setEndpoint(kmsEndpoint);
}
String kmsPassword = properties.getProperty(AliyunConst.KMS_PASSWORD_KEY,
System.getProperty(AliyunConst.KMS_PASSWORD_KEY, System.getenv(AliyunConst.KMS_PASSWORD_KEY)));
if (StringUtils.isBlank(kmsPassword)) {
String errorMsg = String.format("%s is empty", AliyunConst.KMS_PASSWORD_KEY);
localInitException = new RuntimeException(errorMsg);
return null;
} else {
LOGGER.info("using kmsPassword prefix: {}.", kmsPassword.substring(kmsPassword.length() / 8));
config.setPassword(kmsPassword);
}
String kmsCaFileContent = properties.getProperty(AliyunConst.KMS_CA_FILE_CONTENT,
System.getProperty(AliyunConst.KMS_CA_FILE_CONTENT, System.getenv(AliyunConst.KMS_CA_FILE_CONTENT)));
if (!StringUtils.isBlank(kmsCaFileContent)) {
LOGGER.info("using {}: {}.", AliyunConst.KMS_CA_FILE_CONTENT, kmsCaFileContent);
config.setCa(kmsCaFileContent);
} else {
String errorMsg = null;
LOGGER.info("{} is empty, will read from file.", AliyunConst.KMS_CA_FILE_CONTENT);
String kmsCaFilePath = properties.getProperty(AliyunConst.KMS_CA_FILE_PATH_KEY,
System.getProperty(AliyunConst.KMS_CA_FILE_PATH_KEY, System.getenv(AliyunConst.KMS_CA_FILE_PATH_KEY)));
if (!StringUtils.isBlank(kmsCaFilePath)) {
config.setCaFilePath(kmsCaFilePath);
} else {
errorMsg = "kmsCaFilePath is empty";
config.setCaFilePath(null);
}
if (!StringUtils.isBlank(errorMsg)) {
LOGGER.warn(AliyunConst.formatHelpMessage(errorMsg));
profile = DefaultProfile.getProfile(config.getRegionId(), "ak", "sk", "sts");
HttpClientConfig httpClientConfig = HttpClientConfig.getDefault();
httpClientConfig.setIgnoreSSLCerts(true);
profile.setHttpClientConfig(httpClientConfig);
}
}
if (profile == null) {
return new KmsTransferAcsClient(config);
}
return new KmsTransferAcsClient(profile, config);
}
@Override
public void doFilter(IConfigRequest request, IConfigResponse response, IConfigFilterChain filterChain)
@ -318,7 +60,7 @@ public class AliyunConfigFilter extends AbstractConfigFilter {
group = (String) request.getParameter(GROUP);
if (dataId.startsWith(CIPHER_PREFIX)) {
if (!StringUtils.isBlank((String)request.getParameter(CONTENT))) {
request.putParameter(CONTENT, encrypt(keyId, request));
request.putParameter(CONTENT, kmsEncryptor.encrypt(request));
}
}
@ -329,7 +71,7 @@ public class AliyunConfigFilter extends AbstractConfigFilter {
group = (String) response.getParameter(GROUP);
if (dataId.startsWith(CIPHER_PREFIX)) {
if (!StringUtils.isBlank((String)response.getParameter(CONTENT))) {
response.putParameter(CONTENT, decrypt(response));
response.putParameter(CONTENT, kmsEncryptor.decrypt(response));
}
}
}
@ -341,436 +83,6 @@ public class AliyunConfigFilter extends AbstractConfigFilter {
}
}
private String decrypt(IConfigResponse response) throws Exception {
checkIfKmsClientIsReady();
String dataId = (String) response.getParameter(DATA_ID);
String group = (String) response.getParameter(GROUP);
String encryptedContent = (String) response.getParameter(CONTENT);
String encryptedDataKey = (String) response.getParameter(ENCRYPTED_DATA_KEY);
String plainDataKey = null;
String result = null;
Exception requestKmsException = null;
String blankResultErrorMsg = "decrypt from kms failed.";
boolean isUsedCache = true;
try {
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
throwExceptionIfStringBlankWithErrorKey(encryptedDataKey, GroupKeyUtils.getGroupKey2(dataId, group),
"decrypt failed", "response.getParameter(ENCRYPTED_DATA_KEY)");
plainDataKey = decrypt(encryptedDataKey);
result = AesUtils.decrypt(encryptedContent, plainDataKey, ENCODE_UTF8);
} else if (dataId.startsWith(CIPHER_PREFIX)) {
result = decrypt(encryptedContent);
}
} catch (BlankStringException e) {
throw e;
} catch (Exception e) {
//use local cache protection
LOGGER.error("decrypt config:[{}] failed by using kms service: {}.",
GroupKeyUtils.getGroupKey2(dataId, group), e.getMessage(), e);
requestKmsException = e;
}
if (this.localCacheTestMode) {
requestKmsException = requestKmsException == null ? new RuntimeException("test mode exception to use local cache") : requestKmsException;
result = null;
}
if (requestKmsException != null || StringUtils.isBlank(result)) {
LOGGER.warn("decrypt config [{}] failed with exception or empty result by using kms service. try to use local cache.", GroupKeyUtils.getGroupKey2(dataId, group));
result = getDecryptedContentByUsingLocalCache(group, dataId, encryptedDataKey, encryptedContent);
if (requestKmsException != null && StringUtils.isBlank(result)) {
throw requestKmsException;
} else if (StringUtils.isBlank(result)) {
blankResultErrorMsg += "and no kms decryption local cache.";
}
} else {
isUsedCache = false;
}
throwExceptionIfStringBlankWithErrorKey(result, GroupKeyUtils.getGroupKey2(dataId, group), "decrypt failed", blankResultErrorMsg);
if (!isUsedCache) {
this.updateLocalCacheItem(group, dataId, encryptedDataKey, encryptedContent, plainDataKey, result);
}
return result;
}
private String encrypt(String keyId, IConfigRequest configRequest) throws Exception {
checkIfKmsClientIsReady();
throwExceptionIfStringBlankWithErrorKey(keyId, "", "keyId is not set.", KEY_ID);
protectKeyId(keyId);
String dataId = (String) configRequest.getParameter(DATA_ID);
String group = (String) configRequest.getParameter(GROUP);
String plainContent = (String) configRequest.getParameter(CONTENT);
String plainDataKey = null;
String encryptedDataKey = null;
String result = null; //encryptedContent
String blankResultErrorMsg = "encrypt from kms failed.";
// Exception requestKmsException = null;
//prefer to use kms service
try {
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
String keySpec = KmsUtils.getKeySpecByDataIdPrefix(dataId);
GenerateDataKeyResponse generateDataKeyResponse = generateDataKey(keyId, keySpec);
plainDataKey = generateDataKeyResponse.getPlaintext();
throwExceptionIfStringBlankWithErrorKey(plainDataKey, GroupKeyUtils.getGroupKey2(dataId, group),
"generateDataKeyResponse.getPlaintext()", "plainDataKey");
encryptedDataKey = generateDataKeyResponse.getCiphertextBlob();
throwExceptionIfStringBlankWithErrorKey(encryptedDataKey, GroupKeyUtils.getGroupKey2(dataId, group),
"generateDataKeyResponse.getCiphertextBlob()", "encryptedDataKey");
configRequest.putParameter(ENCRYPTED_DATA_KEY, encryptedDataKey);
result = AesUtils.encrypt(plainContent, plainDataKey, ENCODE_UTF8);
} else if (dataId.startsWith(CIPHER_PREFIX)) {
result = encrypt(keyId, plainContent);
}
} catch (Exception e) {
//not use local cache protection
LOGGER.error("encrypt config:[{}] failed by using kms service: {}.",
GroupKeyUtils.getGroupKey2(dataId, group), e.getMessage(), e);
throw e;
// requestKmsException = e;
}
//using cache when encrypt failed by using kms service
// if (requestKmsException != null || StringUtils.isBlank(result)) {
// LOGGER.warn("encrypt config [{}] failed with exception or empty result by using kms service. will use local cache.", GroupKeyUtils.getGroupKey2(dataId, group));
// result = getEncryptedContentByUsingLocalCache(group, dataId, plainContent, configRequest);
// if (requestKmsException != null && StringUtils.isBlank(result)) {
// throw requestKmsException;
// } else if (StringUtils.isBlank(result)) {
// blankResultErrorMsg += " and no kms encryption local cache.";
// }
// }
throwExceptionIfStringBlankWithErrorKey(result, GroupKeyUtils.getGroupKey2(dataId, group), "encrypt failed", blankResultErrorMsg);
//update local cache
this.updateLocalCacheItem(group, dataId, encryptedDataKey, result, plainDataKey, plainContent);
return result;
}
@Deprecated
private String getEncryptedContentByUsingLocalCache(String group, String dataId, String plainContent, IConfigRequest configRequest)
throws Exception {
KmsLocalCache.LocalCacheItem localCacheItem = getLocalCacheItem(group, dataId, plainContent);
String result = null;
if (localCacheItem != null) {
if (!StringUtils.isBlank(localCacheItem.getPlainDataKey())) {
result = AesUtils.encrypt(plainContent, localCacheItem.getPlainDataKey(), ENCODE_UTF8);
configRequest.putParameter(ENCRYPTED_DATA_KEY, localCacheItem.getEncryptedDataKey());
} else if (!StringUtils.isBlank(localCacheItem.getPlainContent())) {
result = localCacheItem.getEncryptedContent();
}
}
return result;
}
private String getDecryptedContentByUsingLocalCache(String group, String dataId, String encryptedDataKey, String encryptedContent)
throws Exception {
KmsLocalCache.LocalCacheItem localCacheItem = getLocalCacheItem(group, dataId, encryptedDataKey, encryptedContent);
if (localCacheItem != null) {
if (!StringUtils.isBlank(localCacheItem.getPlainDataKey())) {
return AesUtils.decrypt(encryptedContent, localCacheItem.getPlainDataKey(), ENCODE_UTF8);
} else if (!StringUtils.isBlank(localCacheItem.getPlainContent())) {
return localCacheItem.getPlainContent();
}
}
return null;
}
private void updateLocalCacheItem(String group, String dataId, String encryptedDataKey, String encryptedContent, String plainDataKey, String plainContent) {
if (!this.isLocalCacheAvailable()) {
return;
}
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
getKmsLocalCache().put(GroupKeyUtils.getGroupKey2(dataId, group), new KmsLocalCache.LocalCacheItem(encryptedDataKey, encryptedContent, plainDataKey));
} else if(dataId.startsWith(CIPHER_PREFIX)) {
getKmsLocalCache().put(GroupKeyUtils.getGroupKey2(dataId, group), new KmsLocalCache.LocalCacheItem(encryptedContent, plainContent));
}
}
private String decrypt(String content) throws Exception {
AtomicReference<String> resultContent = new AtomicReference<>();
final DecryptRequest decReq = new DecryptRequest();
decReq.setSysProtocol(ProtocolType.HTTPS);
decReq.setSysMethod(MethodType.POST);
decReq.setAcceptFormat(FormatType.XML);
decReq.setCiphertextBlob(content);
locallyRunWithRetryTimesAndTimeout(() -> {
try {
resultContent.set(kmsClient.getAcsResponse(decReq).getPlaintext());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (StringUtils.isBlank(resultContent.get())) {
return false;
}
return true;
}, defaultRetryTimes, defaultTimeoutMilliseconds);
return resultContent.get();
}
public String encrypt(String keyId, String plainText) throws Exception {
AtomicReference<String> resultContent = new AtomicReference<>();
final EncryptRequest encReq = new EncryptRequest();
encReq.setProtocol(ProtocolType.HTTPS);
encReq.setAcceptFormat(FormatType.XML);
encReq.setMethod(MethodType.POST);
encReq.setKeyId(keyId);
encReq.setPlaintext(plainText);
locallyRunWithRetryTimesAndTimeout(() -> {
try {
resultContent.set( kmsClient.getAcsResponse(encReq).getCiphertextBlob());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (StringUtils.isBlank(resultContent.get())) {
return false;
}
return true;
}, defaultRetryIntervalMilliseconds, defaultTimeoutMilliseconds);
return resultContent.get();
}
public GenerateDataKeyResponse generateDataKey(String keyId, String keySpec) throws Exception {
GenerateDataKeyRequest generateDataKeyRequest = new GenerateDataKeyRequest();
generateDataKeyRequest.setAcceptFormat(FormatType.XML);
generateDataKeyRequest.setKeyId(keyId);
generateDataKeyRequest.setKeySpec(keySpec);
AtomicReference<GenerateDataKeyResponse> resultContent = new AtomicReference<>();
locallyRunWithRetryTimesAndTimeout(() -> {
try {
resultContent.set(kmsClient.getAcsResponse(generateDataKeyRequest));
} catch (Exception e) {
throw new RuntimeException(e);
}
if (resultContent.get() == null) {
return false;
}
return true;
}, defaultRetryTimes, defaultTimeoutMilliseconds);
return resultContent.get();
}
private void protectKeyId(String keyId) {
if (!addedKeys.contains(keyId)) {
synchronized (addedKeys) {
if (addedKeys.contains(keyId)) {
return;
}
addedKeys.add(keyId);
asyncProcessor.addTack(new Runnable() {
@Override
public void run() {
try {
if (kmsClient == null) {
LOGGER.error("kms client hasn't initiated.");
return;
}
DescribeKeyRequest describeKeyRequest = new DescribeKeyRequest();
describeKeyRequest.setKeyId(keyId);
try {
DescribeKeyResponse describeKeyResponse = kmsClient.getAcsResponse(describeKeyRequest);
if (describeKeyResponse.getKeyMetadata()!= null) {
if (!"Enabled".equals(describeKeyResponse.getKeyMetadata().getKeyState())) {
throw new RuntimeException("Key not available");
}
String arn = describeKeyResponse.getKeyMetadata().getArn();
LOGGER.info("set deletion protection for keyId[{}], arn[{}]", keyId, arn);
SetDeletionProtectionRequest setDeletionProtectionRequest = new SetDeletionProtectionRequest();
setDeletionProtectionRequest.setProtectedResourceArn(arn);
setDeletionProtectionRequest.setEnableDeletionProtection(true);
setDeletionProtectionRequest.setDeletionProtectionDescription("key is used by mse");
try {
kmsClient.getAcsResponse(setDeletionProtectionRequest);
} catch (ClientException e) {
LOGGER.error("set deletion protect failed, keyId: {}.", keyId);
throw e;
}
} else {
addedKeys.remove(keyId);
LOGGER.warn("keyId meta is null, cannot set key protection");
}
} catch (ClientException e) {
LOGGER.error("describe key failed, keyId: {}.", keyId);
throw e;
}
} catch (Exception e) {
addedKeys.remove(keyId);
LOGGER.error("execute async task failed", e);
}
}
});
}
}
}
private static void locallyRunWithRetryTimesAndTimeout(Supplier<Boolean> runnable, int retryTimes, long timeout)
throws Exception {
int locallyRetryTimes = 0;
Exception localException = null;
long beginTime = System.currentTimeMillis();
while (locallyRetryTimes++ < retryTimes && System.currentTimeMillis() < beginTime + timeout) {
try {
if (runnable.get()) {
break;
}
} catch (Exception e) {
localException = e;
}
if (localException == null
|| (localException != null
&& (localException instanceof ClientException)
&& KmsUtils.judgeNeedRecoveryException((ClientException) localException))) {
//some exception need to retry
Thread.sleep(defaultRetryIntervalMilliseconds);
} else {
throw localException;
}
}
if (localException != null) {
throw localException;
}
}
private static String readFileToString(String filePath) {
File file = getFileByPath(filePath);
if (file == null || !file.exists()) {
return null;
}
try {
Path path = Paths.get(file.getAbsolutePath());
byte[] fileContent = Files.readAllBytes(path);
return new String(fileContent, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static File getFileByPath(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
String path = AliyunConfigFilter.class.getClassLoader().getResource("").getPath();
if (!(file = new File(path + filePath)).exists()) {
path = Paths.get(filePath).toAbsolutePath().toString();
if (!(file = new File(path)).exists()) {
return null;
}
}
}
return file;
}
private boolean isUseLocalCache() {
return this.isUseLocalCache;
}
private KmsLocalCache getKmsLocalCache() {
return this.kmsLocalCache;
}
//using by decrypt
private KmsLocalCache.LocalCacheItem getLocalCacheItem(String group, String dataId, String encryptDataKey, String encryptedContent) {
//check if open local cache
if (!this.isLocalCacheAvailable()) {
return null;
}
//check if cache is ready
KmsLocalCache.LocalCacheItem localCacheItem = this.getKmsLocalCache().get(GroupKeyUtils.getGroupKey2(dataId, group));
if (localCacheItem == null) {
return null;
}
//check if cache is valid
if (!checkIfKmsCacheItemValidByDecrypt(localCacheItem, dataId, encryptDataKey, encryptedContent)) {
return null;
}
return localCacheItem;
}
//using by encrypt
private KmsLocalCache.LocalCacheItem getLocalCacheItem(String group, String dataId, String plainText) {
//check if open local cache
if (!this.isLocalCacheAvailable()) {
return null;
}
//check if cache is ready
KmsLocalCache.LocalCacheItem localCacheItem = this.getKmsLocalCache().get(GroupKeyUtils.getGroupKey2(dataId, group));
if (localCacheItem == null) {
return null;
}
//check if cache is valid
if (checkIfKmsCacheItemValidByEncrypt(localCacheItem, dataId, plainText)) {
return null;
}
return localCacheItem;
}
private boolean checkIfKmsCacheItemValidByEncrypt(KmsLocalCache.LocalCacheItem localCacheItem, String dataId, String plainContent) {
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
return !StringUtils.isBlank(localCacheItem.getEncryptedDataKey())
&& !StringUtils.isBlank(localCacheItem.getPlainDataKey());
} else if (dataId.startsWith(CIPHER_PREFIX)) {
return !StringUtils.isBlank(localCacheItem.getEncryptedContent())
&& !StringUtils.isBlank(localCacheItem.getPlainContent())
&& localCacheItem.getPlainContent().equals(plainContent);
}
return false;
}
private boolean checkIfKmsCacheItemValidByDecrypt(KmsLocalCache.LocalCacheItem localCacheItem, String dataId, String encryptedDataKey, String encryptedContent) {
String encryptedContentMd5 = MD5Utils.md5Hex(encryptedContent, ENCODE_UTF8);
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
return !StringUtils.isBlank(localCacheItem.getEncryptedDataKey())
&& !StringUtils.isBlank(localCacheItem.getEncryptedContentMD5())
&& !StringUtils.isBlank(localCacheItem.getPlainDataKey())
&& StringUtils.equals(localCacheItem.getEncryptedDataKey(), encryptedDataKey)
&& StringUtils.equals(localCacheItem.getEncryptedContentMD5(), encryptedContentMd5);
} else if (dataId.startsWith(CIPHER_PREFIX)) {
return !StringUtils.isBlank(localCacheItem.getEncryptedContentMD5())
&& !StringUtils.isBlank(localCacheItem.getPlainContent())
&& StringUtils.equals(localCacheItem.getEncryptedContentMD5(), encryptedContentMd5);
}
return false;
}
private void checkIfKmsClientIsReady() throws Exception {
if (kmsClient == null) {
if (localInitException != null) {
throw localInitException;
} else {
throw new RuntimeException("kms client isn't initialized. " +
"For more information, please check: " + AliyunConst.MSE_ENCRYPTED_CONFIG_USAGE_DOCUMENT_URL);
}
}
}
private void throwExceptionIfStringBlankWithErrorKey(String s, String groupKey, String errorMsg, String errorKey) throws Exception {
if (StringUtils.isBlank(s)) {
throw new BlankStringException(String.format(STRING_VALUE_BLANK_ERROR_MSG_FORMAT, groupKey, errorMsg, errorKey)
+ "For more information, please check: " + AliyunConst.MSE_ENCRYPTED_CONFIG_USAGE_DOCUMENT_URL);
}
}
private static class BlankStringException extends RuntimeException {
public BlankStringException(String message) {
super(message);
}
}
private boolean isLocalCacheAvailable() {
return this.isUseLocalCache() && this.getKmsLocalCache()!= null;
}
@Override
public int getOrder() {
return 1;
@ -780,4 +92,8 @@ public class AliyunConfigFilter extends AbstractConfigFilter {
public String getFilterName() {
return this.getClass().getName();
}
public void close() throws IOException {
this.kmsEncryptor.close();
}
}

View File

@ -27,6 +27,32 @@ public class AliyunConst {
public static final String KMS_KEY_SPEC_AES_256 = "AES_256";
public static final String KMS_ACCESS_KEY = "kmsAccessKey";
public static final String KMS_SECRET_KEY = "kmsSecretKey";
public static final String KMS_RAM_ROLE_NAME = "kmsRamRoleName";
public static final String KMS_ROLE_ARN = "kmsRoleArn";
public static final String KMS_POLICY = "kmsPolicy";
public static final String KMS_ROLE_SESSION_EXPIRATION_SECONDS = "kmsRoleSessionExpiration";
public static final String KMS_OIDC_PROVIDER_ARN = "kmsOidcProviderArn";
public static final String KMS_ROLE_SESSION_NAME = "kmsRoleSessionName";
public static final String KMS_OIDC_TOKEN_FILE_PATH = "kmsOidcTokenFilePath";
public static final String KMS_SECURITY_TOKEN = "kmsSecurityToken";
public static final String KMS_EXTENSION_ACCESS_KEY = "kmsExtensionAccessKey";
public static final String KMS_EXTENSION_SECRET_KEY = "kmsExtensionSecretKey";
public static final String KMS_CREDENTIALS_URI = "kmsCredentialsUri";
public static final String ENCODE_UTF8 = StandardCharsets.UTF_8.displayName();
public static final String ENCODE_UTF16 = StandardCharsets.UTF_16.displayName();
@ -53,6 +79,8 @@ public class AliyunConst {
public static final String KMS_CA_FILE_CONTENT = "kmsCaFileContent";
public static final String OPEN_SSL_KEY = "openSSL";
public static final String MSE_ENCRYPTED_CONFIG_USAGE_DOCUMENT_URL = "https://help.aliyun.com/zh/mse/user-guide/create-and-use-encrypted-configurations?spm=a2c4g.11186623.0.0.55587becdOW3jf";
public static final String NACOS_CONFIG_ENCRYPTION_KMS_LOCAL_CACHE_SWITCH = "nacos.config.encryption.kms.local.cache.switch";

View File

@ -0,0 +1,305 @@
package com.alibaba.nacos.client.aliyun;
import com.alibaba.nacos.api.utils.StringUtils;
import com.aliyun.dkms.gcs.openapi.models.Config;
import com.aliyun.kms.KmsTransferAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.FormatType;
import com.aliyuncs.http.HttpClientConfig;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.http.ProtocolType;
import com.aliyuncs.kms.model.v20160120.DecryptRequest;
import com.aliyuncs.kms.model.v20160120.DescribeKeyRequest;
import com.aliyuncs.kms.model.v20160120.DescribeKeyResponse;
import com.aliyuncs.kms.model.v20160120.EncryptRequest;
import com.aliyuncs.kms.model.v20160120.GenerateDataKeyRequest;
import com.aliyuncs.kms.model.v20160120.GenerateDataKeyResponse;
import com.aliyuncs.kms.model.v20160120.SetDeletionProtectionRequest;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import static com.alibaba.nacos.client.aliyun.AliyunConst.KEY_ID;
public class ClientKeyKmsEncryptor extends KmsEncryptor{
private static final Logger LOGGER = LoggerFactory.getLogger(ClientKeyKmsEncryptor.class);
private IAcsClient kmsClient;
private String keyId;
private Exception localInitException;
private final Set<String> addedKeys = new HashSet<String>();
private AsyncProcessor asyncProcessor;
public ClientKeyKmsEncryptor(Properties properties) {
super(properties);
keyId = properties.getProperty(KEY_ID, System.getProperty(KEY_ID, System.getenv(KEY_ID)));
if (StringUtils.isBlank(keyId)) {
String errorMsg = "keyId is not set up yet, unable to encrypt the configuration.";
localInitException = new RuntimeException(errorMsg);
LOGGER.error(AliyunConst.formatHelpMessage(errorMsg));
} else {
LOGGER.info("using keyId {}.", keyId);
}
try{
kmsClient = createKmsV3Client(properties);
}catch (ClientException e){
localInitException = e;
}
if(localInitException == null){
try {
asyncProcessor = new AsyncProcessor();
} catch (Exception e) {
LOGGER.error("init async processor failed.", e);
}
}
}
public String encrypt(String plainText) throws Exception {
AtomicReference<String> resultContent = new AtomicReference<>();
final EncryptRequest encReq = new EncryptRequest();
encReq.setProtocol(ProtocolType.HTTPS);
encReq.setAcceptFormat(FormatType.XML);
encReq.setMethod(MethodType.POST);
encReq.setKeyId(keyId);
encReq.setPlaintext(plainText);
locallyRunWithRetryTimesAndTimeout(() -> {
try {
resultContent.set( kmsClient.getAcsResponse(encReq).getCiphertextBlob());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (StringUtils.isBlank(resultContent.get())) {
return false;
}
return true;
}, defaultRetryTimes, defaultTimeoutMilliseconds);
return resultContent.get();
}
@Override
public String decrypt(String content) throws Exception {
AtomicReference<String> resultContent = new AtomicReference<>();
final DecryptRequest decReq = new DecryptRequest();
decReq.setSysProtocol(ProtocolType.HTTPS);
decReq.setSysMethod(MethodType.POST);
decReq.setAcceptFormat(FormatType.XML);
decReq.setCiphertextBlob(content);
locallyRunWithRetryTimesAndTimeout(() -> {
try {
resultContent.set(kmsClient.getAcsResponse(decReq).getPlaintext());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (StringUtils.isBlank(resultContent.get())) {
return false;
}
return true;
}, defaultRetryTimes, defaultTimeoutMilliseconds);
return resultContent.get();
}
private IAcsClient createKmsV3Client(Properties properties) throws ClientException{
Config config = new Config();
config.setProtocol("https");
IClientProfile profile = null;
String kmsClientKeyContent = properties.getProperty(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY,
System.getProperty(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, System.getenv(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY)));
if (!StringUtils.isBlank(kmsClientKeyContent)) {
LOGGER.info("using {}: {}.", AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, kmsClientKeyContent);
config.setClientKeyContent(kmsClientKeyContent);
} else {
String errorMsg = null;
LOGGER.info("{} is empty, will read from file.", AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY);
String kmsClientKeyFilePath = properties.getProperty(AliyunConst.KMS_CLIENT_KEY_FILE_PATH_KEY,
System.getProperty(AliyunConst.KMS_CLIENT_KEY_FILE_PATH_KEY, System.getenv(AliyunConst.KMS_CLIENT_KEY_FILE_PATH_KEY)));
if (!StringUtils.isBlank(kmsClientKeyFilePath)) {
String s = readFileToString(kmsClientKeyFilePath);
if (!StringUtils.isBlank(s)) {
LOGGER.info("using kmsClientKeyFilePath: {}.", kmsClientKeyFilePath);
config.setClientKeyFile(kmsClientKeyFilePath);
} else {
errorMsg = "both config from kmsClientKeyContent and kmsClientKeyFilePath is empty";
}
} else {
errorMsg = "kmsClientKeyFilePath and kmsClientKeyContent are both empty";
}
if (!StringUtils.isBlank(errorMsg)) {
localInitException = new RuntimeException(errorMsg);
return null;
}
}
String kmsEndpoint = properties.getProperty(AliyunConst.KMS_ENDPOINT,
System.getProperty(AliyunConst.KMS_ENDPOINT, System.getenv(AliyunConst.KMS_ENDPOINT)));
if (StringUtils.isBlank(kmsEndpoint)) {
String errorMsg = String.format("%s is empty", AliyunConst.KMS_ENDPOINT);
localInitException = new RuntimeException(errorMsg);
return null;
} else {
LOGGER.info("using kmsEndpoint: {}.", kmsEndpoint);
config.setEndpoint(kmsEndpoint);
}
String kmsPassword = properties.getProperty(AliyunConst.KMS_PASSWORD_KEY,
System.getProperty(AliyunConst.KMS_PASSWORD_KEY, System.getenv(AliyunConst.KMS_PASSWORD_KEY)));
if (StringUtils.isBlank(kmsPassword)) {
String errorMsg = String.format("%s is empty", AliyunConst.KMS_PASSWORD_KEY);
localInitException = new RuntimeException(errorMsg);
return null;
} else {
LOGGER.info("using kmsPassword prefix: {}.", kmsPassword.substring(kmsPassword.length() / 8));
config.setPassword(kmsPassword);
}
String kmsCaFileContent = properties.getProperty(AliyunConst.KMS_CA_FILE_CONTENT,
System.getProperty(AliyunConst.KMS_CA_FILE_CONTENT, System.getenv(AliyunConst.KMS_CA_FILE_CONTENT)));
if (!StringUtils.isBlank(kmsCaFileContent)) {
LOGGER.info("using {}: {}.", AliyunConst.KMS_CA_FILE_CONTENT, kmsCaFileContent);
config.setCa(kmsCaFileContent);
} else {
String errorMsg = null;
LOGGER.info("{} is empty, will read from file.", AliyunConst.KMS_CA_FILE_CONTENT);
String kmsCaFilePath = properties.getProperty(AliyunConst.KMS_CA_FILE_PATH_KEY,
System.getProperty(AliyunConst.KMS_CA_FILE_PATH_KEY, System.getenv(AliyunConst.KMS_CA_FILE_PATH_KEY)));
if (!StringUtils.isBlank(kmsCaFilePath)) {
config.setCaFilePath(kmsCaFilePath);
} else {
errorMsg = "kmsCaFilePath is empty";
config.setCaFilePath(null);
}
if (!StringUtils.isBlank(errorMsg)) {
LOGGER.warn(AliyunConst.formatHelpMessage(errorMsg));
profile = DefaultProfile.getProfile(config.getRegionId(), "ak", "sk", "sts");
HttpClientConfig httpClientConfig = HttpClientConfig.getDefault();
httpClientConfig.setIgnoreSSLCerts(true);
profile.setHttpClientConfig(httpClientConfig);
}
}
if (profile == null) {
return new KmsTransferAcsClient(config);
}
return new KmsTransferAcsClient(profile, config);
}
@Override
public DataKey generateDataKey(String keySpec) throws Exception {
GenerateDataKeyRequest generateDataKeyRequest = new GenerateDataKeyRequest();
generateDataKeyRequest.setAcceptFormat(FormatType.XML);
generateDataKeyRequest.setKeyId(keyId);
generateDataKeyRequest.setKeySpec(keySpec);
AtomicReference<GenerateDataKeyResponse> resultContent = new AtomicReference<>();
locallyRunWithRetryTimesAndTimeout(() -> {
try {
resultContent.set(kmsClient.getAcsResponse(generateDataKeyRequest));
} catch (Exception e) {
throw new RuntimeException(e);
}
if (resultContent.get() == null) {
return false;
}
return true;
}, defaultRetryTimes, defaultTimeoutMilliseconds);
DataKey dataKey = new DataKey();
dataKey.setEncryptedDataKey(resultContent.get().getCiphertextBlob());
dataKey.setPlainDataKey(resultContent.get().getPlaintext());
return dataKey;
}
@Override
public void protectKeyId() {
if (!addedKeys.contains(keyId)) {
synchronized (addedKeys) {
if (addedKeys.contains(keyId)) {
return;
}
addedKeys.add(keyId);
asyncProcessor.addTack(new Runnable() {
@Override
public void run() {
try {
if (kmsClient == null) {
LOGGER.error("kms client hasn't initiated.");
return;
}
DescribeKeyRequest describeKeyRequest = new DescribeKeyRequest();
describeKeyRequest.setKeyId(keyId);
try {
DescribeKeyResponse describeKeyResponse = kmsClient.getAcsResponse(describeKeyRequest);
if (describeKeyResponse.getKeyMetadata()!= null) {
if (!"Enabled".equals(describeKeyResponse.getKeyMetadata().getKeyState())) {
throw new RuntimeException("Key not available");
}
String arn = describeKeyResponse.getKeyMetadata().getArn();
LOGGER.info("set deletion protection for keyId[{}], arn[{}]", keyId, arn);
SetDeletionProtectionRequest setDeletionProtectionRequest = new SetDeletionProtectionRequest();
setDeletionProtectionRequest.setProtectedResourceArn(arn);
setDeletionProtectionRequest.setEnableDeletionProtection(true);
setDeletionProtectionRequest.setDeletionProtectionDescription("key is used by mse");
try {
kmsClient.getAcsResponse(setDeletionProtectionRequest);
} catch (ClientException e) {
LOGGER.error("set deletion protect failed, keyId: {}.", keyId);
throw e;
}
} else {
addedKeys.remove(keyId);
LOGGER.warn("keyId meta is null, cannot set key protection");
}
} catch (ClientException e) {
LOGGER.error("describe key failed, keyId: {}.", keyId);
throw e;
}
} catch (Exception e) {
addedKeys.remove(keyId);
LOGGER.error("execute async task failed", e);
}
}
});
}
}
}
@Override
public void checkIfKmsClientIsReady() throws Exception {
if (kmsClient == null) {
if (localInitException != null) {
throw localInitException;
} else {
throw new RuntimeException("kms client isn't initialized. " +
"For more information, please check: " + AliyunConst.MSE_ENCRYPTED_CONFIG_USAGE_DOCUMENT_URL);
}
}
}
@Override
public void checkKeyId() throws Exception {
throwExceptionIfStringBlankWithErrorKey(keyId, "", "keyId is not set.", KEY_ID);
}
@Override
public void close() throws IOException {
asyncProcessor.shutdown();
}
}

View File

@ -0,0 +1,342 @@
package com.alibaba.nacos.client.aliyun;
import com.alibaba.nacos.api.config.filter.IConfigRequest;
import com.alibaba.nacos.api.config.filter.IConfigResponse;
import com.alibaba.nacos.api.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Properties;
import java.util.function.Supplier;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_KMS_AES_128_PREFIX;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_KMS_AES_256_PREFIX;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_PREFIX;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CONTENT;
import static com.alibaba.nacos.client.aliyun.AliyunConst.DATA_ID;
import static com.alibaba.nacos.client.aliyun.AliyunConst.ENCODE_UTF8;
import static com.alibaba.nacos.client.aliyun.AliyunConst.ENCRYPTED_DATA_KEY;
import static com.alibaba.nacos.client.aliyun.AliyunConst.GROUP;
import static com.alibaba.nacos.client.aliyun.AliyunConst.STRING_VALUE_BLANK_ERROR_MSG_FORMAT;
public abstract class KmsEncryptor implements Closeable {
private static final Logger LOGGER = LoggerFactory.getLogger(AliyunConfigFilter.class);
private KmsLocalCache kmsLocalCache;
public static final int defaultRetryTimes = 3;
public static final int defaultRetryIntervalMilliseconds = 2 * 100;
public static final int defaultTimeoutMilliseconds = 3 * 1000;
public boolean isUseLocalCache;
public KmsEncryptor(Properties properties){
this.isUseLocalCache = KmsUtils.parsePropertyValue(properties, AliyunConst.NACOS_CONFIG_ENCRYPTION_KMS_LOCAL_CACHE_SWITCH,
AliyunConst.DEFAULT_KMS_LOCAL_CACHE_SWITCH);
if (this.isUseLocalCache()) {
LOGGER.info("using kms encryption local cache.");
this.kmsLocalCache = new KmsLocalCache(properties);
}
}
public String encrypt(IConfigRequest configRequest) throws Exception {
checkIfKmsClientIsReady();
checkKeyId();
protectKeyId();
String dataId = (String) configRequest.getParameter(DATA_ID);
String group = (String) configRequest.getParameter(GROUP);
String plainContent = (String) configRequest.getParameter(CONTENT);
String plainDataKey = null;
String encryptedDataKey = null;
String result = null; //encryptedContent
String blankResultErrorMsg = "encrypt from kms failed.";
// Exception requestKmsException = null;
//prefer to use kms service
try {
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
String keySpec = KmsUtils.getKeySpecByDataIdPrefix(dataId);
DataKey dataKey = generateDataKey(keySpec);
plainDataKey = dataKey.getPlainDataKey();
throwExceptionIfStringBlankWithErrorKey(plainDataKey, GroupKeyUtils.getGroupKey2(dataId, group),
"generateDataKeyResponse.getPlaintext()", "plainDataKey");
encryptedDataKey = dataKey.getEncryptedDataKey();
throwExceptionIfStringBlankWithErrorKey(encryptedDataKey, GroupKeyUtils.getGroupKey2(dataId, group),
"generateDataKeyResponse.getCiphertextBlob()", "encryptedDataKey");
configRequest.putParameter(ENCRYPTED_DATA_KEY, encryptedDataKey);
result = AesUtils.encrypt(plainContent, plainDataKey, ENCODE_UTF8);
} else if (dataId.startsWith(CIPHER_PREFIX)) {
result = encrypt(plainContent);
}
} catch (Exception e) {
LOGGER.error("encrypt config:[{}] failed by using kms service: {}.",
GroupKeyUtils.getGroupKey2(dataId, group), e.getMessage(), e);
throw e;
}
throwExceptionIfStringBlankWithErrorKey(result, GroupKeyUtils.getGroupKey2(dataId, group), "encrypt failed", blankResultErrorMsg);
//update local cache
this.updateLocalCacheItem(group, dataId, encryptedDataKey, result, plainDataKey, plainContent);
return result;
}
public abstract String encrypt(String plainText) throws Exception;
public String decrypt(IConfigResponse configResponse) throws Exception {
checkIfKmsClientIsReady();
String dataId = (String) configResponse.getParameter(DATA_ID);
String group = (String) configResponse.getParameter(GROUP);
String encryptedContent = (String) configResponse.getParameter(CONTENT);
String encryptedDataKey = (String) configResponse.getParameter(ENCRYPTED_DATA_KEY);
String plainDataKey = null;
String result = null;
Exception requestKmsException = null;
String blankResultErrorMsg = "decrypt from kms failed.";
boolean isUsedCache = true;
try {
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
throwExceptionIfStringBlankWithErrorKey(encryptedDataKey, GroupKeyUtils.getGroupKey2(dataId, group),
"decrypt failed", "response.getParameter(ENCRYPTED_DATA_KEY)");
plainDataKey = decrypt(encryptedDataKey);
result = AesUtils.decrypt(encryptedContent, plainDataKey, ENCODE_UTF8);
} else if (dataId.startsWith(CIPHER_PREFIX)) {
result = decrypt(encryptedContent);
}
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
//use local cache protection
LOGGER.error("decrypt config:[{}] failed by using kms service: {}.",
GroupKeyUtils.getGroupKey2(dataId, group), e.getMessage(), e);
requestKmsException = e;
}
if (requestKmsException != null || StringUtils.isBlank(result)) {
LOGGER.warn("decrypt config [{}] failed with exception or empty result by using kms service. try to use local cache.", GroupKeyUtils.getGroupKey2(dataId, group));
result = getDecryptedContentByUsingLocalCache(group, dataId, encryptedDataKey, encryptedContent);
if (requestKmsException != null && StringUtils.isBlank(result)) {
throw requestKmsException;
} else if (StringUtils.isBlank(result)) {
blankResultErrorMsg += "and no kms decryption local cache.";
}
} else {
isUsedCache = false;
}
throwExceptionIfStringBlankWithErrorKey(result, GroupKeyUtils.getGroupKey2(dataId, group), "decrypt failed", blankResultErrorMsg);
if (!isUsedCache) {
this.updateLocalCacheItem(group, dataId, encryptedDataKey, encryptedContent, plainDataKey, result);
}
return result;
}
public abstract String decrypt(String encryptedContent) throws Exception;
public abstract void protectKeyId();
public abstract DataKey generateDataKey(String keySpec) throws Exception;
public abstract void checkIfKmsClientIsReady() throws Exception;
public abstract void checkKeyId() throws Exception;
public void updateLocalCacheItem(String group, String dataId, String encryptedDataKey, String encryptedContent, String plainDataKey, String plainContent) {
if (!this.isLocalCacheAvailable()) {
return;
}
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
getKmsLocalCache().put(GroupKeyUtils.getGroupKey2(dataId, group), new KmsLocalCache.LocalCacheItem(encryptedDataKey, encryptedContent, plainDataKey));
} else if(dataId.startsWith(CIPHER_PREFIX)) {
getKmsLocalCache().put(GroupKeyUtils.getGroupKey2(dataId, group), new KmsLocalCache.LocalCacheItem(encryptedContent, plainContent));
}
}
public boolean isUseLocalCache() {
return this.isUseLocalCache;
}
public KmsLocalCache getKmsLocalCache() {
return this.kmsLocalCache;
}
public boolean isLocalCacheAvailable() {
return this.isUseLocalCache() && this.getKmsLocalCache()!= null;
}
public String getDecryptedContentByUsingLocalCache(String group, String dataId, String encryptedDataKey, String encryptedContent)
throws Exception {
KmsLocalCache.LocalCacheItem localCacheItem = getLocalCacheItem(group, dataId, encryptedDataKey, encryptedContent);
if (localCacheItem != null) {
if (!StringUtils.isBlank(localCacheItem.getPlainDataKey())) {
return AesUtils.decrypt(encryptedContent, localCacheItem.getPlainDataKey(), ENCODE_UTF8);
} else if (!StringUtils.isBlank(localCacheItem.getPlainContent())) {
return localCacheItem.getPlainContent();
}
}
return null;
}
public KmsLocalCache.LocalCacheItem getLocalCacheItem(String group, String dataId, String encryptDataKey, String encryptedContent) {
//check if open local cache
if (!this.isLocalCacheAvailable()) {
return null;
}
//check if cache is ready
KmsLocalCache.LocalCacheItem localCacheItem = this.getKmsLocalCache().get(GroupKeyUtils.getGroupKey2(dataId, group));
if (localCacheItem == null) {
return null;
}
//check if cache is valid
if (!checkIfKmsCacheItemValidByDecrypt(localCacheItem, dataId, encryptDataKey, encryptedContent)) {
return null;
}
return localCacheItem;
}
public KmsLocalCache.LocalCacheItem getLocalCacheItem(String group, String dataId, String plainText) {
//check if open local cache
if (!this.isLocalCacheAvailable()) {
return null;
}
//check if cache is ready
KmsLocalCache.LocalCacheItem localCacheItem = this.getKmsLocalCache().get(GroupKeyUtils.getGroupKey2(dataId, group));
if (localCacheItem == null) {
return null;
}
//check if cache is valid
if (checkIfKmsCacheItemValidByEncrypt(localCacheItem, dataId, plainText)) {
return null;
}
return localCacheItem;
}
public void locallyRunWithRetryTimesAndTimeout(Supplier<Boolean> runnable, int retryTimes, long timeout)
throws Exception {
int locallyRetryTimes = 0;
Exception localException = null;
long beginTime = System.currentTimeMillis();
while (locallyRetryTimes++ < retryTimes && System.currentTimeMillis() < beginTime + timeout) {
try {
if (runnable.get()) {
break;
}
} catch (Exception e) {
localException = e;
}
Thread.sleep(defaultRetryIntervalMilliseconds);
}
if (localException != null) {
throw localException;
}
}
private boolean checkIfKmsCacheItemValidByEncrypt(KmsLocalCache.LocalCacheItem localCacheItem, String dataId, String plainContent) {
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
return !StringUtils.isBlank(localCacheItem.getEncryptedDataKey())
&& !StringUtils.isBlank(localCacheItem.getPlainDataKey());
} else if (dataId.startsWith(CIPHER_PREFIX)) {
return !StringUtils.isBlank(localCacheItem.getEncryptedContent())
&& !StringUtils.isBlank(localCacheItem.getPlainContent())
&& localCacheItem.getPlainContent().equals(plainContent);
}
return false;
}
private boolean checkIfKmsCacheItemValidByDecrypt(KmsLocalCache.LocalCacheItem localCacheItem, String dataId, String encryptedDataKey, String encryptedContent) {
String encryptedContentMd5 = MD5Utils.md5Hex(encryptedContent, ENCODE_UTF8);
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
return !StringUtils.isBlank(localCacheItem.getEncryptedDataKey())
&& !StringUtils.isBlank(localCacheItem.getEncryptedContentMD5())
&& !StringUtils.isBlank(localCacheItem.getPlainDataKey())
&& StringUtils.equals(localCacheItem.getEncryptedDataKey(), encryptedDataKey)
&& StringUtils.equals(localCacheItem.getEncryptedContentMD5(), encryptedContentMd5);
} else if (dataId.startsWith(CIPHER_PREFIX)) {
return !StringUtils.isBlank(localCacheItem.getEncryptedContentMD5())
&& !StringUtils.isBlank(localCacheItem.getPlainContent())
&& StringUtils.equals(localCacheItem.getEncryptedContentMD5(), encryptedContentMd5);
}
return false;
}
public String readFileToString(String filePath) {
File file = getFileByPath(filePath);
if (file == null || !file.exists()) {
return null;
}
try {
Path path = Paths.get(file.getAbsolutePath());
byte[] fileContent = Files.readAllBytes(path);
return new String(fileContent, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private File getFileByPath(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
String path = AliyunConfigFilter.class.getClassLoader().getResource("").getPath();
if (!(file = new File(path + filePath)).exists()) {
path = Paths.get(filePath).toAbsolutePath().toString();
if (!(file = new File(path)).exists()) {
return null;
}
}
}
return file;
}
public void throwExceptionIfStringBlankWithErrorKey(String s, String groupKey, String errorMsg, String errorKey) throws Exception {
if (StringUtils.isBlank(s)) {
throw new RuntimeException(String.format(STRING_VALUE_BLANK_ERROR_MSG_FORMAT, groupKey, errorMsg, errorKey)
+ "For more information, please check: " + AliyunConst.MSE_ENCRYPTED_CONFIG_USAGE_DOCUMENT_URL);
}
}
public static class DataKey {
private String plainDataKey;
private String encryptedDataKey;
public String getPlainDataKey() {
return plainDataKey;
}
public void setPlainDataKey(String plainDataKey) {
this.plainDataKey = plainDataKey;
}
public String getEncryptedDataKey() {
return encryptedDataKey;
}
public void setEncryptedDataKey(String encryptedDataKey) {
this.encryptedDataKey = encryptedDataKey;
}
}
@Override
public abstract void close() throws IOException;
}

View File

@ -0,0 +1,317 @@
package com.alibaba.nacos.client.aliyun;
import com.alibaba.nacos.client.aliyun.provider.AccessKeyCredentialsProvider;
import com.alibaba.nacos.client.aliyun.provider.CredentialsUriKmsCredentialsProvider;
import com.alibaba.nacos.client.aliyun.provider.EcsRamRoleKmsCredentialsProvider;
import com.alibaba.nacos.client.aliyun.provider.KmsCredentialsProvider;
import com.alibaba.nacos.client.aliyun.provider.OidcRoleArnKmsCredentialsProvider;
import com.alibaba.nacos.client.aliyun.provider.RamRoleArnKmsCredentialsProvider;
import com.alibaba.nacos.client.aliyun.provider.StsTokenKmsCredentialsProvider;
import com.alibaba.nacos.common.utils.StringUtils;
import com.aliyun.kms20160120.Client;
import com.aliyun.kms20160120.models.DecryptRequest;
import com.aliyun.kms20160120.models.DescribeKeyRequest;
import com.aliyun.kms20160120.models.DescribeKeyResponse;
import com.aliyun.kms20160120.models.EncryptRequest;
import com.aliyun.kms20160120.models.GenerateDataKeyRequest;
import com.aliyun.kms20160120.models.GenerateDataKeyResponseBody;
import com.aliyun.kms20160120.models.SetDeletionProtectionRequest;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.aliyuncs.exceptions.ClientException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import static com.alibaba.nacos.client.aliyun.AliyunConst.KEY_ID;
import static com.alibaba.nacos.client.aliyun.AliyunConst.KMS_REGION_ID;
import static com.alibaba.nacos.client.aliyun.AliyunConst.REGION_ID;
public class RamKmsEncryptor extends KmsEncryptor{
private static final Logger LOGGER = LoggerFactory.getLogger(RamKmsEncryptor.class);
private Client kmsClient;
private String keyId;
private RuntimeOptions runtimeOptions;
private Exception localInitException;
private final Set<String> addedKeys = new HashSet<String>();
private AsyncProcessor asyncProcessor;
private final Set<KmsCredentialsProvider> credentialsProviders;
public RamKmsEncryptor(Properties properties){
super(properties);
credentialsProviders = new HashSet<>();
credentialsProviders.add(new AccessKeyCredentialsProvider());
credentialsProviders.add(new StsTokenKmsCredentialsProvider());
credentialsProviders.add(new RamRoleArnKmsCredentialsProvider());
credentialsProviders.add(new EcsRamRoleKmsCredentialsProvider());
credentialsProviders.add(new OidcRoleArnKmsCredentialsProvider());
credentialsProviders.add(new CredentialsUriKmsCredentialsProvider());
try{
kmsClient = createClient(properties);
}catch (Exception e){
localInitException = e;
}
if(localInitException == null){
try {
asyncProcessor = new AsyncProcessor();
} catch (Exception e) {
LOGGER.error("init async processor failed.", e);
}
}
}
private Client createClient(Properties properties) throws Exception {
String regionId = properties.getProperty(REGION_ID, System.getProperty(REGION_ID, System.getenv(REGION_ID)));
String kmsRegionId = properties.getProperty(KMS_REGION_ID, System.getProperty(KMS_REGION_ID, System.getenv(KMS_REGION_ID)));
if (StringUtils.isBlank(regionId)) {
regionId = kmsRegionId;
}
LOGGER.info("using regionId {}.", regionId);
if (StringUtils.isBlank(kmsRegionId)) {
kmsRegionId = regionId;
}
LOGGER.info("using kms regionId {}.", kmsRegionId);
String kmsEndpoint = properties.getProperty(AliyunConst.KMS_ENDPOINT,
System.getProperty(AliyunConst.KMS_ENDPOINT, System.getenv(AliyunConst.KMS_ENDPOINT)));
LOGGER.info("using kmsEndpoint {}.", kmsEndpoint);
Config config = new Config();
runtimeOptions = new RuntimeOptions();
boolean ifAuth = false;
com.aliyun.credentials.models.Config credentialConfig = new com.aliyun.credentials.models.Config();
for (KmsCredentialsProvider each : credentialsProviders) {
if (each.matchProvider(properties)) {
LOGGER.info("Match Kms credentials provider: {}", each.getClass().getName());
credentialConfig = each.generateCredentialsConfig(properties);
ifAuth = true;
break;
}
}
if(!ifAuth){
String msg = "Ram Auth Information are not set up yet";
LOGGER.error(msg);
localInitException = new RuntimeException(msg);
return null;
}
com.aliyun.credentials.Client credentialClient = new com.aliyun.credentials.Client(credentialConfig);
config.setCredential(credentialClient);
config.setRegionId(kmsRegionId);
if(!StringUtils.isBlank(kmsEndpoint)){
config.setEndpoint(kmsEndpoint);
keyId = properties.getProperty(KEY_ID, System.getProperty(KEY_ID, System.getenv(KEY_ID)));
if(StringUtils.isBlank(keyId)){
String msg = "keyId is not set up yet, unable to encrypt the configuration.";
LOGGER.error(msg);
}else{
LOGGER.info("using keyId {}.", keyId);
}
}else{
LOGGER.info("kmsEndpoint is not set up yet, KMS V1.0 mode");
if (StringUtils.isBlank(kmsRegionId) && StringUtils.isBlank(regionId)) {
String errorMsg = "KMS V1.0 mode, region is not set up yet";
LOGGER.error(AliyunConst.formatHelpMessage(errorMsg));
localInitException = new RuntimeException(errorMsg);
return null;
}
keyId = AliyunConst.KMS_DEFAULT_KEY_ID_VALUE;
return new Client(config);
}
String kmsCaFileContent = properties.getProperty(AliyunConst.KMS_CA_FILE_CONTENT,
System.getProperty(AliyunConst.KMS_CA_FILE_CONTENT, System.getenv(AliyunConst.KMS_CA_FILE_CONTENT)));
if(StringUtils.isBlank(kmsCaFileContent)){
String kmsCaFilePath = properties.getProperty(AliyunConst.KMS_CA_FILE_PATH_KEY,
System.getProperty(AliyunConst.KMS_CA_FILE_PATH_KEY,
System.getenv(AliyunConst.KMS_CA_FILE_PATH_KEY)));
if(!StringUtils.isBlank(kmsCaFilePath)){
try{
kmsCaFileContent = readFileToString(kmsCaFilePath);
}catch (Exception e){
LOGGER.error("read kms ca file failed.", e);
}
}
}
if (!StringUtils.isBlank(kmsCaFileContent)) {
LOGGER.info("using {}: {}.", AliyunConst.KMS_CA_FILE_CONTENT, kmsCaFileContent);
config.setCa(kmsCaFileContent);
} else {
runtimeOptions.ignoreSSL=true;
}
String openSSL = properties.getProperty(AliyunConst.OPEN_SSL_KEY,
System.getProperty(AliyunConst.OPEN_SSL_KEY, System.getenv(AliyunConst.OPEN_SSL_KEY)));
if(!StringUtils.isBlank(openSSL)){
if(openSSL.equalsIgnoreCase("false")){
LOGGER.info("openSSL is set to false.");
runtimeOptions.ignoreSSL = true;
} else if (openSSL.equalsIgnoreCase("true")){
LOGGER.info("ignoreSSL is set to true.");
runtimeOptions.ignoreSSL = false;
}
}
return new Client(config);
}
@Override
public String encrypt(String plainText) throws Exception {
AtomicReference<String> resultContent = new AtomicReference<>();
final EncryptRequest encReq = new EncryptRequest();
encReq.setKeyId(keyId);
encReq.setPlaintext(plainText);
locallyRunWithRetryTimesAndTimeout(() -> {
try {
resultContent.set( kmsClient.encryptWithOptions(encReq, runtimeOptions).getBody().getCiphertextBlob());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (StringUtils.isBlank(resultContent.get())) {
return false;
}
return true;
}, defaultRetryTimes, defaultTimeoutMilliseconds);
return resultContent.get();
}
@Override
public String decrypt(String encryptedContent) throws Exception {
AtomicReference<String> resultContent = new AtomicReference<>();
final DecryptRequest decReq = new DecryptRequest();
decReq.setCiphertextBlob(encryptedContent);
locallyRunWithRetryTimesAndTimeout(() -> {
try {
resultContent.set(kmsClient.decryptWithOptions(decReq, runtimeOptions).getBody().getPlaintext());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (StringUtils.isBlank(resultContent.get())) {
return false;
}
return true;
}, defaultRetryTimes, defaultTimeoutMilliseconds);
return resultContent.get();
}
@Override
public void checkIfKmsClientIsReady() throws Exception {
if (kmsClient == null) {
if (localInitException != null) {
throw localInitException;
} else {
throw new RuntimeException("kms client isn't initialized. " +
"For more information, please check: " + AliyunConst.MSE_ENCRYPTED_CONFIG_USAGE_DOCUMENT_URL);
}
}
}
@Override
public void protectKeyId() {
if (!addedKeys.contains(keyId)) {
synchronized (addedKeys) {
if (addedKeys.contains(keyId)) {
return;
}
addedKeys.add(keyId);
asyncProcessor.addTack(new Runnable() {
@Override
public void run() {
try {
if (kmsClient == null) {
LOGGER.error("kms client hasn't initiated.");
return;
}
DescribeKeyRequest describeKeyRequest = new DescribeKeyRequest();
describeKeyRequest.setKeyId(keyId);
try {
DescribeKeyResponse describeKeyResponse = kmsClient.describeKeyWithOptions(describeKeyRequest, runtimeOptions);
if (describeKeyResponse.getBody().getKeyMetadata()!= null) {
if (!"Enabled".equals(describeKeyResponse.getBody().getKeyMetadata().getKeyState())) {
throw new RuntimeException("Key not available");
}
String arn = describeKeyResponse.getBody().getKeyMetadata().getArn();
LOGGER.info("set deletion protection for keyId[{}], arn[{}]", keyId, arn);
SetDeletionProtectionRequest setDeletionProtectionRequest = new SetDeletionProtectionRequest();
setDeletionProtectionRequest.setProtectedResourceArn(arn);
setDeletionProtectionRequest.setEnableDeletionProtection(true);
setDeletionProtectionRequest.setDeletionProtectionDescription("key is used by mse");
try {
kmsClient.setDeletionProtectionWithOptions(setDeletionProtectionRequest, runtimeOptions);
} catch (ClientException e) {
LOGGER.error("set deletion protect failed, keyId: {}.", keyId);
throw e;
}
} else {
addedKeys.remove(keyId);
LOGGER.warn("keyId meta is null, cannot set key protection");
}
} catch (ClientException e) {
LOGGER.error("describe key failed, keyId: {}.", keyId);
throw e;
}
} catch (Exception e) {
addedKeys.remove(keyId);
LOGGER.error("execute async task failed", e);
}
}
});
}
}
}
@Override
public DataKey generateDataKey(String keySpec) throws Exception {
GenerateDataKeyRequest generateDataKeyRequest = new GenerateDataKeyRequest();
generateDataKeyRequest.setKeyId(keyId);
generateDataKeyRequest.setKeySpec(keySpec);
AtomicReference<GenerateDataKeyResponseBody> resultContent = new AtomicReference<>();
locallyRunWithRetryTimesAndTimeout(() -> {
try {
resultContent.set(kmsClient.generateDataKeyWithOptions(generateDataKeyRequest,runtimeOptions).getBody());
} catch (Exception e) {
throw new RuntimeException(e);
}
if (resultContent.get() == null) {
return false;
}
return true;
}, defaultRetryTimes, defaultTimeoutMilliseconds);
DataKey dataKey = new DataKey();
dataKey.setEncryptedDataKey(resultContent.get().getCiphertextBlob());
dataKey.setPlainDataKey(resultContent.get().getPlaintext());
return dataKey;
}
@Override
public void checkKeyId() throws Exception {
throwExceptionIfStringBlankWithErrorKey(keyId, "", "keyId is not set.", KEY_ID);
}
@Override
public void close() throws IOException {
asyncProcessor.shutdown();
}
}

View File

@ -54,7 +54,9 @@ public class AliyunExtensionClientAuthServiceImpl extends AbstractClientAuthServ
public Boolean login(Properties properties) {
for (ExtensionCredentialsProvider each : credentialsProviders) {
if (each.matchProvider(properties)) {
if (null == matchedProvider) {
LOGGER.info("Match credentials provider: {}", each.getClass().getName());
}
matchedProvider = each;
break;
}

View File

@ -2,7 +2,6 @@ package com.alibaba.nacos.client.aliyun.auth.provider;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
import com.alibaba.nacos.api.model.v2.ErrorCode;
import com.alibaba.nacos.api.utils.StringUtils;
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
@ -46,7 +45,7 @@ public class AutoRotateCredentialsProvider implements ExtensionCredentialsProvid
client = SecretCacheClientBuilder.newClient();
}
} catch (Exception e) {
throw new NacosRuntimeException(ErrorCode.ILLEGAL_STATE.getCode(), e.getMessage(), e);
throw new NacosRuntimeException(23000, e.getMessage(), e);
}
}

View File

@ -0,0 +1,41 @@
package com.alibaba.nacos.client.aliyun.provider;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.client.aliyun.AliyunConst;
import com.alibaba.nacos.common.utils.StringUtils;
import com.aliyun.credentials.models.Config;
import java.util.Properties;
public class AccessKeyCredentialsProvider implements KmsCredentialsProvider{
private String accessKey;
private String secretKey;
@Override
public boolean matchProvider(Properties properties) {
accessKey = properties.getProperty(AliyunConst.KMS_ACCESS_KEY,
System.getProperty(AliyunConst.KMS_ACCESS_KEY, System.getenv(AliyunConst.KMS_ACCESS_KEY)));
if(StringUtils.isBlank(accessKey)){
accessKey = properties.getProperty(PropertyKeyConst.ACCESS_KEY,
System.getProperty(PropertyKeyConst.ACCESS_KEY, System.getenv(PropertyKeyConst.ACCESS_KEY)));
}
secretKey = properties.getProperty(AliyunConst.KMS_SECRET_KEY,
System.getProperty(AliyunConst.KMS_SECRET_KEY, System.getenv(AliyunConst.KMS_SECRET_KEY)));
if(StringUtils.isBlank(secretKey)){
secretKey = properties.getProperty(PropertyKeyConst.SECRET_KEY,
System.getProperty(PropertyKeyConst.SECRET_KEY, System.getenv(PropertyKeyConst.SECRET_KEY)));
}
return !StringUtils.isBlank(accessKey) && !StringUtils.isBlank(secretKey);
}
@Override
public Config generateCredentialsConfig(Properties properties) {
Config credentialConfig = new Config();
credentialConfig.setType("access_key");
credentialConfig.setAccessKeyId(accessKey);
credentialConfig.setAccessKeySecret(secretKey);
return credentialConfig;
}
}

View File

@ -0,0 +1,32 @@
package com.alibaba.nacos.client.aliyun.provider;
import com.alibaba.nacos.client.aliyun.AliyunConst;
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
import com.alibaba.nacos.common.utils.StringUtils;
import com.aliyun.credentials.models.Config;
import java.util.Properties;
public class CredentialsUriKmsCredentialsProvider implements KmsCredentialsProvider{
String credentialsUri;
@Override
public boolean matchProvider(Properties properties) {
credentialsUri = properties.getProperty(AliyunConst.KMS_CREDENTIALS_URI,
System.getProperty(AliyunConst.KMS_CREDENTIALS_URI,System.getenv(AliyunConst.KMS_CREDENTIALS_URI)));
if(StringUtils.isBlank(credentialsUri)){
credentialsUri = getNacosProperties(properties, ExtensionAuthPropertyKey.CREDENTIALS_URI);
}
return StringUtils.isNotBlank(credentialsUri);
}
@Override
public Config generateCredentialsConfig(Properties properties) {
Config config = new Config();
config.setType("credentials_uri");
config.setCredentialsUri(credentialsUri);
return config;
}
}

View File

@ -0,0 +1,32 @@
package com.alibaba.nacos.client.aliyun.provider;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.client.aliyun.AliyunConst;
import com.alibaba.nacos.common.utils.StringUtils;
import com.aliyun.credentials.models.Config;
import java.util.Properties;
public class EcsRamRoleKmsCredentialsProvider implements KmsCredentialsProvider{
private String ramRoleName;
@Override
public boolean matchProvider(Properties properties) {
ramRoleName = properties.getProperty(AliyunConst.KMS_RAM_ROLE_NAME,
System.getProperty(AliyunConst.KMS_RAM_ROLE_NAME, System.getenv(AliyunConst.KMS_RAM_ROLE_NAME)));
if(StringUtils.isBlank(ramRoleName)){
ramRoleName= properties.getProperty(PropertyKeyConst.RAM_ROLE_NAME,
System.getProperty(PropertyKeyConst.RAM_ROLE_NAME, System.getenv(PropertyKeyConst.RAM_ROLE_NAME)));
}
return !StringUtils.isBlank(ramRoleName);
}
@Override
public Config generateCredentialsConfig(Properties properties) {
Config credentialConfig = new Config();
credentialConfig.setType("ecs_ram_role");
credentialConfig.setRoleName(ramRoleName);
return credentialConfig;
}
}

View File

@ -0,0 +1,27 @@
package com.alibaba.nacos.client.aliyun.provider;
import com.alibaba.nacos.api.utils.StringUtils;
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
import com.aliyun.credentials.models.Config;
import java.util.Properties;
public interface KmsCredentialsProvider {
boolean matchProvider(Properties properties);
Config generateCredentialsConfig(Properties properties);
default String getNacosProperties(Properties properties, ExtensionAuthPropertyKey key) {
String result = properties.getProperty(key.getKey());
if (StringUtils.isEmpty(result)) {
result = properties.getProperty(key.getEnvKey());
}
// For Adapt 2.1.X, in 2.1.X version, NacosClientProperties not finished all replaced, so properties don't include env.
if (StringUtils.isEmpty(result)) {
result = System.getenv(key.getEnvKey());
}
return result;
}
}

View File

@ -0,0 +1,76 @@
package com.alibaba.nacos.client.aliyun.provider;
import com.alibaba.nacos.client.aliyun.AliyunConst;
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
import com.alibaba.nacos.common.utils.StringUtils;
import com.aliyun.credentials.models.Config;
import java.util.Properties;
public class OidcRoleArnKmsCredentialsProvider implements KmsCredentialsProvider{
private String roleArn;
private String roleSessionName;
private String oidcProviderArn;
private String oidcTokenFilePath;
@Override
public boolean matchProvider(Properties properties) {
roleArn = properties.getProperty(AliyunConst.KMS_ROLE_ARN,
System.getProperty(AliyunConst.KMS_ROLE_ARN, System.getenv(AliyunConst.KMS_ROLE_ARN)));
if(StringUtils.isBlank(roleArn)){
roleArn = getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_ARN);
}
roleSessionName = properties.getProperty(AliyunConst.KMS_ROLE_SESSION_NAME,
System.getProperty(AliyunConst.KMS_ROLE_SESSION_NAME, System.getenv(AliyunConst.KMS_ROLE_SESSION_NAME)));
if(StringUtils.isBlank(roleSessionName)){
roleSessionName = getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_SESSION_NAME);
}
oidcProviderArn = properties.getProperty(AliyunConst.KMS_OIDC_PROVIDER_ARN,
System.getProperty(AliyunConst.KMS_OIDC_PROVIDER_ARN, System.getenv(AliyunConst.KMS_OIDC_PROVIDER_ARN)));
if(StringUtils.isBlank(oidcProviderArn)){
oidcProviderArn = getNacosProperties(properties, ExtensionAuthPropertyKey.OIDC_PROVIDER_ARN);
}
oidcTokenFilePath = properties.getProperty(AliyunConst.KMS_OIDC_TOKEN_FILE_PATH,
System.getProperty(AliyunConst.KMS_OIDC_TOKEN_FILE_PATH, System.getenv(AliyunConst.KMS_OIDC_TOKEN_FILE_PATH)));
if(StringUtils.isBlank(oidcTokenFilePath)){
oidcTokenFilePath = getNacosProperties(properties, ExtensionAuthPropertyKey.OIDC_TOKEN_FILE_PATH);
}
return StringUtils.isNotBlank(roleArn) && StringUtils.isNotBlank(roleSessionName)
&& StringUtils.isNotBlank(oidcProviderArn) && StringUtils.isNotBlank(oidcTokenFilePath);
}
@Override
public Config generateCredentialsConfig(Properties properties) {
Config credentialsConfig = new Config();
credentialsConfig.setType("oidc_role_arn");
credentialsConfig.setRoleArn(roleArn);
credentialsConfig.setRoleSessionName(roleSessionName);
credentialsConfig.setOidcProviderArn(oidcProviderArn);
credentialsConfig.setOidcTokenFilePath(oidcTokenFilePath);
String policy = properties.getProperty(AliyunConst.KMS_POLICY,
System.getProperty(AliyunConst.KMS_POLICY, System.getenv(AliyunConst.KMS_POLICY)));
if(StringUtils.isBlank(policy)){
policy = getNacosProperties(properties, ExtensionAuthPropertyKey.POLICY);
}
if(StringUtils.isNotBlank(policy)){
credentialsConfig.setPolicy(policy);
}
String roleSessionExpiration = properties.getProperty(AliyunConst.KMS_ROLE_SESSION_EXPIRATION_SECONDS,
System.getProperty(AliyunConst.KMS_ROLE_SESSION_EXPIRATION_SECONDS, System.getenv(AliyunConst.KMS_ROLE_SESSION_EXPIRATION_SECONDS)));
if(StringUtils.isBlank(roleSessionExpiration)){
roleSessionExpiration = getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_SESSION_EXPIRATION);
}
if(StringUtils.isNotBlank(roleSessionExpiration)){
credentialsConfig.setRoleSessionExpiration(Integer.parseInt(roleSessionExpiration));
}
return credentialsConfig;
}
}

View File

@ -0,0 +1,76 @@
package com.alibaba.nacos.client.aliyun.provider;
import com.alibaba.nacos.client.aliyun.AliyunConst;
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
import com.alibaba.nacos.common.utils.StringUtils;
import com.aliyun.credentials.models.Config;
import java.util.Properties;
public class RamRoleArnKmsCredentialsProvider implements KmsCredentialsProvider{
private String accessKey;
private String secretKey;
private String roleArn;
private String roleSessionName;
@Override
public boolean matchProvider(Properties properties) {
accessKey = properties.getProperty(AliyunConst.KMS_EXTENSION_ACCESS_KEY,
System.getProperty(AliyunConst.KMS_EXTENSION_ACCESS_KEY, System.getenv(AliyunConst.KMS_EXTENSION_ACCESS_KEY)));
if(StringUtils.isBlank(accessKey)){
accessKey = getNacosProperties(properties, ExtensionAuthPropertyKey.ACCESS_KEY_ID);
}
secretKey = properties.getProperty(AliyunConst.KMS_EXTENSION_SECRET_KEY,
System.getProperty(AliyunConst.KMS_EXTENSION_SECRET_KEY, System.getenv(AliyunConst.KMS_EXTENSION_SECRET_KEY)));
if(StringUtils.isBlank(secretKey)){
secretKey = getNacosProperties(properties, ExtensionAuthPropertyKey.ACCESS_KEY_SECRET);
}
roleArn = properties.getProperty(AliyunConst.KMS_ROLE_ARN,
System.getProperty(AliyunConst.KMS_ROLE_ARN, System.getenv(AliyunConst.KMS_ROLE_ARN)));
if(StringUtils.isBlank(roleArn)){
roleArn = getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_ARN);
}
roleSessionName = properties.getProperty(AliyunConst.KMS_ROLE_SESSION_NAME,
System.getProperty(AliyunConst.KMS_ROLE_SESSION_NAME, System.getenv(AliyunConst.KMS_ROLE_SESSION_NAME)));
if(StringUtils.isBlank(roleSessionName)){
roleSessionName = getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_SESSION_NAME);
}
return StringUtils.isNotBlank(accessKey) && StringUtils.isNotBlank(secretKey) && StringUtils.isNotBlank(roleArn)
&& StringUtils.isNotBlank(roleSessionName);
}
@Override
public Config generateCredentialsConfig(Properties properties) {
Config config = new Config();
config.setType("ram_role_arn");
config.setAccessKeyId(accessKey);
config.setAccessKeySecret(secretKey);
config.setRoleArn(roleArn);
config.setRoleSessionName(roleSessionName);
String policy = properties.getProperty(AliyunConst.KMS_POLICY,
System.getProperty(AliyunConst.KMS_POLICY, System.getenv(AliyunConst.KMS_POLICY)));
if(StringUtils.isBlank(policy)){
policy = getNacosProperties(properties, ExtensionAuthPropertyKey.POLICY);
}
if(StringUtils.isNotBlank(policy)){
config.setPolicy(policy);
}
String roleSessionExpiration = properties.getProperty(AliyunConst.KMS_ROLE_SESSION_EXPIRATION_SECONDS,
System.getProperty(AliyunConst.KMS_ROLE_SESSION_EXPIRATION_SECONDS, System.getenv(AliyunConst.KMS_ROLE_SESSION_EXPIRATION_SECONDS)));
if(StringUtils.isBlank(roleSessionExpiration)){
roleSessionExpiration = getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_SESSION_EXPIRATION);
}
if(StringUtils.isNotBlank(roleSessionExpiration)){
config.setRoleSessionExpiration(Integer.parseInt(roleSessionExpiration));
}
return config;
}
}

View File

@ -0,0 +1,48 @@
package com.alibaba.nacos.client.aliyun.provider;
import com.alibaba.nacos.client.aliyun.AliyunConst;
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
import com.alibaba.nacos.common.utils.StringUtils;
import com.aliyun.credentials.models.Config;
import java.util.Properties;
public class StsTokenKmsCredentialsProvider implements KmsCredentialsProvider{
private String stsAccessKey;
private String stsSecretKey;
private String securityToken;
@Override
public boolean matchProvider(Properties properties) {
stsAccessKey = properties.getProperty(AliyunConst.KMS_EXTENSION_ACCESS_KEY,
System.getProperty(AliyunConst.KMS_EXTENSION_ACCESS_KEY, System.getenv(AliyunConst.KMS_EXTENSION_ACCESS_KEY)));
if(StringUtils.isBlank(stsAccessKey)){
stsAccessKey = getNacosProperties(properties, ExtensionAuthPropertyKey.ACCESS_KEY_ID);
}
stsSecretKey = properties.getProperty(AliyunConst.KMS_EXTENSION_SECRET_KEY,
System.getProperty(AliyunConst.KMS_EXTENSION_SECRET_KEY, System.getenv(AliyunConst.KMS_EXTENSION_SECRET_KEY)));
if(StringUtils.isBlank(stsSecretKey)){
stsSecretKey = getNacosProperties(properties, ExtensionAuthPropertyKey.ACCESS_KEY_SECRET);
}
securityToken = properties.getProperty(AliyunConst.KMS_SECURITY_TOKEN,
System.getProperty(AliyunConst.KMS_SECURITY_TOKEN, System.getenv(AliyunConst.KMS_SECURITY_TOKEN)));
if(StringUtils.isBlank(securityToken)){
securityToken = getNacosProperties(properties, ExtensionAuthPropertyKey.SECURITY_TOKEN);
}
return StringUtils.isNotBlank(stsAccessKey) && StringUtils.isNotBlank(stsSecretKey) && StringUtils.isNotBlank(securityToken);
}
@Override
public Config generateCredentialsConfig(Properties properties) {
Config credentialConfig = new Config();
credentialConfig.setType("sts");
credentialConfig.setAccessKeyId(stsAccessKey);
credentialConfig.setAccessKeySecret(stsSecretKey);
credentialConfig.setSecurityToken(securityToken);
return credentialConfig;
}
}

View File

@ -1,354 +1,102 @@
package com.alibaba.nacos.client.aliyun;
import com.alibaba.nacos.api.config.filter.IConfigFilter;
import com.alibaba.nacos.api.config.filter.IConfigFilterChain;
import com.alibaba.nacos.api.config.filter.IConfigRequest;
import com.alibaba.nacos.api.config.filter.IConfigResponse;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.utils.StringUtils;
import com.alibaba.nacos.client.config.filter.impl.ConfigFilterChainManager;
import com.alibaba.nacos.client.config.filter.impl.ConfigRequest;
import com.alibaba.nacos.client.config.filter.impl.ConfigResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.kms.model.v20160120.GenerateDataKeyResponse;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.MockedConstruction;
import org.mockito.junit.jupiter.MockitoExtension;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_KMS_AES_128_PREFIX;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_KMS_AES_256_PREFIX;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_PREFIX;
import static com.alibaba.nacos.client.aliyun.AliyunConst.ENCODE_UTF8;
import static com.alibaba.nacos.client.aliyun.AliyunConst.KMS_DEFAULT_KEY_ID_VALUE;
import static com.alibaba.nacos.client.aliyun.AliyunConst.KMS_KEY_SPEC_AES_256;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.when;
@Disabled("This unit test depend accessKey to request KMS, default disabled")
public class AliyunConfigFilterTest {
private static final String ENCRYPTED_DATA_KEY = "encryptedDataKey";
private static final String CONTENT = "content";
public static Properties properties;
public static final List<String> dataIdList = new ArrayList<String>(){{
add("cipher-crypt");
add("cipher-kms-aes-256-crypt");
add("cipher-kms-aes-128-crypt");
}};
@ExtendWith(MockitoExtension.class)
class AliyunConfigFilterTest {
public static final String content = "crypt中文&&";
MockedConstruction<RamKmsEncryptor> ramKmsEncryptorMockedConstruction;
public static final String group = "default";
@Mock
IConfigFilterChain chain;
public static final String ak = "LTAIxxx";
public static final String sk = "EdPqxxx";
@BeforeEach
public void preset() {
try {
properties = new Properties();
properties.load(this.getClass().getResourceAsStream("/aliyun-kms.properties"));
} catch (Exception e) {
e.printStackTrace();
void setUp() {
ramKmsEncryptorMockedConstruction = mockConstruction(RamKmsEncryptor.class,(mock,contexnt)->{
when(mock.encrypt(any(IConfigRequest.class))).thenReturn("encryptedContext");
when(mock.decrypt(any(IConfigResponse.class))).thenReturn("plainText");
});
}
@AfterEach
void tearDown() {
ramKmsEncryptorMockedConstruction.close();
}
@Test
public void testAliyunConfigFilterWithKmsV1() {
properties.setProperty(AliyunConst.KMS_VERSION_KEY, AliyunConst.KmsVersion.Kmsv1.getValue());
//ignore kmsEndpoint
properties.setProperty("kmsEndpoint", "");
properties.setProperty("regionId", "cn-beijing");
properties.setProperty("kms_region_id", "cn-beijing");
properties.setProperty("accessKey", ak);
properties.setProperty("secretKey", sk);
properties.setProperty("keyId", "alias/acs/mse");
executeConfigFilter();
}
void init() throws NoSuchFieldException, IllegalAccessException {
Properties properties_1 = new Properties();
properties_1.put(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, "test");
AliyunConfigFilter aliyunConfigFilter_1 = new AliyunConfigFilter();
aliyunConfigFilter_1.init(properties_1);
Field field = aliyunConfigFilter_1.getClass().getDeclaredField("kmsEncryptor");
field.setAccessible(true);
KmsEncryptor kmsEncryptor = (KmsEncryptor) field.get(aliyunConfigFilter_1);
assertTrue(kmsEncryptor instanceof ClientKeyKmsEncryptor);
// must be running in vpc
// @Test
// public void testAliyunConfigFilterWithKmsV3() {
// properties.setProperty(AliyunConst.KMS_VERSION_KEY, AliyunConst.KmsVersion.Kmsv3.getValue());
// properties.setProperty("keyId", "alias/chasu");
// properties.setProperty("kmsEndpoint", "kst-bjxxxxxxxxxc.cryptoservice.kms.aliyuncs.com");
// properties.setProperty("kmsClientKeyFilePath", "/client_key.json");
// properties.setProperty("kmsPasswordKey", "19axxx213");
// properties.setProperty("kmsCaFilePath", "/ca.pem");
// executeConfigFilter();
// }
@Test
public void testAliyunConfigFilterWithKmsV1UsingLocalCache()
throws NoSuchFieldException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {
properties.setProperty(AliyunConst.KMS_VERSION_KEY, AliyunConst.KmsVersion.Kmsv1.getValue());
//ignore kmsEndpoint
properties.setProperty("kmsEndpoint", "");
properties.setProperty("regionId", "cn-beijing");
properties.setProperty("kms_region_id", "cn-beijing");
properties.setProperty("accessKey", ak);
properties.setProperty("secretKey", sk);
properties.setProperty("keyId", "alias/acs/mse");
executeConfigFilterWithCacheAfterSet();
}
// must be running in vpc
// @Test
// public void testAliyunConfigFilterWithKmsV3UsingLocalCache() {
// properties.setProperty(AliyunConst.KMS_VERSION_KEY, AliyunConst.KmsVersion.Kmsv3.getValue());
// properties.setProperty("keyId", "alias/chasu");
// properties.setProperty("kmsEndpoint", "kst-bjxxxxxxxxxc.cryptoservice.kms.aliyuncs.com");
// properties.setProperty("kmsClientKeyFilePath", "/client_key.json");
// properties.setProperty("kmsPasswordKey", "19axxx213");
// properties.setProperty("kmsCaFilePath", "/ca.pem");
// executeConfigFilterWithCacheAfterSet();
// }
@Test
public void testAliyunConfigFilterEncryptIdempotentOfTheSameConfig() throws Exception {
properties.setProperty(AliyunConst.KMS_VERSION_KEY, AliyunConst.KmsVersion.Kmsv1.getValue());
//ignore kmsEndpoint
properties.setProperty("kmsEndpoint", "");
properties.setProperty("regionId", "cn-beijing");
properties.setProperty("kms_region_id", "cn-beijing");
properties.setProperty("accessKey", ak);
properties.setProperty("secretKey", sk);
properties.setProperty("keyId", "alias/acs/mse");
properties.setProperty(AliyunConst.NACOS_CONFIG_ENCRYPTION_KMS_LOCAL_CACHE_SWITCH, "false");
verifyEncryptedConfigByKmsIdempotent();
Properties properties_2 = new Properties();
AliyunConfigFilter aliyunConfigFilter_2 = new AliyunConfigFilter();
aliyunConfigFilter_2.init(properties_2);
KmsEncryptor kmsEncryptor_2 = (KmsEncryptor) field.get(aliyunConfigFilter_2);
assertTrue(kmsEncryptor_2 instanceof RamKmsEncryptor);
}
@Test
public void testLocallyRunWithRetryTimesAndTimeout()
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class<AliyunConfigFilter> aliyunConfigFilterClass = AliyunConfigFilter.class;
Method locallyRunWithRetryTimesAndTimeout = aliyunConfigFilterClass.getDeclaredMethod(
"locallyRunWithRetryTimesAndTimeout", Supplier.class, int.class, long.class);
locallyRunWithRetryTimesAndTimeout.setAccessible(true);
void doFilter() throws NacosException {
Properties properties_2 = new Properties();
AliyunConfigFilter aliyunConfigFilter_2 = new AliyunConfigFilter();
aliyunConfigFilter_2.init(properties_2);
ConfigRequest request = new ConfigRequest();
request.putParameter("dataId", "cipher-test");
request.putParameter("content","test-context");
ConfigResponse response = new ConfigResponse();
response.putParameter("dataId", "cipher-test");
response.putParameter("content","test-context");
doNothing().when(chain).doFilter(any(),any());
aliyunConfigFilter_2.doFilter(request, response, chain);
assertEquals("encryptedContext",request.getParameter("content"));
assertEquals("plainText",response.getParameter("content"));
}
@Test
void getOrder() {
Properties properties = new Properties();
AliyunConfigFilter aliyunConfigFilter = new AliyunConfigFilter();
//return false to retry with defaultRetryTimes
AtomicInteger atomicInteger = new AtomicInteger(0);
locallyRunWithRetryTimesAndTimeout.invoke(aliyunConfigFilter, new Supplier<Boolean>() {
@Override
public Boolean get() {
atomicInteger.incrementAndGet();
return false;
}
}, AliyunConfigFilter.defaultRetryTimes, AliyunConfigFilter.defaultTimeoutMilliseconds);
Assertions.assertEquals(AliyunConfigFilter.defaultRetryTimes, atomicInteger.get());
//return false to retry with timeout
atomicInteger.set(0);
locallyRunWithRetryTimesAndTimeout.invoke(aliyunConfigFilter, new Supplier<Boolean>() {
@Override
public Boolean get() {
atomicInteger.incrementAndGet();
try {
Thread.sleep(AliyunConfigFilter.defaultTimeoutMilliseconds);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return false;
}
}, AliyunConfigFilter.defaultRetryTimes, AliyunConfigFilter.defaultTimeoutMilliseconds);
Assertions.assertEquals(1, atomicInteger.get());
//return false to retry with expectedException
atomicInteger.set(0);
locallyRunWithRetryTimesAndTimeout.invoke(aliyunConfigFilter, new Supplier<Boolean>() {
@Override
public Boolean get() {
atomicInteger.incrementAndGet();
if (atomicInteger.get() == 1) {
return !KmsUtils.judgeNeedRecoveryException(new ClientException(KmsUtils.REJECTED_THROTTLING, "error message"));
} else if (atomicInteger.get() == 2) {
return !KmsUtils.judgeNeedRecoveryException(new ClientException(KmsUtils.SERVICE_UNAVAILABLE_TEMPORARY, "error message"));
} else if (atomicInteger.get() == 3) {
return !KmsUtils.judgeNeedRecoveryException(new ClientException(KmsUtils.INTERNAL_FAILURE, "error message"));
} else if (atomicInteger.get() == 4) {
return !KmsUtils.judgeNeedRecoveryException(new ClientException(KmsUtils.SDK_READ_TIMEOUT, "error message"));
} else if (atomicInteger.get() == 5) {
return !KmsUtils.judgeNeedRecoveryException(new ClientException(KmsUtils.SDK_SERVER_UNREACHABLE, "error message"));
} else {
return true;
}
}
}, 10, AliyunConfigFilter.defaultTimeoutMilliseconds);
Assertions.assertEquals(6, atomicInteger.get());
aliyunConfigFilter.init(properties);
assertEquals(1, aliyunConfigFilter.getOrder());
}
private void executeConfigFilterWithCacheAfterSet()
throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager(properties);
Class<? extends ConfigFilterChainManager> configFilterChainManagerClass = configFilterChainManager.getClass();
Field filtersField = configFilterChainManagerClass.getDeclaredField("filters");
filtersField.setAccessible(true);
List<IConfigFilter> filters = (List<IConfigFilter>) filtersField.get(configFilterChainManager);
AliyunConfigFilter aliyunConfigFilter = (AliyunConfigFilter) filters.get(1);
Class<AliyunConfigFilter> aliyunConfigFilterClass = AliyunConfigFilter.class;
Field kmsLocalCacheField = aliyunConfigFilterClass.getDeclaredField("kmsLocalCache");
kmsLocalCacheField.setAccessible(true);
KmsLocalCache kmsLocalCache = (KmsLocalCache) kmsLocalCacheField.get(aliyunConfigFilter);
for (String dataId : dataIdList) {
String groupKey = GroupKeyUtils.getGroupKey2(dataId, group);
kmsLocalCache.remove(groupKey);
ConfigRequest configRequest = new ConfigRequest();
configRequest.setGroup(group);
configRequest.setDataId(dataId);
configRequest.setContent(content);
try {
configFilterChainManager.doFilter(configRequest, null);
KmsLocalCache.LocalCacheItem localCacheItem = kmsLocalCache.get(groupKey);
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
Assertions.assertEquals(localCacheItem.getEncryptedContentMD5(), MD5Utils.md5Hex(configRequest.getContent(), ENCODE_UTF8));
Assertions.assertEquals(localCacheItem.getEncryptedDataKey(), configRequest.getEncryptedDataKey());
} else if (dataId.startsWith(CIPHER_PREFIX)) {
Assertions.assertEquals(localCacheItem.getEncryptedContentMD5(), MD5Utils.md5Hex(configRequest.getContent(), ENCODE_UTF8));
Assertions.assertEquals(localCacheItem.getPlainContent(), content);
}
} catch (NacosException e) {
e.printStackTrace();
}
ConfigResponse configResponse = new ConfigResponse();
configResponse.setGroup(group);
configResponse.setDataId(dataId);
configResponse.setEncryptedDataKey((String) configRequest.getParameter(ENCRYPTED_DATA_KEY));
configResponse.setContent((String) configRequest.getParameter(CONTENT));
kmsLocalCache.remove(groupKey);
try {
configFilterChainManager.doFilter(null, configResponse);
KmsLocalCache.LocalCacheItem localCacheItem = kmsLocalCache.get(groupKey);
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
Assertions.assertEquals(localCacheItem.getEncryptedContentMD5(), MD5Utils.md5Hex(configRequest.getContent(), ENCODE_UTF8));
Assertions.assertEquals(localCacheItem.getEncryptedDataKey(), configRequest.getEncryptedDataKey());
Assertions.assertEquals(localCacheItem.getEncryptedDataKey(), configResponse.getEncryptedDataKey());
} else if (dataId.startsWith(CIPHER_PREFIX)) {
Assertions.assertEquals(localCacheItem.getPlainContent(), configResponse.getContent());
Assertions.assertEquals(localCacheItem.getEncryptedContentMD5(), MD5Utils.md5Hex(configRequest.getContent(), ENCODE_UTF8));
}
Assertions.assertEquals(content, configResponse.getContent());
} catch (NacosException e) {
throw new RuntimeException(e);
}
Field localCacheTestModeField = aliyunConfigFilterClass.getDeclaredField("localCacheTestMode");
localCacheTestModeField.setAccessible(true);
localCacheTestModeField.set(aliyunConfigFilter, true);
ConfigResponse configResponse1 = new ConfigResponse();
configResponse1.setGroup(group);
configResponse1.setDataId(dataId);
configResponse1.setEncryptedDataKey((String) configRequest.getParameter(ENCRYPTED_DATA_KEY));
configResponse1.setContent((String) configRequest.getParameter(CONTENT));
try {
configFilterChainManager.doFilter(null, configResponse1);
KmsLocalCache.LocalCacheItem localCacheItem = kmsLocalCache.get(groupKey);
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
Assertions.assertEquals(localCacheItem.getEncryptedContentMD5(), MD5Utils.md5Hex(configRequest.getContent(), ENCODE_UTF8));
Assertions.assertEquals(localCacheItem.getEncryptedDataKey(), configRequest.getEncryptedDataKey());
Assertions.assertEquals(localCacheItem.getEncryptedDataKey(), configResponse1.getEncryptedDataKey());
} else if (dataId.startsWith(CIPHER_PREFIX)) {
Assertions.assertEquals(localCacheItem.getPlainContent(), configResponse1.getContent());
Assertions.assertEquals(localCacheItem.getEncryptedContentMD5(), MD5Utils.md5Hex(configRequest.getContent(), ENCODE_UTF8));
}
Assertions.assertEquals(content, configResponse1.getContent());
} catch (NacosException e) {
throw new RuntimeException(e);
} finally {
localCacheTestModeField.set(aliyunConfigFilter, false);
}
}
}
private void executeConfigFilter() {
ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager(properties);
for (String dataId : dataIdList) {
ConfigRequest configRequest = new ConfigRequest();
configRequest.setGroup(group);
configRequest.setDataId(dataId);
configRequest.setContent(content);
String encryptedContent = null;
try {
configFilterChainManager.doFilter(configRequest, null);
encryptedContent = configRequest.getContent();
Assertions.assertFalse(StringUtils.isBlank(encryptedContent));
} catch (NacosException e) {
e.printStackTrace();
}
ConfigResponse configResponse = new ConfigResponse();
configResponse.setGroup(group);
configResponse.setDataId(dataId);
configResponse.setEncryptedDataKey((String) configRequest.getParameter(ENCRYPTED_DATA_KEY));
configResponse.setContent(encryptedContent);
try {
configFilterChainManager.doFilter(null, configResponse);
Assertions.assertEquals(content, configResponse.getContent());
} catch (NacosException e) {
throw new RuntimeException(e);
}
}
}
private void verifyEncryptedConfigByKmsIdempotent() throws Exception {
ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager(properties);
String dataId = "cipher-crypt";
ConfigRequest configRequest = new ConfigRequest();
configRequest.setGroup(group);
configRequest.setDataId(dataId);
configRequest.setContent(content);
ConfigRequest configRequest1 = new ConfigRequest();
configRequest1.setGroup(group);
configRequest1.setDataId(dataId);
configRequest1.setContent(content);
String encryptedContent;
String encryptedContent1;
try {
configFilterChainManager.doFilter(configRequest, null);
configFilterChainManager.doFilter(configRequest1, null);
encryptedContent = configRequest.getContent();
encryptedContent1 = configRequest1.getContent();
Assertions.assertNotEquals(encryptedContent, encryptedContent1);
} catch (NacosException e) {
e.printStackTrace();
}
Class<? extends ConfigFilterChainManager> configFilterChainManagerClass = configFilterChainManager.getClass();
Field filtersField = configFilterChainManagerClass.getDeclaredField("filters");
filtersField.setAccessible(true);
List<IConfigFilter> filters = (List<IConfigFilter>) filtersField.get(configFilterChainManager);
AliyunConfigFilter aliyunConfigFilter = (AliyunConfigFilter) filters.get(1);
GenerateDataKeyResponse generateDataKeyResponse = aliyunConfigFilter.generateDataKey(KMS_DEFAULT_KEY_ID_VALUE,
KMS_KEY_SPEC_AES_256);
GenerateDataKeyResponse generateDataKeyResponse1 = aliyunConfigFilter.generateDataKey(KMS_DEFAULT_KEY_ID_VALUE,
KMS_KEY_SPEC_AES_256);
Assertions.assertNotEquals(generateDataKeyResponse1.getPlaintext(), generateDataKeyResponse.getPlaintext());
Assertions.assertNotEquals(
AesUtils.encrypt(content, generateDataKeyResponse.getPlaintext(), ENCODE_UTF8),
AesUtils.encrypt(content, generateDataKeyResponse1.getPlaintext(), ENCODE_UTF8));
Assertions.assertEquals(
AesUtils.encrypt(content, generateDataKeyResponse.getPlaintext(), ENCODE_UTF8),
AesUtils.encrypt(content, generateDataKeyResponse.getPlaintext(), ENCODE_UTF8));
@Test
void getFilterName() {
Properties properties = new Properties();
AliyunConfigFilter aliyunConfigFilter = new AliyunConfigFilter();
aliyunConfigFilter.init(properties);
assertEquals("com.alibaba.nacos.client.aliyun.AliyunConfigFilter", aliyunConfigFilter.getFilterName());
}
}

View File

@ -0,0 +1,186 @@
package com.alibaba.nacos.client.aliyun;
import com.alibaba.nacos.client.config.filter.impl.ConfigRequest;
import com.alibaba.nacos.client.config.filter.impl.ConfigResponse;
import com.aliyun.kms.KmsTransferAcsClient;
import com.aliyuncs.kms.model.v20160120.DecryptRequest;
import com.aliyuncs.kms.model.v20160120.DecryptResponse;
import com.aliyuncs.kms.model.v20160120.DescribeKeyRequest;
import com.aliyuncs.kms.model.v20160120.DescribeKeyResponse;
import com.aliyuncs.kms.model.v20160120.EncryptRequest;
import com.aliyuncs.kms.model.v20160120.EncryptResponse;
import com.aliyuncs.kms.model.v20160120.GenerateDataKeyRequest;
import com.aliyuncs.kms.model.v20160120.GenerateDataKeyResponse;
import com.aliyuncs.kms.model.v20160120.SetDeletionProtectionRequest;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import java.lang.reflect.Field;
import java.util.Properties;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_KMS_AES_256_PREFIX;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_PREFIX;
import static com.alibaba.nacos.client.aliyun.AliyunConst.ENCODE_UTF8;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class ClientKeyKmsEncryptorTest {
MockedConstruction<KmsTransferAcsClient> mockClientConstruction;
MockedStatic<AesUtils> mockAesUtils;
@BeforeEach
public void setUp() throws Exception {
mockAesUtils = mockStatic(AesUtils.class);
mockAesUtils.when(()->AesUtils.decrypt("cipherText","plainText",ENCODE_UTF8))
.thenReturn("plainTextByAES");
mockAesUtils.when(()->AesUtils.encrypt("plainText","plainKey",ENCODE_UTF8))
.thenReturn("cipherTextByAES");
GenerateDataKeyResponse generateDataKeyResponse = new GenerateDataKeyResponse();
generateDataKeyResponse.setPlaintext("plainKey");
generateDataKeyResponse.setCiphertextBlob("cipherKey");
DescribeKeyResponse describeKeyResponse = new DescribeKeyResponse();
DescribeKeyResponse.KeyMetadata keyMetadata = new DescribeKeyResponse.KeyMetadata();
keyMetadata.setKeyState("Enabled");
describeKeyResponse.setKeyMetadata(keyMetadata);
DecryptResponse decryptResponse = new DecryptResponse();
decryptResponse.setPlaintext("plainText");
EncryptResponse encryptResponse = new EncryptResponse();
encryptResponse.setCiphertextBlob("cipherText");
mockClientConstruction = mockConstruction(KmsTransferAcsClient.class,(mock,context)->{
when(mock.getAcsResponse(any(DescribeKeyRequest.class))).thenReturn(describeKeyResponse);
when(mock.getAcsResponse(any(GenerateDataKeyRequest.class))).thenReturn(generateDataKeyResponse);
when(mock.getAcsResponse(any(SetDeletionProtectionRequest.class))).thenReturn(null);
when(mock.getAcsResponse(any(DecryptRequest.class))).thenReturn(decryptResponse);
when(mock.getAcsResponse(any(EncryptRequest.class))).thenReturn(encryptResponse);
});
}
@AfterEach
public void tearDown() throws Exception {
mockClientConstruction.close();
mockAesUtils.close();
}
@Test
void testInit() throws Exception {
Properties properties = new Properties();
properties.put(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, "clientKeyContent");
properties.put(AliyunConst.KMS_ENDPOINT,"endPoint");
// properties.put(AliyunConst.KEY_ID,"keyId");
properties.put(AliyunConst.KMS_PASSWORD_KEY,"password");
properties.put(AliyunConst.KMS_CA_FILE_CONTENT,"caFileContent");
ClientKeyKmsEncryptor clientKeyKmsEncryptor1 = new ClientKeyKmsEncryptor(properties);
ConfigRequest configRequest = new ConfigRequest();
try{
clientKeyKmsEncryptor1.encrypt(configRequest);
}catch (Exception e){
assertTrue(e.getMessage().contains("keyId is null or empty"));
}
Properties properties2 = new Properties();
// properties2.put(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, "clientKeyContent");
properties2.put(AliyunConst.KMS_ENDPOINT,"endPoint");
properties2.put(AliyunConst.KEY_ID,"keyId");
properties2.put(AliyunConst.KMS_PASSWORD_KEY,"password");
properties2.put(AliyunConst.KMS_CA_FILE_CONTENT,"caFileContent");
ClientKeyKmsEncryptor clientKeyKmsEncryptor2 = new ClientKeyKmsEncryptor(properties2);
try{
clientKeyKmsEncryptor2.encrypt(configRequest);
}catch (Exception e){
assertTrue(e.getMessage().contains( "kmsClientKeyFilePath and kmsClientKeyContent are both empty"));
}
Properties properties3 = new Properties();
properties3.put(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, "clientKeyContent");
// properties3.put(AliyunConst.KMS_ENDPOINT,"endPoint");
properties3.put(AliyunConst.KEY_ID,"keyId");
properties3.put(AliyunConst.KMS_PASSWORD_KEY,"password");
properties3.put(AliyunConst.KMS_CA_FILE_CONTENT,"caFileContent");
ClientKeyKmsEncryptor clientKeyKmsEncryptor3 = new ClientKeyKmsEncryptor(properties3);
try{
clientKeyKmsEncryptor3.encrypt(configRequest);
} catch (Exception e){
assertTrue(e.getMessage().contains("kmsEndpoint is empty"));
}
Properties properties4 = new Properties();
properties4.put(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, "clientKeyContent");
properties4.put(AliyunConst.KMS_ENDPOINT,"endPoint");
properties4.put(AliyunConst.KEY_ID,"keyId");
// properties4.put(AliyunConst.KMS_PASSWORD_KEY,"password");
properties4.put(AliyunConst.KMS_CA_FILE_CONTENT,"caFileContent");
ClientKeyKmsEncryptor clientKeyKmsEncryptor4 = new ClientKeyKmsEncryptor(properties4);
try{
clientKeyKmsEncryptor4.encrypt(configRequest);
} catch (Exception e){
assertTrue(e.getMessage().contains("kmsPasswordKey is empty"));
}
Properties properties5 = new Properties();
properties5.put(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, "clientKeyContent");
properties5.put(AliyunConst.KMS_ENDPOINT,"endPoint");
properties5.put(AliyunConst.KEY_ID,"keyId");
properties5.put(AliyunConst.KMS_PASSWORD_KEY,"password");
properties5.put(AliyunConst.KMS_CA_FILE_CONTENT,"caFileContent");
ClientKeyKmsEncryptor clientKeyKmsEncryptor5 = new ClientKeyKmsEncryptor(properties5);
Field field = ClientKeyKmsEncryptor.class.getDeclaredField("kmsClient");
field.setAccessible(true);
assertNotNull(field.get(clientKeyKmsEncryptor5));
}
@Test
void encryptAndDecryptTest() throws Exception {
ConfigRequest configRequest = new ConfigRequest();
configRequest.setDataId(CIPHER_KMS_AES_256_PREFIX+"dataId");
configRequest.setGroup("DEFAULT_GROUP");
configRequest.setContent("plainText");
Properties properties = new Properties();
properties.put(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, "clientKeyContent");
properties.put(AliyunConst.KMS_ENDPOINT,"endPoint");
properties.put(AliyunConst.KEY_ID,"keyId");
properties.put(AliyunConst.KMS_PASSWORD_KEY,"password");
properties.put(AliyunConst.KMS_CA_FILE_CONTENT,"caFileContent");
ClientKeyKmsEncryptor clientKeyKmsEncryptor = new ClientKeyKmsEncryptor(properties);
assertEquals(clientKeyKmsEncryptor.encrypt(configRequest),"cipherTextByAES");
configRequest.setDataId(CIPHER_PREFIX+"dataId");
assertEquals(clientKeyKmsEncryptor.encrypt(configRequest),"cipherText");
ConfigResponse configResponse = new ConfigResponse();
configResponse.setContent("cipherText");
configResponse.setEncryptedDataKey("encryptedDataKey");
configResponse.setDataId(CIPHER_KMS_AES_256_PREFIX+"dataId");
configResponse.setGroup("DEFAULT_GROUP");
assertEquals(clientKeyKmsEncryptor.decrypt(configResponse),"plainTextByAES");
configResponse.setDataId(CIPHER_PREFIX+"dataId");
assertEquals(clientKeyKmsEncryptor.decrypt(configResponse),"plainText");
}
}

View File

@ -0,0 +1,165 @@
package com.alibaba.nacos.client.aliyun;
import com.alibaba.nacos.client.config.filter.impl.ConfigRequest;
import com.alibaba.nacos.client.config.filter.impl.ConfigResponse;
import com.aliyun.kms20160120.Client;
import com.aliyun.kms20160120.models.DecryptRequest;
import com.aliyun.kms20160120.models.DecryptResponse;
import com.aliyun.kms20160120.models.DecryptResponseBody;
import com.aliyun.kms20160120.models.DescribeKeyRequest;
import com.aliyun.kms20160120.models.DescribeKeyResponse;
import com.aliyun.kms20160120.models.DescribeKeyResponseBody;
import com.aliyun.kms20160120.models.EncryptRequest;
import com.aliyun.kms20160120.models.EncryptResponse;
import com.aliyun.kms20160120.models.EncryptResponseBody;
import com.aliyun.kms20160120.models.GenerateDataKeyRequest;
import com.aliyun.kms20160120.models.GenerateDataKeyResponse;
import com.aliyun.kms20160120.models.GenerateDataKeyResponseBody;
import com.aliyun.kms20160120.models.SetDeletionProtectionRequest;
import com.aliyun.teautil.models.RuntimeOptions;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedConstruction;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import java.lang.reflect.Field;
import java.util.Properties;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_KMS_AES_256_PREFIX;
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_PREFIX;
import static com.alibaba.nacos.client.aliyun.AliyunConst.ENCODE_UTF8;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mockConstruction;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
class RamKmsEncryptorTest {
MockedConstruction<Client> mockClientConstruction;
MockedStatic<AesUtils> mockAesUtils;
@BeforeEach
public void setUp() throws Exception {
mockAesUtils = mockStatic(AesUtils.class);
mockAesUtils.when(() -> AesUtils.decrypt("cipherText", "plainText", ENCODE_UTF8)).thenReturn("plainTextByAES");
mockAesUtils.when(() -> AesUtils.encrypt("plainText", "plainKey", ENCODE_UTF8)).thenReturn("cipherTextByAES");
EncryptResponse encryptResponse = new EncryptResponse();
EncryptResponseBody body = new EncryptResponseBody();
body.setCiphertextBlob("cipherText");
encryptResponse.setBody(body);
DecryptResponse decryptResponse = new DecryptResponse();
DecryptResponseBody body1 = new DecryptResponseBody();
decryptResponse.setBody(body1);
body1.setPlaintext("plainText");
DescribeKeyResponse describeKeyResponse = new DescribeKeyResponse();
DescribeKeyResponseBody body2 = new DescribeKeyResponseBody();
DescribeKeyResponseBody.DescribeKeyResponseBodyKeyMetadata keyMetadata = new DescribeKeyResponseBody.DescribeKeyResponseBodyKeyMetadata();
keyMetadata.setKeyState("Enabled");
body2.setKeyMetadata(keyMetadata);
describeKeyResponse.setBody(body2);
GenerateDataKeyResponse generateDataKeyResponse = new GenerateDataKeyResponse();
GenerateDataKeyResponseBody body3 = new GenerateDataKeyResponseBody();
body3.setPlaintext("plainKey");
body3.setCiphertextBlob("cipherKey");
generateDataKeyResponse.setBody(body3);
mockClientConstruction = mockConstruction(Client.class, (mock, context) -> {
when(mock.encryptWithOptions(any(EncryptRequest.class), any(RuntimeOptions.class))).thenReturn(
encryptResponse);
when(mock.decryptWithOptions(any(DecryptRequest.class), any(RuntimeOptions.class))).thenReturn(
decryptResponse);
when(mock.describeKeyWithOptions(any(DescribeKeyRequest.class), any(RuntimeOptions.class))).thenReturn(
describeKeyResponse);
when(mock.setDeletionProtectionWithOptions(any(SetDeletionProtectionRequest.class),
any(RuntimeOptions.class))).thenReturn(null);
when(mock.generateDataKeyWithOptions(any(GenerateDataKeyRequest.class),
any(RuntimeOptions.class))).thenReturn(generateDataKeyResponse);
});
}
@AfterEach
void tearDown() {
mockClientConstruction.close();
mockAesUtils.close();
}
@Test
void testInit() throws Exception {
Properties properties = new Properties();
properties.setProperty("accessKey", "accessKey");
properties.setProperty("secretKey", "secretKey");
properties.setProperty("regionId", "regionId");
properties.setProperty(AliyunConst.KMS_ENDPOINT, "endPoint");
ConfigRequest configRequest = new ConfigRequest();
RamKmsEncryptor ramKmsEncryptor = new RamKmsEncryptor(properties);
try {
ramKmsEncryptor.encrypt(configRequest);
} catch (Exception e) {
assertTrue(e.getMessage().contains("keyId is null or empty"));
}
Properties properties1 = new Properties();
properties1.setProperty("accessKey", "accessKey");
// properties1.setProperty("secretKey","secretKey");
properties1.setProperty("regionId", "regionId");
RamKmsEncryptor ramKmsEncryptor1 = new RamKmsEncryptor(properties1);
try {
ramKmsEncryptor1.encrypt(configRequest);
} catch (Exception e) {
assertTrue(e.getMessage().contains("Ram Auth Information are not set up yet"));
}
Properties properties2 = new Properties();
properties2.setProperty("ramRoleName", "ramRoleName");
properties2.setProperty("regionId", "regionId");
RamKmsEncryptor ramKmsEncryptor2 = new RamKmsEncryptor(properties2);
Field field = RamKmsEncryptor.class.getDeclaredField("kmsClient");
field.setAccessible(true);
assertNotNull(field.get(ramKmsEncryptor2));
}
@Test
void encryptAndDecryptTest() throws Exception {
ConfigRequest configRequest = new ConfigRequest();
configRequest.setDataId(CIPHER_KMS_AES_256_PREFIX + "dataId");
configRequest.setGroup("DEFAULT_GROUP");
configRequest.setContent("plainText");
Properties properties = new Properties();
properties.setProperty("ramRoleName", "ramRoleName");
properties.setProperty("regionId", "regionId");
RamKmsEncryptor ramKmsEncryptor = new RamKmsEncryptor(properties);
assertEquals(ramKmsEncryptor.encrypt(configRequest), "cipherTextByAES");
configRequest.setDataId(CIPHER_PREFIX + "dataId");
assertEquals(ramKmsEncryptor.encrypt(configRequest), "cipherText");
ConfigResponse configResponse = new ConfigResponse();
configResponse.setContent("cipherText");
configResponse.setEncryptedDataKey("encryptedDataKey");
configResponse.setDataId(CIPHER_KMS_AES_256_PREFIX + "dataId");
configResponse.setGroup("DEFAULT_GROUP");
assertEquals(ramKmsEncryptor.decrypt(configResponse), "plainTextByAES");
configResponse.setDataId(CIPHER_PREFIX + "dataId");
assertEquals(ramKmsEncryptor.decrypt(configResponse), "plainText");
}
}