diff --git a/content/en/docs/tasks/security/authentication/authn-policy/index.md b/content/en/docs/tasks/security/authentication/authn-policy/index.md index cf786850b0..cb2b2dd1ab 100644 --- a/content/en/docs/tasks/security/authentication/authn-policy/index.md +++ b/content/en/docs/tasks/security/authentication/authn-policy/index.md @@ -28,7 +28,7 @@ $ istioctl install --set profile=default ### Setup Our examples use two namespaces `foo` and `bar`, with two services, `httpbin` and `sleep`, both running with an Envoy proxy. We also use second -instances of `httpbin` and `sleep` running without the sidecar in the `legacy` namespace. If you’d like to use the same examples when trying the tasks, +instances of `httpbin` and `sleep` running without the sidecar in the `legacy` namespace. If you’d like to use the same examples when trying the tasks, run the following: {{< text bash >}} @@ -84,7 +84,7 @@ $ kubectl get destinationrules.networking.istio.io --all-namespaces -o yaml | gr {{< /text >}} {{< tip >}} -Depending on the version of Istio, you may see destination rules for hosts other then those shown. However, there should be none with hosts in the `foo`, +Depending on the version of Istio, you may see destination rules for hosts other than those shown. However, there should be none with hosts in the `foo`, `bar` and `legacy` namespace, nor is the match-all wildcard `*` {{< /tip >}} @@ -168,7 +168,7 @@ $ kubectl delete peerauthentication -n istio-system default ### Namespace-wide policy -To change mutual TLS for all workloads within a particular namespace, use a namespace-wide policy. The specification of the policy is the same as for a mesh-wide policy, but you specify the namespace it applies to under `metadata`. For example, the following peer authentication policy enables strict mutual TLS for the `foo` namespace: +To change mutual TLS for all workloads within a particular namespace, use a namespace-wide policy. The specification of the policy is the same as for a mesh-wide policy, but you specify the namespace it applies to under `metadata`. For example, the following peer authentication policy enables strict mutual TLS for the `foo` namespace: {{< text bash >}} $ kubectl apply -f - <}} +$ istioctl install --set profile=default +{{< /text >}} ### 设置{#setup} -我们的示例用到两个命名空间 `foo` 和 `bar`,以及两个服务 `httpbin` 和 `sleep`,这两个服务都带有 Envoy sidecar proxy 一起运行。我们也会用到两个运行在 `legacy` 命名空间下不带 sidecar 的 `httpbin` 和 `sleep` 实例。如果您想要使用相同的示例尝试任务,执行如下命令: +我们将在 `foo` 和 `bar`命名空间下各自创建带有 Envoy 代理(sidecar)的 `httpbin` 和 `sleep`服务。我们还会在 `legacy` 命名空间下创建不带 Envoy 代理(sidecar)的 `httpbin` 和 `sleep` 服务。如果您希望使用相同的示例来完成这些任务,执行如下命令: {{< text bash >}} $ kubectl create ns foo @@ -33,19 +37,19 @@ $ 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` 发送一个 HTTP 请求给 `httpbin.foo`、`httpbin.bar` 或 `httpbin.legacy` 来验证。所有请求应该都成功返回 HTTP 代码 200。 +现在您可以在 `foo`、`bar` 或 `legacy` 三个命名空间下的任意 `sleep` pod 使用 `curl`向 `httpbin.foo`、`httpbin.bar` 或 `httpbin.legacy` 发送 HTTP 请求来验证部署结果。所有请求都应该成功返回 HTTP 200。 -例如,这里的一个从 `sleep.bar` 到 `httpbin.foo` 的检查可达性的命令: +例如,一个检查 `sleep.bar` 到 `httpbin.foo` 可达性的指令如下: {{< text bash >}} -$ kubectl exec $(kubectl get pod -l app=sleep -n bar -o jsonpath={.items..metadata.name}) -c sleep -n bar -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}\n" +$ kubectl exec "$(kubectl get pod -l app=sleep -n bar -o jsonpath={.items..metadata.name})" -c sleep -n bar -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}\n" 200 {{< /text >}} -这个单行命令可以方便地遍历所有可达性组合: +您也可以使用一行指令检查所有可能的组合: {{< 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 +$ 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 -s "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done sleep.foo to httpbin.foo: 200 sleep.foo to httpbin.bar: 200 sleep.foo to httpbin.legacy: 200 @@ -57,274 +61,115 @@ sleep.legacy to httpbin.bar: 200 sleep.legacy to httpbin.legacy: 200 {{< /text >}} -您还应该要验证系统中是否有默认的网格认证策略,可执行如下命令: +使用以下指令确认系统中没有对等身份验证策略: {{< text bash >}} -$ kubectl get policies.authentication.istio.io --all-namespaces -No resources found. +$ kubectl get peerauthentication --all-namespaces +No resources found {{< /text >}} -{{< text bash >}} -$ kubectl get meshpolicies.authentication.istio.io -NAME AGE -default 3m -{{< /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:" - host: istio-policy.istio-system.svc.cluster.local - host: istio-telemetry.istio-system.svc.cluster.local {{< /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 流量发送到这些工作负载,并将纯文本流量发送到没有 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 http://httpbin.foo:8000/headers -s | grep X-Forwarded-Client-Cert -"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=" +$ 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=;/' +"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=;Subject=\"\";URI=spiffe://cluster.local/ns/foo/sa/sleep" {{< /text >}} -当服务器没有 sidecar 时, `X-Forwarded-Client-Cert` 标头将不会存在,这意味着请求是纯文本的。 +当服务器没有 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 +$ 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 {{< /text >}} -## 全局启用 Istio 双向 TLS{#globally-enabling-Istio-mutual-TLS} +## 全局以严格模式启用 Istio 双向 TLS{#globally-enabling-Istio-mutual-TLS-in-STRICT-mode} -设置一个启用双向 TLS 的网格范围的认证策略,提交如下 *mesh authentication policy* : +当 Istio 自动将代理和工作负载之间的所有流量升级到双向 TLS 时,工作负载仍然可以接收 plain-text 流量。为了阻止整个网格的服务以非双向 TLS 通信,需要将整个服务网格的对等认证策略设置为 `STRICT` 模式。 +在整个服务网格范围内,对等认证策略不应该有一个 `selector`,它必须应用于 **根命名空间**,例如: {{< text bash >}} $ kubectl apply -f - <}} - -{{< tip >}} -网格认证策略使用[通用认证策略 API](/zh/docs/reference/config/security/istio.authentication.v1alpha1/),它定义在集群作用域 `MeshPolicy` CRD 中。 - {{< /tip >}} - -该策略规定网格上的所有工作负载只接收使用 TLS 的加密请求。如您所见,该认证策略的类型为:`MeshPolicy`。策略的名字必须是 `default`,并且不含 `targets` 属性(目的是应用到网格中所有服务上)。 - -这时候,只有接收方配置使用双向 TLS。如果您在 *Istio services* 之间执行 `curl` 命令(即,那些带有 sidecars 的服务),由于客户端仍旧使用纯文本,所有请求都会失败并报 503 错误。 - -{{< text bash >}} -$ for from in "foo" "bar"; do for to in "foo" "bar"; 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 -sleep.foo to httpbin.foo: 503 -sleep.foo to httpbin.bar: 503 -sleep.bar to httpbin.foo: 503 -sleep.bar to httpbin.bar: 503 -{{< /text >}} - -配置客户端,您需要设置 [destination rules](/zh/docs/concepts/traffic-management/#destination-rules) 来使用双向 TLS。也可以使用多 destination rules,为每个合适的服务(或命名空间)都配置一个。不过,更方便地方式是创建一个规则使用通配符 `*` 匹配所有服务,因此这也和网格范围的认证策略作用等同。 - -{{< text bash >}} -$ kubectl apply -f - <}} {{< tip >}} -* 从 Istio 1.1 开始,只有客户端命名空间,服务端命名空间和 `global` 命名空间(默认是 `istio-system`)中的 destination rules 会按顺序提供给服务。 -* Host 值 `*.local` 限制只与集群中的服务匹配,而不是外部服务。同时注意,destination rule 的名字或命名空间没有做限制。 -* 在 `ISTIO_MUTUAL` TLS 模式下,Istio 将根据密钥和证书(例如客户端证书,密钥和 CA 证书)的内部实现为它们设置路径。 -{{< /tip >}} +该示例假定命名空间 `istio-system` 是根命名空间。如果在安装过程中使用了不同的值,请将 `istio-system` 替换为所使用的值。 + {{< /tip >}} -别忘了 destination rules 也可用于非授权原因例如设置金丝雀发布,不过要适用同样的优先顺序。因此,如果一个服务不管什么原因要求一个特定的 destination rule —— 例如,配置负载均衡 —— 这个规则必须包含一个简单的 `ISTIO_MUTUAL` 模式的 TLS 块,否则它将会被网格或者命名空间范围的 TLS 设置覆盖并使 TLS 失效。 +此对等身份验证策略将工作负载配置为仅接受使用 TLS 加密的请求。由于未对 `selector` 字段指定值,因此该策略适用于服务网格中的所有工作负载。 -重新执行上述测试命令,您将看到所有 Istio 服务间的请求现在都成功完成。 +再次运行测试指令: {{< text bash >}} -$ for from in "foo" "bar"; do for to in "foo" "bar"; 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 +$ 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 sleep.foo to httpbin.foo: 200 sleep.foo to httpbin.bar: 200 +sleep.foo to httpbin.legacy: 200 sleep.bar to httpbin.foo: 200 sleep.bar to httpbin.bar: 200 -{{< /text >}} - -### 从非 Istio 服务到 Istio 服务的请求{#request-from-non-Istio-services-to-Istio-services} - -非 Istio 服务,例如 `sleep.legacy` 没有 sidecar,所以它不能将要求的 TLS 连接初始化到 Istio 服务。这会导致从 `sleep.legacy` 到 `httpbin.foo` 或者 `httpbin.bar` 的请求失败: - -{{< text bash >}} -$ for from in "legacy"; do for to in "foo" "bar"; 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 +sleep.bar to httpbin.legacy: 200 sleep.legacy to httpbin.foo: 000 command terminated with exit code 56 sleep.legacy to httpbin.bar: 000 command terminated with exit code 56 +sleep.legacy to httpbin.legacy: 200 {{< /text >}} -{{< tip >}} -由于 Envoy 拒绝纯文本请求的方式,您将会在这个例子中看到 `curl` 返回 56 代码(接收网络数据失败)。 -{{< /tip >}} - -这个按预期工作,而且很不幸,没有解决办法,除非降低对这些服务的认证条件要求。 - -### 从 Istio 服务到非 Istio 服务的请求{#request-from-Istio-services-to-non-Istio-services} - -尝试从 `sleep.foo` (或者 `sleep.bar`) 发送请求给 `httpbin.legacy`。您将看到请求失败,因为 Istio 按照指示在 destination rule 中配置了客户端使用双向 TLS,但是 `httpbin.legacy` 没有 sidecar,所以它处理不了。 - -{{< text bash >}} -$ for from in "foo" "bar"; do for to in "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 -sleep.foo to httpbin.legacy: 503 -sleep.bar to httpbin.legacy: 503 -{{< /text >}} - -为了解决这个问题,我们可以为 `httpbin.legacy` 添加一个 destination rule 覆盖 TLS 设置。例如: - -{{< text bash >}} -$ kubectl apply -f - <}} - -在您添加了 destination rule 后再次测试,确保它能通过: - -{{< text bash >}} -$ for from in "foo" "bar"; do for to in "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 -sleep.foo to httpbin.legacy: 200 -sleep.bar to httpbin.legacy: 200 -{{< /text >}} - -{{< tip >}} -这个 destination rule 在服务端(`httpbin.legacy`)的命名空间中,因此它优先于定义在 `istio-system` 中的全局 destination rule。 -{{< /tip >}} - -### 请求从 Istio 服务到 Kubernetes API server{#request-from-Istio-services-to-Kubernetes-API-server} - -Kubernetes API server 没有 sidecar,所以来自 Istio 服务的请求如 `sleep.foo` 将会失败,这跟发送请求给任何非 Istio 服务有相同的问题。 - -{{< text bash >}} -$ TOKEN=$(kubectl describe secret $(kubectl get secrets | grep default-token | cut -f1 -d ' ' | head -1) | grep -E '^token' | cut -f2 -d':' | tr -d ' \t') -$ kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl https://kubernetes.default/api --header "Authorization: Bearer $TOKEN" --insecure -s -o /dev/null -w "%{http_code}\n" -000 -command terminated with exit code 35 -{{< /text >}} - -再次,我们通过覆盖 API server (`kubernetes.default`) 的 destination rule 来纠正它。 - -{{< text bash >}} -$ kubectl apply -f - <}} - -{{< tip >}} -当您安装 Istio 并启用双向 TLS 时,这个规则,会跟全局认证策略和上述 destination rule 一起被自动注入到系统中。 -{{< /tip >}} - -重新执行上述测试命令确认在规则添加后会返回 200: - -{{< text bash >}} -$ TOKEN=$(kubectl describe secret $(kubectl get secrets | grep default-token | cut -f1 -d ' ' | head -1) | grep -E '^token' | cut -f2 -d':' | tr -d ' \t') -$ kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl https://kubernetes.default/api --header "Authorization: Bearer $TOKEN" --insecure -s -o /dev/null -w "%{http_code}\n" -200 -{{< /text >}} +您会发现除了从没有 sidecar 的服务(`sleep.legacy`) 到有 sidecar 的服务(`httpbin.foo` 或 `httpbin.bar`) 的请求外,其他请求依然是返回成功的。 ### 清除部分 1{#cleanup-part-1} -删除在场景中添加的全局认证策略和 destination rules: +删除在会话中添加的全局身份验证策略和 `destination rules`: {{< text bash >}} -$ kubectl delete meshpolicy default -$ kubectl delete destinationrules httpbin-legacy -n legacy -$ kubectl delete destinationrules api-server -n istio-system -$ kubectl delete destinationrules default -n istio-system +$ kubectl delete peerauthentication -n istio-system default {{< /text >}} -## 为每个命名空间或者服务启用双向 TLS{#enable-mutual-TLS-per-namespace-or-service} +## 为每个命名空间或者工作负载启用双向 TLS{#enable-mutual-TLS-per-namespace-or-workload} -除了为您的整个网格指定一个认证策略,Istio 也支持您为特定的命名空间或者服务指定策略。一个命名空间范围的策略优先级高于网格范围的策略,而服务范围的策略优先级更高。 +### 命名空间级别策略{#namespace-wide-policy} -### 命名空间范围的策略{#namespace-wide-policy} - -下述示例展示为命名空间 `foo` 中的所有服务启用双向 TLS 的策略。如你所见,它使用的类型是 `Policy` 而不是 `MeshPolicy`,在这个案例中指定命名空间为 `foo`。如果您没有指定命名空间的值,策略将会应用默认命名空间。 +如果要将特定命名空间内的所有工作负载更改双向 TLS,请使用命名空间级别策略。该策略的规范与整个服务网格级别规范相同,但是您可以在 `metadata` 字段指定命名空间的名称。例如,以下对等身份验证策略在 `foo` 命名空间上启用了严格的双向 TLS: {{< text bash >}} $ kubectl apply -f - <}} -{{< tip >}} -与 *网格范围的策略* 类似,命名空间范围的策略必须命名为 `default`,并且没有限制任何具体服务(没有 `targets` 部分)。 -{{< /tip >}} - -添加相应的 destination rule: +由于这些策略只应用于命名空间 `foo` 中的服务,您会看到只有从没有 sidecar 的客户端(`sleep.legacy`)到有 sidecar 的客户端(`httpbin.foo`)的请求会失败。 {{< text bash >}} -$ kubectl apply -f - <}} - -{{< tip >}} -Host `*.foo.svc.cluster.local` 限制只匹配 `foo` 命名空间中的服务。 -{{< /tip >}} - -由于这些策略和 destination rule 只应用于命名空间 `foo` 中的服务,您应该会看到只有从没有 sidecar 的客户端(`sleep.legacy`) 到 `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 +$ 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 sleep.foo to httpbin.foo: 200 sleep.foo to httpbin.bar: 200 sleep.foo to httpbin.legacy: 200 @@ -337,21 +182,25 @@ sleep.legacy to httpbin.bar: 200 sleep.legacy to httpbin.legacy: 200 {{< /text >}} -### 特定服务策略{#service-specific-policy} +### 为每个工作负载启用双向 TLS{#enable-mutual-TLS-per-workload} -您也可以为特定服务设置认证策略和 destination rule。执行这个命令为 `httpbin.bar` 服务设置另一个策略。 +要为特定工作负载设置对等身份验证策略,必须配置 `selector` 字段并指定与所需工作负载匹配的标签。然而 Istio 不能将出站双向 TLS 流量的工作负载策略聚合到服务。您应该配置 destination rule 来管理该行为。 + +例如,以下对等身份验证策略和 destination rule 将为 `httpbin.bar` 服务启用严格的双向 TLS: {{< text bash >}} $ cat <}} @@ -359,8 +208,8 @@ EOF {{< text bash >}} $ cat <}} -{{< tip >}} -* 本示例中,我们 **不** 在元数据中指定命名空间而是将它放在命令行上(`-n bar`),这也有相同的作用。 -* 认证策略和 destination rule 的名字没有限制。为了简单起见,本示例使用服务本身的名字。 -{{< /tip >}} +再次执行探查命令。跟预期一样,从 `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 +sleep.foo to httpbin.foo: 200 +sleep.foo to httpbin.bar: 200 +sleep.foo to httpbin.legacy: 200 +sleep.bar to httpbin.foo: 200 +sleep.bar to httpbin.bar: 200 +sleep.bar to httpbin.legacy: 200 +sleep.legacy to httpbin.foo: 000 +command terminated with exit code 56 +sleep.legacy to httpbin.bar: 000 +command terminated with exit code 56 +sleep.legacy to httpbin.legacy: 200 +{{< /text >}} {{< text plain >}} ... @@ -384,21 +243,24 @@ sleep.legacy to httpbin.bar: 000 command terminated with exit code 56 {{< /text >}} -如果我们在命名空间 `bar` 中还有其它服务,我们应该会看到请求它们的流量将不会受到影响。除了添加更多服务来演示这个行为,我们也可以稍微编辑策略将其应用到一个具体端口: +要优化每个端口的 双向 TLS 设置,必须配置 `portLevelMtls` 字段。 例如,以下对等身份验证策略要求在除 `80` 端口以外的所有端口上都使用双向 TLS: {{< text bash >}} $ cat <}} @@ -406,28 +268,38 @@ EOF {{< text bash >}} $ cat <}} -这个新策略将只应用在 `httpbin` 服务的 `1234` 端口。结果,双向 TLS 在 `8000` 端口上会再次失效而来自 `sleep.legacy` 的请求将会恢复正常工作。 +1. 对等身份验证策略中的端口值为容器的端口。destination rule 的值是服务的端口。 +1. 如果端口绑定到服务则只能使用 `portLevelMtls` 配置,其他配置将被 Istio 忽略。 {{< text bash >}} -$ kubectl exec $(kubectl get pod -l app=sleep -n legacy -o jsonpath={.items..metadata.name}) -c sleep -n legacy -- curl http://httpbin.bar: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=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 +sleep.foo to httpbin.foo: 200 +sleep.foo to httpbin.bar: 200 +sleep.foo to httpbin.legacy: 200 +sleep.bar to httpbin.foo: 200 +sleep.bar to httpbin.bar: 200 +sleep.bar to httpbin.legacy: 200 +sleep.legacy to httpbin.foo: 000 +command terminated with exit code 56 +sleep.legacy to httpbin.bar: 200 +sleep.legacy to httpbin.legacy: 200 {{< /text >}} ### 策略优先级{#policy-precedence} @@ -437,22 +309,26 @@ $ kubectl exec $(kubectl get pod -l app=sleep -n legacy -o jsonpath={.items..met {{< text bash >}} $ cat <}} -添加 destination rule: +同时修改 destination rule: {{< text bash >}} $ cat <}} -$ 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" +$ 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" 200 {{< /text >}} ### 清除部分 2{#cleanup-part-2} -删除上面步骤中创建的策略和 destination rules: +删除之前步骤中创建的策略和 destination rules: {{< text bash >}} -$ kubectl delete policy default overwrite-example -n foo -$ kubectl delete policy httpbin -n bar -$ kubectl delete destinationrules default overwrite-example -n foo +$ kubectl delete peerauthentication default overwrite-example -n foo +$ kubectl delete peerauthentication httpbin -n bar +$ kubectl delete destinationrules overwrite-example -n foo $ kubectl delete destinationrules httpbin -n bar {{< /text >}} ## 终端用户认证{#end-user-authentication} -为了体验这个特性,您需要一个有效的 JWT。该 JWT 必须和您用于该 demo 的 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 - <}} -获取 ingress IP +参考[确定 ingress IP 和端口](/zh/docs/tasks/traffic-management/ingress/ingress-control/#determining-the-ingress-ip-and-ports)章节配置环境变量 `INGRESS_HOST` 和 `INGRESS_PORT` 的值。 + +执行测试指令 {{< text bash >}} -$ export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}') -{{< /text >}} - -执行一个查询测试 - -{{< text bash >}} -$ curl $INGRESS_HOST/headers -s -o /dev/null -w "%{http_code}\n" +$ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n" 200 {{< /text >}} -现在,为 `httpbin.foo` 添加一个要求配置终端用户 JWT 的策略。下面的命令假定 `httpbin.foo` 没有特定服务策略(如果您执行了[清除](#cleanup-part-2)所述的操作,就会是这样)。您可以执行 `kubectl get policies.authentication.istio.io -n foo` 进行确认。 +现在添加一个身份验证策略,该策略要求入口网关需要指定终端用户的 JWT。 {{< text bash >}} -$ cat <}}/security/tools/jwt/samples/jwks.json" - principalBinding: USE_ORIGIN + selector: + matchLabels: + istio: ingressgateway + jwtRules: + - issuer: "testing@secure.istio.io" + jwksUri: "{{< github_file >}}/security/tools/jwt/samples/jwks.json" EOF {{< /text >}} -之前相同的 `curl` 命令将会返回 401 错误代码,由于服务器结果期望 JWT 却没有提供: +策略生效的命名空间是 `istio-system`,生效的工作负载由 `selector` 字段决定,上面的配置值是带有 `app: ingressgateway` 标签的工作负载。 + +如果您在授权标头(默认位置)中提供了令牌,则 Istio 将使用 [public key set]({{}}/security/tools/jwt/samples/jwks.json) 验证令牌,`bearer` 令牌无效会被拒绝,然而没有 `bearer` 令牌的请求会被接收。因此要观察此行为,请在没有令牌,令牌错误和有效令牌的情况下重试请求: {{< text bash >}} -$ curl $INGRESS_HOST/headers -s -o /dev/null -w "%{http_code}\n" -401 -{{< /text >}} - -附带上上面生成的有效 token 将返回成功: - -{{< text bash >}} -$ TOKEN=$(curl {{< github_file >}}/security/tools/jwt/samples/demo.jwt -s) -$ curl --header "Authorization: Bearer $TOKEN" $INGRESS_HOST/headers -s -o /dev/null -w "%{http_code}\n" +$ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n" 200 {{< /text >}} -为了观察 JWT 验证的其它方面,使用脚本 [`gen-jwt.py`]({{< github_tree >}}/security/tools/jwt/samples/gen-jwt.py) 生成新 tokens 带上不同的发行人、受众、有效期等等进行测试。这个脚本可以从 Istio 库下载: +{{< text bash >}} +$ curl --header "Authorization: Bearer deadbeef" "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n" +401 +{{< /text >}} {{< text bash >}} -$ wget {{< github_file >}}/security/tools/jwt/samples/gen-jwt.py -$ chmod +x gen-jwt.py +$ TOKEN=$(curl {{< github_file >}}/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 +{{< /text >}} + +为了观察 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 {{< /text >}} 您还需要 `key.pem` 文件: {{< text bash >}} -$ wget {{< github_file >}}/security/tools/jwt/samples/key.pem +$ wget --no-verbose {{< github_file >}}/security/tools/jwt/samples/key.pem {{< /text >}} {{< tip >}} -下载 [jwcrypto](https://pypi.org/project/jwcrypto) 库,如果您还没有在您的系统上安装的话。 +如果您的系统尚未安装 `jwcrypto` 库,你需要从 [jwcrypto](https://pypi.org/project/jwcrypto) 下载并安装。 {{< /tip >}} -例如,下述命令创建一个 5 秒钟过期的 token。如您所见,Istio 使用这个 token 刚开始认证请求成功,但是 5 秒后拒绝了它们。 +JWT 认证有 60 秒的时钟偏移(clock skew),这意味着 JWT 令牌会比其配置 `nbf` 早 60 秒成为有效的,其配置 `exp`后 60 秒后仍然有效。 + +例如,下面的命令创建一个令牌,该令牌在5秒钟后过期。 如您所见,Istio 会一直通过认证直到 65 秒后才拒绝这些令牌: {{< text bash >}} -$ TOKEN=$(./gen-jwt.py ./key.pem --expire 5) -$ for i in `seq 1 10`; do curl --header "Authorization: Bearer $TOKEN" $INGRESS_HOST/headers -s -o /dev/null -w "%{http_code}\n"; sleep 1; done +$ 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 @@ -604,165 +484,75 @@ $ for i in `seq 1 10`; do curl --header "Authorization: Bearer $TOKEN" $INGRESS_ 401 401 401 -401 -401 {{< /text >}} -您也可以给一个 ingress gateway 添加一个 JWT 策略(例如,服务 `istio-ingressgateway.istio-system.svc.cluster.local`)。 -这个常用于为绑定到这个 gateway 的所有服务定义一个 JWT 策略,而不是单独的服务。 +您也可以给一个 `ingress gateway` 添加一个 JWT 策略(例如,服务 `istio-ingressgateway.istio-system.svc.cluster.local`)。 +这个常用于为绑定到这个 gateway 的所有服务定义一个 JWT 策略而不是为单独的服务绑定策略。 -### 按路径要求的终端用户认证{#end-user-authentication-with-per-path-requirements} +### 提供有效令牌{#require-a-valid-token} -终端用户认证可以基于请求路径启用或者禁用。如果您想要让某些路径禁用认证就非常有用,例如,用于健康检查或者状态报告的路径。 -您也可以为不同的路径指定不同的 JWT。 - -{{< warning >}} -按路径要求的终端用户认证在 Istio 1.1 中是一个实验性的特性并 **不** 推荐在生产环境中使用。 -{{< /warning >}} - -#### 为指定路径禁用终端用户认证{#disable-end-user-authentication-for-specific-paths} - -修改 `jwt-example` 策略禁用路径 `/user-agent` 的终端用户认证: - -{{< text bash >}} -$ cat <}}/security/tools/jwt/samples/jwks.json" - trigger_rules: - - excluded_paths: - - exact: /user-agent - principalBinding: USE_ORIGIN -EOF -{{< /text >}} - -确认 `/user-agent` 路径允许免 JWT tokens 访问: - -{{< text bash >}} -$ curl $INGRESS_HOST/user-agent -s -o /dev/null -w "%{http_code}\n" -200 -{{< /text >}} - -确认不带 JWT tokens 的非 `/user-agent` 路径拒绝访问: - -{{< text bash >}} -$ curl $INGRESS_HOST/headers -s -o /dev/null -w "%{http_code}\n" -401 -{{< /text >}} - -#### 为指定路径启用终端用户认证{#enable-end-user-authentication-for-specific-paths} - -修改 `jwt-example` 策略启用路径 `/ip` 的终端用户认证: - -{{< text bash >}} -$ cat <}}/security/tools/jwt/samples/jwks.json" - trigger_rules: - - included_paths: - - exact: /ip - principalBinding: USE_ORIGIN -EOF -{{< /text >}} - -确认不带 JWT tokens 的非 `/ip` 路径允许访问: - -{{< text bash >}} -$ curl $INGRESS_HOST/user-agent -s -o /dev/null -w "%{http_code}\n" -200 -{{< /text >}} - -确认不带 JWT tokens 的 `/ip` 路径拒绝访问: - -{{< text bash >}} -$ curl $INGRESS_HOST/ip -s -o /dev/null -w "%{http_code}\n" -401 -{{< /text >}} - -确认带有效 JWT token 的 `/ip` 路径允许访问: - -{{< text bash >}} -$ TOKEN=$(curl {{< github_file >}}/security/tools/jwt/samples/demo.jwt -s) -$ curl --header "Authorization: Bearer $TOKEN" $INGRESS_HOST/ip -s -o /dev/null -w "%{http_code}\n" -200 -{{< /text >}} - -### 带双向 TLS 的终端用户认证{#end-user-authentication-with-mutual-TLS} - -终端用户认证和双向 TLS 可以共用。修改上面的策略定义双向 TLS 和终端用户 JWT 认证: - -{{< text bash >}} -$ cat <}}/security/tools/jwt/samples/jwks.json" - principalBinding: USE_ORIGIN -EOF -{{< /text >}} - -添加一个 destination rule: +拒绝没有有效的令牌的请求,需要增加名为 `DENY` 认证策略,可参考以下例子中的 `notRequestPrincipals:["*"]` 配置。仅当提供有效的JWT令牌时请求主体才可用,因此该规则将拒绝没有有效令牌的请求。 {{< text bash >}} $ kubectl apply -f - <}} -{{< tip >}} -如果您已经启用网格范围或者命名空间范围的 TLS,那么 host `httpbin.foo` 已经被这些 destination rule 覆盖。 -因此,您不需要添加这个 destination rule 。另外,您仍然需要添加 `mtls` 段到认证策略,因为特定服务策略将完全覆盖网格范围(或者命名空间范围)的策略。 -{{< /tip >}} - -修改这些后,从 Istio 服务,包括 ingress gateway,到 `httpbin.foo` 的流量将使用双向 TLS。上述测试命令将仍然会正常工作。给定正确的 token,从 Istio 服务直接到 `httpbin.foo` 的请求也会正常工作: +再次尝试不使用 token 请求服务,结果返回 `403` {{< text bash >}} -$ TOKEN=$(curl {{< github_file >}}/security/tools/jwt/samples/demo.jwt -s) -$ kubectl exec $(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name}) -c sleep -n foo -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}\n" --header "Authorization: Bearer $TOKEN" -200 +$ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n" +403 {{< /text >}} -然而,来自非 Istio 服务,使用纯文本的请求将会失败: +### 按路径提供有效令牌{#require-valid-tokens-per-path} + +为了按路径(路径指 host、path 或者 method)提供有效令牌我们需要在其认证策略中指定这些路径,如下列配置中的 `/headers`。待规则生效后,对`$INGRESS_HOST:$INGRESS_PORT/headers` 的请求将失败,错误代码为 `403`。而到其他所有路径的请求 —— 例如:`$INGRESS_HOST:$INGRESS_PORT/ip` —— 都会成功。 {{< 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" --header "Authorization: Bearer $TOKEN" -000 -command terminated with exit code 56 +$ kubectl apply -f - <}} + +{{< text bash >}} +$ curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n" +403 +{{< /text >}} + +{{< text bash >}} +$ curl "$INGRESS_HOST:$INGRESS_PORT/ip" -s -o /dev/null -w "%{http_code}\n" +200 {{< /text >}} ### 清除部分 3{#cleanup-part-3} @@ -770,16 +560,22 @@ command terminated with exit code 56 1. 删除认证策略: {{< text bash >}} - $ kubectl -n foo delete policy jwt-example + $ kubectl -n istio-system delete requestauthentication jwt-example {{< /text >}} -1. 删除 destination rule: +1. 删除授权策略: {{< text bash >}} - $ kubectl -n foo delete destinationrule httpbin + $ kubectl -n istio-system delete authorizationpolicy frontend-ingress {{< /text >}} -1. 如果您不打算研究后续任务,您只需简单删除测试命名空间即可删除所有资源: +1. 删除生成令牌的脚本和密钥文件: + + {{< text bash >}} + $ rm -f ./gen-jwt.py ./key.pem + {{< /text >}} + +1. 如果您不不打算继续后续章节的任务,您可以通过删除命名空间的方式清除所有资源: {{< text bash >}} $ kubectl delete ns foo bar legacy diff --git a/content/zh/docs/tasks/security/authentication/authn-policy/snips.sh b/content/zh/docs/tasks/security/authentication/authn-policy/snips.sh new file mode 100644 index 0000000000..4d7beca160 --- /dev/null +++ b/content/zh/docs/tasks/security/authentication/authn-policy/snips.sh @@ -0,0 +1,510 @@ +#!/bin/bash +# shellcheck disable=SC2034,SC2153,SC2155,SC2164 + +# Copyright Istio Authors. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +#################################################################################################### +# WARNING: THIS IS AN AUTO-GENERATED FILE, DO NOT EDIT. PLEASE MODIFY THE ORIGINAL MARKDOWN FILE: +# docs/tasks/security/authentication/authn-policy/index.md +#################################################################################################### + +snip_before_you_begin_1() { +istioctl install --set profile=default +} + +snip_setup_1() { +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/sleep/sleep.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/sleep/sleep.yaml) -n bar +kubectl create ns legacy +kubectl apply -f samples/httpbin/httpbin.yaml -n legacy +kubectl apply -f samples/sleep/sleep.yaml -n legacy +} + +snip_setup_2() { +kubectl exec "$(kubectl get pod -l app=sleep -n bar -o jsonpath={.items..metadata.name})" -c sleep -n bar -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}\n" +} + +! read -r -d '' snip_setup_2_out <<\ENDSNIP +200 +ENDSNIP + +snip_setup_3() { +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 -s "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done +} + +! read -r -d '' snip_setup_3_out <<\ENDSNIP +sleep.foo to httpbin.foo: 200 +sleep.foo to httpbin.bar: 200 +sleep.foo to httpbin.legacy: 200 +sleep.bar to httpbin.foo: 200 +sleep.bar to httpbin.bar: 200 +sleep.bar to httpbin.legacy: 200 +sleep.legacy to httpbin.foo: 200 +sleep.legacy to httpbin.bar: 200 +sleep.legacy to httpbin.legacy: 200 +ENDSNIP + +snip_setup_4() { +kubectl get peerauthentication --all-namespaces +} + +! read -r -d '' snip_setup_4_out <<\ENDSNIP +No resources found +ENDSNIP + +snip_setup_5() { +kubectl get destinationrules.networking.istio.io --all-namespaces -o yaml | grep "host:" +} + +! read -r -d '' snip_setup_5_out <<\ENDSNIP + +ENDSNIP + +snip_auto_mutual_tls_1() { +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=;/' +} + +! read -r -d '' snip_auto_mutual_tls_1_out <<\ENDSNIP + "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=;Subject=\"\";URI=spiffe://cluster.local/ns/foo/sa/sleep" +ENDSNIP + +snip_auto_mutual_tls_2() { +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 +} + +! read -r -d '' snip_auto_mutual_tls_2_out <<\ENDSNIP + +ENDSNIP + +snip_globally_enabling_istio_mutual_tls_in_strict_mode_1() { +kubectl apply -f - <