Skip to content

Commit

Permalink
Merge pull request #72 from AlibabaCloudLandingZone/solution-cross-ac…
Browse files Browse the repository at this point in the history
…count-sts-token/0.0.1

solution-cross-account-sts-token/0.0.1
  • Loading branch information
wibud authored Aug 9, 2024
2 parents cd0fdca + 9173ec3 commit a06e366
Show file tree
Hide file tree
Showing 13 changed files with 634 additions and 0 deletions.
37 changes: 37 additions & 0 deletions solution/solution-cross-account-sts-token/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/

### IntelliJ IDEA ###
.idea/

### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/

### VS Code ###
.vscode/

### Mac OS ###
.DS_Store

### Terraform ###
.terraform
.terraform.lock.hcl
*.tfstate
48 changes: 48 additions & 0 deletions solution/solution-cross-account-sts-token/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# 通过角色扮演实现跨账号临时凭证的获取和使用

过角色链式扮演的方式,使应用基于STS临时凭证访问其他账号的云资源,保证全链路无需透露长期AccessKey,既可减少密钥泄露的风险,也可借助访问控制RAM精细化控制资源访问权限,避免权限过度分配。

本方案提供Java/Terraform等代码示例,客户能够快速完成应用改造,减少开发和部署的复杂度。

## 使用步骤

### 目录结构说明

```
.
├── code-example
│ ├── java // Java代码示例
│ └── terraform // Terraform代码示例
└── deployment // 自动化部署代码
└── create-role-cross-account
```

### Java代码示例

```
.
└── common
└── src/main/java
└── org
└── example
├── sdk1_0 // 1.0版本SDK简单示例
└── sdk2_0 // 2.0版本SDK简单示例
```

执行前,请确保运行环境中已配置好Java和Maven。

1. Java Development Kit (JDK):确保已安装Java 8或更高版本。
2. Apache Maven:确保已安装Maven 3.6.0或更高版本。

### 自动化部署代码

本方案提供了基于ROS的批量跨账号创建角色的Terraform自动化代码,其模版入参输入:

| **参数名称** | **参数值示例** | **描述** |
| --- | --- | --- |
| role_name | CentralizedOperationRole | 批量创建的角色的名称 |
| policy_name | CentralizedOperationRolePolicy | 绑定到该角色的权限策略的名称 |
| policy_document | {"Version":"1","Statement":[{"Action":"ecs:*","Resource":"*","Effect":"Allow"}]} | 绑定到该角色的权限策略内容。既跨账号资源操作的所有权限。|
| assume_role_principal_account | 1254004******** | 角色的可信账号,既允许扮演到该新建角色的账号,不填默认为当前账号(运维账号)|
| assume_role_principal_type | RamRole | 可信账号下允许扮演该新建角色的对象的类型,枚举值:RamRole(RAM角色)、RamUser(RAM用户)|
| assume_role_principal_name | EcsInstanceRole | 可信账号下允许扮演该新建角色的对象的名称,如果授信对象类型为RamRole(RAM角色),那么请填写对应RAM角色名称,如果RamUser(RAM用户),那么请填写对应RAM用户名称。请确保填写的RAM角色或者RAM用户在可信账号下已经存在,否则会创建失败。|
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId>
<artifactId>cross-account-sts-token</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
<exec.cleanupDaemonThreads>false</exec.cleanupDaemonThreads>
</properties>

<dependencies>
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.51</version>
</dependency>
<!--2.0 sdk-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>sts20150401</artifactId>
<version>1.1.4</version>
</dependency>
<!--1.0 sdk-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-sts</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>[4.0.0,5.0.0)</version>
</dependency>
<!-- Requires: version >= 0.3.4 -->
<!-- 推荐使用最新版本 -->
<!--获取所有已发布的版本列表,请参见https://github.com/aliyun/credentials-java/blob/master/ChangeLog.txt-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>credentials-java</artifactId>
<version>0.3.4</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package org.example.sdk1_0;

import com.alibaba.fastjson2.JSON;
import com.aliyun.credentials.models.CredentialModel;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.auth.BasicSessionCredentials;
import com.aliyuncs.auth.STSAssumeRoleSessionCredentialsProvider;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.sts.model.v20150401.GetCallerIdentityRequest;
import com.aliyuncs.sts.model.v20150401.GetCallerIdentityResponse;

