我们一般情况下会把运维账号和业务账号分开,然而又不太可能在每个账号下建立RAM账号管理。而是使用资源目录把各个账号都邀请进来,然后利用角色扮演的方式实现控制对应账号下的资源。作为概念验证,我是直接使用CLI来实现,而不是通过写代码SDK接口实现。下面我先分享3个使用临时凭证的卓越架构:
通过FC函数角色实现临时凭证的获取和使用
通过ECS实例角色实现临时凭证的获取和使用
通过容器服务RRSA实现临时凭证的获取和使用
Landing Zone通常包含资源规划、财务管理、网络规划、安全防护、合规审计、身份权限、运维管理、自动化。而账号的管理在Landing Zone中通常占据非常重要的地位,通过资源目录以及账号中的角色扮演的方式实现跨账号跨BU统一运维控制。
主页:https://www.aliyun.com/landing-zone?spm=a2c4g.29094516.0.0.7a1361df6y7mRA
企业多账号统一架构方案
Light Landing Zone
企业级DMZ上云方案
多账号网络安全统一防护方案
多主体集团账号统一财务管理方案
多账号操作日志统一归集与审计
通过配置审计和函数计算实现云主机风险巡检
通过主账号授权
通过管理账号的RAM用户扮演成员RAM角色的方式登录阿里云控制台
这个文档实际上是直接使用主账号(资源目录的管理账号)AK/SK来实现角色扮演。下面我们多出一步,是先由资源目录下B账号扮演为管理账号,然后再去扮演C账号去操作资源。这其实不是最佳实践,因为在资源目录里面,CloudAdmin(管理账号)是具有最大权限。只不过我们作为概念验证的步骤,先行使用最大权限来验证,需要最佳实践的可以略过本章节。
我们资源目录下账号B,想要获得资源目录下账号C的权限可以通过先角色扮演为主账号A,然后再扮演C。
A账号UID: A0001
B账号UID: B0002
C账号UID: C0003
首先先在主账号创建角色,角色允许另一云账号(UID:B0002)操作,并授权这个角色以下权限:AliyunSTSAssumeRoleAccess,AliyunResourceDirectoryFullAccess。
复制好这个角色的ARN路径,例如:acs:ram::A0001:role/rd-allow-assume-root-from-B0002
在运维部署账号B中创建RAM用户并保存AccessKey ID和AccessKey Secret。
使用CLI概念验证:
aliyun configure --mode AK --profile AccountB aliyun sts AssumeRole --region cn-beijing --DurationSeconds 3600 --RoleArn acs:ram::A0001:role/rd-allow-assume-root-from-B0002 --RoleSessionName alice
RoleSessionName是用于审计,你叫什么都可以。
RoleArn就是填写之前我们在主账号创建角色的ARN。 这个授权是让B账户获取资源目录全部权限,也就是允许资源目录下成员扮演管理账号。 acs:ram::<对应的UID>:role/rd-allow-assume-root-from-B0002
DurationSeconds就说最长有效期多少秒,实际测试最多只能填3600(1小时)。
输出如下:
{
"AssumedRoleUser": {
"Arn": "acs:ram::A0001:role/rd-allow-assume-root-from-B0002",
"AssumedRoleId": "1111111111111111:alice"
},
"Credentials": {
"AccessKeyId": "STS.f1Aap4ykmxWkQLt7rsb6kZId3",
"AccessKeySecret": "lyQBUVVwySDFxd6eUAYF66GGlC1QWpvvlXpl8zfXgJi",
"Expiration": "2025-03-12T07:32:26Z",
"SecurityToken": "YE1pddbiSFxk436EEgW5lv0rXpUefySwpyiy0HgpJsCcW1bw41JaVFWxFo1EPILJEb6VvbTcd7auetIKho1piiFVJrqDLc24Cwez16I387VW8WeSe7FUocMGvTC32Sjwa3hQ4zDM4hMad4I6pzRiwKaED9llQqK9slrbc9BBU3JirRj0ykvZp9A5XewKAYqXplY7piYGEHKkI5VXQpaDZU1ohGcRYyYSzWCxDZGbHsGaZIE5y5wDDrBz8AJ5uWFLoprVYAWW2bpaEOzEEhG1yYzBBH4zyhA346fgJ3d1fERiwKkDLaxvgFUoumuHdVzvJhhKE9PfsLZoPQdUBuiVfXwxhyzgjbBoucrYdeLhZTURk75xtaaZmsAU0qAyyMuQiDEyt9DRmJqaOKKvDuZRYyOfD6D3fpYomlgxPWXZ15sHpuiEwpYmxiifZs0rPeFpampl1tbui6XVMvh733gWLEutHAZ4Tpz70zxsSewyZfYRMiwoIWV82kbSyrpPdmOoS7oO8eOSzSZYbMF4WvsrvA15PFRDbFhHd3vBSS6Pjzkd63hKZAmc4Z5IWTFTxrxp9ipQSLqbilTfIhkCX7MTHpCoLi0d8rYwDDVzx21t0RlVzAjBs3pKK2hd51qz"
},
"RequestId": "5aa57662-4faa-4d9e-ba32-e9726c715660"
}
然后我们使用aliyun configure --mode StsToken --profile assume-root-from-B0002将AK/SK和Token保存。
这时候我们已经在主账号下了,但是我们只有角色扮演和查看资源目录权限,其实是看不到主账号下的内容的。我们接着扮演角色到子账号C上。
因为是主账号,本身加入资源目录的时候已经自动加了角色(resourcedirectoryaccountaccessrole),允许主账号操作。所以接下来我们:aliyun sts AssumeRole --region cn-beijing --DurationSeconds 3600 --RoleSessionName alice --RoleArn acs:ram::C0003:role/resourcedirectoryaccountaccessrole
然后再继续记录AK/SK和Token,并使用这STS AK密钥方式来登录CLI(也就是实现了扮演角色)。这个时候,你就已经可以DescribeInstances来操作账号C的资源了。
使用主账号授权的好处是,只要你知道资源目录下UID,就能直接控制。
授权资源目录下B账号操作C或D账号
但是有没有可能我完全不希望主账号创建角色,而是在C账号,D账号等其他资源目录账号创建角色并授权B账号查看或者操作呢?
我们先忽略掉如何批量给资源目录下的账号创建角色问题,直接B-->C同样也是可行的。不过授权角色上,不同于管理账号授权,我们只需要授权想让B账号看到什么或者操作什么即可,比如ECS Full Access。
我们只需要在对应账号创建角色,并授权允许对应的权限即可。
acs:ram::UIDC0003:role/allow-access-from-B0002
aliyun sts AssumeRole --region cn-beijing --DurationSeconds 3600 --RoleArn acs:ram::UIDC0003:role/allow-access-from-B0002 --RoleSessionName alice
获得AK/SK和STS Token后,我们就能扮演C账号来管理了。
Python 角色扮演概念验证
将从B账号扮演C账号,然后列出C账号ECS资源。
# -*- coding: utf-8 -*-
# This file is auto-generated, don't edit it. Thanks.
import os
import sys
from typing import List
from alibabacloud_sts20150401.client import Client as Sts20150401Client
from alibabacloud_ecs20140526.client import Client as Ecs20140526Client
from alibabacloud_tea_openapi import models as open_api_models
from alibabacloud_sts20150401 import models as sts_20150401_models
from alibabacloud_ecs20140526 import models as ecs_20140526_models
from alibabacloud_tea_util import models as util_models
from alibabacloud_tea_util.client import Client as UtilClient
from alibabacloud_credentials.client import Client as CredClient
from alibabacloud_tea_rpc.models import Config
class Sample:
def __init__(self):
pass
@staticmethod
def create_sts_client() -> Sts20150401Client:
"""
使用AK&SK初始化STS Client
@return: Sts20150401Client
"""
config = open_api_models.Config(
access_key_id='AAAAAAAAA',
access_key_secret='BBBBBBBBBBBBB'
)
# STS Endpoint 请参考 https://api.aliyun.com/product/Sts
config.endpoint = f'sts.cn-beijing.aliyuncs.com'
return Sts20150401Client(config)
@staticmethod
def create_ecs_client(access_key_id: str, access_key_secret: str, security_token: str) -> Ecs20140526Client:
"""
使用临时凭证初始化ECS Client
@param access_key_id: 临时 AccessKey ID
@param access_key_secret: 临时 AccessKey Secret
@param security_token: STS Token
@return: Ecs20140526Client
"""
config = open_api_models.Config(
access_key_id=access_key_id,
access_key_secret=access_key_secret,
security_token=security_token
)
# ECS Endpoint 请参考 https://api.aliyun.com/product/Ecs
config.endpoint = f'ecs.cn-beijing.aliyuncs.com'
return Ecs20140526Client(config)
@staticmethod
def assume_role(client: Sts20150401Client) -> dict:
"""
调用 AssumeRole 获取临时凭证
@param client: Sts20150401Client
@return: 包含临时凭证的字典
"""
request = sts_20150401_models.AssumeRoleRequest(
role_arn='acs:ram::UIDC0003:role/allow-from-B0002', # 替换为你的角色 ARN
role_session_name='alice' # 替换为你的会话名称
)
runtime = util_models.RuntimeOptions()
response = client.assume_role_with_options(request, runtime)
credentials = response.body.credentials
sts_result = {
'AccessKeyId': credentials.access_key_id,
'AccessKeySecret': credentials.access_key_secret,
'SecurityToken': credentials.security_token
}
print("Temporary Credentials:", sts_result) # 添加的调试打印语句
return sts_result
@staticmethod
def describe_ecs_instances(client: Ecs20140526Client) -> List[dict]:
"""
查询 ECS 实例信息
@param client: Ecs20140526Client
@return: ECS 实例列表
"""
request = ecs_20140526_models.DescribeInstancesRequest(
region_id='cn-beijing'
)
runtime = util_models.RuntimeOptions()
response = client.describe_instances_with_options(request, runtime)
instances = []
for instance in response.body.instances.instance:
private_ip = instance.vpc_attributes.private_ip_address.ip_address[
0] if instance.vpc_attributes and instance.vpc_attributes.private_ip_address.ip_address else "N/A"
instances.append({
'InstanceId': instance.instance_id,
'InstanceName': instance.instance_name,
'PrivateIP': private_ip,
'Status': instance.status
})
return instances
def main():
try:
# 初始化 STS 客户端
sts_client = Sample.create_sts_client()
# 获取临时凭证
credentials = Sample.assume_role(sts_client)
# 使用临时凭证初始化 ECS 客户端
ecs_client = Sample.create_ecs_client(
access_key_id=credentials['AccessKeyId'],
access_key_secret=credentials['AccessKeySecret'],
security_token=credentials['SecurityToken']
)
# 查询 ECS 实例信息
instances = Sample.describe_ecs_instances(ecs_client)
# 打印 ECS 实例信息
print("ECS Instances:")
for instance in instances:
print(f"Instance ID: {instance['InstanceId']}")
print(f"Instance Name: {instance['InstanceName']}")
print(f"Private IP: {instance['PrivateIP']}")
print(f"Status: {instance['Status']}")
print("-" * 40)
except Exception as e:
print(f"Error: {e}")
if __name__ == '__main__':
main()