大家好,很多朋友,特别是通过合作伙伴或服务商使用 AWS 的同学,可能会发现自己的 IAM Identity Center 功能受限,无法像在组织管理账户里那样轻松配置 CLI 的 SSO (aws configure sso
)。那么,我们就要放弃治疗,退回使用古老的、不安全的静态 IAM 用户密钥吗?
绝对不行!今天,我就教大家如何利用 AWS Cognito,在自己的单一账户内,从零开始打造一个支持 MFA 的 CLI 登录门户,彻底告别固定凭据的烦恼。
在前面提到的场景下,我们需要的本质是一个“身份验证服务”,它能:
Cognito 完美地满足了这三点。它就像一个为我们自己搭建的、轻量级的“迷你 IAM Identity Center”。
而另一个选项 AWS Managed Microsoft AD,虽然技术上也能通过 SAML 联合登录实现,但它是一个重量级且昂贵的解决方案,通常用于已经深度绑定微软生态的大型企业。对于个人或小团队在单一账户下的需求来说,完全是“杀鸡用牛刀”,成本和复杂度都太高了。
所以,我们的主角就是 Cognito。
这个方案的核心是 Cognito 的两大组件协同工作:
整个流程就像这样:
CLI -> Cognito 用户池 (输入密码+MFA码) -> 获取身份令牌 -> Cognito 身份池 (用身份令牌交换) -> 获取临时 AWS 凭证 (Access Key, Secret Key, Session Token)
AdministratorAccess
策略。如果我们只想拥有 S3 的读写权限,就附加相应的策略。这是决定我们登录后能力范围的关键一步。标准的 AWS CLI 没有内置 aws configure cognito
命令,所以我们需要一个小脚本来自动化这个登录流程。我们可以用任何我们喜欢的语言(Python, Bash, PowerShell)来写。
下面是一个使用 Python 和 Boto3 的简单示例 login.py
:
import boto3
import getpass
import configparser
import os
# --- 配置Cognito信息 ---
USER_POOL_ID = 'us-east-1_xxxxxxxxx' # 替换为我们的用户池ID
CLIENT_ID = 'xxxxxxxxxxxxxxxxxxxxxx' # 替换为我们的用户池应用客户端ID
IDENTITY_POOL_ID = 'us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' # 替换为我们的身份池ID
REGION = 'us-east-1' # 替换为我们的区域
ROLE_ARN = 'arn:aws:iam::123456789012:role/Cognito_YourIdentityPoolAuth_Role' # 替换为身份池为我们创建的角色ARN
# -----------------------------
def get_cognito_tokens(username, password):
client = boto3.client('cognito-idp', region_name=REGION)
try:
# 第一步:发起认证
resp = client.initiate_auth(
ClientId=CLIENT_ID,
AuthFlow='USER_PASSWORD_AUTH',
AuthParameters={
'USERNAME': username,
'PASSWORD': password,
}
)
# 第二步:处理MFA挑战
if 'ChallengeName' in resp and resp['ChallengeName'] == 'SOFTWARE_TOKEN_MFA':
mfa_code = input("请输入我们的MFA验证码: ")
resp = client.respond_to_auth_challenge(
ClientId=CLIENT_ID,
ChallengeName='SOFTWARE_TOKEN_MFA',
Session=resp['Session'],
ChallengeResponses={
'USERNAME': username,
'SOFTWARE_TOKEN_MFA_CODE': mfa_code
}
)
return resp['AuthenticationResult']['IdToken']
except client.exceptions.NotAuthorizedException:
print("错误:用户名或密码不正确。")
return None
except Exception as e:
print(f"发生未知错误: {e}")
return None
def get_aws_credentials(id_token):
client = boto3.client('cognito-identity', region_name=REGION)
# 用ID Token从身份池获取身份ID
identity_id_resp = client.get_id(
IdentityPoolId=IDENTITY_POOL_ID,
Logins={
f'cognito-idp.{REGION}.amazonaws.com/{USER_POOL_ID}': id_token
}
)
identity_id = identity_id_resp['IdentityId']
# 用身份ID获取临时AWS凭证
creds_resp = client.get_credentials_for_identity(
IdentityId=identity_id,
Logins={
f'cognito-idp.{REGION}.amazonaws.com/{USER_POOL_ID}': id_token
}
)
return creds_resp['Credentials']
def update_aws_config(credentials):
aws_config_path = os.path.expanduser('~/.aws/credentials')
config = configparser.ConfigParser()
if os.path.exists(aws_config_path):
config.read(aws_config_path)
profile_name = 'cognito-sso' # 我们可以自定义profile名称
if not config.has_section(profile_name):
config.add_section(profile_name)
config.set(profile_name, 'aws_access_key_id', credentials['AccessKeyId'])
config.set(profile_name, 'aws_secret_access_key', credentials['SecretAccessKey'])
config.set(profile_name, 'aws_session_token', credentials['SessionToken'])
with open(aws_config_path, 'w') as configfile:
config.write(configfile)
print(f"凭证已成功更新到profile '{profile_name}' 中。")
print("现在我们可以使用 'aws s3 ls --profile cognito-sso' 来测试了。")
if __name__ == "__main__":
username = input("请输入用户名: ")
password = getpass.getpass("请输入密码: ")
id_token = get_cognito_tokens(username, password)
if id_token:
credentials = get_aws_credentials(id_token)
update_aws_config(credentials)
如何使用这个脚本:
pip install boto3
python login.py
~/.aws/credentials
文件的一个新的 profile 中(例如 [cognito-sso]
)。--profile cognito-sso
来使用这些临时凭证,例如 aws s3 ls --profile cognito-sso
。虽然我们无法使用组织级的 IAM Identity Center,但这并不意味着我们必须在安全上妥协。通过组合 Cognito 用户池和身份池,我们成功地为自己构建了一个轻量、安全、且支持 MFA 的 CLI 登录解决方案。
这个方案不仅解决了我们的燃眉之急,更让我们深入理解了 AWS 身份联邦的强大能力。当我们需要为我们的应用程序或团队成员提供安全的 AWS 资源访问时,这套思路同样适用。