public class AssumeRoleSample {
public static void main(String[] args) {
// 初始化凭据客户端,使用Credentials工具,保证您的应用程序本身是无AK的
// 借助Credentials工具的默认凭据链,您可以用同一套代码,通过程序之外的配置来控制不同环境下的凭据获取方式
// 当您在初始化凭据客户端不传入任何参数时,Credentials工具将会尝试按照如下顺序查找相关凭据信息(优先级由高到低):
// 1. 使用系统属性
// 2. 使用环境变量
// 3. 使用OIDC RAM角色
// 4. 使用配置文件
// 5. 使用ECS实例RAM角色(需要通过环境变量 ALIBABA_CLOUD_ECS_METADATA 指定 ECS 实例角色名称;通过环境变量 ALIBABA_CLOUD_ECS_IMDSV2_ENABLE=true 开启在加固模式下获取STS Token)
// 详情请参考:https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-access-credentials#3ca299f04bw3c
// 要使用默认凭据链,初始化 Client 时,必须使用空的构造函数,不能配置 Config 入参
// 除了使用默认凭据链,您也可以在代码中显式配置,来初始化凭据客户端
// 详情请参考:https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-access-credentials#a9e9aa404bzfy
com.aliyun.credentials.Client credentialClient = new com.aliyun.credentials.Client();

// 以华东1(杭州)为例
DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou");
// 用凭据客户端初始化角色扮演的CredentialsProvider:STSAssumeRoleSessionCredentialsProvider,实现跨账号角色扮演
// 该CredentialsProvider支持自动刷新STS Token
STSAssumeRoleSessionCredentialsProvider provider = new STSAssumeRoleSessionCredentialsProvider(
() -> {
// 保证线程安全,从 CredentialModel 中获取 ak/sk/security token
CredentialModel credentialModel = credentialClient.getCredential();
String ak = credentialModel.getAccessKeyId();
String sk = credentialModel.getAccessKeySecret();
String token = credentialModel.getSecurityToken();
return new BasicSessionCredentials(ak, sk, token);
},
// 请替换为您实际要扮演的RAM角色ARN
// 格式为 acs:ram::${账号 ID}:role/${角色名称}
"<role-arn>",
profile
)
// 角色会话名称
.withRoleSessionName("WellArchitectedSolutionDemo")
// STS Token 有效期,单位:秒
.withRoleSessionDurationSeconds(3600L);

// 初始化SDK 1.0客户端
IAcsClient iAcsClient = new DefaultAcsClient(profile, provider);

// 调用API,跨账号进行资源操作
// 以调用GetCallerIdentity获取当前调用者身份信息为例
GetCallerIdentityRequest getCallerIdentityRequest = new GetCallerIdentityRequest();
try {
GetCallerIdentityResponse getCallerIdentityResponse = iAcsClient.getAcsResponse(getCallerIdentityRequest);
System.out.println(JSON.toJSONString(getCallerIdentityResponse));
} catch (ServerException e) {
// 示例仅做打印展示。请重视异常处理,在工程项目中切勿直接忽略异常。
// 打印整体的错误输出
e.printStackTrace();
// 打印错误码
System.out.println(e.getErrCode());
// 打印 RequestId
System.out.println(e.getRequestId());
// 打印错误信息
System.out.println(e.getErrMsg());
} catch (ClientException e) {
// 示例仅做打印展示。请重视异常处理,在工程项目中切勿直接忽略异常。
// 打印整体的错误输出
e.printStackTrace();
// 打印错误码
System.out.println(e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package org.example.sdk2_0;

import com.aliyun.credentials.models.CredentialModel;
import com.aliyun.credentials.utils.ParameterHelper;
import com.aliyun.sts20150401.models.AssumeRoleRequest;
import com.aliyun.sts20150401.models.AssumeRoleResponse;
import com.aliyun.sts20150401.models.AssumeRoleResponseBody;
import com.aliyun.sts20150401.models.GetCallerIdentityResponse;
import com.aliyun.tea.TeaException;
import com.aliyun.teautil.models.RuntimeOptions;
import com.alibaba.fastjson2.JSON;

public class AssumeRoleSample {

public static void main(String[] args) throws Exception {
// 初始化凭据客户端,使用Credentials工具,保证您的应用程序本身是无AK的
// 借助Credentials工具的默认凭据链,您可以用同一套代码,通过程序之外的配置来控制不同环境下的凭据获取方式
// 当您在初始化凭据客户端不传入任何参数时,Credentials工具将会尝试按照如下顺序查找相关凭据信息(优先级由高到低):
// 1. 使用系统属性
// 2. 使用环境变量
// 3. 使用OIDC RAM角色
// 4. 使用配置文件
// 5. 使用ECS实例RAM角色(需要通过环境变量 ALIBABA_CLOUD_ECS_METADATA 指定 ECS 实例角色名称;通过环境变量 ALIBABA_CLOUD_ECS_IMDSV2_ENABLE=true 开启在加固模式下获取STS Token)
// 详情请参考:https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-access-credentials#3ca299f04bw3c
// 要使用默认凭据链,初始化 Client 时,必须使用空的构造函数,不能配置 Config 入参
// 除了使用默认凭据链,您也可以在代码中显式配置,来初始化凭据客户端
// 详情请参考:https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-access-credentials#a9e9aa404bzfy
com.aliyun.credentials.Client credentialClient = new com.aliyun.credentials.Client();

// 跨账号角色扮演获取STS Token
// 如果您缓存了该STS Token,需要特别注意STS Toke的到期时间,避免缓存时间过长而STS Token过期导致程序错误
CredentialModel assumeRoleCredentialModel = createAssumeRoleCredential(credentialClient);

// 调用API,跨账号进行资源操作
// 以调用GetCallerIdentity获取当前调用者身份信息为例
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
.setAccessKeyId(assumeRoleCredentialModel.getAccessKeyId())
.setAccessKeySecret(assumeRoleCredentialModel.getAccessKeySecret())
.setSecurityToken(assumeRoleCredentialModel.getSecurityToken())
// 地域,以华东1(杭州)为例
.setRegionId("cn-hangzhou");
com.aliyun.sts20150401.Client stsClient = new com.aliyun.sts20150401.Client(config);
RuntimeOptions runtimeOptions = new RuntimeOptions()
// 开启自动重试机制,只会对超时等网络异常进行重试
.setAutoretry(true)
// 设置自动重试次数,默认3次
.setMaxAttempts(3);

try {
GetCallerIdentityResponse getCallerIdentityResponse = stsClient.getCallerIdentityWithOptions(runtimeOptions);
System.out.println(JSON.toJSONString(getCallerIdentityResponse));
} catch (TeaException e) {
// 此处仅做打印展示,请谨慎对待异常处理,在工程项目中切勿直接忽略异常。
e.printStackTrace();
// 打印错误码
System.out.println(e.getCode());
// 打印错误信息,错误信息中包含 RequestId
System.out.println(e.getMessage());
// 打印服务端返回的具体错误内容
System.out.println(e.getData());
}
}

public static CredentialModel createAssumeRoleCredential(com.aliyun.credentials.Client credentialClient) throws Exception {
com.aliyun.teaopenapi.models.Config config = new com.aliyun.teaopenapi.models.Config()
.setCredential(credentialClient)
// 地域,以华东1(杭州)为例
.setRegionId("cn-hangzhou");

com.aliyun.sts20150401.Client stsClient = new com.aliyun.sts20150401.Client(config);
RuntimeOptions runtimeOptions = new RuntimeOptions()
// 开启自动重试机制,只会对超时等网络异常进行重试
.setAutoretry(true)
// 设置自动重试次数,默认3次
.setMaxAttempts(3);
AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest()
// 请替换为您实际要扮演的RAM角色ARN
// 格式为 acs:ram::${账号 ID}:role/${角色名称}
.setRoleArn("<role-arn>")
// 角色会话名称
.setRoleSessionName("WellArchitectedSolutionDemo")
// 设置会话权限策略,进一步限制STS Token 的权限,如果指定该权限策略,则 STS Token 最终的权限策略取 RAM 角色权限策略与该权限策略的交集
// 非必填。示例值:{"Statement": [{"Action": ["*"],"Effect": "Allow","Resource": ["*"]}],"Version":"1"}
.setPolicy("{\"Statement\": [{\"Action\": [\"*\"],\"Effect\": \"Allow\",\"Resource\": [\"*\"]}],"
+ "\"Version\":\"1\"}")
// STS Token 有效期,单位:秒
.setDurationSeconds(3600L);
AssumeRoleResponse assumeRoleResponse = stsClient.assumeRoleWithOptions(assumeRoleRequest, runtimeOptions);
AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials credentials = assumeRoleResponse.getBody().getCredentials();

// 返回角色扮演获取到的STS Token
return CredentialModel.builder()
.accessKeyId(credentials.getAccessKeyId())
.accessKeySecret(credentials.getAccessKeySecret())
.securityToken(credentials.getSecurityToken())
.expiration(ParameterHelper.getUTCDate(credentials.getExpiration()).getTime())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# OIDC身份提供商的ARN
# 格式为 acs:ram::${账号ID}:oidc-provider/ack-rrsa-${集群ID}
variable "oidc_provider_arn" {
type = string
}

# Pod绑定的RAM角色ARN
# 格式为 acs:ram::${账号ID}:role/${角色名称}
variable "oidc_role_arn" {
type = string
}

# 集群生成的OIDC Token的文件路径
variable "oidc_token_file" {
type = string
}

# 配置provider,跨账号扮演角色
provider "alicloud" {
region = "cn-hangzhou"
assume_role_with_oidc {
oidc_provider_arn = var.oidc_provider_arn
role_arn = var.oidc_role_arn
oidc_token_file = var.oidc_token_file
role_session_name = "WellArchitectedSolutionDemo"
}
assume_role {
# 请替换为您实际要扮演的RAM角色ARN
# 格式为 acs:ram::${账号ID}:role/${角色名称}
role_arn = "<role-arn>"
# 设置会话权限策略,进一步限制STS Token 的权限,如果指定该权限策略,则 STS Token 最终的权限策略取 RAM 角色权限策略与该权限策略的交集
# 非必填。示例值:{"Statement": [{"Action": ["*"],"Effect": "Allow","Resource": ["*"]}],"Version":"1"}
policy = "{\"Statement\": [{\"Action\": [\"*\"],\"Effect\": \"Allow\",\"Resource\": [\"*\"]}],\"Version\":\"1\"}"
# 角色会话名称
session_name = "WellArchitectedSolutionDemo"
# STS Token 有效期,单位:秒
session_expiration = 3600
}
}

# 跨账号进行资源操作
# 以获取当前调用者身份信息为例
data "alicloud_caller_identity" "current" {
}

output "caller_identity" {
value = data.alicloud_caller_identity.current
}
Loading

0 comments on commit a06e366

Please sign in to comment.