认证策略

本任务涵盖了启用、配置和使用 Istio 身份验证策略时可能需要执行的主要活动。在 身份验证概述 中了解更多关于底层概念的信息。

开始之前

$ istioctl install --set profile=default

设置

我们的示例使用两个命名空间 foobar,以及两个服务 httpbincurl,两者都使用 Envoy 代理运行。我们还在 legacy 命名空间中使用了 httpbincurl 的第二个实例,它们没有使用 sidecar。如果您想在尝试这些任务时使用相同的示例,请运行以下命令

ZipZipZipZipZipZip
$ kubectl create ns foo $ kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml) -n foo $ kubectl apply -f <(istioctl kube-inject -f samples/curl/curl.yaml) -n foo $ kubectl create ns bar $ kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml) -n bar $ kubectl apply -f <(istioctl kube-inject -f samples/curl/curl.yaml) -n bar $ kubectl create ns legacy $ kubectl apply -f samples/httpbin/httpbin.yaml -n legacy $ kubectl apply -f samples/curl/curl.yaml -n legacy

您可以通过从 foobarlegacy 命名空间中的任何 curl Pod 发送 HTTP 请求到 httpbin.foohttpbin.barhttpbin.legacy 来验证设置。所有请求都应以 HTTP 代码 200 成功。

例如,以下是一个检查 curl.barhttpbin.foo 可达性的命令

$ kubectl exec "$(kubectl get pod -l app=curl -n bar -o jsonpath={.items..metadata.name})" -c curl -n bar -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}\n"
200

此单行命令方便地迭代所有可达性组合

$ for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=curl -n ${from} -o jsonpath={.items..metadata.name})" -c curl -n ${from} -- curl -s "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "curl.${from} to httpbin.${to}: %{http_code}\n"; done; done
curl.foo to httpbin.foo: 200 curl.foo to httpbin.bar: 200 curl.foo to httpbin.legacy: 200 curl.bar to httpbin.foo: 200 curl.bar to httpbin.bar: 200 curl.bar to httpbin.legacy: 200 curl.legacy to httpbin.foo: 200 curl.legacy to httpbin.bar: 200 curl.legacy to httpbin.legacy: 200

使用以下命令验证系统中是否存在对等身份验证策略

$ kubectl get peerauthentication --all-namespaces
No resources found

最后但并非最不重要的一点是,验证没有应用于示例服务的任何目标规则。您可以通过检查现有目标规则的 host: 值并确保它们不匹配来做到这一点。例如

$ kubectl get destinationrules.networking.istio.io --all-namespaces -o yaml | grep "host:"

自动双向 TLS

默认情况下,Istio 会跟踪迁移到 Istio 代理的服务器工作负载,并配置客户端代理自动向这些工作负载发送双向 TLS 流量,并向没有 sidecar 的工作负载发送纯文本流量。

因此,在具有代理的工作负载之间所有流量都使用双向 TLS,而无需您执行任何操作。例如,获取对 httpbin/header 的请求的响应。在使用双向 TLS 时,代理会将 X-Forwarded-Client-Cert 标头注入到上游请求以发送到后端。该标头的存在证明使用了双向 TLS。例如

$ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl -s http://httpbin.foo:8000/headers -s | jq '.headers["X-Forwarded-Client-Cert"][0]' | sed 's/Hash=[a-z0-9]*;/Hash=<redacted>;/'
"By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=<redacted>;Subject=\"\";URI=spiffe://cluster.local/ns/foo/sa/curl"

当服务器没有 sidecar 时,X-Forwarded-Client-Cert 标头不存在,这意味着请求为纯文本。

$ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl http://httpbin.legacy:8000/headers -s | grep X-Forwarded-Client-Cert

在 STRICT 模式下全局启用 Istio 双向 TLS

