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.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<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>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<inceptionYear>2018</inceptionYear>
|
<inceptionYear>2018</inceptionYear>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<artifactId>nacos-client-mse-extension</artifactId>
|
<artifactId>nacos-client-mse-extension</artifactId>
|
||||||
<version>1.0.4</version>
|
<version>1.0.6</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<developers>
|
<developers>
|
||||||
|
@ -51,32 +51,32 @@
|
||||||
<name>Aliyun NACOS extension for MSE</name>
|
<name>Aliyun NACOS extension for MSE</name>
|
||||||
<description>Aliyun NACOS extension for MSE</description>
|
<description>Aliyun NACOS extension for MSE</description>
|
||||||
<url>http://nacos.io</url>
|
<url>http://nacos.io</url>
|
||||||
|
|
||||||
<scm>
|
<scm>
|
||||||
<url>git@github.com:nacos-group/nacos-client-mse-extension.git</url>
|
<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>
|
<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>
|
<developerConnection>scm:git@github.com:nacos-group/nacos-client-mse-extension.git</developerConnection>
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<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>
|
<guava.version>30.1-jre</guava.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.aliyun</groupId>
|
<groupId>com.aliyun</groupId>
|
||||||
<artifactId>aliyun-java-sdk-core</artifactId>
|
<artifactId>aliyun-java-sdk-core</artifactId>
|
||||||
<version>4.5.17</version>
|
<version>4.5.17</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.aliyun</groupId>
|
<groupId>com.aliyun</groupId>
|
||||||
<artifactId>aliyun-java-sdk-kms</artifactId>
|
<artifactId>aliyun-java-sdk-kms</artifactId>
|
||||||
<version>2.16.3</version>
|
<version>2.16.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.aliyun.kms</groupId>
|
<groupId>com.aliyun.kms</groupId>
|
||||||
<artifactId>kms-transfer-client</artifactId>
|
<artifactId>kms-transfer-client</artifactId>
|
||||||
|
@ -88,14 +88,32 @@
|
||||||
</exclusions>
|
</exclusions>
|
||||||
<version>0.1.0</version>
|
<version>0.1.0</version>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>com.alibaba.nacos</groupId>
|
||||||
<artifactId>nacos-api</artifactId>
|
<artifactId>nacos-client</artifactId>
|
||||||
<version>${nacos.version}</version>
|
<version>${nacos.version}</version>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<artifactId>commons-codec</artifactId>
|
<artifactId>commons-codec</artifactId>
|
||||||
<groupId>commons-codec</groupId>
|
<groupId>commons-codec</groupId>
|
||||||
|
@ -108,7 +126,7 @@
|
||||||
<version>${guava.version}</version>
|
<version>${guava.version}</version>
|
||||||
<optional>true</optional>
|
<optional>true</optional>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<!-- for test-->
|
<!-- for test-->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
@ -116,15 +134,23 @@
|
||||||
<version>RELEASE</version>
|
<version>RELEASE</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.alibaba.nacos</groupId>
|
<groupId>org.mockito</groupId>
|
||||||
<artifactId>nacos-client</artifactId>
|
<artifactId>mockito-inline</artifactId>
|
||||||
<version>${nacos.version}</version>
|
<version>4.11.0</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-junit-jupiter</artifactId>
|
||||||
|
<version>4.11.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
@ -136,8 +162,8 @@
|
||||||
<target>8</target>
|
<target>8</target>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-source-plugin</artifactId>
|
<artifactId>maven-source-plugin</artifactId>
|
||||||
|
@ -151,8 +177,8 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
@ -168,8 +194,8 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
|
||||||
|
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-gpg-plugin</artifactId>
|
<artifactId>maven-gpg-plugin</artifactId>
|
||||||
|
|
|
@ -1,58 +1,22 @@
|
||||||
package com.alibaba.nacos.client.aliyun;
|
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.AbstractConfigFilter;
|
||||||
import com.alibaba.nacos.api.config.filter.IConfigFilterChain;
|
import com.alibaba.nacos.api.config.filter.IConfigFilterChain;
|
||||||
import com.alibaba.nacos.api.config.filter.IConfigRequest;
|
import com.alibaba.nacos.api.config.filter.IConfigRequest;
|
||||||
import com.alibaba.nacos.api.config.filter.IConfigResponse;
|
import com.alibaba.nacos.api.config.filter.IConfigResponse;
|
||||||
import com.alibaba.nacos.api.exception.NacosException;
|
import com.alibaba.nacos.api.exception.NacosException;
|
||||||
import com.alibaba.nacos.api.utils.StringUtils;
|
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.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.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
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.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.CIPHER_PREFIX;
|
||||||
import static com.alibaba.nacos.client.aliyun.AliyunConst.CONTENT;
|
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.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.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.
|
* 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 {
|
public class AliyunConfigFilter extends AbstractConfigFilter {
|
||||||
|
|
||||||
private static final Logger LOGGER = LoggerFactory.getLogger(AliyunConfigFilter.class);
|
private static final Logger LOGGER = LoggerFactory.getLogger(AliyunConfigFilter.class);
|
||||||
|
|
||||||
public static final int defaultRetryTimes = 3;
|
|
||||||
|
|
||||||
public static final int defaultRetryIntervalMilliseconds = 2 * 100;
|
private KmsEncryptor kmsEncryptor;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Properties properties) {
|
public void init(Properties properties) {
|
||||||
LOGGER.info("init ConfigFilter: {}, for more information, please check: {}",
|
LOGGER.info("init ConfigFilter: {}, for more information, please check: {}",
|
||||||
this.getFilterName(), AliyunConst.MSE_ENCRYPTED_CONFIG_USAGE_DOCUMENT_URL);
|
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,
|
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)));
|
System.getProperty(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, System.getenv(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY)));
|
||||||
if (!StringUtils.isBlank(kmsClientKeyContent)) {
|
String kmsClientKeyFilePath = properties.getProperty(AliyunConst.KMS_CLIENT_KEY_FILE_PATH_KEY,
|
||||||
LOGGER.info("using {}: {}.", AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, kmsClientKeyContent);
|
System.getProperty(AliyunConst.KMS_CLIENT_KEY_FILE_PATH_KEY, System.getenv(AliyunConst.KMS_CLIENT_KEY_FILE_PATH_KEY)));
|
||||||
config.setClientKeyContent(kmsClientKeyContent);
|
|
||||||
} else {
|
if(!StringUtils.isBlank(kmsClientKeyContent)||!StringUtils.isBlank(kmsClientKeyFilePath)){
|
||||||
String errorMsg = null;
|
LOGGER.info("kmsClientKeyContent or kmsClientKeyFilePath is set up, using ClientKey to connect KMS.");
|
||||||
LOGGER.info("{} is empty, will read from file.", AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY);
|
this.kmsEncryptor = new ClientKeyKmsEncryptor(properties);
|
||||||
String kmsClientKeyFilePath = properties.getProperty(AliyunConst.KMS_CLIENT_KEY_FILE_PATH_KEY,
|
}else{
|
||||||
System.getProperty(AliyunConst.KMS_CLIENT_KEY_FILE_PATH_KEY, System.getenv(AliyunConst.KMS_CLIENT_KEY_FILE_PATH_KEY)));
|
LOGGER.info("kmsClientKeyContent and kmsClientKeyFilePath are not set up, using Ram to connect KMS.");
|
||||||
if (!StringUtils.isBlank(kmsClientKeyFilePath)) {
|
this.kmsEncryptor = new RamKmsEncryptor(properties);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
@Override
|
||||||
public void doFilter(IConfigRequest request, IConfigResponse response, IConfigFilterChain filterChain)
|
public void doFilter(IConfigRequest request, IConfigResponse response, IConfigFilterChain filterChain)
|
||||||
|
@ -318,7 +60,7 @@ public class AliyunConfigFilter extends AbstractConfigFilter {
|
||||||
group = (String) request.getParameter(GROUP);
|
group = (String) request.getParameter(GROUP);
|
||||||
if (dataId.startsWith(CIPHER_PREFIX)) {
|
if (dataId.startsWith(CIPHER_PREFIX)) {
|
||||||
if (!StringUtils.isBlank((String)request.getParameter(CONTENT))) {
|
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);
|
group = (String) response.getParameter(GROUP);
|
||||||
if (dataId.startsWith(CIPHER_PREFIX)) {
|
if (dataId.startsWith(CIPHER_PREFIX)) {
|
||||||
if (!StringUtils.isBlank((String)response.getParameter(CONTENT))) {
|
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);
|
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
|
@Override
|
||||||
public int getOrder() {
|
public int getOrder() {
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -780,4 +92,8 @@ public class AliyunConfigFilter extends AbstractConfigFilter {
|
||||||
public String getFilterName() {
|
public String getFilterName() {
|
||||||
return this.getClass().getName();
|
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_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_UTF8 = StandardCharsets.UTF_8.displayName();
|
||||||
|
|
||||||
public static final String ENCODE_UTF16 = StandardCharsets.UTF_16.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_PATH_KEY = "kmsCaFilePath";
|
||||||
|
|
||||||
public static final String KMS_CA_FILE_CONTENT = "kmsCaFileContent";
|
public static final String KMS_CA_FILE_CONTENT = "kmsCaFileContent";
|
||||||
|
|
||||||
|
public static final String OPEN_SSL_KEY = "openSSL";
|
||||||
|
|
||||||
public static final String MSE_ENCRYPTED_CONFIG_USAGE_DOCUMENT_URL = "https://help.aliyun.com/zh/mse/user-guide/create-and-use-encrypted-configurations?spm=a2c4g.11186623.0.0.55587becdOW3jf";
|
public static final String 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;
|
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.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.ConfigRequest;
|
||||||
import com.alibaba.nacos.client.config.filter.impl.ConfigResponse;
|
import com.alibaba.nacos.client.config.filter.impl.ConfigResponse;
|
||||||
import com.aliyuncs.exceptions.ClientException;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import com.aliyuncs.kms.model.v20160120.GenerateDataKeyResponse;
|
|
||||||
import org.junit.jupiter.api.Assertions;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
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.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.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 org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_KMS_AES_256_PREFIX;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static com.alibaba.nacos.client.aliyun.AliyunConst.CIPHER_PREFIX;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static com.alibaba.nacos.client.aliyun.AliyunConst.ENCODE_UTF8;
|
import static org.mockito.Mockito.doNothing;
|
||||||
import static com.alibaba.nacos.client.aliyun.AliyunConst.KMS_DEFAULT_KEY_ID_VALUE;
|
import static org.mockito.Mockito.mockConstruction;
|
||||||
import static com.alibaba.nacos.client.aliyun.AliyunConst.KMS_KEY_SPEC_AES_256;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class AliyunConfigFilterTest {
|
@ExtendWith(MockitoExtension.class)
|
||||||
private static final String ENCRYPTED_DATA_KEY = "encryptedDataKey";
|
class AliyunConfigFilterTest {
|
||||||
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";
|
|
||||||
|
|
||||||
public static final String ak = "LTAIxxx";
|
MockedConstruction<RamKmsEncryptor> ramKmsEncryptorMockedConstruction;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
IConfigFilterChain chain;
|
||||||
|
|
||||||
public static final String sk = "EdPqxxx";
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void preset() {
|
void setUp() {
|
||||||
try {
|
ramKmsEncryptorMockedConstruction = mockConstruction(RamKmsEncryptor.class,(mock,contexnt)->{
|
||||||
properties = new Properties();
|
when(mock.encrypt(any(IConfigRequest.class))).thenReturn("encryptedContext");
|
||||||
properties.load(this.getClass().getResourceAsStream("/aliyun-kms.properties"));
|
when(mock.decrypt(any(IConfigResponse.class))).thenReturn("plainText");
|
||||||
} 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// must be running in vpc
|
@AfterEach
|
||||||
// @Test
|
void tearDown() {
|
||||||
// public void testAliyunConfigFilterWithKmsV3UsingLocalCache() {
|
ramKmsEncryptorMockedConstruction.close();
|
||||||
// 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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLocallyRunWithRetryTimesAndTimeout()
|
void init() throws NoSuchFieldException, IllegalAccessException {
|
||||||
throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
|
Properties properties_1 = new Properties();
|
||||||
Class<AliyunConfigFilter> aliyunConfigFilterClass = AliyunConfigFilter.class;
|
properties_1.put(AliyunConst.KMS_CLIENT_KEY_CONTENT_KEY, "test");
|
||||||
Method locallyRunWithRetryTimesAndTimeout = aliyunConfigFilterClass.getDeclaredMethod(
|
AliyunConfigFilter aliyunConfigFilter_1 = new AliyunConfigFilter();
|
||||||
"locallyRunWithRetryTimesAndTimeout", Supplier.class, int.class, long.class);
|
aliyunConfigFilter_1.init(properties_1);
|
||||||
locallyRunWithRetryTimesAndTimeout.setAccessible(true);
|
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();
|
AliyunConfigFilter aliyunConfigFilter = new AliyunConfigFilter();
|
||||||
|
aliyunConfigFilter.init(properties);
|
||||||
//return false to retry with defaultRetryTimes
|
assertEquals(1, aliyunConfigFilter.getOrder());
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
private void executeConfigFilterWithCacheAfterSet()
|
void getFilterName() {
|
||||||
throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
|
Properties properties = new Properties();
|
||||||
ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager(properties);
|
AliyunConfigFilter aliyunConfigFilter = new AliyunConfigFilter();
|
||||||
Class<? extends ConfigFilterChainManager> configFilterChainManagerClass = configFilterChainManager.getClass();
|
aliyunConfigFilter.init(properties);
|
||||||
Field filtersField = configFilterChainManagerClass.getDeclaredField("filters");
|
assertEquals("com.alibaba.nacos.client.aliyun.AliyunConfigFilter", aliyunConfigFilter.getFilterName());
|
||||||
filtersField.setAccessible(true);
|
|
||||||
List<IConfigFilter> filters = (List<IConfigFilter>) filtersField.get(configFilterChainManager);
|
|
||||||
|
|
||||||
AliyunConfigFilter aliyunConfigFilter = (AliyunConfigFilter) filters.get(1);
|
|
||||||
|
|
||||||
Class<AliyunConfigFilter> aliyunConfigFilterClass = AliyunConfigFilter.class;
|
|
||||||
|
|
||||||
Field kmsLocalCacheField = aliyunConfigFilterClass.getDeclaredField("kmsLocalCache");
|
|
||||||
kmsLocalCacheField.setAccessible(true);
|
|
||||||
|
|
||||||
KmsLocalCache kmsLocalCache = (KmsLocalCache) kmsLocalCacheField.get(aliyunConfigFilter);
|
|
||||||
|
|
||||||
for (String dataId : dataIdList) {
|
|
||||||
String groupKey = GroupKeyUtils.getGroupKey2(dataId, group);
|
|
||||||
kmsLocalCache.remove(groupKey);
|
|
||||||
ConfigRequest configRequest = new ConfigRequest();
|
|
||||||
configRequest.setGroup(group);
|
|
||||||
configRequest.setDataId(dataId);
|
|
||||||
configRequest.setContent(content);
|
|
||||||
try {
|
|
||||||
configFilterChainManager.doFilter(configRequest, null);
|
|
||||||
KmsLocalCache.LocalCacheItem localCacheItem = kmsLocalCache.get(groupKey);
|
|
||||||
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
|
|
||||||
Assertions.assertEquals(localCacheItem.getEncryptedContentMD5(), MD5Utils.md5Hex(configRequest.getContent(), ENCODE_UTF8));
|
|
||||||
Assertions.assertEquals(localCacheItem.getEncryptedDataKey(), configRequest.getEncryptedDataKey());
|
|
||||||
} else if (dataId.startsWith(CIPHER_PREFIX)) {
|
|
||||||
Assertions.assertEquals(localCacheItem.getEncryptedContentMD5(), MD5Utils.md5Hex(configRequest.getContent(), ENCODE_UTF8));
|
|
||||||
Assertions.assertEquals(localCacheItem.getPlainContent(), content);
|
|
||||||
}
|
|
||||||
} catch (NacosException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigResponse configResponse = new ConfigResponse();
|
|
||||||
configResponse.setGroup(group);
|
|
||||||
configResponse.setDataId(dataId);
|
|
||||||
configResponse.setEncryptedDataKey((String) configRequest.getParameter(ENCRYPTED_DATA_KEY));
|
|
||||||
configResponse.setContent((String) configRequest.getParameter(CONTENT));
|
|
||||||
kmsLocalCache.remove(groupKey);
|
|
||||||
try {
|
|
||||||
configFilterChainManager.doFilter(null, configResponse);
|
|
||||||
KmsLocalCache.LocalCacheItem localCacheItem = kmsLocalCache.get(groupKey);
|
|
||||||
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
|
|
||||||
Assertions.assertEquals(localCacheItem.getEncryptedContentMD5(), MD5Utils.md5Hex(configRequest.getContent(), ENCODE_UTF8));
|
|
||||||
Assertions.assertEquals(localCacheItem.getEncryptedDataKey(), configRequest.getEncryptedDataKey());
|
|
||||||
Assertions.assertEquals(localCacheItem.getEncryptedDataKey(), configResponse.getEncryptedDataKey());
|
|
||||||
} else if (dataId.startsWith(CIPHER_PREFIX)) {
|
|
||||||
Assertions.assertEquals(localCacheItem.getPlainContent(), configResponse.getContent());
|
|
||||||
Assertions.assertEquals(localCacheItem.getEncryptedContentMD5(), MD5Utils.md5Hex(configRequest.getContent(), ENCODE_UTF8));
|
|
||||||
}
|
|
||||||
Assertions.assertEquals(content, configResponse.getContent());
|
|
||||||
} catch (NacosException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Field localCacheTestModeField = aliyunConfigFilterClass.getDeclaredField("localCacheTestMode");
|
|
||||||
localCacheTestModeField.setAccessible(true);
|
|
||||||
localCacheTestModeField.set(aliyunConfigFilter, true);
|
|
||||||
ConfigResponse configResponse1 = new ConfigResponse();
|
|
||||||
configResponse1.setGroup(group);
|
|
||||||
configResponse1.setDataId(dataId);
|
|
||||||
configResponse1.setEncryptedDataKey((String) configRequest.getParameter(ENCRYPTED_DATA_KEY));
|
|
||||||
configResponse1.setContent((String) configRequest.getParameter(CONTENT));
|
|
||||||
|
|
||||||
try {
|
|
||||||
configFilterChainManager.doFilter(null, configResponse1);
|
|
||||||
KmsLocalCache.LocalCacheItem localCacheItem = kmsLocalCache.get(groupKey);
|
|
||||||
if (dataId.startsWith(CIPHER_KMS_AES_128_PREFIX) || dataId.startsWith(CIPHER_KMS_AES_256_PREFIX)) {
|
|
||||||
Assertions.assertEquals(localCacheItem.getEncryptedContentMD5(), MD5Utils.md5Hex(configRequest.getContent(), ENCODE_UTF8));
|
|
||||||
Assertions.assertEquals(localCacheItem.getEncryptedDataKey(), configRequest.getEncryptedDataKey());
|
|
||||||
Assertions.assertEquals(localCacheItem.getEncryptedDataKey(), configResponse1.getEncryptedDataKey());
|
|
||||||
} else if (dataId.startsWith(CIPHER_PREFIX)) {
|
|
||||||
Assertions.assertEquals(localCacheItem.getPlainContent(), configResponse1.getContent());
|
|
||||||
Assertions.assertEquals(localCacheItem.getEncryptedContentMD5(), MD5Utils.md5Hex(configRequest.getContent(), ENCODE_UTF8));
|
|
||||||
}
|
|
||||||
Assertions.assertEquals(content, configResponse1.getContent());
|
|
||||||
} catch (NacosException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
localCacheTestModeField.set(aliyunConfigFilter, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private void executeConfigFilter() {
|
|
||||||
ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager(properties);
|
|
||||||
|
|
||||||
for (String dataId : dataIdList) {
|
|
||||||
ConfigRequest configRequest = new ConfigRequest();
|
|
||||||
configRequest.setGroup(group);
|
|
||||||
configRequest.setDataId(dataId);
|
|
||||||
configRequest.setContent(content);
|
|
||||||
String encryptedContent = null;
|
|
||||||
try {
|
|
||||||
configFilterChainManager.doFilter(configRequest, null);
|
|
||||||
encryptedContent = configRequest.getContent();
|
|
||||||
Assertions.assertFalse(StringUtils.isBlank(encryptedContent));
|
|
||||||
} catch (NacosException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
ConfigResponse configResponse = new ConfigResponse();
|
|
||||||
configResponse.setGroup(group);
|
|
||||||
configResponse.setDataId(dataId);
|
|
||||||
configResponse.setEncryptedDataKey((String) configRequest.getParameter(ENCRYPTED_DATA_KEY));
|
|
||||||
configResponse.setContent(encryptedContent);
|
|
||||||
try {
|
|
||||||
configFilterChainManager.doFilter(null, configResponse);
|
|
||||||
Assertions.assertEquals(content, configResponse.getContent());
|
|
||||||
} catch (NacosException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void verifyEncryptedConfigByKmsIdempotent() throws Exception {
|
|
||||||
ConfigFilterChainManager configFilterChainManager = new ConfigFilterChainManager(properties);
|
|
||||||
|
|
||||||
String dataId = "cipher-crypt";
|
|
||||||
ConfigRequest configRequest = new ConfigRequest();
|
|
||||||
configRequest.setGroup(group);
|
|
||||||
configRequest.setDataId(dataId);
|
|
||||||
configRequest.setContent(content);
|
|
||||||
ConfigRequest configRequest1 = new ConfigRequest();
|
|
||||||
configRequest1.setGroup(group);
|
|
||||||
configRequest1.setDataId(dataId);
|
|
||||||
configRequest1.setContent(content);
|
|
||||||
String encryptedContent;
|
|
||||||
String encryptedContent1;
|
|
||||||
try {
|
|
||||||
configFilterChainManager.doFilter(configRequest, null);
|
|
||||||
configFilterChainManager.doFilter(configRequest1, null);
|
|
||||||
encryptedContent = configRequest.getContent();
|
|
||||||
encryptedContent1 = configRequest1.getContent();
|
|
||||||
Assertions.assertNotEquals(encryptedContent, encryptedContent1);
|
|
||||||
} catch (NacosException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
Class<? extends ConfigFilterChainManager> configFilterChainManagerClass = configFilterChainManager.getClass();
|
|
||||||
Field filtersField = configFilterChainManagerClass.getDeclaredField("filters");
|
|
||||||
filtersField.setAccessible(true);
|
|
||||||
List<IConfigFilter> filters = (List<IConfigFilter>) filtersField.get(configFilterChainManager);
|
|
||||||
|
|
||||||
AliyunConfigFilter aliyunConfigFilter = (AliyunConfigFilter) filters.get(1);
|
|
||||||
|
|
||||||
GenerateDataKeyResponse generateDataKeyResponse = aliyunConfigFilter.generateDataKey(KMS_DEFAULT_KEY_ID_VALUE,
|
|
||||||
KMS_KEY_SPEC_AES_256);
|
|
||||||
GenerateDataKeyResponse generateDataKeyResponse1 = aliyunConfigFilter.generateDataKey(KMS_DEFAULT_KEY_ID_VALUE,
|
|
||||||
KMS_KEY_SPEC_AES_256);
|
|
||||||
|
|
||||||
Assertions.assertNotEquals(generateDataKeyResponse1.getPlaintext(), generateDataKeyResponse.getPlaintext());
|
|
||||||
|
|
||||||
Assertions.assertNotEquals(
|
|
||||||
AesUtils.encrypt(content, generateDataKeyResponse.getPlaintext(), ENCODE_UTF8),
|
|
||||||
AesUtils.encrypt(content, generateDataKeyResponse1.getPlaintext(), ENCODE_UTF8));
|
|
||||||
|
|
||||||
Assertions.assertEquals(
|
|
||||||
AesUtils.encrypt(content, generateDataKeyResponse.getPlaintext(), ENCODE_UTF8),
|
|
||||||
AesUtils.encrypt(content, generateDataKeyResponse.getPlaintext(), ENCODE_UTF8));
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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