Principal 元素详解:用户、角色、服务
讲完了 Action 和 Resource,我们来看看策略里另一个关键角色——Principal。说白了,Principal 就是那个「谁」的问题。你允许谁进来?你拒绝谁访问?这个「谁」就是 Principal。
我刚开始搞云安全那会儿,总觉得 Principal 不就是个用户嘛,有什么好研究的。后来被坑过几次才明白,Principal 的水很深。它可以是 AWS 用户、IAM 角色,甚至是一个 AWS 服务。而且不同场景下,Principal 的写法差别很大。
一句话总结:Principal 就是「你是谁」的身份证。策略里写清楚它,桶才知道该不该放你进门。
三类 Principal 的底层逻辑
我先给你拆开讲。Principal 在桶策略里主要有三种形式:
- AWS 用户:一个具体的 IAM 用户或根用户。格式像
AWS: arn:aws:iam::123456789012:user/zhangsan。 - IAM 角色:一个角色实体,比如 EC2 实例上挂的角色。格式类似
AWS: arn:aws:iam::123456789012:role/ec2-read-role。 - AWS 服务:某个 AWS 服务代表你发出的请求,比如 S3 的日志写入、 CloudFront 的源站访问。写法是
Service: s3.amazonaws.com这种。
这三种看起来差不多,但背后的授权逻辑完全不同。嗯,这里要注意,别混用。
我的经验:权限最小化原则在 Principal 这里同样适用。能写具体 ARN 就别写通配符,能用角色就别用根用户。我曾经见过有人直接把 Principal: "*" 写进策略,结果桶对外公开了三天才发现。别问我怎么知道的。
1. 用户 Principal:最基础的「身份证」
用户 Principal 是最直观的。你指定一个用户的 ARN,策略就只对该用户生效。但这里有几个坑:
- 主体与授权人是两码事:你写了一个用户 A 允许写桶的策略,但用户 A 的权限还不够,因为桶策略和用户策略是「与」关系。换句话说,用户 A 必须同时有
s3:PutObject的权限才能真正上传。 - 跨账号用户需要写完整 ARN:本账号内可以写简写,但跨账号场景下,必须用
AWS: arn:aws:iam::其他账号ID:user/用户名。少一个字段都可能被拒。 - 根用户 vs. IAM 用户:根用户的 ARN 不带
user/,而 IAM 用户带。写错了,策略直接不认。
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:user/zhangsan"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-bucket/*"
}
上面这段,说白了就是只允许 zhangsan 这个用户读桶里的所有对象。其他人来了,就算他有权显式允许,也要看这个策略的脸色。
避坑提醒:我曾经犯过一个低级错误:把 AWS 拼成了 aws(小写)。大小写敏感,AWS 策略引擎直接忽略这条。排查了半天才发现是大小写不对。所以写 Principal 时,AWS 必须大写,Service 首字母大写。
2. 角色 Principal:适合临时身份的场景
角色比用户更灵活。为什么?因为角色可以跨账号继承,也可以被多个实体扮演。我项目里最常用的场景是:让 EC2 实例读取 S3 桶中的数据。
怎么做?给 EC2 实例附加一个 IAM 角色,然后在桶策略里允许该角色的 ARN。这里有个关键点:
- 角色 ARN 与角色会话 ARN 不同:你写策略时用的是角色本身的 ARN(
arn:aws:iam::account-id:role/role-name)。当 EC2 实例扮演角色后,它会获得一个临时凭证,但这个凭证的来源仍然是角色 ARN。策略匹配的是源头,不是会话 ID。 - 跨账号角色需要显式信任:如果角色在账号 A,桶在账号 B,那账号 B 的桶策略里要写
AWS: arn:aws:iam::账号A:role/角色名。同时,角色本身也要有信任策略允许账号 B 访问。
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/ec2-read-role"
},
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
}
这段路的意思很直接:只允许扮演了 ec2-read-role 这个角色的实体来读桶。其他实体,包括这个账号下的其他用户,只要没扮演这个角色,都进不来。
我建议:角色 Principal 是权限最小化的最好工具。每个应用组件用独立角色,桶策略里只允许这些角色。这样万一某个角色泄露了,不会波及整个桶。
3. 服务 Principal:隐式授权与信任
服务 Principal 最容易被忽视。它指的是 AWS 服务代表你发起的请求。比如 S3 的日志写入、SNS 的推送通知、CloudFront 回源。
为什么需要服务 Principal?因为这些服务在内部会调用你账号下的资源。如果不显式允许,服务可能会报权限错误。举个例子:
- S3 服务写入日志:你开启了 S3 访问日志,日志要写入另一个桶。这时 S3 服务自己会扮演一个角色来写。你需要在目标桶的策略里允许
Service: s3.amazonaws.com。 - CloudFront 回源 S3:你用 CloudFront 加速 S3 内容,CloudFront 需要从 S3 拉取数据。同样需要在 S3 桶策略里允许
Service: cloudfront.amazonaws.com。
{
"Effect": "Allow",
"Principal": {
"Service": "s3.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::log-bucket/AWSLogs/123456789012/*"
}
这段的意思是:允许 S3 服务把我的日志写入 log-bucket 桶里。注意,这里没有指定具体用户,而是授权给了一个服务。服务代表谁?代表你这个账号。
避坑指南:服务 Principal 的权限要清晰。我记得有一次,我写了个允许 Service: s3.amazonaws.com 的策略,但忘了限制 Resource 路径。结果 S3 的日志写到了桶的根目录,和其他数据混在一起。后来我加上了具体的前缀路径,这才干净。
Principal 的矩阵对比:一句话说清差异
| Principal 类型 | 典型场景 | 权限范围 | 跨账号支持 | 常见问题 |
|---|---|---|---|---|
| AWS 用户 | 单个 IAM 用户访问桶 | 仅该用户 | 支持 | 大小写写错、ARN 写错 |
| IAM 角色 | EC2 实例读桶 | 扮演该角色的实体 | 支持(需信任策略) | 角色与角色会话混淆 |
| AWS 服务 | S3 日志写入、CloudFront 回源 | 服务代表账号执行 | 不支持(服务只能在同账号内授权) | 忘记限制 Resource 路径 |
这张图,把 Principal 的关系讲清了
实战中怎么选?我的一些原则
其实没有的对错,主要看场景。我给你几个我的判断标准:
- 如果是个人或固定团队:用 IAM 用户 Principal。简单直接,权限好管理。
- 如果是自动化程序或跨账号:用 IAM 角色 Principal。角色可以轮换凭证,跨账号时也能保持安全。
- 如果是日志、通知等系统服务:用服务 Principal。不要手动写入 IP 或凭证,让 AWS 服务代劳。
我个人的习惯:能写显式 ARN 绝不写 *,能分组授权绝不一把抓。Principal 越小,安全边界越清晰。写完策略后,我会用 IAM 模拟器或 aws s3api 命令测试一下,确保权限刚刚好,不松不紧。