凭证配置:环境变量、AWS 配置文件与 IAM 角色

说实话,我刚开始用 AWS SDK 那会儿,最头疼的就是“到底该在哪设凭证”。环境变量?配置文件?还是直接写在代码里?
后来被同事提醒了一句——永远别把 Access Key 硬编码。从那以后,我才认真研究起凭证配置的几种正规姿势。

Secrets Manager SDK(底层就是 boto3)获取凭证有一套固定的查找顺序。理解这个顺序,你就知道该把密钥放在哪。我把它画成了下面的流程,帮你一眼看明白。

boto3 凭证查找顺序(优先级从上到下) 环境变量 AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY AWS 配置文件 ~/.aws/credentials ~/.aws/config IAM 角色(EC2 实例、ECS 任务等) 自动从元数据服务获取 其他(容器、SSO、进程等) ⏳ 如果上一级没有找到有效凭证,SDK 自动降级到下一级。全部没有则抛出 NoCredentialsError。

1. 环境变量:最简单,也最容易翻车

我个人习惯在本地开发时用环境变量——因为改起来最快。你只要导出两个变量:

export AWS_ACCESS_KEY_ID=AKIDEXAMPLE
export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY

然后你的 boto3 客户端就能直接工作。SDK 内部会先检查 os.environ,找到就用。

一点经验:我会在 .bashrc 或者项目根目录放一个 .env 文件(记得加到 .gitignore 里)。但注意,环境变量对 export 的 session 生效,重启终端就没了。我吃过一次亏——调试到一半切了个 tab,环境变量没带过来,报了个 AccessDenied,愣是查了半天。

不过,环境变量也有个小坑:如果你同时配了临时凭证(AWS_SESSION_),必须三个一起设,缺一个会报失效。还有,别把生产环境的 Key 设到全局变量里——一旦不小心被日志打印出来,安全风险很大。

2. AWS 配置文件:项目级的推荐姿势

我在团队里一直推荐用配置文件。它比环境变量更结构化,而且支持多个 profile。你可以在 ~/.aws/credentials 里这样写:

[default]
aws_access_key_id = AKIDEXAMPLE
aws_secret_access_key = wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY

[production]
aws_access_key_id = AKIDPROD
aws_secret_access_key = ProDSecretExampleKey123

然后在 ~/.aws/config 里配 region 和 output 格式:

[default]
region = us-east-1
output = json

[profile production]
region = ap-northeast-1
output = json

调用时通过 profile_name 指定:

import boto3
session = boto3.Session(profile_name='production')
client = session.client('secretsmanager')
注意:config 文件里的 profile 名字要加 profile 前缀,credentials 文件里不加。这是一个非常容易忘记的细节——我曾经就因为配置文件和凭证文件里的 profile 名称不一致,折腾了半小时。

3. IAM 角色:生产环境的标准答案

如果你把应用部署到 EC2、ECS 或者 Lambda 上,千万不要再手动塞 Key 了。用 IAM 角色。给计算资源绑定一个角色,SDK 会自动从实例元数据服务(IMDS)获取临时凭证。

代码里什么都不用写:

import boto3
# 只要实例绑定了 IAM 角色,下面这句话自动找到凭证
client = boto3.client('secretsmanager')
region_name = 'ap-southeast-1'  # 通常从实例 metadata 拿到

IAM 角色背后是 AWS STS 服务颁发的短期凭证,默认有效期 6 小时,SDK 会自动续期。我在一次生产故障复盘里发现,之前有人把长期 Access Key 写死在容器环境变量里,半年后 Key 泄露导致账单爆增。换用 IAM 角色后,这个问题再也没出现过。

我曾经踩过的坑:EC2 实例的 IAM 角色需要重启实例才能生效(旧会话不会自动刷新)。另外,如果实例使用了 IMDSv2 限制,而你用的 SDK 版本太低,可能拿不到凭证。检查一下 boto3 >= 1.15.0 和 botocore >= 1.18.0。

三种方式的对比

方式适用场景安全等级推荐指数
环境变量本地开发、快速测试⭐ 较低(可能泄露到日志)⭐⭐⭐
配置文件个人项目、单个应用⭐⭐ 中等(文件权限需设好)⭐⭐⭐⭐
IAM 角色生产部署(EC2/ECS/Lambda)⭐⭐⭐⭐⭐ 高(临时凭证 + 自动轮转)⭐⭐⭐⭐⭐

另外还有一个细节:如果你同时配了环境变量和配置文件,环境变量优先级更高。所以调试时你可以用环境变量覆盖配置文件里的 Key,非常方便。

一个典型的初始化示例

把上面三种方式整合到一个函数里,保证在任何环境都能用:

import boto3
import os

def get_secrets_client(profile_name=None, region_name='ap-northeast-1'):
    """安全地创建 Secrets Manager 客户端"""
    
    # 1. 优先用环境变量
    if os.environ.get('AWS_ACCESS_KEY_ID'):
        return boto3.client('secretsmanager', region_name=region_name)
    
    # 2. 其次用 profile
    if profile_name:
        session = boto3.Session(profile_name=profile_name)
        return session.client('secretsmanager', region_name=region_name)
    
    # 3. 最后走默认链(包含 IAM 角色)
    return boto3.client('secretsmanager', region_name=region_name)

这个模式我在好几个项目里用过。本地跑单元测试就设个环境变量,CI/CD 里用 profile,上生产直接靠 IAM 角色——代码一行不用改。

避坑指南

  • 永远别硬编码——Access Key 如果在代码库里提交了,即使马上删掉,它仍可能存在于 Git 历史中。用 git-secrets 或者 truffleHog 扫描一下。
  • 临时凭证过期处理:如果你手动调用 STS 拿临时凭证,记得存到 AWS_SESSION_ 里。SDK 遇到 401 会尝试刷新,但不是所有操作都支持,最好在代码里加个重试。
  • 配置文件权限:~/.aws/credentials 文件应该设成 600 权限。我见过有人把配置文件的权限设成 644,结果被共享服务器上的其他用户读到——直接拉取了一把 Key。
  • 多账户场景:可以使用 AWS_PROFILE 环境变量来指定默认 profile,不用每次都传 profile_name。不过记得检查你当前 terminal 是否不小心设了这个变量,容易混淆。

好了,凭证配置这块就这么多。我的建议是:本地开发先用环境变量试水,项目稳定后切到配置文件,上生产一定用 IAM 角色。你想想看,这样既灵活又安全,是不是挺简单的?