使用RAM角色實現(xiàn)對ECS的訪問控制
公司在接待外部客戶進行參觀活動時,常見的方案是給訪客一張臨時工牌,訪客憑此工牌可在限定時間內(nèi),在授權(quán)區(qū)域內(nèi)活動。阿里云提供的RAM角色功能,可以解決類似的云上運維場景。它允許用戶扮演特定的臨時身份,并執(zhí)行授權(quán)范圍內(nèi)的管理動作,適用于跨賬號訪問、角色單點登錄(SSO)以及臨時授權(quán)等場景。
場景介紹
接下來,我們將以常見的跨賬號訪問場景為例,為您完整演示操作流程。假設(shè)您的公司運營著一個電子商務(wù)網(wǎng)站,網(wǎng)站服務(wù)部署在阿里云的ECS上。日常運維工作由公司的IT部門負責(zé),但有時也需要外部合作伙伴的幫助來解決特定的技術(shù)問題或是執(zhí)行一些特殊的維護任務(wù)。為了保證云上資源的安全性,您不希望直接分享長期有效的賬號(阿里云賬號、RAM用戶)給這些外部合作伙伴。
使用RAM角色進行跨賬號訪問授權(quán),整體流程如下:
賬號A:您公司的阿里云賬號(主賬號)。
賬號B:外部合作伙伴的阿里云賬號(主賬號)。
賬號A創(chuàng)建RAM角色,并授權(quán)允許賬號B扮演。
賬號B創(chuàng)建RAM用戶,并授權(quán)其進行角色扮演。
賬號B使用RAM用戶扮演賬號A的RAM角色,完成賬號A相關(guān)資源的管理操作。
操作步驟
賬號A創(chuàng)建RAM角色并授權(quán)
1. 創(chuàng)建RAM角色
使用賬號A登錄RAM控制臺,創(chuàng)建一個可信實體類型為阿里云賬號的RAM角色,在選擇信任的云賬號處填入賬號B的賬號ID(UID)。更多關(guān)于創(chuàng)建RAM角色的信息,請參見創(chuàng)建可信實體為阿里云賬號的RAM角色。
2. 為RAM角色授權(quán)
創(chuàng)建一個權(quán)限策略,允許查看實例信息及通過Workbench登錄ECS實例,并將權(quán)限策略授予給RAM角色。詳細授權(quán)操作,請參見創(chuàng)建自定義權(quán)限策略。
該權(quán)限策略支持查看ECS實例信息,并允許用戶登錄ECS進行相關(guān)操作。
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecs:List*",
"ecs:Describe*",
"ecs-workbench:LoginInstance"
],
"Resource": "*"
}
]
}
賬號B創(chuàng)建RAM用戶并授權(quán)
1. 創(chuàng)建RAM用戶
使用賬號B登錄RAM控制臺,創(chuàng)建一個RAM用戶,注意在創(chuàng)建RAM用戶時選擇允許控制臺登錄,并允許OpenAPI調(diào)用訪問。關(guān)于如何創(chuàng)建RAM用戶,請參見創(chuàng)建RAM用戶。
2. 為RAM用戶授權(quán)
在扮演RAM角色之前,需要為RAM用戶授予扮演RAM角色的權(quán)限。使用賬號B為該RAM用戶授予AliyunSTSAssumeRoleAccess
權(quán)限,以允許該RAM用戶扮演所有RAM角色。有關(guān)如何為RAM用戶授權(quán)的詳細信息,請參見為RAM用戶授權(quán)。
如果希望該RAM用戶僅能扮演指定的RAM角色,具體操作,請參見能否指定RAM用戶具體可以扮演哪個RAM角色。
扮演RAM角色
云產(chǎn)品控制臺扮演角色
程序調(diào)用時扮演
您也可以通過程序代碼實現(xiàn)對賬號A的資源訪問。大體流程如下:
將賬號B創(chuàng)建RAM用戶時獲取的AccessKey寫入系統(tǒng)環(huán)境變量。不同系統(tǒng)的設(shè)置方法存在差異,請參考在Linux、macOS和Windows系統(tǒng)配置環(huán)境變量
使用賬號B的RAM用戶調(diào)用
AssumeRole
接口,傳入賬號A的RAM角色的ARN,換取臨時身份憑證STS Token。使用換取的臨時身份憑證STS Token調(diào)用云服務(wù)提供的API查看賬號A的云資源。
Java示例代碼
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>ecs20140526</artifactId>
<version>5.4.4</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>sts20150401</artifactId>
<version>1.1.4</version>
</dependency>
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>credentials-java</artifactId>
<version>0.3.10</version>
</dependency>
import com.aliyun.ecs20140526.models.DescribeInstancesRequest;
import com.aliyun.ecs20140526.models.DescribeInstancesResponse;
import com.aliyun.sts20150401.Client;
import com.aliyun.sts20150401.models.AssumeRoleRequest;
import com.aliyun.sts20150401.models.AssumeRoleResponse;
import com.aliyun.sts20150401.models.AssumeRoleResponseBody;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.google.gson.Gson;
/**
* 通過扮演RAM角色獲取臨時訪問憑證,然后使用這些憑證訪問ECS資源。
*/
public class Sample {
public static void main(String[] args) {
// 僅供示例參考,務(wù)必根據(jù)業(yè)務(wù)實際情況選擇特定region的endpoint
String stsEndpoint = "sts.cn-shanghai.aliyuncs.com";
String ecsEndpoint = "ecs.cn-shanghai.aliyuncs.com";
// 查詢cn-shanghai的ECS實例信息
String regionId = "cn-shanghai";
// 要扮演的RAM角色的ARN。
String ramRoleArn = "acs:ram::14************16:role/cooperativepartnerrole";
// 使用賬號B的RAM用戶扮演賬號A的RAM角色,獲取臨時訪問憑證。
AssumeRoleResponse assumeRoleResponse = playRamRole(stsEndpoint, ramRoleArn);
// 調(diào)用云服務(wù)提供的API查看賬號A的云資源。
accessResources(assumeRoleResponse, ecsEndpoint, regionId);
}
/**
* 使用臨時訪問憑證訪問云資源。
*
* @param assumeRoleResponse 包含臨時訪問憑證的響應(yīng)對象。
*/
private static void accessResources(AssumeRoleResponse assumeRoleResponse, String ecsEndpoint, String regionId) {
try {
// 提取臨時訪問憑證信息。
AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials assumeRoleResponseBodyCredentials = assumeRoleResponse.body.credentials;
com.aliyun.credentials.models.Config credentialsConfig = new com.aliyun.credentials.models.Config()
.setType("sts") // 憑證類型。
.setAccessKeyId(assumeRoleResponseBodyCredentials.accessKeyId)
.setAccessKeySecret(assumeRoleResponseBodyCredentials.accessKeySecret)
.setSecurityToken(assumeRoleResponseBodyCredentials.securityToken);
com.aliyun.credentials.Client credentialClient = new com.aliyun.credentials.Client(credentialsConfig);
// 創(chuàng)建ECS客戶端。
Config ecsConfig = new Config()
.setEndpoint(ecsEndpoint)
.setCredential(credentialClient);
com.aliyun.ecs20140526.Client ecsClient = new com.aliyun.ecs20140526.Client(ecsConfig);
DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest()
.setRegionId(regionId);
RuntimeOptions runtimeOptions = new RuntimeOptions();
// 調(diào)用DescribeInstances接口并獲得響應(yīng)。
DescribeInstancesResponse response = ecsClient.describeInstancesWithOptions(describeInstancesRequest, runtimeOptions);
// 打印響應(yīng)結(jié)果。
System.out.println(new Gson().toJson(response.body));
} catch (Exception e) {
throw new RuntimeException("AccessResources failed: " + e.getMessage());
}
}
/**
* 扮演RAM角色獲取臨時訪問憑證。
*
* @return 包含臨時訪問憑證的響應(yīng)對象。
*/
private static AssumeRoleResponse playRamRole(String stsEndpoint, String ramRoleArn) {
try {
// 創(chuàng)建StsClient對象,并調(diào)用assumeRole服務(wù)獲取STS Token
Config config = new Config()
// System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")表示從環(huán)境變量中獲取AccessKey ID的值
.setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
// System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")表示從環(huán)境變量中獲取AccessKey Secret的值
.setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
config.endpoint = stsEndpoint;
Client client = new Client(config);
// 創(chuàng)建AssumeRole請求對象,指定要扮演的RAM角色ARN和角色會話名稱。
AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest()
.setRoleArn(ramRoleArn)
.setRoleSessionName("CooperativePartner");
RuntimeOptions runtime = new RuntimeOptions();
return client.assumeRoleWithOptions(assumeRoleRequest, runtime);
} catch (Exception e) {
throw new RuntimeException("play RAM role failed: " + e.getMessage());
}
}
}
調(diào)用結(jié)果:程序返回了賬號A在杭州地域保有的ECS資源列表。
{
"instances":{
"instance":[
{
"creationTime":"2024-10-23T09:12Z",
"expiredTime":"2099-12-31T15:59Z",
"hostName":"iZ********************pZ",
"imageId":"m-uf****************jf",
"instanceChargeType":"PostPaid",
"instanceId":"i-uf****************ap",
"instanceName":"launch-advisor-20241023-c6",
"instanceNetworkType":"vpc",
"instanceType":"ecs.c6.xlarge",
...
// 省略了部分參數(shù)的展示
...
"vpcAttributes":{
"natIpAddress":"",
"privateIpAddress":{
"ipAddress":[
"17*.**.**.*15"
]
},
"vSwitchId":"vsw-uf*****************tk",
"vpcId":"vpc-uf*****************kr"
},
"zoneId":"cn-shanghai-b"
}
]
},
"nextToken":"",
"pageNumber":1,
"pageSize":10,
"requestId":"C1468F7E********************7A3A712",
"totalCount":1
}