Compare commits
3 Commits
Author | SHA1 | Date |
---|---|---|
|
7a26e3940e | |
|
3ba4dc8e91 | |
|
098c9c5a46 |
8
pom.xml
8
pom.xml
|
@ -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>
|
||||
|
|
|
@ -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.
|
||||
|
@ -62,250 +26,28 @@ import static com.alibaba.nacos.client.aliyun.AliyunConst.STRING_VALUE_BLANK_ERR
|
|||
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);
|
||||
} 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;
|
||||
}
|
||||
System.getProperty(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, System.getenv(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(kmsClientKeyContent)||!StringUtils.isBlank(kmsClientKeyFilePath)){
|
||||
LOGGER.info("kmsClientKeyContent or kmsClientKeyFilePath is set up, using ClientKey to connect KMS.");
|
||||
this.kmsEncryptor = new ClientKeyKmsEncryptor(properties);
|
||||
}else{
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -340,437 +82,7 @@ public class AliyunConfigFilter extends AbstractConfigFilter {
|
|||
throw new NacosException(NacosException.INVALID_PARAM, AliyunConst.formatHelpMessage(e.getMessage()), e);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
@ -52,6 +78,8 @@ public class AliyunConst {
|
|||
public static final String KMS_CA_FILE_PATH_KEY = "kmsCaFilePath";
|
||||
|
||||
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";
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -54,7 +54,9 @@ public class AliyunExtensionClientAuthServiceImpl extends AbstractClientAuthServ
|
|||
public Boolean login(Properties properties) {
|
||||
for (ExtensionCredentialsProvider each : credentialsProviders) {
|
||||
if (each.matchProvider(properties)) {
|
||||
LOGGER.info("Match credentials provider: {}", each.getClass().getName());
|
||||
if (null == matchedProvider) {
|
||||
LOGGER.info("Match credentials provider: {}", each.getClass().getName());
|
||||
}
|
||||
matchedProvider = each;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}};
|
||||
|
||||
public static final String content = "crypt中文&&";
|
||||
|
||||
public static final String group = "default";
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AliyunConfigFilterTest {
|
||||
|
||||
public static final String ak = "LTAIxxx";
|
||||
MockedConstruction<RamKmsEncryptor> ramKmsEncryptorMockedConstruction;
|
||||
|
||||
@Mock
|
||||
IConfigFilterChain chain;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
// 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();
|
||||
void setUp() {
|
||||
ramKmsEncryptorMockedConstruction = mockConstruction(RamKmsEncryptor.class,(mock,contexnt)->{
|
||||
when(mock.encrypt(any(IConfigRequest.class))).thenReturn("encryptedContext");
|
||||
when(mock.decrypt(any(IConfigResponse.class))).thenReturn("plainText");
|
||||
});
|
||||
}
|
||||
|
||||
// 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();
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
ramKmsEncryptorMockedConstruction.close();
|
||||
}
|
||||
|
||||
@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 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);
|
||||
|
||||
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
|
||||
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);
|
||||
}
|
||||
}
|
||||
@Test
|
||||
void getFilterName() {
|
||||
Properties properties = new Properties();
|
||||
AliyunConfigFilter aliyunConfigFilter = new AliyunConfigFilter();
|
||||
aliyunConfigFilter.init(properties);
|
||||
assertEquals("com.alibaba.nacos.client.aliyun.AliyunConfigFilter", aliyunConfigFilter.getFilterName());
|
||||
}
|
||||
|
||||
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));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue