授权策略被翻译成过滤器链
一句话总结
AuthorizationPolicy 会变成工作负载入站监听器中的 envoy.filters.http.rbac 过滤器。所有 DENY 策略汇集到一个过滤器,所有 ALLOW 策略汇集到另一个,DENY 过滤器排在前面。两个过滤器串成一条链,两者都通过,请求才能到达应用。dry-run 则是把同样的策略放进 shadow_rules,而不是 rules。
为什么需要它
如果授权规则在每个应用中用代码来写,各种语言的实现会各不相同,审计时也不知道哪里有什么。Istio 把这件事挪到了代理上。规则作为 Kubernetes 资源集中写在一处,执行则由每个 Pod 前面的 Envoy 以完全相同的方式完成。
然而,人写的规则是“这个要拦住(DENY),那个要放行(ALLOW)”混在一起的,而 Envoy 的一个 RBAC 过滤器只能有一个 action。所以转换需要规则。不了解这套规则,就无法解释生产环境中常见的两类事故:添加一条 ALLOW 策略之后,其余流量全部变成 403;以及一条只写了路径的 DENY 策略,连数据库连接也被切断。
工作原理
Istio 文档中的评估顺序有五行。
| 顺序 | 条件 | 判定 |
|---|---|---|
| 1 | CUSTOM 策略拒绝 | 拒绝 |
| 2 | 匹配 DENY 策略 | 拒绝 |
| 3 | 该工作负载上没有任何 ALLOW 策略 | 允许 |
| 4 | 匹配 ALLOW 策略 | 允许 |
| 5 | 其他情况 | 拒绝 |
istiod 把这张表转换成过滤器链。第 2 行是 action: DENY 的 RBAC 过滤器,第 4、5 行是 action: ALLOW 的 RBAC 过滤器。ALLOW 过滤器的含义是“必须有匹配的策略才能通过”,所以不匹配时就在当场拒绝——这就是第 5 行不需要单独存在的原因。第 3 行是通过不创建过滤器来实现的。如果 RBAC 过滤器的 rules 中一条策略都没有,就会拒绝所有请求,所以对于没有 ALLOW 策略的工作负载,根本不放置 ALLOW 过滤器。
在过滤器链中,每个过滤器能做的事只有两件:“交给下一个”和“当场拒绝”。没有跳过后面的过滤器直接放行的路径。所以,请求要到达应用,既要通过 DENY 过滤器,也要通过 ALLOW 过滤器。从逻辑上讲是 AND。因此即使调换两个过滤器的顺序,判定也相同。顺序一变,不同的是统计数据。两个过滤器会一起累加同一个 http.<stat_prefix>.rbac.allowed/denied,所以一个请求可能在第一个过滤器中累加 allowed,又在第二个过滤器中累加 denied。
策略属性大多会原样转换。paths: ["/admin*"] 会变成 url_path.path.prefix: /admin——所以 /administrator 也会被命中——而 methods: ["GET"] 则变成 :method 头的精确匹配。每条策略都会带上 ns[default]-policy[deny-admin]-rule[0] 这样的名称,因此可以从拒绝日志或 config_dump 追溯到原始资源。
dry-run 是给策略加上 istio.io/dry-run: "true" 注解。istiod 会把这条策略放进 shadow_rules。RBAC 过滤器会评估 shadow 一侧,只留下 shadow_allowed/shadow_denied 统计和动态元数据,并不用于判定。没有 rules 的过滤器不会拒绝任何请求。如果设置了 shadow_rules_stat_prefix,统计名称中间会插入一段前缀。
在现场相遇的样子
加了一条 ALLOW,结果全被拦住了。这是第 3 行造成的。ALLOW 策略为 0 条时是“全部允许”的工作负载,一旦变成 1 条,就变成“只允许匹配的”。如果健康检查路径或其他服务的调用不在新的 ALLOW 中,就会立刻变成 403。正文如果是 RBAC: access denied,就是代理拦住的,而应用的 403 则要靠正文来区分。
DENY 策略切断了 TCP 服务。DENY 会把请求中不存在的属性视为匹配。这是为了让拦截的一方不留缺口而做的设计。所以只写了路径的 DENY 会匹配所有没有路径这个概念的 TCP 连接,并将其切断。istioctl validate 会对此给出警告,并建议用 ports 缩小范围。但退出码是 0,在 CI 中很容易被漏掉。
不敢直接启用新策略。先以 dry-run 放进去,查看 shadow_denied 是否只在预期的请求上增加,然后再去掉注解。要在生产环境的仪表板中查看这个统计,必须知道包含前缀在内的准确名称。
官方文档:Authorization Policy · Envoy RBAC filter
下一项实验要做什么
编写 DENY 和 ALLOW 策略,读取 istioctl 的警告,然后按评估顺序先预测四个请求的结果。把两条策略转换成两个 RBAC 过滤器并启动 Envoy,与预测对照。调换过滤器顺序、去掉 ALLOW 过滤器、把 DENY 移到 shadow_rules 中,亲自确认评估顺序的这三行。