如何在通过 SSO 凭证连接时使用 AWS Python SDK

use*_*214 6 python boto amazon-web-services single-sign-on

我正在尝试创建一个 python 脚本来连接到我的 AWS 账户并与之交互。我在这里阅读了它https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html

我看到它从 ~/.aws/credentials (在 Linux 机器上)读取您的凭据。然而,我并没有与 IAM 用户连接,而是与 SSO 用户连接。因此,我使用的配置文件连接数据位于 ~/.aws/sso/cache 目录。

在该目录中,我看到两个 json 文件。一个有以下键:

  • 起始网址
  • 地区
  • 访问令牌
  • 过期时间

第二个有以下键:

  • 客户编号
  • 客户秘密
  • 过期时间

我在文档中没有看到关于如何告诉它使用我的 SSO 用户的任何地方。

因此,当我尝试运行我的脚本时,我收到错误,例如

botocore.exceptions.ClientError: An error occurred (AuthFailure) when calling the DescribeSecurityGroups operation: AWS was not able to validate the provided access credentials
Run Code Online (Sandbox Code Playgroud)

即使我可以从命令提示符运行相同的命令。

2ps*_*2ps 22

于 2023 年 10 月 23 日更新了最新的 boto3(帽子评论者@Adam Smith,他的无形之手引导我们更新在新版本的 boto3 中提取角色凭证):

所以这是在 上测试的又长又毛茸茸的答案boto3==1.28.69

这是一个八步过程,其中:

  1. 注册客户端使用sso-oidc.register_client
  2. 使用启动设备授权流程sso-oidc.start_device_authorization
  3. 使用以下命令将用户重定向到 sso 登录页面webbrowser.open
  4. 轮询sso-oidc.create_token直到用户完成登录
  5. 使用以下命令列出帐户角色并将其呈现给用户sso.list_account_roles
  6. 使用获取角色凭证sso.get_role_credentials
  7. 使用 (6) 中的会话凭据创建新的 boto3 会话
  8. 吃一块饼干

第 8 步确实很关键,作为任何成功授权流程的一部分,不应被忽视。

在下面的示例中,account_id应该是您尝试获取其凭据的帐户的帐户 ID。并且start_url应该是 aws 为您生成的用于启动 sso 流程的 url(在 AWS SSO 管理控制台的“设置”下)。

from time import time, sleep
import webbrowser
from boto3.session import Session

# if your sso is setup in a different region, you will
# want to include region_name=sso_region in the 
# session constructor below
session = Session()
account_id = '1234567890'
start_url = 'https://d-0987654321.awsapps.com/start'
region = 'us-east-1' 
sso_oidc = session.client('sso-oidc')
client_creds = sso_oidc.register_client(
    clientName='myapp',
    clientType='public',
)
device_authorization = sso_oidc.start_device_authorization(
    clientId=client_creds['clientId'],
    clientSecret=client_creds['clientSecret'],
    startUrl=start_url,
)
url = device_authorization['verificationUriComplete']
device_code = device_authorization['deviceCode']
expires_in = device_authorization['expiresIn']
interval = device_authorization['interval']
webbrowser.open(url, autoraise=True)
for n in range(1, expires_in // interval + 1):
    sleep(interval)
    try:
        token = sso_oidc.create_token(
            grantType='urn:ietf:params:oauth:grant-type:device_code',
            deviceCode=device_code,
            clientId=client_creds['clientId'],
            clientSecret=client_creds['clientSecret'],
        )
        break
    except sso_oidc.exceptions.AuthorizationPendingException:
        pass
 
access_token = token['accessToken']
sso = session.client('sso')
account_roles = sso.list_account_roles(
    accessToken=access_token,
    accountId=account_id,
)
roles = account_roles['roleList']
# simplifying here for illustrative purposes
role = roles[0]

# earlier versions of the sso api returned the 
# role credentials directly, but now they appear
# to be in a subkey called `roleCredentials`
role_creds = sso.get_role_credentials(
    roleName=role['roleName'],
    accountId=account_id,
    accessToken=access_token,
)['roleCredentials']
session = Session(
    region_name=region,
    aws_access_key_id=role_creds['accessKeyId'],
    aws_secret_access_key=role_creds['secretAccessKey'],
    aws_session_token=role_creds['sessionToken'],
)
Run Code Online (Sandbox Code Playgroud)


geo*_*nza 9

您当前的 .aws/sso/cache 文件夹结构如下所示:

$ ls
botocore-client-XXXXXXXX.json       cXXXXXXXXXXXXXXXXXXX.json
Run Code Online (Sandbox Code Playgroud)

这 2 个 json 文件包含 3 个有用的不同参数。

botocore-client-XXXXXXXX.json -> clientId and clientSecret
cXXXXXXXXXXXXXXXXXXX.json -> accessToken
Run Code Online (Sandbox Code Playgroud)

使用 cXXXXXXXXXXXXXXXXXXXXX.json 中的访问令牌,您可以调用get-role-credentials。该命令的输出可用于创建新会话。

您的 Python 文件应该如下所示:

import json
import os
import boto3

dir = os.path.expanduser('~/.aws/sso/cache')

json_files = [pos_json for pos_json in os.listdir(dir) if pos_json.endswith('.json')]

for json_file in json_files :
    path = dir + '/' + json_file
    with open(path) as file :
        data = json.load(file)
        if 'accessToken' in data:
            accessToken = data['accessToken']

client = boto3.client('sso',region_name='us-east-1')
response = client.get_role_credentials(
    roleName='string',
    accountId='string',
    accessToken=accessToken
)

session = boto3.Session(aws_access_key_id=response['roleCredentials']['accessKeyId'], aws_secret_access_key=response['roleCredentials']['secretAccessKey'], aws_session_token=response['roleCredentials']['sessionToken'], region_name='us-east-1')
Run Code Online (Sandbox Code Playgroud)


Kar*_*nka 6

这已在boto3 1.14修复

因此,鉴于您的个人资料中有这样的个人资料~/.aws/config

[profile sso_profile]
sso_start_url = <sso-url>
sso_region = <sso-region>
sso_account_id = <account-id>
sso_role_name = <role>
region = <default region>
output = <default output (json or text)>
Run Code Online (Sandbox Code Playgroud)

然后登录 $ aws sso login --profile sso_profile

您将能够创建一个会话:

import boto3
boto3.setup_default_session(profile_name='sso_profile')
client = boto3.client('<whatever service you want>')
Run Code Online (Sandbox Code Playgroud)