使用RAM角色实现对ECS的访问控制

公司在接待外部客户进行参观活动时,常见的方案是给访客一张临时工牌,访客凭此工牌可在限定时间内,在授权区域内活动。阿里云提供的RAM角色功能,可以解决类似的云上运维场景。它允许用户扮演特定的临时身份,并执行授权范围内的管理动作,适用于跨账号访问、角色单点登录(SSO)以及临时授权等场景。

场景介绍

接下来,我们将以常见的跨账号访问场景为例,为您完整演示操作流程。假设您的公司运营着一个电子商务网站,网站服务部署在阿里云的ECS上。日常运维工作由公司的IT部门负责,但有时也需要外部合作伙伴的帮助来解决特定的技术问题或是执行一些特殊的维护任务。为了保证云上资源的安全性,您不希望直接分享长期有效的账号(阿里云账号、RAM用户)给这些外部合作伙伴。

使用RAM角色进行跨账号访问授权,整体流程如下:

image

账号A:您公司的阿里云账号(主账号)。

账号B:外部合作伙伴的阿里云账号(主账号)。

  1. 账号A创建RAM角色,并授权允许账号B扮演。

  2. 账号B创建RAM用户,并授权其进行角色扮演。

  3. 账号B使用RAM用户扮演账号A的RAM角色,完成账号A相关资源的管理操作。

操作步骤

账号A创建RAM角色并授权

1. 创建RAM角色

使用账号A登录RAM控制台,创建一个可信实体类型为阿里云账号的RAM角色,在选择信任的云账号处填入账号B的账号ID(UID)。更多关于创建RAM角色的信息,请参见创建可信实体为阿里云账号的RAM角色image

2. 为RAM角色授权

创建一个权限策略,允许查看实例信息及通过Workbench登录ECS实例,并将权限策略授予给RAM角色。详细授权操作,请参见创建自定义权限策略

支持访客扮演的临时权限策略

该权限策略支持查看ECS实例信息,并允许用户登录ECS进行相关操作。

{
    "Version": "1",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecs:List*",
                "ecs:Describe*",
                "ecs-workbench:LoginInstance"
            ],
            "Resource": "*"
        }
    ]
}

账号B创建RAM用户并授权

1. 创建RAM用户

使用账号B登录RAM控制台,创建一个RAM用户,注意在创建RAM用户时选择允许控制台登录,并允许OpenAPI调用访问。关于如何创建RAM用户,请参见创建RAM用户

image

2. 为RAM用户授权

在扮演RAM角色之前,需要为RAM用户授予扮演RAM角色的权限。使用账号B为该RAM用户授予AliyunSTSAssumeRoleAccess权限,以允许该RAM用户扮演所有RAM角色。有关如何为RAM用户授权的详细信息,请参见为RAM用户授权

说明

如果希望该RAM用户仅能扮演指定的RAM角色,具体操作,请参见能否指定RAM用户具体可以扮演哪个RAM角色

扮演RAM角色

云产品控制台扮演角色

  1. 使用账号B创建的RAM用户访问RAM用户登录页进行登录。

  2. 将鼠标悬停在右上角头像的位置,单击切换身份image

  3. 角色切换页面,输入相关信息后,请单击提交进行登录。本示例输入的值为账号A的账号ID(UID)和RAM角色名称。image

  4. 验证是否具有访问权限。

    登录ECS控制台,查看阿里云ECS实例信息。

    验证一:RAM用户能够扮演账号A的RAM角色查看ECS实例信息,也可通过远程连接工具Workbench登录到实例上。

    image

    验证二:当账号A取消授权时,RAM用户将无法查看账号A中的ECS实例信息。

    image

程序调用时扮演

您也可以通过程序代码实现对账号A的资源访问。大体流程如下:

  1. 将账号B创建RAM用户时获取的AccessKey写入系统环境变量。不同系统的设置方法存在差异,请参考在Linux、macOS和Windows系统配置环境变量

  2. 使用账号B的RAM用户调用AssumeRole接口,传入账号A的RAM角色的ARN,换取临时身份凭证STS Token。

  3. 使用换取的临时身份凭证STS Token调用云服务提供的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) {
        // 仅供示例参考,务必根据业务实际情况选择特定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);
        // 调用云服务提供的API查看账号A的云资源。
        accessResources(assumeRoleResponse, ecsEndpoint, regionId);
    }

    /**
     * 使用临时访问凭证访问云资源。
     *
     * @param assumeRoleResponse 包含临时访问凭证的响应对象。
     */
    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);

            // 创建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();

            // 调用DescribeInstances接口并获得响应。
            DescribeInstancesResponse response = ecsClient.describeInstancesWithOptions(describeInstancesRequest, runtimeOptions);
            // 打印响应结果。
            System.out.println(new Gson().toJson(response.body));
        } catch (Exception e) {
            throw new RuntimeException("AccessResources failed: " + e.getMessage());
        }
    }

    /**
     * 扮演RAM角色获取临时访问凭证。
     *
     * @return 包含临时访问凭证的响应对象。
     */
    private static AssumeRoleResponse playRamRole(String stsEndpoint, String ramRoleArn) {
        try {
            // 创建StsClient对象,并调用assumeRole服务获取STS Token
            Config config = new Config()
                    // System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID")表示从环境变量中获取AccessKey ID的值
                    .setAccessKeyId(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID"))
                    // System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET")表示从环境变量中获取AccessKey Secret的值
                    .setAccessKeySecret(System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET"));
            config.endpoint = stsEndpoint;
            Client client = new Client(config);
            // 创建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());
        }
    }
}

调用结果:程序返回了账号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",
                ...
                // 省略了部分参数的展示
                ...
                "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
}

相关文档

  • 访问控制RAM是阿里云提供的管理用户身份与资源访问权限的服务,更多信息,请参见什么是访问控制

  • 如何使用RAM用户进行角色扮演,请参见扮演RAM角色

  • 什么是角色SSO,请参见角色SSO