欢迎光临
我们一直在努力

几十条 IAM policy 还在抄桶标签?用 OPA 给 RustFS 做集中授权

封面

很多团队给 bucket 打了环境、团队、合规等级标签,访问控制却还要在 IAM policy 里再抄一遍:同一个桶,IAM 里写一条带 Condition 的 policy,OPA 或审计那边又描述一遍"这是生产桶",改一次策略要全局搜几十个文件。RustFS 的外部策略插件把桶上真实存储的标签送进了授权请求(#8298 起),OPA 这类策略引擎可以直接拿标签当条件。标签从挂在桶上的备注,变成了授权判断的输入。

RustFS 的 OPA 集成是 fail-closed 的

启用靠两个环境变量:RUSTFS_POLICY_PLUGIN_URL(必填,指向 OPA 的 POST 数据端点,例如 http://localhost:8181/v1/data/rustfs/authz/allow)、RUSTFS_POLICY_PLUGIN_AUTH_TOKEN(可选,作为 Authorization: Bearer 头发送)。没配 URL 时走纯内置评估,OPA 完全不参与;配了但还在初始化或调用失败,走 Deny 模式,不放行任何请求——读也拒,写也拒,宁可拦错也不放过。

流程

从状态机看,OPA 插件有 Disabled、Initializing、Ready、Failed 四态:没配就是 Disabled,内置引擎干活;配了且连通就是 Ready,授权交给外部;初始化中或失败都按 Deny 处理。这意味着接 OPA 的第一天有一件事排在写策略前面:确认插件真的到了 Ready。RustFS 启动日志会打 OPA 的收敛状态,从 Disabled 或 Initializing 收敛到 Ready 之前,所有走外部授权的请求都会被 Deny。staging 上先看日志确认状态,再放量到生产,别一上线就把全网请求拦在门外。

标签注进了 context,键名和 IAM Condition 是同一套

早期版本里 OPA 拿到的 input 主要是身份、资源(bucket、object、arn)、动作这几类,标签进不来,想在 OPA 里按标签分流就得自己先查一次。#8298 起桶上存储的标签注进了 input.context.conditions,而且走的不是什么新命名空间,就是 IAM 条件键那一套:ExistingBucketTag/<key>。你在 IAM policy 的 Condition 里写过 ExistingBucketTag/env,到 OPA 里读的还是这个键,已有的策略写法直接对上。

一次授权请求的 input 大致长这样(省略部分字段):

{
"input": {
"identity": { "account": "…", "groups": ["…"], "is_owner": false, "claims": {} },
"resource": { "bucket": "…", "object": "…", "arn": "arn:aws:s3:::bucket/object" },
"action": "s3:DeleteBucket",
"context": {
"conditions": {
"ExistingBucketTag/env": ["prod"],
"ExistingBucketTag/team": ["payments"]
}
}
}
}

注意标签的值是数组,不是标量,Rego 里判断的是成员关系,不是等号。

给生产 bucket 打了 env=prod,"只有 storage-admins 能删生产桶"在 OPA 里就是一条规则。标签是管理员或用户打的,别把它当成唯一可信来源,关键动作仍然要配合显式 Deny 兜底,标签只做精细化分层。实际场景里标签往往是多维组合:一个桶同时有 env=prod、team=payments、compliance=pii,"支付团队能读写生产桶里的 PII 数据"就能写成一条 Rego,按三个标签的交集放行,比在 IAM 里把三个维度交叉出来的几十种组合逐个写 statement 干净得多。

一段能直接抄的 Rego 与启动方式

最小可用的思路是默认拒绝,只在标签和动作同时满足时放行:

package rustfs.authz

default allow = false

allow {
input.action == "s3:DeleteBucket"
"prod" in input.context.conditions["ExistingBucketTag/env"]
"storage-admins" in input.identity.groups
}

启动 RustFS 时把策略引擎指过去:

RUSTFS_POLICY_PLUGIN_URL=http://localhost:8181/v1/data/rustfs/authz/allow
RUSTFS_POLICY_PLUGIN_AUTH_TOKEN=<opa-token>

这是示例策略,实际要按你们的标签规范改,OPA 服务地址也换成自己的实例,别直接照搬。Rego 的 allow 可以多写几条规则,每条覆盖一类动作,比在 IAM 里堆 statement 好维护。

一个容易踩的坑:进 context 的必须是桶上真实存储的标签,请求里临时传的元数据不会进 context。如果你们平时靠桶名前缀(如 env-prod-)区环境而不打标签,这条就用不上,得先把打标签的习惯补齐,否则 OPA 读到的 conditions 里没有标签键,所有依赖标签的规则都会落到默认拒绝。

对照

叠加层,不是替换

配了 OPA 不等于内置引擎被换掉。官方 IAM 文档写明评估语义:显式拒绝优先、必须显式允许、默认拒绝;服务账户和 STS 会话的会话策略作为额外限制,父身份策略和会话策略必须同时允许。OPA 是在这套语义之上再叠一层外部判断,内置的五个 canned policy(readwrite、readonly、writeonly、diagnostics、consoleAdmin)照样生效。

IAM 这边只给组一个宽松 baseline 时,有个官方提醒要知道:内置 readonly 策略不包含 s3:ListBucket 或 s3:ListAllMyBuckets,拿 readonly 当 baseline 的用户连桶内列对象都做不到,得补一条自定义策略。这也是"baseline 别拿 canned policy 凑合、标签层只管精细约束"的一个注脚。

延迟要算进接口的 SLA,而且可以算得具体:OPA 客户端连接超时 1 秒、请求超时 5 秒。OPA 挂住时,一个授权请求最坏要多等 5 秒。把 OPA 部署成高可用:跑两个副本 behind 一个 LB,或者用 OPA 的 bundle 推送,策略改动走 GitOps 流程。fail-closed 的默认值保证了 OPA 短暂不可用时集群不会悄悄放行,但代价要想清楚:那段时间所有请求全拒,得当成一个明确的运维事件来处理。

这事值不值得上

适合 bucket 多、团队多、标签已经打得规范的集群。环境、owner、合规等级这类标签收进统一策略,比在每条 IAM policy 里散着写清爽得多。小集群几条 policy 就能管完,上 OPA 是额外的运维面,为了用而用反而多一个故障点,OPA 挂了等于整集群 fail-closed。

还有一笔标签治理的账:标签靠人打,就会漏打、打错。fail-closed 保证的是"OPA 不可用时不放行",它不保证“策略基于的标签是对的”:标签值错了,规则照样按错的判定。方案成立的前提是标签本身可靠,上线前先把打标签的动作收进自动化(Terraform、CI 或管理脚本),别指望人肉。

先在 staging 把拒绝日志看熟

staging 起好 OPA,用 RUSTFS_POLICY_PLUGIN_URL 指过去,观察拒绝日志。fail-closed 时请求会被拦,日志只记状态码和稳定分类,不打印 token 和 URL,排查时重点看放行逻辑有没有写反。策略上线前建议用 conftest 之类的工具跑一遍 Rego 单测,把"生产桶删除只允许 storage-admins"这类规则用样例 input 验证过,确认对了再推生产。这一步和前面"确认 Ready"是同一件事的两半:状态到了 Ready 只说明链路通,规则验证过才说明行为对。

OPA 的 input 字段、fail-closed 状态机的实现,官方文档页目前没有专门章节,权威出处是源码:授权输入构造在 crates/policy/src/policy/opa.rs,标签进 conditions 的逻辑在 crates/iam/src/sys.rs,环境变量清单见环境变量参考。

赞(0)
未经允许不得转载:171主机测评 » 几十条 IAM policy 还在抄桶标签?用 OPA 给 RustFS 做集中授权
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址