mirror of https://github.com/istio/istio.io.git
zh-translation:content/zh/docs/tasks/security/authentication/authn-policy/index.md (#9432)
This commit is contained in:
parent
e6de49f5df
commit
f8c4793af6
|
|
@ -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 - <<EOF
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: 认证策略
|
||||
description: 为您展示如何使用 Istio 认证策略设置双向 TLS 和基础终端用户认证。
|
||||
description: 向您展示如何通过使用 Istio 认证策略来设置双向 TLS 和基本的终端用户认证。
|
||||
weight: 10
|
||||
keywords: [security,authentication]
|
||||
aliases:
|
||||
|
|
@ -15,11 +15,15 @@ test: yes
|
|||
## 开始之前{#before-you-begin}
|
||||
|
||||
* 理解 Istio [认证策略](/zh/docs/concepts/security/#authentication-policies)和[双向 TLS 认证](/zh/docs/concepts/security/#mutual-TLS-authentication)相关概念。
|
||||
* 在 Kubernetes 集群中安装 Istio 并禁用全局双向 TLS (例如,使用[安装步骤](/zh/docs/setup/getting-started)提到的 demo 配置文件,或者设置 `global.mtls.enabled` 安装选项为 false )。
|
||||
* 参照[安装步骤](/zh/docs/setup/getting-started),使用 `default` 模板在 Kubernetes 集群中安装 Istio。
|
||||
|
||||
{{< text bash >}}
|
||||
$ 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=<redacted>"
|
||||
$ 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` 标头将不会存在,这意味着请求是纯文本的。
|
||||
当服务器没有 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 - <<EOF
|
||||
apiVersion: "authentication.istio.io/v1alpha1"
|
||||
kind: "MeshPolicy"
|
||||
metadata:
|
||||
name: "default"
|
||||
spec:
|
||||
peers:
|
||||
- mtls: {}
|
||||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
{{< 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 - <<EOF
|
||||
apiVersion: "networking.istio.io/v1alpha3"
|
||||
kind: "DestinationRule"
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: "default"
|
||||
namespace: "istio-system"
|
||||
spec:
|
||||
host: "*.local"
|
||||
trafficPolicy:
|
||||
tls:
|
||||
mode: ISTIO_MUTUAL
|
||||
mtls:
|
||||
mode: STRICT
|
||||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
{{< 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 - <<EOF
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: DestinationRule
|
||||
metadata:
|
||||
name: "httpbin-legacy"
|
||||
namespace: "legacy"
|
||||
spec:
|
||||
host: "httpbin.legacy.svc.cluster.local"
|
||||
trafficPolicy:
|
||||
tls:
|
||||
mode: DISABLE
|
||||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
在您添加了 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 - <<EOF
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: DestinationRule
|
||||
metadata:
|
||||
name: "api-server"
|
||||
namespace: istio-system
|
||||
spec:
|
||||
host: "kubernetes.default.svc.cluster.local"
|
||||
trafficPolicy:
|
||||
tls:
|
||||
mode: DISABLE
|
||||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
{{< 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 - <<EOF
|
||||
apiVersion: "authentication.istio.io/v1alpha1"
|
||||
kind: "Policy"
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: "default"
|
||||
namespace: "foo"
|
||||
spec:
|
||||
peers:
|
||||
- mtls: {}
|
||||
mtls:
|
||||
mode: STRICT
|
||||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
{{< tip >}}
|
||||
与 *网格范围的策略* 类似,命名空间范围的策略必须命名为 `default`,并且没有限制任何具体服务(没有 `targets` 部分)。
|
||||
{{< /tip >}}
|
||||
|
||||
添加相应的 destination rule:
|
||||
由于这些策略只应用于命名空间 `foo` 中的服务,您会看到只有从没有 sidecar 的客户端(`sleep.legacy`)到有 sidecar 的客户端(`httpbin.foo`)的请求会失败。
|
||||
|
||||
{{< text bash >}}
|
||||
$ kubectl apply -f - <<EOF
|
||||
apiVersion: "networking.istio.io/v1alpha3"
|
||||
kind: "DestinationRule"
|
||||
metadata:
|
||||
name: "default"
|
||||
namespace: "foo"
|
||||
spec:
|
||||
host: "*.foo.svc.cluster.local"
|
||||
trafficPolicy:
|
||||
tls:
|
||||
mode: ISTIO_MUTUAL
|
||||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
{{< 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 <<EOF | kubectl apply -n bar -f -
|
||||
apiVersion: "authentication.istio.io/v1alpha1"
|
||||
kind: "Policy"
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: "httpbin"
|
||||
namespace: "bar"
|
||||
spec:
|
||||
targets:
|
||||
- name: httpbin
|
||||
peers:
|
||||
- mtls: {}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: httpbin
|
||||
mtls:
|
||||
mode: STRICT
|
||||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
|
|
@ -359,8 +208,8 @@ EOF
|
|||
|
||||
{{< text bash >}}
|
||||
$ cat <<EOF | kubectl apply -n bar -f -
|
||||
apiVersion: "networking.istio.io/v1alpha3"
|
||||
kind: "DestinationRule"
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: DestinationRule
|
||||
metadata:
|
||||
name: "httpbin"
|
||||
spec:
|
||||
|
|
@ -371,12 +220,22 @@ spec:
|
|||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
{{< 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 <<EOF | kubectl apply -n bar -f -
|
||||
apiVersion: "authentication.istio.io/v1alpha1"
|
||||
kind: "Policy"
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: "httpbin"
|
||||
namespace: "bar"
|
||||
spec:
|
||||
targets:
|
||||
- name: httpbin
|
||||
ports:
|
||||
- number: 1234
|
||||
peers:
|
||||
- mtls: {}
|
||||
selector:
|
||||
matchLabels:
|
||||
app: httpbin
|
||||
mtls:
|
||||
mode: STRICT
|
||||
portLevelMtls:
|
||||
80:
|
||||
mode: DISABLE
|
||||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
|
|
@ -406,28 +268,38 @@ EOF
|
|||
|
||||
{{< text bash >}}
|
||||
$ cat <<EOF | kubectl apply -n bar -f -
|
||||
apiVersion: "networking.istio.io/v1alpha3"
|
||||
kind: "DestinationRule"
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: DestinationRule
|
||||
metadata:
|
||||
name: "httpbin"
|
||||
spec:
|
||||
host: httpbin.bar.svc.cluster.local
|
||||
trafficPolicy:
|
||||
tls:
|
||||
mode: DISABLE
|
||||
mode: ISTIO_MUTUAL
|
||||
portLevelSettings:
|
||||
- port:
|
||||
number: 1234
|
||||
number: 8000
|
||||
tls:
|
||||
mode: ISTIO_MUTUAL
|
||||
mode: DISABLE
|
||||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
这个新策略将只应用在 `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 <<EOF | kubectl apply -n foo -f -
|
||||
apiVersion: "authentication.istio.io/v1alpha1"
|
||||
kind: "Policy"
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: "overwrite-example"
|
||||
namespace: "foo"
|
||||
spec:
|
||||
targets:
|
||||
- name: httpbin
|
||||
selector:
|
||||
matchLabels:
|
||||
app: httpbin
|
||||
mtls:
|
||||
mode: DISABLE
|
||||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
添加 destination rule:
|
||||
同时修改 destination rule:
|
||||
|
||||
{{< text bash >}}
|
||||
$ cat <<EOF | kubectl apply -n foo -f -
|
||||
apiVersion: "networking.istio.io/v1alpha3"
|
||||
kind: "DestinationRule"
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: DestinationRule
|
||||
metadata:
|
||||
name: "overwrite-example"
|
||||
spec:
|
||||
|
|
@ -466,25 +342,25 @@ EOF
|
|||
重新执行来自 `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"
|
||||
$ 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 - <<EOF
|
||||
|
|
@ -495,7 +371,7 @@ metadata:
|
|||
namespace: foo
|
||||
spec:
|
||||
selector:
|
||||
istio: ingressgateway # use Istio default gateway implementation
|
||||
istio: ingressgateway # 使用 Istio 的默认网关实现
|
||||
servers:
|
||||
- port:
|
||||
number: 80
|
||||
|
|
@ -527,75 +403,79 @@ spec:
|
|||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
获取 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 <<EOF | kubectl apply -n foo -f -
|
||||
apiVersion: "authentication.istio.io/v1alpha1"
|
||||
kind: "Policy"
|
||||
$ kubectl apply -f - <<EOF
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: RequestAuthentication
|
||||
metadata:
|
||||
name: "jwt-example"
|
||||
namespace: istio-system
|
||||
spec:
|
||||
targets:
|
||||
- name: httpbin
|
||||
origins:
|
||||
- jwt:
|
||||
issuer: "testing@secure.istio.io"
|
||||
jwksUri: "{{< github_file >}}/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]({{<github_file>}}/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 <<EOF | kubectl apply -n foo -f -
|
||||
apiVersion: "authentication.istio.io/v1alpha1"
|
||||
kind: "Policy"
|
||||
metadata:
|
||||
name: "jwt-example"
|
||||
spec:
|
||||
targets:
|
||||
- name: httpbin
|
||||
origins:
|
||||
- jwt:
|
||||
issuer: "testing@secure.istio.io"
|
||||
jwksUri: "{{< github_file >}}/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 <<EOF | kubectl apply -n foo -f -
|
||||
apiVersion: "authentication.istio.io/v1alpha1"
|
||||
kind: "Policy"
|
||||
metadata:
|
||||
name: "jwt-example"
|
||||
spec:
|
||||
targets:
|
||||
- name: httpbin
|
||||
origins:
|
||||
- jwt:
|
||||
issuer: "testing@secure.istio.io"
|
||||
jwksUri: "{{< github_file >}}/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 <<EOF | kubectl apply -n foo -f -
|
||||
apiVersion: "authentication.istio.io/v1alpha1"
|
||||
kind: "Policy"
|
||||
metadata:
|
||||
name: "jwt-example"
|
||||
spec:
|
||||
targets:
|
||||
- name: httpbin
|
||||
peers:
|
||||
- mtls: {}
|
||||
origins:
|
||||
- jwt:
|
||||
issuer: "testing@secure.istio.io"
|
||||
jwksUri: "{{< github_file >}}/security/tools/jwt/samples/jwks.json"
|
||||
principalBinding: USE_ORIGIN
|
||||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
添加一个 destination rule:
|
||||
拒绝没有有效的令牌的请求,需要增加名为 `DENY` 认证策略,可参考以下例子中的 `notRequestPrincipals:["*"]` 配置。仅当提供有效的JWT令牌时请求主体才可用,因此该规则将拒绝没有有效令牌的请求。
|
||||
|
||||
{{< text bash >}}
|
||||
$ kubectl apply -f - <<EOF
|
||||
apiVersion: "networking.istio.io/v1alpha3"
|
||||
kind: "DestinationRule"
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: AuthorizationPolicy
|
||||
metadata:
|
||||
name: "httpbin"
|
||||
namespace: "foo"
|
||||
name: "frontend-ingress"
|
||||
namespace: istio-system
|
||||
spec:
|
||||
host: "httpbin.foo.svc.cluster.local"
|
||||
trafficPolicy:
|
||||
tls:
|
||||
mode: ISTIO_MUTUAL
|
||||
selector:
|
||||
matchLabels:
|
||||
istio: ingressgateway
|
||||
action: DENY
|
||||
rules:
|
||||
- from:
|
||||
- source:
|
||||
notRequestPrincipals: ["*"]
|
||||
EOF
|
||||
{{< /text >}}
|
||||
|
||||
{{< 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 - <<EOF
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
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
|
||||
{{< /text >}}
|
||||
|
||||
{{< 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
|
||||
|
|
|
|||
|
|
@ -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=<redacted>;/'
|
||||
}
|
||||
|
||||
! read -r -d '' snip_auto_mutual_tls_1_out <<\ENDSNIP
|
||||
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=<redacted>;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 - <<EOF
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: "default"
|
||||
namespace: "istio-system"
|
||||
spec:
|
||||
mtls:
|
||||
mode: STRICT
|
||||
EOF
|
||||
}
|
||||
|
||||
snip_globally_enabling_istio_mutual_tls_in_strict_mode_2() {
|
||||
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
|
||||
}
|
||||
|
||||
! read -r -d '' snip_globally_enabling_istio_mutual_tls_in_strict_mode_2_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: 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
|
||||
ENDSNIP
|
||||
|
||||
snip_cleanup_part_1_1() {
|
||||
kubectl delete peerauthentication -n istio-system default
|
||||
}
|
||||
|
||||
snip_namespacewide_policy_1() {
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: "default"
|
||||
namespace: "foo"
|
||||
spec:
|
||||
mtls:
|
||||
mode: STRICT
|
||||
EOF
|
||||
}
|
||||
|
||||
snip_namespacewide_policy_2() {
|
||||
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
|
||||
}
|
||||
|
||||
! read -r -d '' snip_namespacewide_policy_2_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: 000
|
||||
command terminated with exit code 56
|
||||
sleep.legacy to httpbin.bar: 200
|
||||
sleep.legacy to httpbin.legacy: 200
|
||||
ENDSNIP
|
||||
|
||||
snip_enable_mutual_tls_per_workload_1() {
|
||||
cat <<EOF | kubectl apply -n bar -f -
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: "httpbin"
|
||||
namespace: "bar"
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: httpbin
|
||||
mtls:
|
||||
mode: STRICT
|
||||
EOF
|
||||
}
|
||||
|
||||
snip_enable_mutual_tls_per_workload_2() {
|
||||
cat <<EOF | kubectl apply -n bar -f -
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: DestinationRule
|
||||
metadata:
|
||||
name: "httpbin"
|
||||
spec:
|
||||
host: "httpbin.bar.svc.cluster.local"
|
||||
trafficPolicy:
|
||||
tls:
|
||||
mode: ISTIO_MUTUAL
|
||||
EOF
|
||||
}
|
||||
|
||||
snip_enable_mutual_tls_per_workload_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 "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "sleep.${from} to httpbin.${to}: %{http_code}\n"; done; done
|
||||
}
|
||||
|
||||
! read -r -d '' snip_enable_mutual_tls_per_workload_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: 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
|
||||
ENDSNIP
|
||||
|
||||
! read -r -d '' snip_enable_mutual_tls_per_workload_4 <<\ENDSNIP
|
||||
...
|
||||
sleep.legacy to httpbin.bar: 000
|
||||
command terminated with exit code 56
|
||||
ENDSNIP
|
||||
|
||||
snip_enable_mutual_tls_per_workload_5() {
|
||||
cat <<EOF | kubectl apply -n bar -f -
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: "httpbin"
|
||||
namespace: "bar"
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: httpbin
|
||||
mtls:
|
||||
mode: STRICT
|
||||
portLevelMtls:
|
||||
80:
|
||||
mode: DISABLE
|
||||
EOF
|
||||
}
|
||||
|
||||
snip_enable_mutual_tls_per_workload_6() {
|
||||
cat <<EOF | kubectl apply -n bar -f -
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: DestinationRule
|
||||
metadata:
|
||||
name: "httpbin"
|
||||
spec:
|
||||
host: httpbin.bar.svc.cluster.local
|
||||
trafficPolicy:
|
||||
tls:
|
||||
mode: ISTIO_MUTUAL
|
||||
portLevelSettings:
|
||||
- port:
|
||||
number: 8000
|
||||
tls:
|
||||
mode: DISABLE
|
||||
EOF
|
||||
}
|
||||
|
||||
snip_enable_mutual_tls_per_workload_7() {
|
||||
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
|
||||
}
|
||||
|
||||
! read -r -d '' snip_enable_mutual_tls_per_workload_7_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: 000
|
||||
command terminated with exit code 56
|
||||
sleep.legacy to httpbin.bar: 200
|
||||
sleep.legacy to httpbin.legacy: 200
|
||||
ENDSNIP
|
||||
|
||||
snip_policy_precedence_1() {
|
||||
cat <<EOF | kubectl apply -n foo -f -
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: PeerAuthentication
|
||||
metadata:
|
||||
name: "overwrite-example"
|
||||
namespace: "foo"
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: httpbin
|
||||
mtls:
|
||||
mode: DISABLE
|
||||
EOF
|
||||
}
|
||||
|
||||
snip_policy_precedence_2() {
|
||||
cat <<EOF | kubectl apply -n foo -f -
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: DestinationRule
|
||||
metadata:
|
||||
name: "overwrite-example"
|
||||
spec:
|
||||
host: httpbin.foo.svc.cluster.local
|
||||
trafficPolicy:
|
||||
tls:
|
||||
mode: DISABLE
|
||||
EOF
|
||||
}
|
||||
|
||||
snip_policy_precedence_3() {
|
||||
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"
|
||||
}
|
||||
|
||||
! read -r -d '' snip_policy_precedence_3_out <<\ENDSNIP
|
||||
200
|
||||
ENDSNIP
|
||||
|
||||
snip_cleanup_part_2_1() {
|
||||
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
|
||||
}
|
||||
|
||||
snip_enduser_authentication_1() {
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: Gateway
|
||||
metadata:
|
||||
name: httpbin-gateway
|
||||
namespace: foo
|
||||
spec:
|
||||
selector:
|
||||
istio: ingressgateway # use Istio default gateway implementation
|
||||
servers:
|
||||
- port:
|
||||
number: 80
|
||||
name: http
|
||||
protocol: HTTP
|
||||
hosts:
|
||||
- "*"
|
||||
EOF
|
||||
}
|
||||
|
||||
snip_enduser_authentication_2() {
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: networking.istio.io/v1alpha3
|
||||
kind: VirtualService
|
||||
metadata:
|
||||
name: httpbin
|
||||
namespace: foo
|
||||
spec:
|
||||
hosts:
|
||||
- "*"
|
||||
gateways:
|
||||
- httpbin-gateway
|
||||
http:
|
||||
- route:
|
||||
- destination:
|
||||
port:
|
||||
number: 8000
|
||||
host: httpbin.foo.svc.cluster.local
|
||||
EOF
|
||||
}
|
||||
|
||||
snip_enduser_authentication_3() {
|
||||
curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
|
||||
}
|
||||
|
||||
! read -r -d '' snip_enduser_authentication_3_out <<\ENDSNIP
|
||||
200
|
||||
ENDSNIP
|
||||
|
||||
snip_enduser_authentication_4() {
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
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/master/security/tools/jwt/samples/jwks.json"
|
||||
EOF
|
||||
}
|
||||
|
||||
snip_enduser_authentication_5() {
|
||||
curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
|
||||
}
|
||||
|
||||
! read -r -d '' snip_enduser_authentication_5_out <<\ENDSNIP
|
||||
200
|
||||
ENDSNIP
|
||||
|
||||
snip_enduser_authentication_6() {
|
||||
curl --header "Authorization: Bearer deadbeef" "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
|
||||
}
|
||||
|
||||
! read -r -d '' snip_enduser_authentication_6_out <<\ENDSNIP
|
||||
401
|
||||
ENDSNIP
|
||||
|
||||
snip_enduser_authentication_7() {
|
||||
TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/master/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"
|
||||
}
|
||||
|
||||
! read -r -d '' snip_enduser_authentication_7_out <<\ENDSNIP
|
||||
200
|
||||
ENDSNIP
|
||||
|
||||
snip_enduser_authentication_8() {
|
||||
wget --no-verbose https://raw.githubusercontent.com/istio/istio/master/security/tools/jwt/samples/gen-jwt.py
|
||||
}
|
||||
|
||||
snip_enduser_authentication_9() {
|
||||
wget --no-verbose https://raw.githubusercontent.com/istio/istio/master/security/tools/jwt/samples/key.pem
|
||||
}
|
||||
|
||||
snip_enduser_authentication_10() {
|
||||
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
|
||||
}
|
||||
|
||||
! read -r -d '' snip_enduser_authentication_10_out <<\ENDSNIP
|
||||
200
|
||||
200
|
||||
200
|
||||
200
|
||||
200
|
||||
200
|
||||
200
|
||||
401
|
||||
401
|
||||
401
|
||||
ENDSNIP
|
||||
|
||||
snip_require_a_valid_token_1() {
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
kind: AuthorizationPolicy
|
||||
metadata:
|
||||
name: "frontend-ingress"
|
||||
namespace: istio-system
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
istio: ingressgateway
|
||||
action: DENY
|
||||
rules:
|
||||
- from:
|
||||
- source:
|
||||
notRequestPrincipals: ["*"]
|
||||
EOF
|
||||
}
|
||||
|
||||
snip_require_a_valid_token_2() {
|
||||
curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
|
||||
}
|
||||
|
||||
! read -r -d '' snip_require_a_valid_token_2_out <<\ENDSNIP
|
||||
403
|
||||
ENDSNIP
|
||||
|
||||
snip_require_valid_tokens_perpath_1() {
|
||||
kubectl apply -f - <<EOF
|
||||
apiVersion: security.istio.io/v1beta1
|
||||
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
|
||||
}
|
||||
|
||||
snip_require_valid_tokens_perpath_2() {
|
||||
curl "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n"
|
||||
}
|
||||
|
||||
! read -r -d '' snip_require_valid_tokens_perpath_2_out <<\ENDSNIP
|
||||
403
|
||||
ENDSNIP
|
||||
|
||||
snip_require_valid_tokens_perpath_3() {
|
||||
curl "$INGRESS_HOST:$INGRESS_PORT/ip" -s -o /dev/null -w "%{http_code}\n"
|
||||
}
|
||||
|
||||
! read -r -d '' snip_require_valid_tokens_perpath_3_out <<\ENDSNIP
|
||||
200
|
||||
ENDSNIP
|
||||
|
||||
snip_cleanup_part_3_1() {
|
||||
kubectl -n istio-system delete requestauthentication jwt-example
|
||||
}
|
||||
|
||||
snip_cleanup_part_3_2() {
|
||||
kubectl -n istio-system delete authorizationpolicy frontend-ingress
|
||||
}
|
||||
|
||||
snip_cleanup_part_3_3() {
|
||||
rm -f ./gen-jwt.py ./key.pem
|
||||
}
|
||||
|
||||
snip_cleanup_part_3_4() {
|
||||
kubectl delete ns foo bar legacy
|
||||
}
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC1090,SC2154
|
||||
|
||||
# Copyright Istio Authors
|
||||
#
|
||||
# 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.
|
||||
|
||||
set -e
|
||||
set -u
|
||||
set -o pipefail
|
||||
|
||||
# @setup profile=default
|
||||
_wait_for_deployment istio-system istiod
|
||||
|
||||
snip_setup_1
|
||||
|
||||
_wait_for_deployment foo httpbin
|
||||
_wait_for_deployment foo sleep
|
||||
_wait_for_deployment bar httpbin
|
||||
_wait_for_deployment bar sleep
|
||||
_wait_for_deployment legacy httpbin
|
||||
_wait_for_deployment legacy sleep
|
||||
|
||||
_verify_same snip_setup_2 "$snip_setup_2_out"
|
||||
_verify_same snip_setup_3 "$snip_setup_3_out"
|
||||
_verify_same snip_setup_4 "$snip_setup_4_out"
|
||||
snip_setup_5
|
||||
|
||||
_verify_like snip_auto_mutual_tls_1 "$snip_auto_mutual_tls_1_out"
|
||||
_verify_same snip_auto_mutual_tls_2 "$snip_auto_mutual_tls_2_out"
|
||||
|
||||
snip_globally_enabling_istio_mutual_tls_in_strict_mode_1
|
||||
_wait_for_istio peerauthentication istio-system default
|
||||
|
||||
_verify_same snip_globally_enabling_istio_mutual_tls_in_strict_mode_2 "$snip_globally_enabling_istio_mutual_tls_in_strict_mode_2_out"
|
||||
|
||||
snip_cleanup_part_1_1
|
||||
|
||||
snip_namespacewide_policy_1
|
||||
_wait_for_istio peerauthentication foo default
|
||||
|
||||
_verify_same snip_namespacewide_policy_2 "$snip_namespacewide_policy_2_out"
|
||||
|
||||
snip_enable_mutual_tls_per_workload_1
|
||||
snip_enable_mutual_tls_per_workload_2
|
||||
_wait_for_istio peerauthentication bar httpbin
|
||||
_wait_for_istio destinationrule bar httpbin
|
||||
|
||||
_verify_same snip_enable_mutual_tls_per_workload_3 "$snip_enable_mutual_tls_per_workload_3_out"
|
||||
|
||||
# Ignore snip_enable_mutual_tls_per_workload_4()--it's just text.
|
||||
|
||||
snip_enable_mutual_tls_per_workload_5
|
||||
snip_enable_mutual_tls_per_workload_6
|
||||
_wait_for_istio peerauthentication bar httpbin
|
||||
_wait_for_istio destinationrule bar httpbin
|
||||
|
||||
_verify_same snip_enable_mutual_tls_per_workload_7 "$snip_enable_mutual_tls_per_workload_7_out"
|
||||
|
||||
snip_policy_precedence_1
|
||||
snip_policy_precedence_2
|
||||
_wait_for_istio peerauthentication foo overwrite-example
|
||||
_wait_for_istio destinationrule foo overwrite-example
|
||||
|
||||
_verify_same snip_policy_precedence_3 "$snip_policy_precedence_3_out"
|
||||
|
||||
snip_cleanup_part_2_1
|
||||
|
||||
snip_enduser_authentication_1
|
||||
snip_enduser_authentication_2
|
||||
_wait_for_istio gateway foo httpbin-gateway
|
||||
_wait_for_istio virtualservice foo httpbin
|
||||
|
||||
# Export the INGRESS_ environment variables
|
||||
_set_ingress_environment_variables
|
||||
|
||||
_verify_same snip_enduser_authentication_3 "$snip_enduser_authentication_3_out"
|
||||
|
||||
snip_enduser_authentication_4
|
||||
_wait_for_istio requestauthentication istio-system jwt-example
|
||||
|
||||
_verify_same snip_enduser_authentication_5 "$snip_enduser_authentication_5_out"
|
||||
_verify_same snip_enduser_authentication_6 "$snip_enduser_authentication_6_out"
|
||||
_verify_same snip_enduser_authentication_7 "$snip_enduser_authentication_7_out"
|
||||
|
||||
snip_enduser_authentication_8
|
||||
snip_enduser_authentication_9
|
||||
|
||||
# snip_enduser_authentication_10 is highly timing dependent, so just check
|
||||
# that the token times out during the run.
|
||||
expected="200
|
||||
401"
|
||||
_verify_contains snip_enduser_authentication_10 "$expected"
|
||||
|
||||
snip_require_a_valid_token_1
|
||||
_wait_for_istio authorizationpolicy istio-system frontend-ingress
|
||||
|
||||
_verify_same snip_require_a_valid_token_2 "$snip_require_a_valid_token_2_out"
|
||||
|
||||
snip_require_valid_tokens_perpath_1
|
||||
_wait_for_istio authorizationpolicy istio-system frontend-ingress
|
||||
|
||||
_verify_same snip_require_valid_tokens_perpath_2 "$snip_require_valid_tokens_perpath_2_out"
|
||||
_verify_same snip_require_valid_tokens_perpath_3 "$snip_require_valid_tokens_perpath_3_out"
|
||||
|
||||
# @cleanup
|
||||
snip_cleanup_part_1_1
|
||||
snip_cleanup_part_2_1
|
||||
snip_cleanup_part_3_1
|
||||
snip_cleanup_part_3_2
|
||||
snip_cleanup_part_3_3
|
||||
snip_cleanup_part_3_4
|
||||
Loading…
Reference in New Issue