虽然 Istio 会自动将代理与工作负载之间的所有流量升级到双向 TLS,但工作负载仍然可以接收纯文本流量。要防止整个网格中的非双向 TLS 流量,请设置一个网格范围的对等身份验证策略,并将双向 TLS 模式设置为 STRICT。网格范围的对等身份验证策略不应具有 selector,并且必须应用于**根命名空间**,例如

$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1 kind: PeerAuthentication metadata: name: "default" namespace: "istio-system" spec: mtls: mode: STRICT EOF

此对等身份验证策略配置工作负载以仅接受使用 TLS 加密的请求。由于它没有为 selector 字段指定值,因此该策略适用于网格中的所有工作负载。

再次运行测试命令

$ for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=curl -n ${from} -o jsonpath={.items..metadata.name})" -c curl -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "curl.${from} to httpbin.${to}: %{http_code}\n"; done; done
curl.foo to httpbin.foo: 200 curl.foo to httpbin.bar: 200 curl.foo to httpbin.legacy: 200 curl.bar to httpbin.foo: 200 curl.bar to httpbin.bar: 200 curl.bar to httpbin.legacy: 200 curl.legacy to httpbin.foo: 000 command terminated with exit code 56 curl.legacy to httpbin.bar: 000 command terminated with exit code 56 curl.legacy to httpbin.legacy: 200

您会看到请求仍然成功,除了来自没有代理的客户端 curl.legacy 到具有代理的服务器 httpbin.foohttpbin.bar 的请求。这是预期的,因为现在严格要求双向 TLS,但没有 sidecar 的工作负载无法满足。

清理步骤 1

删除本会话中添加的全局身份验证策略

$ kubectl delete peerauthentication -n istio-system default

按命名空间或工作负载启用双向 TLS

命名空间范围策略

要更改特定命名空间内所有工作负载的双向 TLS,请使用命名空间范围的策略。策略的规范与网格范围的策略相同,但您在 metadata 下指定它适用的命名空间。例如,以下对等身份验证策略为 foo 命名空间启用严格的双向 TLS

$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1 kind: PeerAuthentication metadata: name: "default" namespace: "foo" spec: mtls: mode: STRICT EOF

由于此策略仅应用于 foo 命名空间中的工作负载,因此您应该只看到来自无 sidecar 客户端 (curl.legacy) 到 httpbin.foo 的请求开始失败。

$ for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=curl -n ${from} -o jsonpath={.items..metadata.name})" -c curl -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "curl.${from} to httpbin.${to}: %{http_code}\n"; done; done
curl.foo to httpbin.foo: 200 curl.foo to httpbin.bar: 200 curl.foo to httpbin.legacy: 200 curl.bar to httpbin.foo: 200 curl.bar to httpbin.bar: 200 curl.bar to httpbin.legacy: 200 curl.legacy to httpbin.foo: 000 command terminated with exit code 56 curl.legacy to httpbin.bar: 200 curl.legacy to httpbin.legacy: 200

按工作负载启用双向 TLS

要为特定工作负载设置对等身份验证策略,您必须配置 selector 部分并指定与所需工作负载匹配的标签。例如,以下对等身份验证策略为 httpbin.bar 工作负载启用严格的双向 TLS

$ cat <<EOF | kubectl apply -n bar -f - apiVersion: security.istio.io/v1 kind: PeerAuthentication metadata: name: "httpbin" namespace: "bar" spec: selector: matchLabels: app: httpbin mtls: mode: STRICT EOF

再次运行探测命令。正如预期的那样,来自 curl.legacyhttpbin.bar 的请求开始因相同原因而失败。

$ for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=curl -n ${from} -o jsonpath={.items..metadata.name})" -c curl -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "curl.${from} to httpbin.${to}: %{http_code}\n"; done; done
curl.foo to httpbin.foo: 200 curl.foo to httpbin.bar: 200 curl.foo to httpbin.legacy: 200 curl.bar to httpbin.foo: 200 curl.bar to httpbin.bar: 200 curl.bar to httpbin.legacy: 200 curl.legacy to httpbin.foo: 000 command terminated with exit code 56 curl.legacy to httpbin.bar: 000 command terminated with exit code 56 curl.legacy to httpbin.legacy: 200
...
curl.legacy to httpbin.bar: 000
command terminated with exit code 56

要按端口优化双向 TLS 设置,您必须配置 portLevelMtls 部分。例如,以下对等身份验证策略要求在所有端口上使用双向 TLS,除了端口 8080

$ cat <<EOF | kubectl apply -n bar -f - apiVersion: security.istio.io/v1 kind: PeerAuthentication metadata: name: "httpbin" namespace: "bar" spec: selector: matchLabels: app: httpbin mtls: mode: STRICT portLevelMtls: 8080: mode: DISABLE EOF
  1. 对等身份验证策略中的端口值是容器的端口。
  2. 只有当端口绑定到服务时,才能使用 portLevelMtls。否则,Istio 会忽略它。
$ for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=curl -n ${from} -o jsonpath={.items..metadata.name})" -c curl -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "curl.${from} to httpbin.${to}: %{http_code}\n"; done; done
curl.foo to httpbin.foo: 200 curl.foo to httpbin.bar: 200 curl.foo to httpbin.legacy: 200 curl.bar to httpbin.foo: 200 curl.bar to httpbin.bar: 200 curl.bar to httpbin.legacy: 200 curl.legacy to httpbin.foo: 000 command terminated with exit code 56 curl.legacy to httpbin.bar: 200 curl.legacy to httpbin.legacy: 200

策略优先级

工作负载特定的对等身份验证策略优先于命名空间范围的策略。如果您添加一个策略来禁用 httpbin.foo 工作负载的双向 TLS,则可以测试此行为,例如。请注意,您已经创建了一个命名空间范围的策略,该策略为 foo 命名空间中的所有服务启用双向 TLS,并观察到来自 curl.legacyhttpbin.foo 的请求正在失败(见上文)。

$ cat <<EOF | kubectl apply -n foo -f - apiVersion: security.istio.io/v1 kind: PeerAuthentication metadata: name: "overwrite-example" namespace: "foo" spec: selector: matchLabels: app: httpbin mtls: mode: DISABLE EOF

重新运行来自 curl.legacy 的请求,您应该再次看到成功返回码 (200),确认服务特定的策略覆盖了命名空间范围的策略。

$ kubectl exec "$(kubectl get pod -l app=curl -n legacy -o jsonpath={.items..metadata.name})" -c curl -n legacy -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}\n"
200

清理步骤 2

删除上述步骤中创建的策略

$ kubectl delete peerauthentication default overwrite-example -n foo $ kubectl delete peerauthentication httpbin -n bar

最终用户身份验证

要试验此功能,您需要一个有效的 JWT。JWT 必须对应于您要用于演示的 JWKS 端点。本教程使用 Istio 代码库中的测试令牌 JWT 测试JWKS 端点

此外,为方便起见,通过入口网关公开 httpbin.foo(有关更多详细信息,请参阅 入口任务)。

配置网关

Zip
$ kubectl apply -f samples/httpbin/httpbin-gateway.yaml -n foo

按照 确定入口 IP 和端口 中的说明定义 INGRESS_PORTINGRESS_HOST 环境变量。

通过网关运行测试查询

$ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
200

现在,添加一个请求身份验证策略,该策略要求入口网关的最终用户 JWT。

$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1 kind: RequestAuthentication metadata: name: "jwt-example" namespace: istio-system spec: selector: matchLabels: istio: ingressgateway jwtRules: - issuer: "testing@secure.istio.io" jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.24/security/tools/jwt/samples/jwks.json" EOF

在它选择的工作负载(在本例中为入口网关)的命名空间中应用策略。

