从全面拒绝一路收窄到最小权限
目标
从默认拒绝出发,仅按调用图开放权限,从而构建最小权限授权,并能够用矩阵说明每次调用的结果。
为什么重要
授权设计中最常见的错误,是采用逐一阻止不需要内容的方向。这样一来,遗漏的路径会永远保持开放,而且无人知晓。相反,从全面拒绝出发,遗漏的路径会立即以 403 暴露出来,因此设计能够闭环。Istio 用一行 spec: {} 表达这种反转——ALLOW 策略存在,却没有任何匹配规则,所以任何请求都无法通过。
还必须熟练掌握评估顺序。顺序是 CUSTOM → DENY → ALLOW,而 ALLOW 的规则是“只要存在任意一个策略,请求就必须匹配才能通过”。也就是说,**首次添加 ALLOW 策略的瞬间,该工作负载便会切换为白名单模式。**不了解这一点而添加一条策略,导致现有流量全部被阻断,是常见事故。DENY 先于 ALLOW 评估这一特性,反过来可以成为安全网——对于管理路径这类“无论如何都必须阻止”的内容,再额外铺设一层 DENY。
最后,授权策略的 principals 来自 mTLS 证书中的身份。以明文进入的请求没有 principal,因此绝不可能匹配,所以 切换到 STRICT 是授权的前提条件。
步骤
**开始前的准备。**每次实验都会启动新的实验 Pod,因此不会保留前一实验的服务网格设置。如果 kubectl get crd authorizationpolicies.security.istio.io 没有输出,请执行 istioctl manifest generate --set profile=minimal > /root/istio/manifest.yaml,然后将 kubectl apply -f /root/istio/manifest.yaml 执行两次,再通过 kubectl create ns mesh-lab && kubectl label ns mesh-lab istio-injection=enabled 准备命名空间。第 2 步和第 4 步中 selector 指向的 payments、ratings 工作负载也需要在本实验中重新创建。
- 在
mesh-lab中创建 AuthorizationPolicydeny-all——只保留spec: {},在没有 selector 和 rules的情况下应用(省略 action,使用默认值 ALLOW)。并在/root/istio/authz/out/denyall-note.txt中写明没有规则时任何请求都无法通过的原因。 - 在
mesh-lab中创建 ServiceAccountfrontend(如果不存在前一实验的 payments 工作负载,也请一并创建),再创建 AuthorizationPolicyallow-frontend:selector.matchLabels.app为payments,action为ALLOW,并在rules[0].from[0].source.principals中填写cluster.local/ns/mesh-lab/sa/frontend。不得添加spiffe://前缀。 - 在同一个
allow-frontend策略中添加rules[0].to[0].operation。methods只能有["GET"]一项,paths中应包含/api/*。并在/root/istio/authz/out/path-note.txt中写明路径匹配不是正则表达式,只支持前缀/后缀通配符。 - 在
mesh-lab中创建 ratings 工作负载(Serviceratings、Deploymentratings-v1、Pod 标签app: ratings),再创建 AuthorizationPolicyallow-mesh-lab:selector.matchLabels.app为ratings,rules[0].from[0].source.namespaces为["mesh-lab"]。并在/root/istio/authz/out/ns-note.txt中写明,命名空间级控制比服务账号(principal)级控制更粗粒度。 - 在
mesh-lab中创建 AuthorizationPolicydeny-admin:action为DENY,并在rules[0].to[0].operation.paths中包含/admin/*。然后在/root/istio/authz/order.txt中将评估顺序每行写一个,共三行——第一行写CUSTOM,第二行写DENY,第三行写ALLOW(不得全部写在同一行)。 - 在
mesh-lab中创建 AuthorizationPolicyallow-v2-clients。在rules[0].when中设置两个条件:一个是key: request.headers[x-api-version],值为values: ["v2"];另一个是key: source.namespace,值为notValues: ["legacy"]。使用notValues的条件,其 key 中不得包含request.headers。 - 在
mesh-lab中创建 AuthorizationPolicyaudit-sensitive:action为AUDIT,在rules[0].to[0].operation.paths中指定要审计的路径(例如/internal/*)。并在/root/istio/authz/out/audit-note.txt中同时写明,AUDIT 不会阻止流量,只会记录,以及它可用于在真正启用策略前预先查看影响。 - 创建
/root/istio/authz/out/authz-matrix.json。cases数组中应包含至少 6 条记录,每项都必须包含expected(只能是"allow"或"deny")和policy(产生该结果的策略名称——必须真实存在于mesh-lab中,且是一个不含空格的单词)。至少需要 2 条预期允许和 2 条预期拒绝的记录。mesh-lab中必须至少存在 5 个 AuthorizationPolicy;将istioctl analyze -n mesh-lab的结果保存到/root/istio/authz/out/analyze.txt,其中不得出现Error [。
参考
- 每次实验都会启动新的实验 Pod,因此不会保留前一实验的集群状态。所以,将服务网格设置保存为清单,本身就是可复现性。用 Git 管理授权策略时,变更历史和审批流程也会直接成为审计轨迹,道理相同。
- 一行矩阵示例:
{"from": "frontend", "to": "payments", "method": "GET", "path": "/api/v1/charges", "expected": "allow", "policy": "allow-frontend"}。拒绝示例可以使用对/admin/*的请求(deny-admin)和策略中不存在的调用方(deny-all)。 - 第 6 步的
when条件键可以使用request.headers[...]、source.namespace、source.principal、request.auth.claims[...]等。 - 如果策略名称在集群中不存在,第 8 步会失败。请先用
kubectl get authorizationpolicy -n mesh-lab确认名称,再编写矩阵。 - 常见错误 1:在
principals中添加spiffe://。概念说明中会带此前缀,但在该字段中必须去掉。 - 常见错误 2:用箭头将评估顺序写在同一行。系统根据行号判断顺序,因此必须分成三行。
用空规则建立全面拒绝
在 mesh-lab 中创建 AuthorizationPolicy deny-all——只保留 spec: {},在没有 selector 和 rules的情况下应用(省略 action,使用默认值 ALLOW)。并在 /root/istio/authz/out/denyall-note.txt 中写明没有规则时任何请求都无法通过的原因。
没有任何规则的允许策略就是全面拒绝。原因可以从评估顺序的最后一项找到。该策略必须作用于整个命名空间,因此不要设置 selector。
按调用方身份只开放一条路径
在 mesh-lab 中创建 ServiceAccount frontend(如果不存在前一实验的 payments 工作负载,也请一并创建),再创建 AuthorizationPolicy allow-frontend:selector.matchLabels.app 为 payments,action 为 ALLOW,并在 rules[0].from[0].source.principals 中填写 cluster.local/ns/mesh-lab/sa/frontend。不得添加 spiffe:// 前缀。
策略附加在接收请求的工作负载上。身份不是 IP,而是服务账号;请注意,该字段中不添加前缀。
进一步限定方法和路径
在同一个 allow-frontend 策略中添加 rules[0].to[0].operation。methods 只能有 ["GET"] 一项,paths 中应包含 /api/*。并在 /root/istio/authz/out/path-note.txt 中写明路径匹配不是正则表达式,只支持前缀/后缀通配符。
本步骤只允许读取。请确认路径匹配并非正则表达式,并将这一点写入说明。
按命名空间允许访问
在 mesh-lab 中创建 ratings 工作负载(Service ratings、Deployment ratings-v1、Pod 标签 app: ratings),再创建 AuthorizationPolicy allow-mesh-lab:selector.matchLabels.app 为 ratings,rules[0].from[0].source.namespaces 为 ["mesh-lab"]。并在 /root/istio/authz/out/ns-note.txt 中写明,命名空间级控制比服务账号(principal)级控制更粗粒度。
这是比身份级别更粗粒度的控制。请在说明中写明何时这种粒度足够、何时不足。
建立拒绝安全网并写出评估顺序
在 mesh-lab 中创建 AuthorizationPolicy deny-admin:action 为 DENY,并在 rules[0].to[0].operation.paths 中包含 /admin/*。然后在 /root/istio/authz/order.txt 中将评估顺序每行写一个,共三行——第一行写 CUSTOM,第二行写 DENY,第三行写 ALLOW(不得全部写在同一行)。
拒绝先于允许评估,因此可以防御允许策略中的错误。整理顺序时必须每行写一项。
用条件进行更精细的限制
在 mesh-lab 中创建 AuthorizationPolicy allow-v2-clients。在 rules[0].when 中设置两个条件:一个是 key: request.headers[x-api-version],值为 values: ["v2"];另一个是 key: source.namespace,值为 notValues: ["legacy"]。使用 notValues 的条件,其 key 中不得包含 request.headers。
可以设置多个条件,也可以表达排除条件。请将请求头条件和排除条件拆成两个不同的条目。
创建只记录而不阻断的策略
在 mesh-lab 中创建 AuthorizationPolicy audit-sensitive:action 为 AUDIT,在 rules[0].to[0].operation.paths 中指定要审计的路径(例如 /internal/*)。并在 /root/istio/authz/out/audit-note.txt 中同时写明,AUDIT 不会阻止流量,只会记录,以及它可用于在真正启用策略前预先查看影响。
这是在真正启用策略前先观察影响的机制。请准确写明它会对流量产生什么影响。
构建逐次调用的允许/拒绝矩阵
创建 /root/istio/authz/out/authz-matrix.json。cases 数组中应包含至少 6 条记录,每项都必须包含 expected(只能是 "allow" 或 "deny")和 policy(产生该结果的策略名称——必须真实存在于 mesh-lab 中,且是一个不含空格的单词)。至少需要 2 条预期允许和 2 条预期拒绝的记录。mesh-lab 中必须至少存在 5 个 AuthorizationPolicy;将 istioctl analyze -n mesh-lab 的结果保存到 /root/istio/authz/out/analyze.txt,其中不得出现 Error [。
必须同时写明每次调用为何会由哪条策略作出该决定。策略名称必须真实存在于集群中。