Compare commits
5 Commits
Author | SHA1 | Date |
---|---|---|
|
7a26e3940e | |
|
3ba4dc8e91 | |
|
098c9c5a46 | |
|
44020825d6 | |
|
889d67b5b5 |
72
pom.xml
72
pom.xml
|
@ -12,12 +12,12 @@
|
|||
limitations under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<inceptionYear>2018</inceptionYear>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client-mse-extension</artifactId>
|
||||
<version>1.0.4</version>
|
||||
<version>1.0.6</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<developers>
|
||||
|
@ -51,32 +51,32 @@
|
|||
<name>Aliyun NACOS extension for MSE</name>
|
||||
<description>Aliyun NACOS extension for MSE</description>
|
||||
<url>http://nacos.io</url>
|
||||
|
||||
|
||||
<scm>
|
||||
<url>git@github.com:nacos-group/nacos-client-mse-extension.git</url>
|
||||
<connection>scm:git@github.com:nacos-group/nacos-client-mse-extension.git</connection>
|
||||
<developerConnection>scm:git@github.com:nacos-group/nacos-client-mse-extension.git</developerConnection>
|
||||
</scm>
|
||||
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<nacos.version>2.2.4</nacos.version>
|
||||
<nacos.version>2.4.0</nacos.version>
|
||||
<guava.version>30.1-jre</guava.version>
|
||||
</properties>
|
||||
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>aliyun-java-sdk-core</artifactId>
|
||||
<version>4.5.17</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>aliyun-java-sdk-kms</artifactId>
|
||||
<version>2.16.3</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aliyun.kms</groupId>
|
||||
<artifactId>kms-transfer-client</artifactId>
|
||||
|
@ -88,14 +88,32 @@
|
|||
</exclusions>
|
||||
<version>0.1.0</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>credentials-java</artifactId>
|
||||
<version>0.3.12</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.aliyun</groupId>
|
||||
<artifactId>alibabacloud-secretsmanager-client</artifactId>
|
||||
<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-api</artifactId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
<version>${nacos.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<groupId>commons-codec</groupId>
|
||||
|
@ -108,7 +126,7 @@
|
|||
<version>${guava.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
|
||||
<!-- for test-->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
|
@ -116,15 +134,23 @@
|
|||
<version>RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
<version>${nacos.version}</version>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-inline</artifactId>
|
||||
<version>4.11.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-junit-jupiter</artifactId>
|
||||
<version>4.11.0</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
@ -136,8 +162,8 @@
|
|||
<target>8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
||||
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-source-plugin</artifactId>
|
||||
|
@ -151,8 +177,8 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
||||
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
|
@ -168,8 +194,8 @@
|
|||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
|
||||
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</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();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth;
|
||||
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.client.aliyun.auth.injector.ConfigExtensionResourceInjector;
|
||||
import com.alibaba.nacos.client.aliyun.auth.injector.NamingExtensionResourceInjector;
|
||||
import com.alibaba.nacos.client.aliyun.auth.provider.AutoRotateCredentialsProvider;
|
||||
import com.alibaba.nacos.client.aliyun.auth.provider.CredentialsUriCredentialsProvider;
|
||||
import com.alibaba.nacos.client.aliyun.auth.provider.ExtensionCredentialsProvider;
|
||||
import com.alibaba.nacos.client.aliyun.auth.provider.OidcRoleArnCredentialsProvider;
|
||||
import com.alibaba.nacos.client.aliyun.auth.provider.RamRoleArnCredentialsProvider;
|
||||
import com.alibaba.nacos.client.aliyun.auth.provider.StsTokenCredentialsProvider;
|
||||
import com.alibaba.nacos.client.auth.ram.injector.AbstractResourceInjector;
|
||||
import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext;
|
||||
import com.alibaba.nacos.plugin.auth.api.RequestResource;
|
||||
import com.alibaba.nacos.plugin.auth.constant.SignType;
|
||||
import com.alibaba.nacos.plugin.auth.spi.client.AbstractClientAuthService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Nacos ClientAuthServiceImpl for aliyun extension auth way.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public class AliyunExtensionClientAuthServiceImpl extends AbstractClientAuthService {
|
||||
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(AliyunExtensionClientAuthServiceImpl.class);
|
||||
|
||||
private final Set<ExtensionCredentialsProvider> credentialsProviders;
|
||||
|
||||
private final Map<String, AbstractResourceInjector> resourceInjectors;
|
||||
|
||||
private ExtensionCredentialsProvider matchedProvider;
|
||||
|
||||
public AliyunExtensionClientAuthServiceImpl() {
|
||||
this.credentialsProviders = new HashSet<>();
|
||||
this.credentialsProviders.add(new CredentialsUriCredentialsProvider());
|
||||
this.credentialsProviders.add(new StsTokenCredentialsProvider());
|
||||
this.credentialsProviders.add(new OidcRoleArnCredentialsProvider());
|
||||
this.credentialsProviders.add(new RamRoleArnCredentialsProvider());
|
||||
this.credentialsProviders.add(new AutoRotateCredentialsProvider());
|
||||
this.resourceInjectors = new HashMap<>();
|
||||
this.resourceInjectors.put(SignType.NAMING, new NamingExtensionResourceInjector());
|
||||
this.resourceInjectors.put(SignType.CONFIG, new ConfigExtensionResourceInjector());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean login(Properties properties) {
|
||||
for (ExtensionCredentialsProvider each : credentialsProviders) {
|
||||
if (each.matchProvider(properties)) {
|
||||
if (null == matchedProvider) {
|
||||
LOGGER.info("Match credentials provider: {}", each.getClass().getName());
|
||||
}
|
||||
matchedProvider = each;
|
||||
break;
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (null == matchedProvider) {
|
||||
return false;
|
||||
}
|
||||
matchedProvider.init(properties);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Init for Credential Provider {} failed.", matchedProvider.getClass().getName(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoginIdentityContext getLoginIdentityContext(RequestResource resource) {
|
||||
LoginIdentityContext result = new LoginIdentityContext();
|
||||
if (null == matchedProvider) {
|
||||
return result;
|
||||
}
|
||||
ExtensionRamContext ramContext = matchedProvider.getCredentialsForNacosClient();
|
||||
if (!ramContext.validate() || notFountInjector(resource.getType())) {
|
||||
return result;
|
||||
}
|
||||
resourceInjectors.get(resource.getType()).doInject(resource, ramContext, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean notFountInjector(String type) {
|
||||
if (!resourceInjectors.containsKey(type)) {
|
||||
LOGGER.warn("Injector for type {} not found, will use default ram identity context.", type);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws NacosException {
|
||||
if (null != matchedProvider) {
|
||||
matchedProvider.shutdown();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth;
|
||||
|
||||
/**
|
||||
* Constants for aliyun extension auth.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public class ExtensionAuthConstants {
|
||||
|
||||
/**
|
||||
* Original signature region id key, added by nacos-client 2.4.0.
|
||||
*/
|
||||
public static final String SIGNATURE_REGION_ID_KEY = "signatureRegionId";
|
||||
|
||||
public static final String SECURITY_TOKEN_HEADER = "Spas-SecurityToken";
|
||||
|
||||
public static final String V4_SIGNATURE_UTIL_CLASS = "com.alibaba.nacos.client.auth.ram.utils.CalculateV4SigningKeyUtil";
|
||||
|
||||
/**
|
||||
* Nacos properties keys
|
||||
*/
|
||||
private static final String PREFIX = "alibabaCloud";
|
||||
|
||||
public static final String SECRET_NAME_KEY = PREFIX + "SecretName";
|
||||
|
||||
public static final String CREDENTIALS_URI_KEY = PREFIX + "CredentialsUri";
|
||||
|
||||
public static final String OIDC_TOKEN_FILE_PATH_KEY = PREFIX + "OidcTokenFile";
|
||||
|
||||
public static final String OIDC_PROVIDER_ARN_KEY = PREFIX + "OidcProviderArn";
|
||||
|
||||
public static final String ROLE_SESSION_EXPIRATION_KEY = PREFIX + "RoleSessionExpiration";
|
||||
|
||||
public static final String POLICY_KEY = PREFIX + "Policy";
|
||||
|
||||
public static final String ROLE_SESSION_NAME_KEY = PREFIX + "RoleSessionName";
|
||||
|
||||
public static final String ROLE_ARN_KEY = PREFIX + "RoleArn";
|
||||
|
||||
public static final String SECURITY_TOKEN_KEY = PREFIX + "SecurityToken";
|
||||
|
||||
public static final String ACCESS_KEY_SECRET_KEY = PREFIX + "AccessKeySecret";
|
||||
|
||||
public static final String ACCESS_KEY_ID_KEY = PREFIX + "AccessKeyId";
|
||||
|
||||
/**
|
||||
* Env properties keys.
|
||||
*/
|
||||
private static final String ENV_PREFIX = "ALIBABA_CLOUD_";
|
||||
|
||||
public static final String ENV_ACCESS_KEY_ID_KEY = ENV_PREFIX + "ACCESS_KEY_ID";
|
||||
|
||||
public static final String ENV_ACCESS_KEY_SECRET_KEY = ENV_PREFIX + "ACCESS_KEY_SECRET";
|
||||
|
||||
public static final String ENV_SECURITY_TOKEN_KEY = ENV_PREFIX + "SECURITY_TOKEN";
|
||||
|
||||
public static final String ENV_SIGNATURE_REGION_ID_KEY = ENV_PREFIX + "SIGNATURE_REGION_ID";
|
||||
|
||||
public static final String ENV_ROLE_ARN_KEY = ENV_PREFIX + "ROLE_ARN";
|
||||
|
||||
public static final String ENV_ROLE_SESSION_NAME_KEY = ENV_PREFIX + "ROLE_SESSION_NAME";
|
||||
|
||||
public static final String ENV_POLICY_KEY = ENV_PREFIX + "POLICY";
|
||||
|
||||
public static final String ENV_ROLE_SESSION_EXPIRATION_KEY = ENV_PREFIX + "ROLE_SESSION_EXPIRATION";
|
||||
|
||||
public static final String ENV_OIDC_PROVIDER_ARN_KEY = ENV_PREFIX + "OIDC_PROVIDER_ARN";
|
||||
|
||||
public static final String ENV_OIDC_TOKEN_FILE_KEY = ENV_PREFIX + "OIDC_TOKEN_FILE";
|
||||
|
||||
public static final String ENV_CREDENTIALS_URI_KEY = ENV_PREFIX + "CREDENTIALS_URI";
|
||||
|
||||
public static final String ENV_SECRET_NAME_KEY = ENV_PREFIX + "SECRET_NAME";
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth;
|
||||
|
||||
/**
|
||||
* Aliyun Extension Auth Property Key.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public enum ExtensionAuthPropertyKey {
|
||||
|
||||
ACCESS_KEY_ID(ExtensionAuthConstants.ACCESS_KEY_ID_KEY, ExtensionAuthConstants.ENV_ACCESS_KEY_ID_KEY),
|
||||
|
||||
ACCESS_KEY_SECRET(ExtensionAuthConstants.ACCESS_KEY_SECRET_KEY, ExtensionAuthConstants.ENV_ACCESS_KEY_SECRET_KEY),
|
||||
|
||||
SECURITY_TOKEN(ExtensionAuthConstants.SECURITY_TOKEN_KEY, ExtensionAuthConstants.ENV_SECURITY_TOKEN_KEY),
|
||||
|
||||
SIGNATURE_REGION_ID(ExtensionAuthConstants.SIGNATURE_REGION_ID_KEY,
|
||||
ExtensionAuthConstants.ENV_SIGNATURE_REGION_ID_KEY),
|
||||
|
||||
ROLE_ARN(ExtensionAuthConstants.ROLE_ARN_KEY, ExtensionAuthConstants.ENV_ROLE_ARN_KEY),
|
||||
|
||||
ROLE_SESSION_NAME(ExtensionAuthConstants.ROLE_SESSION_NAME_KEY, ExtensionAuthConstants.ENV_ROLE_SESSION_NAME_KEY),
|
||||
|
||||
POLICY(ExtensionAuthConstants.POLICY_KEY, ExtensionAuthConstants.ENV_POLICY_KEY),
|
||||
|
||||
ROLE_SESSION_EXPIRATION(ExtensionAuthConstants.ROLE_SESSION_EXPIRATION_KEY,
|
||||
ExtensionAuthConstants.ENV_ROLE_SESSION_EXPIRATION_KEY),
|
||||
|
||||
OIDC_PROVIDER_ARN(ExtensionAuthConstants.OIDC_PROVIDER_ARN_KEY, ExtensionAuthConstants.ENV_OIDC_PROVIDER_ARN_KEY),
|
||||
|
||||
OIDC_TOKEN_FILE_PATH(ExtensionAuthConstants.OIDC_TOKEN_FILE_PATH_KEY,
|
||||
ExtensionAuthConstants.ENV_OIDC_TOKEN_FILE_KEY),
|
||||
|
||||
CREDENTIALS_URI(ExtensionAuthConstants.CREDENTIALS_URI_KEY, ExtensionAuthConstants.ENV_CREDENTIALS_URI_KEY),
|
||||
|
||||
SECRET_NAME(ExtensionAuthConstants.SECRET_NAME_KEY, ExtensionAuthConstants.ENV_SECRET_NAME_KEY);
|
||||
|
||||
private final String key;
|
||||
|
||||
private final String envKey;
|
||||
|
||||
ExtensionAuthPropertyKey(String key, String envKey) {
|
||||
this.key = key;
|
||||
this.envKey = envKey;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
public String getEnvKey() {
|
||||
return envKey;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth;
|
||||
|
||||
import com.alibaba.nacos.client.auth.ram.RamContext;
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
|
||||
/**
|
||||
* Extension ram context.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public class ExtensionRamContext extends RamContext {
|
||||
|
||||
private String securityToken;
|
||||
|
||||
/**
|
||||
* For adapter nacos-client 2.4.0 v4 signature.
|
||||
*/
|
||||
private String extensionSignatureRegionId;
|
||||
|
||||
private boolean ephemeralAccessKeyId = true;
|
||||
|
||||
public String getSecurityToken() {
|
||||
return securityToken;
|
||||
}
|
||||
|
||||
public void setSecurityToken(String securityToken) {
|
||||
this.securityToken = securityToken;
|
||||
}
|
||||
|
||||
public String getExtensionSignatureRegionId() {
|
||||
return extensionSignatureRegionId;
|
||||
}
|
||||
|
||||
public void setExtensionSignatureRegionId(String extensionSignatureRegionId) {
|
||||
this.extensionSignatureRegionId = extensionSignatureRegionId;
|
||||
}
|
||||
|
||||
public boolean isEphemeralAccessKeyId() {
|
||||
return ephemeralAccessKeyId;
|
||||
}
|
||||
|
||||
public void setEphemeralAccessKeyId(boolean ephemeralAccessKeyId) {
|
||||
this.ephemeralAccessKeyId = ephemeralAccessKeyId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validate() {
|
||||
if (ephemeralAccessKeyId && StringUtils.isEmpty(securityToken)) {
|
||||
return false;
|
||||
}
|
||||
return StringUtils.isNotBlank(super.getAccessKey()) && StringUtils.isNotBlank(super.getSecretKey());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.injector;
|
||||
|
||||
import com.alibaba.nacos.api.utils.StringUtils;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthConstants;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
|
||||
import com.alibaba.nacos.client.auth.ram.RamConstants;
|
||||
import com.alibaba.nacos.client.auth.ram.RamContext;
|
||||
import com.alibaba.nacos.client.auth.ram.injector.AbstractResourceInjector;
|
||||
import com.alibaba.nacos.client.auth.ram.utils.CalculateV4SigningKeyUtil;
|
||||
import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext;
|
||||
import com.alibaba.nacos.plugin.auth.api.RequestResource;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Abstract resource injector for extension.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public abstract class AbstractExtensionResourceInjector extends AbstractResourceInjector {
|
||||
|
||||
private boolean supportV4signature;
|
||||
|
||||
protected AbstractExtensionResourceInjector() {
|
||||
try {
|
||||
Class.forName(ExtensionAuthConstants.V4_SIGNATURE_UTIL_CLASS);
|
||||
supportV4signature = true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
supportV4signature = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doInject(RequestResource resource, RamContext context, LoginIdentityContext result) {
|
||||
ExtensionRamContext ramContext = (ExtensionRamContext) context;
|
||||
result.setParameter(getAccessKeyHeaderKey(), ramContext.getAccessKey());
|
||||
if (ramContext.isEphemeralAccessKeyId()) {
|
||||
result.setParameter(ExtensionAuthConstants.SECURITY_TOKEN_HEADER, ramContext.getSecurityToken());
|
||||
}
|
||||
String secretKey = trySignatureWithV4(ramContext, result);
|
||||
Map<String, String> signatures = calculateSignature(resource, secretKey, ramContext);
|
||||
result.setParameters(signatures);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to sign with v4 signature.
|
||||
*
|
||||
* @param context ram context with extension
|
||||
* @param result login identity context
|
||||
* @return actual secret key, if not support v4 signature or not config signature region, return secret key directly,otherwise return v4 signature.
|
||||
*/
|
||||
protected String trySignatureWithV4(ExtensionRamContext context, LoginIdentityContext result) {
|
||||
if (!supportV4signature || StringUtils.isEmpty(context.getExtensionSignatureRegionId())) {
|
||||
return context.getSecretKey();
|
||||
}
|
||||
result.setParameter(RamConstants.SIGNATURE_VERSION, RamConstants.V4);
|
||||
return CalculateV4SigningKeyUtil.finalSigningKeyStringWithDefaultInfo(context.getSecretKey(),
|
||||
context.getExtensionSignatureRegionId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get access key header key according to child module.
|
||||
*
|
||||
* @return access key header key.
|
||||
*/
|
||||
protected abstract String getAccessKeyHeaderKey();
|
||||
|
||||
/**
|
||||
* Calculate signature according to child module.
|
||||
*
|
||||
* @param resource request resource
|
||||
* @param actualSecretKey actual secret key, if support v4 and config signature region, it's v4 key, otherwise it's original key.
|
||||
* @param ramContext extension ram context
|
||||
* @return signature item maps.
|
||||
*/
|
||||
protected abstract Map<String, String> calculateSignature(RequestResource resource, String actualSecretKey,
|
||||
ExtensionRamContext ramContext);
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.injector;
|
||||
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
|
||||
import com.alibaba.nacos.client.auth.ram.utils.SpasAdapter;
|
||||
import com.alibaba.nacos.client.utils.LogUtils;
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
import com.alibaba.nacos.plugin.auth.api.RequestResource;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Ram resource injector with aliyun extension for nacos config module.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public class ConfigExtensionResourceInjector extends AbstractExtensionResourceInjector {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.logger(ConfigExtensionResourceInjector.class);
|
||||
|
||||
private static final String ACCESS_KEY_HEADER = "Spas-AccessKey";
|
||||
|
||||
private static final String DEFAULT_RESOURCE = "";
|
||||
|
||||
@Override
|
||||
protected String getAccessKeyHeaderKey() {
|
||||
return ACCESS_KEY_HEADER;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, String> calculateSignature(RequestResource resource, String actualSecretKey,
|
||||
ExtensionRamContext ramContext) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
try {
|
||||
String resourceString = getResource(resource.getNamespace(), resource.getGroup());
|
||||
Map<String, String> signHeaders = SpasAdapter.getSignHeaders(resourceString, actualSecretKey);
|
||||
result.putAll(signHeaders);
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Calculate auth signature for config failed.", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getResource(String tenant, String group) {
|
||||
if (StringUtils.isNotBlank(tenant) && StringUtils.isNotBlank(group)) {
|
||||
return tenant + "+" + group;
|
||||
}
|
||||
if (StringUtils.isNotBlank(group)) {
|
||||
return group;
|
||||
}
|
||||
if (StringUtils.isNotBlank(tenant)) {
|
||||
return tenant;
|
||||
}
|
||||
return DEFAULT_RESOURCE;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.injector;
|
||||
|
||||
import com.alibaba.nacos.api.common.Constants;
|
||||
import com.alibaba.nacos.api.naming.utils.NamingUtils;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
|
||||
import com.alibaba.nacos.client.auth.ram.utils.SignUtil;
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
import com.alibaba.nacos.plugin.auth.api.RequestResource;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.alibaba.nacos.client.utils.LogUtils.NAMING_LOGGER;
|
||||
|
||||
/**
|
||||
* Ram resource injector with aliyun extension for nacos naming module.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public class NamingExtensionResourceInjector extends AbstractExtensionResourceInjector {
|
||||
|
||||
private static final String SIGNATURE_FILED = "signature";
|
||||
|
||||
private static final String DATA_FILED = "data";
|
||||
|
||||
private static final String AK_FILED = "ak";
|
||||
|
||||
@Override
|
||||
protected String getAccessKeyHeaderKey() {
|
||||
return AK_FILED;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, String> calculateSignature(RequestResource resource, String actualSecretKey,
|
||||
ExtensionRamContext ramContext) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
try {
|
||||
String signData = getSignData(getGroupedServiceName(resource));
|
||||
String signature = SignUtil.sign(signData, actualSecretKey);
|
||||
result.put(SIGNATURE_FILED, signature);
|
||||
result.put(DATA_FILED, signData);
|
||||
} catch (Exception e) {
|
||||
NAMING_LOGGER.warn("Calculate auth signature for naming failed.", e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getGroupedServiceName(RequestResource resource) {
|
||||
if (resource.getResource().contains(Constants.SERVICE_INFO_SPLITER) || StringUtils
|
||||
.isBlank(resource.getGroup())) {
|
||||
return resource.getResource();
|
||||
}
|
||||
return NamingUtils.getGroupedNameOptional(resource.getResource(), resource.getGroup());
|
||||
}
|
||||
|
||||
private String getSignData(String serviceName) {
|
||||
return StringUtils.isNotEmpty(serviceName) ? System.currentTimeMillis() + Constants.SERVICE_INFO_SPLITER
|
||||
+ serviceName : String.valueOf(System.currentTimeMillis());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.provider;
|
||||
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.api.utils.StringUtils;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
|
||||
import com.aliyun.credentials.Client;
|
||||
import com.aliyun.credentials.models.Config;
|
||||
import com.aliyun.credentials.models.CredentialModel;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Abstract credentials provider by aliyun credentials client.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public abstract class AbstractCredentialClientProvider implements ExtensionCredentialsProvider {
|
||||
|
||||
private Client credentialsClient;
|
||||
|
||||
private String signatureRegionId;
|
||||
|
||||
@Override
|
||||
public void init(Properties properties) {
|
||||
synchronized (this) {
|
||||
if (null == credentialsClient) {
|
||||
Config credentialsConfig = generateCredentialsConfig(properties);
|
||||
credentialsClient = new Client(credentialsConfig);
|
||||
}
|
||||
}
|
||||
signatureRegionId = getSignatureRegionId(properties);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate credentials config by properties.
|
||||
*
|
||||
* @param properties nacos client properties which same as parameters {@link #matchProvider(Properties)}
|
||||
* @return credentials config
|
||||
*/
|
||||
protected abstract Config generateCredentialsConfig(Properties properties);
|
||||
|
||||
@Override
|
||||
public ExtensionRamContext getCredentialsForNacosClient() {
|
||||
ExtensionRamContext ramContext = new ExtensionRamContext();
|
||||
if (null != credentialsClient) {
|
||||
CredentialModel credentialModel = credentialsClient.getCredential();
|
||||
ramContext.setAccessKey(credentialModel.getAccessKeyId());
|
||||
ramContext.setSecretKey(credentialModel.getAccessKeySecret());
|
||||
ramContext.setSecurityToken(credentialModel.getSecurityToken());
|
||||
}
|
||||
ramContext.setExtensionSignatureRegionId(signatureRegionId);
|
||||
return ramContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws NacosException {
|
||||
if (null != credentialsClient) {
|
||||
doCloseCredentialsClient();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Aliyun Credentials Client don't include close or shutdown method, but it might be with session or Thread Pool.
|
||||
*
|
||||
* <p>
|
||||
* For invalid leak of connection and thread pool, template use reflect to close inner resource.
|
||||
* If new version provide close method, will use it.
|
||||
* </p>
|
||||
*/
|
||||
private void doCloseCredentialsClient() {
|
||||
try {
|
||||
Field field = credentialsClient.getClass().getDeclaredField("credentialsProvider");
|
||||
field.setAccessible(true);
|
||||
Object innerProvider = field.get(credentialsClient);
|
||||
if (innerProvider instanceof AutoCloseable) {
|
||||
((AutoCloseable) innerProvider).close();
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
protected Config injectCommonBasicConfig(Properties properties, Config config) {
|
||||
config.setAccessKeyId(getNacosProperties(properties, ExtensionAuthPropertyKey.ACCESS_KEY_ID));
|
||||
config.setAccessKeySecret(getNacosProperties(properties, ExtensionAuthPropertyKey.ACCESS_KEY_SECRET));
|
||||
String securityToken = getNacosProperties(properties, ExtensionAuthPropertyKey.SECURITY_TOKEN);
|
||||
if (!StringUtils.isEmpty(securityToken)) {
|
||||
config.setSecurityToken(securityToken);
|
||||
}
|
||||
String policy = getNacosProperties(properties, ExtensionAuthPropertyKey.POLICY);
|
||||
if (!StringUtils.isEmpty(policy)) {
|
||||
config.setPolicy(policy);
|
||||
}
|
||||
String roleSessionExpiration = getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_SESSION_EXPIRATION);
|
||||
if (!StringUtils.isEmpty(roleSessionExpiration)) {
|
||||
config.setRoleSessionExpiration(Integer.parseInt(roleSessionExpiration));
|
||||
}
|
||||
return config;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
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.utils.StringUtils;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
|
||||
import com.alibaba.nacos.common.utils.JacksonUtils;
|
||||
import com.aliyuncs.kms.secretsmanager.client.SecretCacheClient;
|
||||
import com.aliyuncs.kms.secretsmanager.client.SecretCacheClientBuilder;
|
||||
import com.aliyuncs.kms.secretsmanager.client.exception.CacheSecretException;
|
||||
import com.aliyuncs.kms.secretsmanager.client.model.SecretInfo;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Aliyun credentials provider for auto rotate access key by KMS.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public class AutoRotateCredentialsProvider implements ExtensionCredentialsProvider {
|
||||
|
||||
private SecretCacheClient client;
|
||||
|
||||
private String secretName;
|
||||
|
||||
private String signatureRegionId;
|
||||
|
||||
@Override
|
||||
public boolean matchProvider(Properties properties) {
|
||||
return !StringUtils.isEmpty(getNacosProperties(properties, ExtensionAuthPropertyKey.SECRET_NAME));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Properties properties) {
|
||||
secretName = getNacosProperties(properties, ExtensionAuthPropertyKey.SECRET_NAME);
|
||||
signatureRegionId = getSignatureRegionId(properties);
|
||||
buildSecretClient();
|
||||
}
|
||||
|
||||
private synchronized void buildSecretClient() {
|
||||
try {
|
||||
if (null == client) {
|
||||
client = SecretCacheClientBuilder.newClient();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new NacosRuntimeException(23000, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionRamContext getCredentialsForNacosClient() {
|
||||
ExtensionRamContext result = new ExtensionRamContext();
|
||||
result.setEphemeralAccessKeyId(false);
|
||||
if (null == client) {
|
||||
return result;
|
||||
}
|
||||
try {
|
||||
SecretInfo secretInfo = client.getSecretInfo(secretName);
|
||||
JsonNode jsonNode = JacksonUtils.toObj(secretInfo.getSecretValue());
|
||||
result.setAccessKey(jsonNode.get("AccessKeyId").asText());
|
||||
result.setSecretKey(jsonNode.get("AccessKeySecret").asText());
|
||||
result.setExtensionSignatureRegionId(signatureRegionId);
|
||||
} catch (CacheSecretException e) {
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws NacosException {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.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;
|
||||
|
||||
/**
|
||||
* Aliyun credentials provider for custom credential uri type.
|
||||
*
|
||||
* <p>
|
||||
* This credentials provider is supported to get credential from custom credential uri.
|
||||
* The custom uri should be in the following format: <a href="http://local_or_remote_uri/">http://local_or_remote_uri/</a>
|
||||
* And the custom uri should response with code {@code 200} and with body json format:
|
||||
* <code>
|
||||
* {
|
||||
* "Code": "Success",
|
||||
* "AccessKeySecret": "AccessKeySecret",
|
||||
* "AccessKeyId": "AccessKeyId",
|
||||
* "Expiration": "2021-09-26T03:46:38Z",
|
||||
* "SecurityToken": "SecurityToken"
|
||||
* }
|
||||
* </code>
|
||||
* </p>
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public class CredentialsUriCredentialsProvider extends AbstractCredentialClientProvider {
|
||||
|
||||
@Override
|
||||
public boolean matchProvider(Properties properties) {
|
||||
String credentialsURI = getNacosProperties(properties, ExtensionAuthPropertyKey.CREDENTIALS_URI);
|
||||
return !StringUtils.isEmpty(credentialsURI);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Config generateCredentialsConfig(Properties properties) {
|
||||
Config config = new Config();
|
||||
config.setType("credentials_uri");
|
||||
config.setCredentialsUri(getNacosProperties(properties, ExtensionAuthPropertyKey.CREDENTIALS_URI));
|
||||
return config;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.provider;
|
||||
|
||||
import com.alibaba.nacos.api.utils.StringUtils;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
|
||||
import com.alibaba.nacos.common.lifecycle.Closeable;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Aliyun CredentialsProvider for nacos extension plugin.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public interface ExtensionCredentialsProvider extends Closeable {
|
||||
|
||||
/**
|
||||
* The properties are generated by {@link com.alibaba.nacos.client.env.NacosClientProperties}, which loaded from init Properties, JVM and Env.
|
||||
*
|
||||
* @param properties nacos client properties
|
||||
* @return {@code true} if match and will use this Credentials Provider, {@code false} otherwise.
|
||||
*/
|
||||
boolean matchProvider(Properties properties);
|
||||
|
||||
/**
|
||||
* Initialize the provider.
|
||||
*
|
||||
* @param properties nacos client properties which same as parameters {@link #matchProvider(Properties)}
|
||||
*/
|
||||
void init(Properties properties);
|
||||
|
||||
/**
|
||||
* Get Aliyun Credentials and transform to Nacos Credentials.
|
||||
*
|
||||
* @return Nacos Credentials as {@link ExtensionRamContext}
|
||||
*/
|
||||
ExtensionRamContext getCredentialsForNacosClient();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
default String getSignatureRegionId(Properties properties) {
|
||||
return getNacosProperties(properties, ExtensionAuthPropertyKey.SIGNATURE_REGION_ID);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.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;
|
||||
|
||||
/**
|
||||
* Aliyun CredentialsProvider for OIDC role arn type which most of Kubernetes situation.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public class OidcRoleArnCredentialsProvider extends AbstractCredentialClientProvider {
|
||||
|
||||
@Override
|
||||
public boolean matchProvider(Properties properties) {
|
||||
String arnRole = getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_ARN);
|
||||
String roleSessionName = getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_SESSION_NAME);
|
||||
String oidcProviderArn = getNacosProperties(properties, ExtensionAuthPropertyKey.OIDC_PROVIDER_ARN);
|
||||
return !StringUtils.isEmpty(arnRole) && !StringUtils.isEmpty(roleSessionName) && !StringUtils.isEmpty(
|
||||
oidcProviderArn);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Config generateCredentialsConfig(Properties properties) {
|
||||
Config config = new Config();
|
||||
config.setType("oidc_role_arn");
|
||||
config.setRoleArn(getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_ARN));
|
||||
config.setRoleSessionName(getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_SESSION_NAME));
|
||||
config.setOidcProviderArn(getNacosProperties(properties, ExtensionAuthPropertyKey.OIDC_PROVIDER_ARN));
|
||||
config.setOidcTokenFilePath(getNacosProperties(properties, ExtensionAuthPropertyKey.OIDC_TOKEN_FILE_PATH));
|
||||
return injectCommonBasicConfig(properties, config);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.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;
|
||||
|
||||
/**
|
||||
* Aliyun CredentialsProvider for ram role arn type which most of assume role situation.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public class RamRoleArnCredentialsProvider extends AbstractCredentialClientProvider {
|
||||
|
||||
@Override
|
||||
public boolean matchProvider(Properties properties) {
|
||||
String arnRole = getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_ARN);
|
||||
String roleSessionName = getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_SESSION_NAME);
|
||||
String oidcProviderArn = getNacosProperties(properties, ExtensionAuthPropertyKey.OIDC_PROVIDER_ARN);
|
||||
return !StringUtils.isEmpty(arnRole) && !StringUtils.isEmpty(roleSessionName) && StringUtils.isEmpty(
|
||||
oidcProviderArn);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Config generateCredentialsConfig(Properties properties) {
|
||||
Config config = new Config();
|
||||
config.setType("ram_role_arn");
|
||||
config.setRoleArn(getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_ARN));
|
||||
config.setRoleSessionName(getNacosProperties(properties, ExtensionAuthPropertyKey.ROLE_SESSION_NAME));
|
||||
return injectCommonBasicConfig(properties, config);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.provider;
|
||||
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
|
||||
import com.alibaba.nacos.common.utils.StringUtils;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Aliyun CredentialsProvider for Sts token type which for some untrusted environment template used.
|
||||
*
|
||||
* @author xiweng.yy
|
||||
*/
|
||||
public class StsTokenCredentialsProvider implements ExtensionCredentialsProvider {
|
||||
|
||||
private String accessKey;
|
||||
|
||||
private String secretKey;
|
||||
|
||||
private String securityToken;
|
||||
|
||||
private String signatureRegionId;
|
||||
|
||||
@Override
|
||||
public boolean matchProvider(Properties properties) {
|
||||
String accessKey = getNacosProperties(properties, ExtensionAuthPropertyKey.ACCESS_KEY_ID);
|
||||
String secretKey = getNacosProperties(properties, ExtensionAuthPropertyKey.ACCESS_KEY_SECRET);
|
||||
String securityToken = getNacosProperties(properties, ExtensionAuthPropertyKey.SECURITY_TOKEN);
|
||||
return StringUtils.isNotBlank(accessKey) && StringUtils.isNotBlank(secretKey) && StringUtils.isNotBlank(
|
||||
securityToken);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Properties properties) {
|
||||
accessKey = getNacosProperties(properties, ExtensionAuthPropertyKey.ACCESS_KEY_ID);
|
||||
secretKey = getNacosProperties(properties, ExtensionAuthPropertyKey.ACCESS_KEY_SECRET);
|
||||
securityToken = getNacosProperties(properties, ExtensionAuthPropertyKey.SECURITY_TOKEN);
|
||||
signatureRegionId = getSignatureRegionId(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionRamContext getCredentialsForNacosClient() {
|
||||
ExtensionRamContext extensionRamContext = new ExtensionRamContext();
|
||||
extensionRamContext.setAccessKey(accessKey);
|
||||
extensionRamContext.setSecretKey(secretKey);
|
||||
extensionRamContext.setSecurityToken(securityToken);
|
||||
extensionRamContext.setExtensionSignatureRegionId(signatureRegionId);
|
||||
return extensionRamContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws NacosException {
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
#
|
||||
# Copyright 1999-2024 Alibaba Group Holding Ltd.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
#
|
||||
|
||||
com.alibaba.nacos.client.aliyun.auth.AliyunExtensionClientAuthServiceImpl
|
|
@ -1,352 +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.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;
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth;
|
||||
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.client.aliyun.auth.provider.ExtensionCredentialsProvider;
|
||||
import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext;
|
||||
import com.alibaba.nacos.plugin.auth.api.RequestResource;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class AliyunExtensionClientAuthServiceImplTest {
|
||||
|
||||
AliyunExtensionClientAuthServiceImpl clientAuthService;
|
||||
|
||||
RequestResource resource;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
clientAuthService = new AliyunExtensionClientAuthServiceImpl();
|
||||
resource = RequestResource.configBuilder().build();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() throws NacosException {
|
||||
clientAuthService.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
void loginNoMatch() {
|
||||
assertFalse(clientAuthService.login(new Properties()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void loginWithException() {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(ExtensionAuthPropertyKey.SECRET_NAME.getKey(), "secret");
|
||||
assertFalse(clientAuthService.login(properties));
|
||||
}
|
||||
|
||||
@Test
|
||||
void loginSuccess() {
|
||||
Properties properties = new Properties();
|
||||
properties.setProperty(ExtensionAuthPropertyKey.SECURITY_TOKEN.getKey(), "securityToken");
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ACCESS_KEY_ID.getKey(), "accessKeyId");
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ACCESS_KEY_SECRET.getKey(), "accessKeySecret");
|
||||
assertTrue(clientAuthService.login(properties));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLoginIdentityContextForStsToken() throws NoSuchFieldException, IllegalAccessException {
|
||||
injectMockProvider(true, true);
|
||||
LoginIdentityContext context = clientAuthService.getLoginIdentityContext(resource);
|
||||
assertEquals("accessKey", context.getParameter("Spas-AccessKey"));
|
||||
assertEquals("securityToken", context.getParameter(ExtensionAuthConstants.SECURITY_TOKEN_HEADER));
|
||||
assertNotNull(context.getParameter("Spas-Signature"));
|
||||
assertNotNull(context.getParameter("Timestamp"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLoginIdentityContextForAkSk() throws NoSuchFieldException, IllegalAccessException {
|
||||
injectMockProvider(false, true);
|
||||
LoginIdentityContext context = clientAuthService.getLoginIdentityContext(resource);
|
||||
assertEquals("accessKey", context.getParameter("Spas-AccessKey"));
|
||||
assertNull(context.getParameter(ExtensionAuthConstants.SECURITY_TOKEN_HEADER));
|
||||
assertNotNull(context.getParameter("Spas-Signature"));
|
||||
assertNotNull(context.getParameter("Timestamp"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLoginIdentityContextForStsTokenInvalid() throws NoSuchFieldException, IllegalAccessException {
|
||||
injectMockProvider(true, false);
|
||||
LoginIdentityContext context = clientAuthService.getLoginIdentityContext(resource);
|
||||
assertNull(context.getParameter("Spas-AccessKey"));
|
||||
assertNull(context.getParameter(ExtensionAuthConstants.SECURITY_TOKEN_HEADER));
|
||||
assertNull(context.getParameter("Spas-Signature"));
|
||||
assertNull(context.getParameter("Timestamp"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLoginIdentityContextForAkSkInvalid() throws NoSuchFieldException, IllegalAccessException {
|
||||
injectMockProvider(false, false);
|
||||
LoginIdentityContext context = clientAuthService.getLoginIdentityContext(resource);
|
||||
assertNull(context.getParameter("Spas-AccessKey"));
|
||||
assertNull(context.getParameter(ExtensionAuthConstants.SECURITY_TOKEN_HEADER));
|
||||
assertNull(context.getParameter("Spas-Signature"));
|
||||
assertNull(context.getParameter("Timestamp"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLoginIdentityContextForNoInjector() throws NoSuchFieldException, IllegalAccessException {
|
||||
injectMockProvider(true, true);
|
||||
resource.setType("Mock");
|
||||
LoginIdentityContext context = clientAuthService.getLoginIdentityContext(resource);
|
||||
assertNull(context.getParameter("Spas-AccessKey"));
|
||||
assertNull(context.getParameter(ExtensionAuthConstants.SECURITY_TOKEN_HEADER));
|
||||
assertNull(context.getParameter("Spas-Signature"));
|
||||
assertNull(context.getParameter("Timestamp"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getLoginIdentityContextWithoutInit() {
|
||||
LoginIdentityContext context = clientAuthService.getLoginIdentityContext(resource);
|
||||
assertNull(context.getParameter("Spas-AccessKey"));
|
||||
assertNull(context.getParameter(ExtensionAuthConstants.SECURITY_TOKEN_HEADER));
|
||||
assertNull(context.getParameter("Spas-Signature"));
|
||||
assertNull(context.getParameter("Timestamp"));
|
||||
}
|
||||
|
||||
private void injectMockProvider(boolean ephemeralAccessKeyId, boolean validate)
|
||||
throws NoSuchFieldException, IllegalAccessException {
|
||||
MockCredentialsProvider mockProvider = new MockCredentialsProvider();
|
||||
mockProvider.ephemeralAccessKeyId = ephemeralAccessKeyId;
|
||||
mockProvider.validate = validate;
|
||||
Field matchedProviderField = clientAuthService.getClass().getDeclaredField("matchedProvider");
|
||||
matchedProviderField.setAccessible(true);
|
||||
matchedProviderField.set(clientAuthService, mockProvider);
|
||||
}
|
||||
|
||||
private static class MockCredentialsProvider implements ExtensionCredentialsProvider {
|
||||
|
||||
boolean ephemeralAccessKeyId = true;
|
||||
|
||||
boolean validate;
|
||||
|
||||
@Override
|
||||
public boolean matchProvider(Properties properties) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Properties properties) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtensionRamContext getCredentialsForNacosClient() {
|
||||
ExtensionRamContext ramContext = new ExtensionRamContext();
|
||||
ramContext.setEphemeralAccessKeyId(ephemeralAccessKeyId);
|
||||
if (validate) {
|
||||
ramContext.setSecretKey("secretKey");
|
||||
ramContext.setAccessKey("accessKey");
|
||||
ramContext.setSecurityToken(ephemeralAccessKeyId ? "securityToken" : "");
|
||||
} else {
|
||||
ramContext.setSecurityToken(ephemeralAccessKeyId ? "" : "securityToken");
|
||||
}
|
||||
return ramContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() throws NacosException {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.injector;
|
||||
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthConstants;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
|
||||
import com.alibaba.nacos.plugin.auth.api.LoginIdentityContext;
|
||||
import com.alibaba.nacos.plugin.auth.api.RequestResource;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
class AbstractExtensionResourceInjectorTest {
|
||||
|
||||
AbstractExtensionResourceInjector resourceInjector;
|
||||
|
||||
ExtensionRamContext ramContext;
|
||||
|
||||
RequestResource resource;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
resourceInjector = new MockExtensionResourceInjector();
|
||||
ramContext = new ExtensionRamContext();
|
||||
ramContext.setSecretKey("secret");
|
||||
ramContext.setEphemeralAccessKeyId(false);
|
||||
resource = new RequestResource();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void doInjectForV4WithoutRegionId() {
|
||||
LoginIdentityContext result = new LoginIdentityContext();
|
||||
resourceInjector.doInject(resource, ramContext, result);
|
||||
assertEquals("secret", result.getParameter("sk"));
|
||||
assertNull(result.getParameter(ExtensionAuthConstants.SECURITY_TOKEN_HEADER));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doInjectForV4WithRegionId() {
|
||||
ramContext.setExtensionSignatureRegionId("cn-hangzhou");
|
||||
LoginIdentityContext result = new LoginIdentityContext();
|
||||
resourceInjector.doInject(resource, ramContext, result);
|
||||
assertNotEquals("secret", result.getParameter("sk"));
|
||||
assertNull(result.getParameter(ExtensionAuthConstants.SECURITY_TOKEN_HEADER));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doInjectForV4WithRegionIdAndStsToken() {
|
||||
ramContext.setExtensionSignatureRegionId("cn-hangzhou");
|
||||
ramContext.setSecurityToken("token");
|
||||
ramContext.setEphemeralAccessKeyId(true);
|
||||
LoginIdentityContext result = new LoginIdentityContext();
|
||||
resourceInjector.doInject(resource, ramContext, result);
|
||||
assertNotEquals("secret", result.getParameter("sk"));
|
||||
assertEquals("token", result.getParameter(ExtensionAuthConstants.SECURITY_TOKEN_HEADER));
|
||||
}
|
||||
|
||||
@Test
|
||||
void doInjectForV1WithRegionId() throws NoSuchFieldException, IllegalAccessException {
|
||||
Field supportV4signatureField = resourceInjector.getClass().getSuperclass()
|
||||
.getDeclaredField("supportV4signature");
|
||||
supportV4signatureField.setAccessible(true);
|
||||
supportV4signatureField.set(resourceInjector, false);
|
||||
ramContext.setExtensionSignatureRegionId("cn-hangzhou");
|
||||
LoginIdentityContext result = new LoginIdentityContext();
|
||||
resourceInjector.doInject(resource, ramContext, result);
|
||||
assertEquals("secret", result.getParameter("sk"));
|
||||
assertNull(result.getParameter(ExtensionAuthConstants.SECURITY_TOKEN_HEADER));
|
||||
}
|
||||
|
||||
private static class MockExtensionResourceInjector extends AbstractExtensionResourceInjector {
|
||||
|
||||
@Override
|
||||
protected String getAccessKeyHeaderKey() {
|
||||
return "Mock";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<String, String> calculateSignature(RequestResource resource, String actualSecretKey,
|
||||
ExtensionRamContext ramContext) {
|
||||
return Collections.singletonMap("sk", actualSecretKey);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.injector;
|
||||
|
||||
import com.alibaba.nacos.client.auth.ram.utils.SpasAdapter;
|
||||
import com.alibaba.nacos.plugin.auth.api.RequestResource;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ConfigExtensionResourceInjectorTest {
|
||||
|
||||
ConfigExtensionResourceInjector resourceInjector;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
resourceInjector = new ConfigExtensionResourceInjector();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAccessKeyHeaderKey() {
|
||||
assertEquals("Spas-AccessKey", resourceInjector.getAccessKeyHeaderKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculateSignatureWithTenant() {
|
||||
RequestResource resource = RequestResource.configBuilder().setNamespace("TestNamespace").build();
|
||||
Map<String, String> result = resourceInjector.calculateSignature(resource, "secret", null);
|
||||
assertEquals(2, result.size());
|
||||
assertTrue(result.containsKey("Timestamp"));
|
||||
String expected = SpasAdapter.signWithHmacSha1Encrypt("TestNamespace+" + result.get("Timestamp"), "secret");
|
||||
assertEquals(expected, result.get("Spas-Signature"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculateSignatureWithGroup() {
|
||||
RequestResource resource = RequestResource.namingBuilder().setGroup("TestGroup").build();
|
||||
Map<String, String> result = resourceInjector.calculateSignature(resource, "secret", null);
|
||||
assertEquals(2, result.size());
|
||||
assertTrue(result.containsKey("Timestamp"));
|
||||
String expected = SpasAdapter.signWithHmacSha1Encrypt("TestGroup+" + result.get("Timestamp"), "secret");
|
||||
assertEquals(expected, result.get("Spas-Signature"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculateSignatureWithAll() {
|
||||
RequestResource resource = RequestResource.namingBuilder().setGroup("TestGroup").setNamespace("TestNamespace")
|
||||
.build();
|
||||
Map<String, String> result = resourceInjector.calculateSignature(resource, "secret", null);
|
||||
assertEquals(2, result.size());
|
||||
assertTrue(result.containsKey("Timestamp"));
|
||||
String expected = SpasAdapter.signWithHmacSha1Encrypt("TestNamespace+TestGroup+" + result.get("Timestamp"),
|
||||
"secret");
|
||||
assertEquals(expected, result.get("Spas-Signature"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculateSignatureWithEmptyResource() {
|
||||
RequestResource resource = RequestResource.namingBuilder().setResource("").build();
|
||||
Map<String, String> result = resourceInjector.calculateSignature(resource, "secret", null);
|
||||
assertEquals(2, result.size());
|
||||
assertTrue(result.containsKey("Timestamp"));
|
||||
String expected = SpasAdapter.signWithHmacSha1Encrypt(result.get("Timestamp"), "secret");
|
||||
assertEquals(expected, result.get("Spas-Signature"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculateSignatureWithException() {
|
||||
// Will Throw NPE.
|
||||
Map<String, String> result = resourceInjector.calculateSignature(null, "secret", null);
|
||||
assertEquals(0, result.size());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.injector;
|
||||
|
||||
import com.alibaba.nacos.plugin.auth.api.RequestResource;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class NamingExtensionResourceInjectorTest {
|
||||
|
||||
NamingExtensionResourceInjector resourceInjector;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
resourceInjector = new NamingExtensionResourceInjector();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
}
|
||||
|
||||
@Test
|
||||
void getAccessKeyHeaderKey() {
|
||||
assertEquals("ak", resourceInjector.getAccessKeyHeaderKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculateSignatureWithoutGroup() {
|
||||
RequestResource resource = RequestResource.namingBuilder().setResource("TestService").build();
|
||||
Map<String, String> result = resourceInjector.calculateSignature(resource, "secret", null);
|
||||
assertEquals(2, result.size());
|
||||
assertTrue(result.get("data").endsWith("TestService"));
|
||||
assertTrue(result.containsKey("signature"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculateSignatureWithGroup() {
|
||||
RequestResource resource = RequestResource.namingBuilder().setGroup("TestGroup").setResource("TestService")
|
||||
.build();
|
||||
Map<String, String> result = resourceInjector.calculateSignature(resource, "secret", null);
|
||||
assertEquals(2, result.size());
|
||||
assertTrue(result.get("data").endsWith("TestGroup@@TestService"));
|
||||
assertTrue(result.containsKey("signature"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculateSignatureWithGroupedService() {
|
||||
RequestResource resource = RequestResource.namingBuilder().setResource("TestGroup@@TestService").build();
|
||||
Map<String, String> result = resourceInjector.calculateSignature(resource, "secret", null);
|
||||
assertEquals(2, result.size());
|
||||
assertTrue(result.get("data").endsWith("TestGroup@@TestService"));
|
||||
assertTrue(result.containsKey("signature"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculateSignatureWithEmptyResource() {
|
||||
RequestResource resource = RequestResource.namingBuilder().setResource("").build();
|
||||
Map<String, String> result = resourceInjector.calculateSignature(resource, "secret", null);
|
||||
assertEquals(2, result.size());
|
||||
assertTrue(result.get("data").matches("^\\d*$"));
|
||||
assertTrue(result.containsKey("signature"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void calculateSignatureWithException() {
|
||||
// Will Throw NPE.
|
||||
RequestResource resource = RequestResource.namingBuilder().build();
|
||||
Map<String, String> result = resourceInjector.calculateSignature(resource, "secret", null);
|
||||
assertEquals(0, result.size());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.provider;
|
||||
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
|
||||
import com.aliyun.credentials.Client;
|
||||
import com.aliyun.credentials.models.CredentialModel;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
abstract class AbstractCredentialClientProviderTest extends AbstractCredentialsProviderTest {
|
||||
|
||||
@Mock
|
||||
Client credentialsClient;
|
||||
|
||||
protected CredentialModel credentialModel;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
super.setUp();
|
||||
credentialModel = CredentialModel.builder().accessKeyId(ACCESS_KEY_ID).accessKeySecret(ACCESS_KEY_SECRET)
|
||||
.securityToken(SECURITY_TOKEN).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCredentialsForNacosClient() throws NoSuchFieldException, IllegalAccessException {
|
||||
injectMockCredentialsClient();
|
||||
initWithProperties();
|
||||
mockCredentialsClientReturn(credentialModel);
|
||||
ExtensionRamContext context = getCredentialsProvider().getCredentialsForNacosClient();
|
||||
assertEquals(ACCESS_KEY_ID, context.getAccessKey());
|
||||
assertEquals(ACCESS_KEY_SECRET, context.getSecretKey());
|
||||
assertEquals(SECURITY_TOKEN, context.getSecurityToken());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCredentialsForNacosClientByEnv() throws NoSuchFieldException, IllegalAccessException {
|
||||
injectMockCredentialsClient();
|
||||
initWithEnvProperties();
|
||||
mockCredentialsClientReturn(credentialModel);
|
||||
ExtensionRamContext context = getCredentialsProvider().getCredentialsForNacosClient();
|
||||
assertEquals(ACCESS_KEY_ID, context.getAccessKey());
|
||||
assertEquals(ACCESS_KEY_SECRET, context.getSecretKey());
|
||||
assertEquals(SECURITY_TOKEN, context.getSecurityToken());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCredentialsForNacosClientWithoutInit() {
|
||||
ExtensionRamContext context = getCredentialsProvider().getCredentialsForNacosClient();
|
||||
assertNull(context.getAccessKey());
|
||||
assertNull(context.getSecretKey());
|
||||
assertNull(context.getSecurityToken());
|
||||
}
|
||||
|
||||
protected void injectMockCredentialsClient() throws NoSuchFieldException, IllegalAccessException {
|
||||
Field clientField = credentialsProvider.getClass().getSuperclass().getDeclaredField("credentialsClient");
|
||||
clientField.setAccessible(true);
|
||||
clientField.set(credentialsProvider, credentialsClient);
|
||||
}
|
||||
|
||||
protected void mockCredentialsClientReturn(CredentialModel credential)
|
||||
throws NoSuchFieldException, IllegalAccessException {
|
||||
when(credentialsClient.getCredential()).thenReturn(credential);
|
||||
}
|
||||
|
||||
protected AbstractCredentialClientProvider getCredentialsProvider() {
|
||||
return (AbstractCredentialClientProvider) credentialsProvider;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.provider;
|
||||
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public abstract class AbstractCredentialsProviderTest {
|
||||
|
||||
protected static final String ACCESS_KEY_ID = "accessKeyId";
|
||||
|
||||
protected static final String ACCESS_KEY_SECRET = "accessKeySecret";
|
||||
|
||||
protected static final String SECURITY_TOKEN = "securityToken";
|
||||
|
||||
protected static final String SIGNATURE_REGION_ID = "signatureRegionId";
|
||||
|
||||
ExtensionCredentialsProvider credentialsProvider;
|
||||
|
||||
Properties properties;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
credentialsProvider = buildCredentialsProvider();
|
||||
properties = new Properties();
|
||||
}
|
||||
|
||||
protected abstract ExtensionCredentialsProvider buildCredentialsProvider();
|
||||
|
||||
@AfterEach
|
||||
void tearDown() throws NacosException {
|
||||
credentialsProvider.shutdown();
|
||||
}
|
||||
|
||||
@Test
|
||||
void matchProvider() {
|
||||
assertFalse(credentialsProvider.matchProvider(properties));
|
||||
injectProperties(properties);
|
||||
assertTrue(credentialsProvider.matchProvider(properties));
|
||||
}
|
||||
|
||||
protected void initWithProperties() {
|
||||
injectProperties(properties);
|
||||
credentialsProvider.init(properties);
|
||||
}
|
||||
|
||||
protected void initWithEnvProperties() {
|
||||
injectEnvProperties(properties);
|
||||
credentialsProvider.init(properties);
|
||||
}
|
||||
|
||||
protected abstract void injectProperties(Properties properties);
|
||||
|
||||
protected abstract void injectEnvProperties(Properties properties);
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.provider;
|
||||
|
||||
import com.alibaba.nacos.api.exception.runtime.NacosRuntimeException;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
|
||||
import com.aliyuncs.kms.secretsmanager.client.SecretCacheClient;
|
||||
import com.aliyuncs.kms.secretsmanager.client.exception.CacheSecretException;
|
||||
import com.aliyuncs.kms.secretsmanager.client.model.SecretInfo;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class AutoRotateCredentialsProviderTest extends AbstractCredentialsProviderTest {
|
||||
|
||||
@Mock
|
||||
private SecretCacheClient client;
|
||||
|
||||
@Override
|
||||
protected ExtensionCredentialsProvider buildCredentialsProvider() {
|
||||
return new AutoRotateCredentialsProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void injectProperties(Properties properties) {
|
||||
properties.setProperty(ExtensionAuthPropertyKey.SECRET_NAME.getKey(), "secretName");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void injectEnvProperties(Properties properties) {
|
||||
properties.setProperty(ExtensionAuthPropertyKey.SECRET_NAME.getEnvKey(), "secretName");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInitWithException() {
|
||||
assertThrows(NacosRuntimeException.class, this::initWithProperties);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCredentialsForNacosClient() throws NoSuchFieldException, IllegalAccessException, CacheSecretException {
|
||||
setClient();
|
||||
initWithProperties();
|
||||
ExtensionRamContext result = credentialsProvider.getCredentialsForNacosClient();
|
||||
assertEquals("accessKeyId", result.getAccessKey());
|
||||
assertEquals("accessKeySecret", result.getSecretKey());
|
||||
assertFalse(result.isEphemeralAccessKeyId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCredentialsForNacosClientByEnv() throws NoSuchFieldException, IllegalAccessException, CacheSecretException {
|
||||
setClient();
|
||||
initWithEnvProperties();
|
||||
ExtensionRamContext result = credentialsProvider.getCredentialsForNacosClient();
|
||||
assertEquals("accessKeyId", result.getAccessKey());
|
||||
assertEquals("accessKeySecret", result.getSecretKey());
|
||||
assertFalse(result.isEphemeralAccessKeyId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCredentialsForNacosClientWithException()
|
||||
throws NoSuchFieldException, IllegalAccessException, CacheSecretException {
|
||||
setClient();
|
||||
when(client.getSecretInfo("secretName")).thenThrow(new CacheSecretException());
|
||||
initWithEnvProperties();
|
||||
ExtensionRamContext result = credentialsProvider.getCredentialsForNacosClient();
|
||||
assertNull(result.getAccessKey());
|
||||
assertNull(result.getSecretKey());
|
||||
assertFalse(result.isEphemeralAccessKeyId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCredentialsForNacosClientWithoutInit() {
|
||||
ExtensionRamContext result = credentialsProvider.getCredentialsForNacosClient();
|
||||
assertNull(result.getAccessKey());
|
||||
assertNull(result.getSecretKey());
|
||||
assertFalse(result.isEphemeralAccessKeyId());
|
||||
}
|
||||
|
||||
private void setClient() throws NoSuchFieldException, IllegalAccessException, CacheSecretException {
|
||||
Field clientField = credentialsProvider.getClass().getDeclaredField("client");
|
||||
clientField.setAccessible(true);
|
||||
clientField.set(credentialsProvider, client);
|
||||
SecretInfo secretInfo = new SecretInfo();
|
||||
secretInfo.setSecretValue("{\"AccessKeyId\":\"accessKeyId\",\"AccessKeySecret\":\"accessKeySecret\"}");
|
||||
when(client.getSecretInfo("secretName")).thenReturn(secretInfo);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.provider;
|
||||
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
|
||||
import com.aliyun.credentials.models.Config;
|
||||
import com.aliyun.credentials.models.CredentialModel;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class CredentialsUriCredentialsProviderTest extends AbstractCredentialClientProviderTest {
|
||||
|
||||
@Override
|
||||
protected ExtensionCredentialsProvider buildCredentialsProvider() {
|
||||
return new CredentialsUriCredentialsProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void injectProperties(Properties properties) {
|
||||
properties.setProperty(ExtensionAuthPropertyKey.CREDENTIALS_URI.getKey(), "http://localhost");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void injectEnvProperties(Properties properties) {
|
||||
properties.setProperty(ExtensionAuthPropertyKey.CREDENTIALS_URI.getEnvKey(), "http://envhost");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCredentialsConfig() {
|
||||
initWithProperties();
|
||||
Config config = getCredentialsProvider().generateCredentialsConfig(properties);
|
||||
assertEquals("credentials_uri", config.getType());
|
||||
assertEquals("http://localhost", config.getCredentialsUri());
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCredentialsConfigByEnv() {
|
||||
initWithEnvProperties();
|
||||
Config config = getCredentialsProvider().generateCredentialsConfig(properties);
|
||||
assertEquals("credentials_uri", config.getType());
|
||||
assertEquals("http://envhost", config.getCredentialsUri());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.provider;
|
||||
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
|
||||
import com.aliyun.credentials.models.Config;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
class OidcRoleArnCredentialsProviderTest extends AbstractCredentialClientProviderTest {
|
||||
|
||||
@Override
|
||||
protected ExtensionCredentialsProvider buildCredentialsProvider() {
|
||||
return new OidcRoleArnCredentialsProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void injectProperties(Properties properties) {
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ROLE_ARN.getKey(), "role_arn");
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ROLE_SESSION_NAME.getKey(), "ram_role_arn_test");
|
||||
properties.setProperty(ExtensionAuthPropertyKey.OIDC_PROVIDER_ARN.getKey(), "oidc_provider_arn");
|
||||
properties.setProperty(ExtensionAuthPropertyKey.OIDC_TOKEN_FILE_PATH.getKey(), "oidc_token_file");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void injectEnvProperties(Properties properties) {
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ROLE_ARN.getEnvKey(), "role_arn");
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ROLE_SESSION_NAME.getEnvKey(), "ram_role_arn_test");
|
||||
properties.setProperty(ExtensionAuthPropertyKey.OIDC_PROVIDER_ARN.getEnvKey(), "oidc_provider_arn");
|
||||
properties.setProperty(ExtensionAuthPropertyKey.OIDC_TOKEN_FILE_PATH.getEnvKey(), "oidc_token_file");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCredentialsConfig() {
|
||||
initWithProperties();
|
||||
Config config = getCredentialsProvider().generateCredentialsConfig(properties);
|
||||
assertEquals("oidc_role_arn", config.getType());
|
||||
assertEquals("role_arn", config.getRoleArn());
|
||||
assertEquals("ram_role_arn_test", config.getRoleSessionName());
|
||||
assertEquals("oidc_provider_arn", config.getOidcProviderArn());
|
||||
assertEquals("oidc_token_file", config.getOidcTokenFilePath());
|
||||
assertNull(config.getPolicy());
|
||||
assertEquals(3600, config.getRoleSessionExpiration());
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCredentialsConfigByEnv() {
|
||||
initWithEnvProperties();
|
||||
Config config = getCredentialsProvider().generateCredentialsConfig(properties);
|
||||
assertEquals("oidc_role_arn", config.getType());
|
||||
assertEquals("role_arn", config.getRoleArn());
|
||||
assertEquals("ram_role_arn_test", config.getRoleSessionName());
|
||||
assertEquals("oidc_provider_arn", config.getOidcProviderArn());
|
||||
assertEquals("oidc_token_file", config.getOidcTokenFilePath());
|
||||
assertNull(config.getPolicy());
|
||||
assertEquals(3600, config.getRoleSessionExpiration());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.provider;
|
||||
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
|
||||
import com.aliyun.credentials.models.Config;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
class RamRoleArnCredentialsProviderTest extends AbstractCredentialClientProviderTest {
|
||||
|
||||
@Override
|
||||
protected ExtensionCredentialsProvider buildCredentialsProvider() {
|
||||
return new RamRoleArnCredentialsProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void injectProperties(Properties properties) {
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ACCESS_KEY_ID.getKey(), ACCESS_KEY_ID);
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ACCESS_KEY_SECRET.getKey(), ACCESS_KEY_SECRET);
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ROLE_ARN.getKey(), "role_arn");
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ROLE_SESSION_NAME.getKey(), "ram_role_arn_test");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void injectEnvProperties(Properties properties) {
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ACCESS_KEY_ID.getEnvKey(), ACCESS_KEY_ID);
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ACCESS_KEY_SECRET.getEnvKey(), ACCESS_KEY_SECRET);
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ROLE_ARN.getEnvKey(), "role_arn");
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ROLE_SESSION_NAME.getEnvKey(), "ram_role_arn_test");
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCredentialsConfig() {
|
||||
initWithProperties();
|
||||
Config config = getCredentialsProvider().generateCredentialsConfig(properties);
|
||||
assertEquals("ram_role_arn", config.getType());
|
||||
assertEquals(ACCESS_KEY_ID, config.getAccessKeyId());
|
||||
assertEquals(ACCESS_KEY_SECRET, config.getAccessKeySecret());
|
||||
assertNull(config.getSecurityToken());
|
||||
assertEquals("role_arn", config.getRoleArn());
|
||||
assertEquals("ram_role_arn_test", config.getRoleSessionName());
|
||||
assertNull(config.getPolicy());
|
||||
assertEquals(3600, config.getRoleSessionExpiration());
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCredentialsConfigFull() {
|
||||
initWithProperties();
|
||||
properties.setProperty(ExtensionAuthPropertyKey.SECURITY_TOKEN.getKey(), SECURITY_TOKEN);
|
||||
properties.setProperty(ExtensionAuthPropertyKey.POLICY.getKey(), "policy");
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ROLE_SESSION_EXPIRATION.getKey(), "360");
|
||||
Config config = getCredentialsProvider().generateCredentialsConfig(properties);
|
||||
assertEquals("ram_role_arn", config.getType());
|
||||
assertEquals(ACCESS_KEY_ID, config.getAccessKeyId());
|
||||
assertEquals(ACCESS_KEY_SECRET, config.getAccessKeySecret());
|
||||
assertEquals(SECURITY_TOKEN, config.getSecurityToken());
|
||||
assertEquals("role_arn", config.getRoleArn());
|
||||
assertEquals("ram_role_arn_test", config.getRoleSessionName());
|
||||
assertEquals("policy", config.getPolicy());
|
||||
assertEquals(360, config.getRoleSessionExpiration());
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCredentialsConfigByEnv() {
|
||||
initWithEnvProperties();
|
||||
Config config = getCredentialsProvider().generateCredentialsConfig(properties);
|
||||
assertEquals("ram_role_arn", config.getType());
|
||||
assertEquals(ACCESS_KEY_ID, config.getAccessKeyId());
|
||||
assertEquals(ACCESS_KEY_SECRET, config.getAccessKeySecret());
|
||||
assertNull(config.getSecurityToken());
|
||||
assertEquals("role_arn", config.getRoleArn());
|
||||
assertEquals("ram_role_arn_test", config.getRoleSessionName());
|
||||
assertNull(config.getPolicy());
|
||||
assertEquals(3600, config.getRoleSessionExpiration());
|
||||
}
|
||||
|
||||
@Test
|
||||
void generateCredentialsConfigByEnvFull() {
|
||||
initWithProperties();
|
||||
properties.setProperty(ExtensionAuthPropertyKey.SECURITY_TOKEN.getEnvKey(), SECURITY_TOKEN);
|
||||
properties.setProperty(ExtensionAuthPropertyKey.POLICY.getEnvKey(), "policy");
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ROLE_SESSION_EXPIRATION.getEnvKey(), "360");
|
||||
Config config = getCredentialsProvider().generateCredentialsConfig(properties);
|
||||
assertEquals("ram_role_arn", config.getType());
|
||||
assertEquals(ACCESS_KEY_ID, config.getAccessKeyId());
|
||||
assertEquals(ACCESS_KEY_SECRET, config.getAccessKeySecret());
|
||||
assertEquals(SECURITY_TOKEN, config.getSecurityToken());
|
||||
assertEquals("role_arn", config.getRoleArn());
|
||||
assertEquals("ram_role_arn_test", config.getRoleSessionName());
|
||||
assertEquals("policy", config.getPolicy());
|
||||
assertEquals(360, config.getRoleSessionExpiration());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
package com.alibaba.nacos.client.aliyun.auth.provider;
|
||||
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionAuthPropertyKey;
|
||||
import com.alibaba.nacos.client.aliyun.auth.ExtensionRamContext;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
class StsTokenCredentialsProviderTest extends AbstractCredentialsProviderTest {
|
||||
|
||||
@Override
|
||||
protected ExtensionCredentialsProvider buildCredentialsProvider() {
|
||||
return new StsTokenCredentialsProvider();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void injectProperties(Properties properties) {
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ACCESS_KEY_ID.getKey(), ACCESS_KEY_ID);
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ACCESS_KEY_SECRET.getKey(), ACCESS_KEY_SECRET);
|
||||
properties.setProperty(ExtensionAuthPropertyKey.SECURITY_TOKEN.getKey(), SECURITY_TOKEN);
|
||||
properties.setProperty(ExtensionAuthPropertyKey.SIGNATURE_REGION_ID.getKey(), SIGNATURE_REGION_ID);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void injectEnvProperties(Properties properties) {
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ACCESS_KEY_ID.getEnvKey(), ACCESS_KEY_ID);
|
||||
properties.setProperty(ExtensionAuthPropertyKey.ACCESS_KEY_SECRET.getEnvKey(), ACCESS_KEY_SECRET);
|
||||
properties.setProperty(ExtensionAuthPropertyKey.SECURITY_TOKEN.getEnvKey(), SECURITY_TOKEN);
|
||||
properties.setProperty(ExtensionAuthPropertyKey.SIGNATURE_REGION_ID.getEnvKey(), SIGNATURE_REGION_ID);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCredentialsForNacosClient() {
|
||||
initWithProperties();
|
||||
ExtensionRamContext context = credentialsProvider.getCredentialsForNacosClient();
|
||||
assertEquals(ACCESS_KEY_ID, context.getAccessKey());
|
||||
assertEquals(ACCESS_KEY_SECRET, context.getSecretKey());
|
||||
assertEquals(SECURITY_TOKEN, context.getSecurityToken());
|
||||
assertEquals(SIGNATURE_REGION_ID, context.getExtensionSignatureRegionId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCredentialsForNacosClientWithEnv() {
|
||||
initWithEnvProperties();
|
||||
ExtensionRamContext context = credentialsProvider.getCredentialsForNacosClient();
|
||||
assertEquals(ACCESS_KEY_ID, context.getAccessKey());
|
||||
assertEquals(ACCESS_KEY_SECRET, context.getSecretKey());
|
||||
assertEquals(SECURITY_TOKEN, context.getSecurityToken());
|
||||
assertEquals(SIGNATURE_REGION_ID, context.getExtensionSignatureRegionId());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getCredentialsForNacosClientWithoutSignatureRegionId() {
|
||||
injectProperties(properties);
|
||||
properties.remove(ExtensionAuthPropertyKey.SIGNATURE_REGION_ID.getKey());
|
||||
credentialsProvider.init(properties);
|
||||
ExtensionRamContext context = credentialsProvider.getCredentialsForNacosClient();
|
||||
assertEquals(ACCESS_KEY_ID, context.getAccessKey());
|
||||
assertEquals(ACCESS_KEY_SECRET, context.getSecretKey());
|
||||
assertEquals(SECURITY_TOKEN, context.getSecurityToken());
|
||||
assertNull(context.getExtensionSignatureRegionId());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue