[zh] improve authentication/authn-policy (#13031)

Signed-off-by: xin.li <xin.li@daocloud.io>
This commit is contained in:
my-git9 2023-04-09 22:58:59 +08:00 committed by GitHub
parent 321ed30b9c
commit 38ce3174c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 86 additions and 35 deletions

View File

@ -10,12 +10,14 @@ owner: istio/wg-security-maintainers
test: yes
---
本任务涵盖了您在启用、配置和使用 Istio 认证策略时可能需要做的主要工作。更多基本概念介绍请查看[认证总览](/zh/docs/concepts/security/#authentication)。
本任务涵盖了您在启用、配置和使用 Istio 认证策略时可能需要做的主要工作。
更多基本概念介绍请查看[认证总览](/zh/docs/concepts/security/#authentication)。
## 开始之前{#before-you-begin}
* 理解 Istio [认证策略](/zh/docs/concepts/security/#authentication-policies)和[双向 TLS 认证](/zh/docs/concepts/security/#mutual-TLS-authentication)相关概念。
* 参照[安装步骤](/zh/docs/setup/getting-started),使用 `default` 模板在 Kubernetes 集群中安装 Istio。
* 参照[安装步骤](/zh/docs/setup/getting-started),使用 `default`
模板在 Kubernetes 集群中安装 Istio。
{{< text bash >}}
$ istioctl install --set profile=default
@ -23,7 +25,10 @@ $ istioctl install --set profile=default
### 设置{#setup}
我们将在 `foo``bar`命名空间下各自创建带有 Envoy 代理(sidecar)的 `httpbin``sleep`服务。我们还会在 `legacy` 命名空间下创建不带 Envoy 代理(sidecar)的 `httpbin``sleep` 服务。如果您希望使用相同的示例来完成这些任务,执行如下命令:
我们将在 `foo``bar`命名空间下各自创建带有 Envoy 代理sidecar
`httpbin``sleep`服务。我们还会在 `legacy` 命名空间下创建不带
Envoy 代理sidecar`httpbin``sleep` 服务。如果您希望使用相同的示例来完成这些任务,
执行如下命令:
{{< text bash >}}
$ kubectl create ns foo
@ -37,7 +42,9 @@ $ kubectl apply -f @samples/httpbin/httpbin.yaml@ -n legacy
$ kubectl apply -f @samples/sleep/sleep.yaml@ -n legacy
{{< /text >}}
现在您可以在 `foo`、`bar` 或 `legacy` 三个命名空间下的任意 `sleep` pod 使用 `curl``httpbin.foo`、`httpbin.bar` 或 `httpbin.legacy` 发送 HTTP 请求来验证部署结果。所有请求都应该成功返回 HTTP 200。
现在您可以在 `foo`、`bar` 或 `legacy` 三个命名空间下的任意 `sleep` Pod
使用 `curl``httpbin.foo`、`httpbin.bar` 或 `httpbin.legacy`
发送 HTTP 请求来验证部署结果。所有请求都应该成功返回 HTTP 200。
例如,一个检查 `sleep.bar``httpbin.foo` 可达性的指令如下:
@ -68,29 +75,38 @@ $ kubectl get peerauthentication --all-namespaces
No resources found
{{< /text >}}
最后同样重要的是,确认验证示例服务没有应用 destination rule。您可以检查现有 destination rule 中的 `host:` 值并确保它们不匹配。例如:
最后同样重要的是,确认验证示例服务没有应用 destination rule。
您可以检查现有 destination rule 中的 `host:` 值并确保它们不匹配。例如:
{{< text bash >}}
$ kubectl get destinationrules.networking.istio.io --all-namespaces -o yaml | grep "host:"
{{< /text >}}
{{< tip >}}
您可能会看到 destination rules 配置了除上面显示以外的其他 hosts这依赖于 Istio 的版本。但是,应该没有 destination rules 配置 `foo`、`bar` 和 `legacy` 命名空间中的 hosts也没有配置通配符 `*`
您可能会看到 destination rules 配置了除上面显示以外的其他 hosts
这依赖于 Istio 的版本。但是,应该没有 destination rules 配置
`foo`、`bar` 和 `legacy` 命名空间中的 hosts
也没有配置通配符 `*`
{{< /tip >}}
## 自动双向 TLS{#auto-mutual-TLS}
默认情况下Istio 会跟踪迁移到 Istio 代理的服务器工作负载并配置客户端代理,将双向 TLS 流量自动发送到这些工作负载,并将 plain-text 流量发送到没有 sidecar 的工作负载。
默认情况下Istio 会跟踪迁移到 Istio 代理的服务器工作负载并配置客户端代理,
将双向 TLS 流量自动发送到这些工作负载,并将 plain-text 流量发送到没有
sidecar 的工作负载。
因此,您无需做额外操作,具有代理的工作负载之间的所有流量即可启用双向 TLS。例如检查请求 `httpbin/header` 的响应。
使用双向 TLS 时,代理会将 `X-Forwarded-Client-Cert` 标头注入到后端的上游请求。存在该标头则说启用了双向 TLS。例如
因此,您无需做额外操作,具有代理的工作负载之间的所有流量即可启用双向 TLS。
例如检查请求 `httpbin/header` 的响应。
使用双向 TLS 时,代理会将 `X-Forwarded-Client-Cert` 标头注入到后端的上游请求。
存在该标头则说启用了双向 TLS。例如
{{< text bash >}}
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl -s http://httpbin.foo:8000/headers -s | grep X-Forwarded-Client-Cert | sed 's/Hash=[a-z0-9]*;/Hash=<redacted>;/'
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=<redacted>;Subject=\"\";URI=spiffe://cluster.local/ns/foo/sa/sleep"
{{< /text >}}
当服务器没有 sidecar 时, `X-Forwarded-Client-Cert` 标头将不会存在,这意味着请求是 plain-text 的。
当服务器没有 sidecar 时,`X-Forwarded-Client-Cert` 标头将不会存在,
这意味着请求是 plain-text 的。
{{< text bash >}}
$ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl http://httpbin.legacy:8000/headers -s | grep X-Forwarded-Client-Cert
@ -98,8 +114,11 @@ $ kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metad
## 全局以严格模式启用 Istio 双向 TLS{#globally-enabling-Istio-mutual-TLS-in-STRICT-mode}
当 Istio 自动将代理和工作负载之间的所有流量升级到双向 TLS 时,工作负载仍然可以接收 plain-text 流量。为了阻止整个网格的服务以非双向 TLS 通信,需要将整个服务网格的对等认证策略设置为 `STRICT` 模式。
在整个服务网格范围内,对等认证策略不应该有一个 `selector`,它必须应用于 **根命名空间**,例如:
当 Istio 自动将代理和工作负载之间的所有流量升级到双向 TLS 时,
工作负载仍然可以接收 plain-text 流量。为了阻止整个网格的服务以非双向 TLS 通信,
需要将整个服务网格的对等认证策略设置为 `STRICT` 模式。
在整个服务网格范围内,对等认证策略不应该有一个 `selector`
它必须应用于**根命名空间**,例如:
{{< text bash >}}
$ kubectl apply -f - <<EOF
@ -115,10 +134,12 @@ EOF
{{< /text >}}
{{< tip >}}
该示例假定命名空间 `istio-system` 是根命名空间。如果在安装过程中使用了不同的值,请将 `istio-system` 替换为所使用的值。
该示例假定命名空间 `istio-system` 是根命名空间。如果在安装过程中使用了不同的值,
请将 `istio-system` 替换为所使用的值。
{{< /tip >}}
此对等身份验证策略将工作负载配置为仅接受使用 TLS 加密的请求。由于未对 `selector` 字段指定值,因此该策略适用于服务网格中的所有工作负载。
此对等身份验证策略将工作负载配置为仅接受使用 TLS 加密的请求。
由于未对 `selector` 字段指定值,因此该策略适用于服务网格中的所有工作负载。
再次运行测试指令:
@ -137,7 +158,8 @@ command terminated with exit code 56
sleep.legacy to httpbin.legacy: 200
{{< /text >}}
您会发现除了从没有 sidecar 的服务(`sleep.legacy`) 到有 sidecar 的服务(`httpbin.foo` 或 `httpbin.bar`) 的请求外,其他请求依然是返回成功的。
您会发现除了从没有 sidecar 的服务(`sleep.legacy`)到有 sidecar
的服务(`httpbin.foo` 或 `httpbin.bar`)的请求外,其他请求依然是返回成功的。
### 清除部分 1{#cleanup-part-1}
@ -151,7 +173,9 @@ $ kubectl delete peerauthentication -n istio-system default
### 命名空间级别策略{#namespace-wide-policy}
如果要将特定命名空间内的所有工作负载更改双向 TLS请使用命名空间级别策略。该策略的规范与整个服务网格级别规范相同但是您可以在 `metadata` 字段指定命名空间的名称。例如,以下对等身份验证策略在 `foo` 命名空间上启用了严格的双向 TLS
如果要将特定命名空间内的所有工作负载更改双向 TLS请使用命名空间级别策略。
该策略的规范与整个服务网格级别规范相同,但是您可以在 `metadata` 字段指定命名空间的名称。
例如,以下对等身份验证策略在 `foo` 命名空间上启用了严格的双向 TLS
{{< text bash >}}
$ kubectl apply -f - <<EOF
@ -166,7 +190,8 @@ spec:
EOF
{{< /text >}}
由于这些策略只应用于命名空间 `foo` 中的服务,您会看到只有从没有 sidecar 的客户端(`sleep.legacy`)到有 sidecar 的客户端(`httpbin.foo`)的请求会失败。
由于这些策略只应用于命名空间 `foo` 中的服务,您会看到只有从没有 sidecar
的客户端(`sleep.legacy`)到有 sidecar 的客户端(`httpbin.foo`)的请求会失败。
{{< text bash >}}
$ for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name})" -c sleep -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done
@ -184,7 +209,9 @@ sleep.legacy to httpbin.legacy: 200
### 为每个工作负载启用双向 TLS{#enable-mutual-TLS-per-workload}
要为特定工作负载设置对等身份验证策略,必须配置 `selector` 字段并指定与所需工作负载匹配的标签。例如,以下对等身份验证策略和 destination rule 将为 `httpbin.bar` 服务启用严格的双向 TLS
要为特定工作负载设置对等身份验证策略,必须配置 `selector`
字段并指定与所需工作负载匹配的标签。例如,以下对等身份验证策略和
destination rule 将为 `httpbin.bar` 服务启用严格的双向 TLS
{{< text bash >}}
$ cat <<EOF | kubectl apply -n bar -f -
@ -202,7 +229,8 @@ spec:
EOF
{{< /text >}}
再次执行探查命令。跟预期一样,从 `sleep.legacy``httpbin.bar` 的请求因为同样的原因失败。
再次执行测试命令。跟预期一样,从 `sleep.legacy``httpbin.bar`
的请求因为同样的原因失败。
{{< text bash >}}
$ for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=sleep -n ${from} -o jsonpath={.items..metadata.name})" -c sleep -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done
@ -225,7 +253,8 @@ sleep.legacy to httpbin.bar: 000
command terminated with exit code 56
{{< /text >}}
要优化每个端口的 双向 TLS 设置,必须配置 `portLevelMtls` 字段。 例如,以下对等身份验证策略要求在除 `80` 端口以外的所有端口上都使用双向 TLS
要优化每个端口的 双向 TLS 设置,必须配置 `portLevelMtls` 字段。
例如,以下对等身份验证策略要求在除 `80` 端口以外的所有端口上都使用双向 TLS
{{< text bash >}}
$ cat <<EOF | kubectl apply -n bar -f -
@ -265,8 +294,10 @@ sleep.legacy to httpbin.legacy: 200
### 策略优先级{#policy-precedence}
为了演示特定服务策略比命名空间范围的策略优先级高,您可以像下面一样为 `httpbin.foo` 添加一个禁用双向 TLS 的策略。
注意您已经为所有在命名空间 `foo` 中的服务创建了命名空间范围的策略来启用双向 TLS 并观察到从 `sleep.legacy``httpbin.foo` 的请求都会失败(如上所示)。
为了演示特定服务策略比命名空间范围的策略优先级高,您可以像下面一样为
`httpbin.foo` 添加一个禁用双向 TLS 的策略。
注意您已经为所有在命名空间 `foo` 中的服务创建了命名空间范围的策略来启用双向
TLS 并观察到从 `sleep.legacy``httpbin.foo` 的请求都会失败(如上所示)。
{{< text bash >}}
$ cat <<EOF | kubectl apply -n foo -f -
@ -284,7 +315,8 @@ spec:
EOF
{{< /text >}}
重新执行来自 `sleep.legacy` 的请求,您应该又会看到请求成功返回 200 代码,证明了特定服务策略覆盖了命名空间范围的策略。
重新执行来自 `sleep.legacy` 的请求,您应该又会看到请求成功返回 200 代码,
证明了特定服务策略覆盖了命名空间范围的策略。
{{< text bash >}}
$ kubectl exec "$(kubectl get pod -l app=sleep -n legacy -o jsonpath={.items..metadata.name})" -c sleep -n legacy -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}\n"
@ -302,8 +334,12 @@ $ kubectl delete peerauthentication httpbin -n bar
## 终端用户认证{#end-user-authentication}
为了体验这个特性,您需要一个有效的 JWT。该 JWT 必须和您用于该示例的 JWKS 终端对应。在这个教程中,我们使用来自 Istio 代码基础库的 [JWT test]({{< github_file >}}/security/tools/jwt/samples/demo.jwt) 和 [JWKS endpoint]({{< github_file >}}/security/tools/jwt/samples/jwks.json)
同时为了方便访问我们将通过 `ingressgateway` 暴露 `httpbin.foo` 服务(详细细节请查看 [ingress 任务](/zh/docs/tasks/traffic-management/ingress/))。
为了体验这个特性,您需要一个有效的 JWT。该 JWT 必须和您用于该示例的 JWKS 终端对应。
在这个教程中,我们使用来自 Istio 代码基础库的
[JWT test]({{< github_file >}}/security/tools/jwt/samples/demo.jwt) 和
[JWKS endpoint]({{< github_file >}}/security/tools/jwt/samples/jwks.json)
同时为了方便访问我们将通过 `ingressgateway` 暴露 `httpbin.foo`
服务(详细细节请查看 [ingress 任务](/zh/docs/tasks/traffic-management/ingress/))。
{{< text bash >}}
$ kubectl apply -f - <<EOF
@ -346,7 +382,8 @@ spec:
EOF
{{< /text >}}
参考[确定 ingress IP 和端口](/zh/docs/tasks/traffic-management/ingress/ingress-control/#determining-the-ingress-ip-and-ports)章节配置环境变量 `INGRESS_HOST``INGRESS_PORT` 的值。
参考[确定 ingress IP 和端口](/zh/docs/tasks/traffic-management/ingress/ingress-control/#determining-the-ingress-ip-and-ports)章节配置环境变量
`INGRESS_HOST``INGRESS_PORT` 的值。
执行测试指令
@ -374,9 +411,13 @@ spec:
EOF
{{< /text >}}
策略生效的命名空间是 `istio-system`,生效的工作负载由 `selector` 字段决定,上面的配置值是带有 `app: ingressgateway` 标签的工作负载。
策略生效的命名空间是 `istio-system`,生效的工作负载由 `selector` 字段决定,
上面的配置值是带有 `app: ingressgateway` 标签的工作负载。
如果您在授权标头(默认位置)中提供了令牌,则 Istio 将使用 [public key set]({{<github_file>}}/security/tools/jwt/samples/jwks.json) 验证令牌,`bearer` 令牌无效会被拒绝,然而没有 `bearer` 令牌的请求会被接收。因此要观察此行为,请在没有令牌,令牌错误和有效令牌的情况下重试请求:
如果您在授权标头(默认位置)中提供了令牌,则 Istio 将使用
[public key set]({{<github_file>}}/security/tools/jwt/samples/jwks.json) 验证令牌,
`bearer` 令牌无效会被拒绝,然而没有 `bearer` 令牌的请求会被接收。因此要观察此行为,请在没有令牌,
令牌错误和有效令牌的情况下重试请求:
{{< text bash >}}
$ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
@ -394,7 +435,9 @@ $ curl --header "Authorization: Bearer $TOKEN" "$INGRESS_HOST:$INGRESS_PORT/head
200
{{< /text >}}
为了观察 JWT 验证的其它方面,使用脚本 [`gen-jwt.py`]({{< github_tree >}}/security/tools/jwt/samples/gen-jwt.py) 生成新 tokens 带上不同的发行人、受众、有效期等等进行测试。可以从 Istio 库下载此脚本:
为了观察 JWT 验证的其它方面,使用脚本
[`gen-jwt.py`]({{< github_tree >}}/security/tools/jwt/samples/gen-jwt.py)
生成新 tokens 带上不同的发行人、受众、有效期等等进行测试。可以从 Istio 库下载此脚本:
{{< text bash >}}
$ wget --no-verbose {{< github_file >}}/security/tools/jwt/samples/gen-jwt.py
@ -407,12 +450,15 @@ $ wget --no-verbose {{< github_file >}}/security/tools/jwt/samples/key.pem
{{< /text >}}
{{< tip >}}
如果您的系统尚未安装 `jwcrypto` 库,你需要从 [jwcrypto](https://pypi.org/project/jwcrypto) 下载并安装。
如果您的系统尚未安装 `jwcrypto` 库,你需要从
[jwcrypto](https://pypi.org/project/jwcrypto) 下载并安装。
{{< /tip >}}
JWT 认证有 60 秒的时钟偏移clock skew这意味着 JWT 令牌会比其配置 `nbf` 早 60 秒成为有效的,其配置 `exp`后 60 秒后仍然有效。
JWT 认证有 60 秒的时钟偏移clock skew这意味着 JWT
令牌会比其配置 `nbf` 早 60 秒成为有效的,其配置 `exp`后 60 秒后仍然有效。
例如下面的命令创建一个令牌该令牌在5秒钟后过期。 如您所见Istio 会一直通过认证直到 65 秒后才拒绝这些令牌:
例如下面的命令创建一个令牌该令牌在5秒钟后过期。如您所见
Istio 会一直通过认证直到 65 秒后才拒绝这些令牌:
{{< text bash >}}
$ TOKEN=$(python3 ./gen-jwt.py ./key.pem --expire 5)
@ -429,12 +475,15 @@ $ for i in $(seq 1 10); do curl --header "Authorization: Bearer $TOKEN" "$INGRES
401
{{< /text >}}
您也可以给一个 `ingress gateway` 添加一个 JWT 策略(例如,服务 `istio-ingressgateway.istio-system.svc.cluster.local`)。
您也可以给一个 `ingress gateway` 添加一个 JWT 策略(例如,服务
`istio-ingressgateway.istio-system.svc.cluster.local`)。
这个常用于为绑定到这个 gateway 的所有服务定义一个 JWT 策略而不是为单独的服务绑定策略。
### 提供有效令牌{#require-a-valid-token}
拒绝没有有效的令牌的请求,需要增加名为 `DENY` 认证策略,可参考以下例子中的 `notRequestPrincipals:["*"]` 配置。仅当提供有效的JWT令牌时请求主体才可用因此该规则将拒绝没有有效令牌的请求。
拒绝没有有效的令牌的请求,需要增加名为 `DENY` 认证策略,
可参考以下例子中的 `notRequestPrincipals:["*"]` 配置。
仅当提供有效的JWT令牌时请求主体才可用因此该规则将拒绝没有有效令牌的请求。
{{< text bash >}}
$ kubectl apply -f - <<EOF
@ -464,7 +513,9 @@ $ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
### 按路径提供有效令牌{#require-valid-tokens-per-path}
为了按路径(路径指 host、path 或者 method)提供有效令牌我们需要在其认证策略中指定这些路径,如下列配置中的 `/headers`。待规则生效后,对`$INGRESS_HOST:$INGRESS_PORT/headers` 的请求将失败,错误代码为 `403`。而到其他所有路径的请求 —— 例如:`$INGRESS_HOST:$INGRESS_PORT/ip` —— 都会成功。
为了按路径(路径指 host、path 或者 method)提供有效令牌我们需要在其认证策略中指定这些路径,
如下列配置中的 `/headers`。待规则生效后,对`$INGRESS_HOST:$INGRESS_PORT/headers`
的请求将失败,错误代码为 `403`。而到其他所有路径的请求 —— 例如:`$INGRESS_HOST:$INGRESS_PORT/ip` —— 都会成功。
{{< text bash >}}
$ kubectl apply -f - <<EOF