如果您在授权标头(其隐式默认位置)中提供令牌,则 Istio 会使用 公钥集 验证令牌,如果承载令牌无效,则会拒绝请求。但是,会接受没有令牌的请求。要观察此行为,请重试没有令牌、带有错误令牌和带有有效令牌的请求

$ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
200
$ curl --header "Authorization: Bearer deadbeef" "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
401
$ TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.24/security/tools/jwt/samples/demo.jwt -s) $ curl --header "Authorization: Bearer $TOKEN" "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
200

要观察 JWT 验证的其他方面,请使用脚本 gen-jwt.py 生成新的令牌以测试不同的发行者、受众、过期日期等。可以从 Istio 存储库下载该脚本

$ wget --no-verbose https://raw.githubusercontent.com/istio/istio/release-1.24/security/tools/jwt/samples/gen-jwt.py

您还需要 key.pem 文件

$ wget --no-verbose https://raw.githubusercontent.com/istio/istio/release-1.24/security/tools/jwt/samples/key.pem

JWT 身份验证具有 60 秒的时钟偏差,这意味着 JWT 令牌将在其配置的 nbf 之前 60 秒变为有效,并在其配置的 exp 之后 60 秒保持有效。

例如,以下命令创建一个在 5 秒内过期的令牌。如您所见,Istio 最初成功地使用该令牌对请求进行身份验证,但在 65 秒后拒绝了它们

$ TOKEN=$(python3 ./gen-jwt.py ./key.pem --expire 5) $ for i in $(seq 1 10); do curl --header "Authorization: Bearer $TOKEN" "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"; sleep 10; done
200 200 200 200 200 200 200 401 401 401

您还可以将 JWT 策略添加到入口网关(例如,服务 istio-ingressgateway.istio-system.svc.cluster.local)。这通常用于为绑定到网关的所有服务定义 JWT 策略,而不是为各个服务定义。

要求有效的令牌

要拒绝没有有效令牌的请求,请添加一个授权策略,其中包含一个规则,该规则指定对没有请求主体(在以下示例中显示为 notRequestPrincipals: ["*"])的请求执行 DENY 操作。只有在提供有效的 JWT 令牌时,请求主体才可用。因此,该规则会拒绝没有有效令牌的请求。

$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1 kind: AuthorizationPolicy metadata: name: "frontend-ingress" namespace: istio-system spec: selector: matchLabels: istio: ingressgateway action: DENY rules: - from: - source: notRequestPrincipals: ["*"] EOF

重试没有令牌的请求。请求现在失败,错误代码为 403

$ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
403

按路径要求有效的令牌

要按主机、路径或方法优化具有令牌要求的授权,请更改授权策略,仅在 /headers 上要求 JWT。当此授权规则生效时,对 $INGRESS_HOST:$INGRESS_PORT/headers 的请求将失败,错误代码为 403。对所有其他路径(例如 $INGRESS_HOST:$INGRESS_PORT/ip)的请求将成功。

$ kubectl apply -f - <<EOF apiVersion: security.istio.io/v1 kind: AuthorizationPolicy metadata: name: "frontend-ingress" namespace: istio-system spec: selector: matchLabels: istio: ingressgateway action: DENY rules: - from: - source: notRequestPrincipals: ["*"] to: - operation: paths: ["/headers"] EOF
$ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
403
$ curl "$INGRESS_HOST:$INGRESS_PORT/ip" -s -o /dev/null -w "%{http_code}\n"
200

清理步骤 3

  1. 删除身份验证策略

    $ kubectl -n istio-system delete requestauthentication jwt-example
  2. 删除授权策略

    $ kubectl -n istio-system delete authorizationpolicy frontend-ingress
  3. 删除令牌生成器脚本和密钥文件

    $ rm -f ./gen-jwt.py ./key.pem
  4. 如果您不打算探索任何后续任务,则只需删除测试命名空间即可删除所有资源。

    $ kubectl delete ns foo bar legacy
这些信息是否有用?
您是否有任何改进建议?

感谢您的反馈!