我给自己写了一个IAM策略,用于一个太阳能报告代理。当时一次写就,自我感觉良好,以为已经做到了最小权限。
后来逐行细读,才发现并没有。
策略长什么样
这个代理实际只做两件事:从Secrets Manager读取一个密钥,在Bedrock上调用一个Claude模型生成每日报告。就两个动作。我按照这两个动作写了策略,当时还挺满意,直到我回头一行一行地看,才发现自己实际授予的权限和记忆里写的不完全一样。
下面是真实挂在角色上的策略:
{"Version": "2012-10-17","Statement": ["Sid": "SecretsManagerReadConfig","Effect": "Allow","Action": "secretsmanager:GetSecretValue","Resource": "arn:aws:secretsmanager:us-east-1:111122223333:secret:solar-daily-brief/config-*"},"Sid": "BedrockInvokeModel","Effect": "Allow","Action": "bedrock:InvokeModel","Resource": ["arn:aws:bedrock:*::foundation-model/anthropic.claude-*","arn:aws:bedrock:us-east-1:111122223333:inference-profile/us.anthropic.claude-*"}第一段没问题,第二段开了大口子
先看第一条。一个动作,一个密钥,结尾的-*只是覆盖Secrets Manager在ARN后面自动追加的随机后缀。也就是说,这一条实际上只授权访问一个真实存在的资源,非常精准。
再看第二条。anthropic.claude-*匹配的是账号范围内所有Claude模型,而代码实际只调用其中一个。资源里的bedrock:*::还带了区域通配符,意味着任意区域都可以。这个范围显然超出实际需要。
同一个代理,同一个策略,两条语句的精度完全不同。这个落差就是这篇文章的重点。
这种宽松是怎么来的
没有人会主动决定写一个宽松的策略。它往往是直觉“顺手”产生的。你大概知道代码做什么,就大概给一下权限,想着“多给一点更安全”,然后就过去了。
Secrets Manager那条之所以精确,是因为从控制台复制一个ARN很简单,而且只有一个密钥需要指向。Bedrock那条就松了,因为我没有去看一个具体的ARN,而是在脑子里想“Claude模型”,于是anthropic.claude-*看起来是个合理的表达,省得我去查代码里到底用的哪个模型ID。
问题不在于“再努力记清楚一点”,而在于授权时不该靠记忆,应该靠代码实际做了什么。
代码实际需要的权限
代理自己的源码能解决直觉说不清的事。deye_client.py和collector.py中,对secretsmanager:*的调用只有一次get_secret_value(),针对的是一个固定密钥名,启动时解析一次,进程生命周期内缓存。写报告那一步调用bedrock-runtime.invoke_model(),模型ID从配置读取,只用了一个,不是一整个模型族,也不是运行时动态选择。
这个项目里没有任何代码路径需要第二个密钥或第二个模型。策略里多给的部分,不是最小权限,而是穿着最小权限外衣的猜测。
检查任何策略的方法
把这段逻辑推广到任何一个IAM策略:
- 打开实际代码,找到所有对云服务的调用点
- 记录每个调用涉及的资源ARN和操作
- 对照策略里的每一条Action和Resource,删除没有对应调用的部分
- 特别注意通配符:是覆盖一个确定前缀,还是覆盖了一整类资源
- 检查区域字段,避免用代替具体区域
策略里的每一个字符,都应该能对应到代码里的具体行为。如果对应不上,那它就不是最小权限,只是看起来像。
特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.