AWS 切换用户
AWS 要授权给他人访问指定资源有哪几种方式呢?
- 在自己帐号下创建一个用户,把 Access Key ID 和 Secret Access Key 告诉别人。可为该用户限定权限,但任何获得那两个 Key 的人都能使用该用户。不够安全。
- 创建一个 IAM Role, 并指定谁(帐号或 Role) 能以该 Role 的身份来访问。被 Assume 的 Role 可限定权限和会话有效期。
所以,用 Assume Role 的方式具有更高的安全可控性,还不用维护 Access Key ID 和 Secret Access Key。
比如在构建和部署时通常是有一个特定的 Account, 然后 Assume 到别的 IAM Role 去操作资源。
本文将详细介绍在帐号 A 创建一个 IAM Role(标注为 R) 并分配一些权限,然后允许另一个帐号 B 以 IAM Role - R 的身份来访问帐号 A 下的资源。
IAM Role 将用 awscli 来创建,Assume Role 的过程用 awscli 和 boto3 Python 代码两种方式来演示。
已知两个账号A,B
~/.aws/credentials 添加好key
[a]
aws_access_key_id=AKIA5*****USBKPN4DIH
aws_secret_access_key=OdUsUew**********MEgoC8*****9LCvbqkaCQQS
[b]
aws_access_key_id=AKIA*****2USOGAHFVAU
aws_secret_access_key=b2nXQ**********7EuBO*****5ngKM3Msg2CLqma
帐号 A 下创建 IAM Role
aws s3 ls --profile a
# 或者用环境变量,这是更推荐的方式
export AWS_DEFAULT_PROFILE=a
# 查看账户A下的S3资源
aws s3 ls
# 在账号A下创建 test-assumed-role
aws iam create-role --role-name test-assumed-role --assume-role-policy-document file://role-trust-policy.json
# 给新建的 test-assumed-role 加上 S3 的只读权限
aws iam attach-role-policy --role-name test-assumed-role --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
帐号 B Assume 帐号 A 的 role
export AWS_DEFAULT_PROFILE=b
# 924612875556是账户A的account id
# 这句话的意思是用,将账户B切换为账户A
aws sts assume-role --role-arn arn:aws:iam::924612875556:role/test-assumed-role --role-session-name awscli-session
返回内容:
{
"Credentials": {
"AccessKeyId": "ASIA*******O5OOFMMB",
"SecretAccessKey": "qLR4rNZ*******PPJAIBx22plNN8oWIRtp2bbq",
"SessionToken": "IQoJb3JpZ2luX2VjEPb//////////wEaDmFwLW5v*******xIkcwRQIgCKgl/h9gP4430qtSRfnp*******VddkekMUcN2ECIQC06q/7vYhcVMj7jujstIVzBhecnYQgB3bZf0l5qaxjzyqbAggwEAEaDDkyNDYxMjg3NTU1NiIMKP1BdAa6NQhoo2FYKvgBy5B1tyKn0GPz7DwG+YWxdfc9+ayNwzulKsF895wLpzuC9Hkyd2+KL22PgcaAOHV+PU3CPicDS8xTlanAQZvlPQy3egXv+JNOwlrJaVmyKuNbtzGCpYlBFs9TnC1sD+Uz0MGtXPh3GLhoZZ9gHt7fktDwohoz5+fbA+6zXUvO4xmFAicoYy7PCSM1v8weQ+oXqMAFREJ3Pd3Zs3y5adQYK100+reEJ1uvMIIdk3KSKYsF3T8ZByU+MdP+YBSgilfaY/YVgXExUp0B2dwWMRRh95FSdmmIfAtqSrt/0mXhah5zxTaoVxbPUT68A6Fj4Gecw+3iZiIeM2MwycSrlgY6nQGlo4fNrVvHEgw8yBFPE6wiY+jAi1vLNplxJ1WN59OMK+0rfdyBO91JFeoOEiQNXzbZJSorI2SuEUi3dVgVotvGwCMYsOYByM4zyJa9tdsjXTKX6UL2CdHyGKm6y5QK1DhXhl9mtEMqNqEWoQN4LkgGHv/4fzJLoFqKO2cC+VZDQ40AofaTVEsKaJjU3zt3NCUa+Ltq5qyfyTkHxoky",
"Expiration": "2022-07-10T15:29:29+00:00"
},
"AssumedRoleUser": {
"AssumedRoleId": "AROA5ORZY2USEASI2XI4F:awscli-session",
"Arn": "arn:aws:sts::924612875556:assumed-role/test-assumed-role/awscli-session"
}
}
这时候得到一组新的 AccessKeyId, SecretAccessKey 和 SessionToken,可以在 ~/.aws/credentials
中配置一个新的 profile C, 然后 export AWS_DEFAULT_PROFILE=C 来使用。 或都用 export 分别导出三个环境变量 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, 和 AWS_SESSION_TOKEN, 分别对应前面的三个值。
$ export AWS_ACCESS_KEY_ID=<Credentials.AccessKeyId>
$ export AWS_SECRET_ACCESS_KEY=<Credentials.SecretAccessKey>
$ export AWS_SESSION_TOKEN=<Credentials.SessionToken>
aws s3 ls --profile c
# 想访问超出 test-assumed-role 之外的权限将被提示 Access Denied
aws s3 cp Desktop/jump-server.sh s3://blog.finleyma.ml --profile ty-assume
upload failed: Desktop/jump-server.sh to s3://blog.finleyma.ml/jump-server.sh An error occurred (AccessDenied) when calling the PutObject operation: Access Denied
# 查看当前所使用的角色
aws sts get-caller-identity --profile c
{
"UserId": "AROA5ORZY2USEASI2XI4F:awscli-session",
"Account": "924612875556",
"Arn": "arn:aws:sts::924612875556:assumed-role/test-assumed-role/awscli-session"
}
用 Python 的 boto3 包实现
帐号 B 登陆,调用 boto3 的 sts.assume_role() 函数切换到帐号 A 下的 IAM Role test-assumed-role,之后的操作就限定到 test-assumed-role 的约束中了。
import boto3
aws_credentials_b = {
'region_name': 'us-east-1',
'aws_access_key_id':'PNKDIESJGWAURFEWDLLT',
'aws_secret_access_key':'TdTMlDUSKecRadKeMlNIBEmIkRjmZOSvtnhgQDZc',
'aws_session_token':'IQoJb3JpZ2luX2VjEDYabEbMG5J2lzlv......IEQisSAwzmnkv7LNf+'
}
sts=boto3.client('sts', **aws_credentials_b)
stsresponse = sts.assume_role(
RoleArn="arn:aws:iam::123456789011:role/test-assumed-role", # under account A
RoleSessionName='assumed'
)
aws_credentials_assumed_role = {
'region_name':'us-east-1',
'aws_access_key_id':stsresponse["Credentials"]["AccessKeyId"],
'aws_secret_access_key':stsresponse["Credentials"]["SecretAccessKey"],
'aws_session_token':stsresponse["Credentials"]["SessionToken"]
}
boto3.setup_default_session(**aws_credentials_assumed_role)
s3 = boto3.client('s3')
buckets_of_a = [bucket['Name'] for bucket in s3.list_buckets()['Buckets']]
当然,使用 Python 的话可以进一步封装,比如默认以帐号 B 登陆,然后执行一个函数 switch_role(role_arn) 后,后续的 boto3 client 就全部变成了 assumed role 的角色了
import boto3
def switch_role(assume_role_arn):
sts=boto3.client('sts')
sts_res = sts.assume_role(RoleArn=assume_role_arn, RoleSessionName='new_session')
new_credentials = {'aws' + re.sub('([A-Z]+)', r'_\1', key).lower(): value
for (key, value) in sts_res["Credentials"].items() if key != 'Expiration'}
boto3.setup_default_session(**new_credentials)
switch_role('arn:aws:iam::123456789011:role/test-assumed-role')
s3 = boto3.client('s3')
buckets_of_a = [bucket['Name'] for bucket in s3.list_buckets()['Buckets']]
把 sts_res['Credentials'] 转换为 session 要求的格式是简化,但是要注意以后 assume_role() 响应格式的变化有可能影响到程序的正常执行。
参考
https://docs.aws.amazon.com/zh_cn/IAM/latest/UserGuide/tutorial_cross-account-with-roles.html