认证

此任务介绍了在启用,配置和使用Istio身份验证策略时可能需要执行的主要活动。在身份验证概述中找到有关基础概念的更多信息。

在你开始之前 了解Istio身份验证策略和相关的 相互TLS身份验证概念。

default如安装步骤中所述,在具有配置文件 的Kubernetes集群上安装Istio 。

$ istioctl install --set profile=default

建立 我们的示例使用两个命名空间foo和bar,以及两个服务httpbin和sleep,两个命名空间都通过Envoy代理运行。我们还使用命名空间中没有sidecarhttpbin且sleep在没有sidecar的情况下运行的第二个实例legacy。如果要在尝试任务时使用相同的示例,请运行以下命令:

$ 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

你可以通过发送HTTP请求验证设置curl从任何sleep pod在命名空间foo,bar或legacy以任一httpbin.foo, httpbin.bar或httpbin.legacy。所有请求应以HTTP代码200成功。

例如,这里是一个命令检查sleep.bar到httpbin.foo的可达性:

$ 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

此单线命令方便地遍历所有可达性组合:

$ 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: 200 sleep.legacy to httpbin.bar: 200 sleep.legacy to httpbin.legacy: 200

使用以下命令验证系统中没有对等身份验证策略:

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

最后但并非最不重要的一点是,验证没有适用于示例服务的目标规则。您可以通过检查host:现有目标规则的值并确保它们不匹配来执行此操作。例如:

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

根据Istio的版本,您可能会看到除所示主机以外的其他主机的目标规则。但是,应该有没有与主机foo, bar并legacy命名空间,也不是全匹配通配符* 自动相互TLS 默认情况下,Istio跟踪迁移到Istio代理的服务器工作负载,并将客户端代理配置为自动向这些工作负载发送相互TLS流量,并将纯文本流量发送给没有附带服务的工作负载。

因此,带有代理的工作负载之间的所有流量都使用相互TLS,而无需您进行任何操作。例如,将请求的响应作为httpbin/header。使用双向TLS时,代理会将X-Forwarded-Client-Cert标头注入上游请求的后端。标头的存在证明使用了相互TLS。例如:

$ 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 | 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"

当服务器没有杂物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

在STRICT模式下全局启用Istio双向TLS 虽然Istio将代理和工作负载之间的所有流量自动升级为相互TLS,但工作负载仍可以接收纯文本流量。为防止整个网格的非相互TLS流量,请在相互TLS模式设置为的情况下设置网格范围的对等身份验证策略STRICT。网格范围的对等身份验证策略不应具有,selector并且必须在根名称空间中应用,例如:

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

该示例假定istio-system是根名称空间。如果在安装过程中使用了其他值,请替换istio-system为您使用的值。 此对等身份验证策略将工作负载配置为仅接受使用TLS加密的请求。由于未为selector字段指定值,因此该策略适用于网格中的所有工作负载。

再次运行测试命令:

$ 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

你看,请求仍然取得成功,除了那些从客户端不具有代理sleep.legacy,用代理,服务器httpbin.foo或httpbin.bar。这是可以预期的,因为现在严格要求使用相互TLS,但是没有附带工具的工作负载无法满足要求。

清理第1部分 删除在会话中添加的全局身份验证策略和目标规则:

$ kubectl delete peerauthentication -n istio-system default

为每个名称空间或工作负载启用双向TLS 命名空间范围的政策 要为特定名称空间内的所有工作负载更改相互TLS,请使用名称空间范围的策略。该策略的规范与网状网范围的策略相同,但是您可以在下指定要应用的名称空间metadata。例如,以下对等身份验证策略为foo名称空间启用严格的双向TLS :

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

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

$ 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

为每个工作负载启用双向TLS 要为特定工作负载设置对等身份验证策略,您必须配置此selector部分并指定与所需工作负载匹配的标签。但是,Istio无法聚合针对服务的出站双向TLS流量的工作负载级别策略。配置目标规则以管理该行为。

例如,以下对等身份验证策略和目标规则为httpbin.bar工作负载启用严格的双向TLS :

$ 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

和目标规则:

$ 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

再次,运行探测命令。如预期的那样,从sleep.legacy到的请求httpbin.bar开始失败的原因相同。

$ 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

... sleep.legacy to httpbin.bar: 000 command terminated with exit code 56

要优化每个端口的相互TLS设置,必须配置该portLevelMtls部分。例如,以下对等身份验证策略要求在除port之外的所有端口上都使用相互TLS 80:

$ 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

和以前一样,您还需要一个目标规则:

$ 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

对等身份验证策略中的端口值是容器的端口。目标规则的值是服务的端口。 仅portLevelMtls当端口绑定到服务时才能使用。Istio否则会忽略它。 $ 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

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

$ 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

和目标规则:

$ 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

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

$ 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

清理第二部分 删除上述步骤中创建的策略和目标规则:

$ 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

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

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

$ 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

$ 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

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

并运行测试查询

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

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

$ kubectl apply -f - <<EOF apiVersion: "security.istio.io/v1beta1" kind: "RequestAuthentication" metadata: name: "jwt-example" namespace: istio-system spec: selector: matchLabels: istio: ingressgateway jwtRules:

ingressgateway在这种情况下,将策略应用于它选择的工作负载的名称空间。然后,您需要指定的名称空间是istio-system。

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

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

$ curl --header "Authorization: Bearer deadbeef" "$INGRESS_HOST:$INGRESS_PORT/headers" -s -o /dev/null -w "%{http_code}\n" 401

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

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

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

您还需要以下key.pem文件:

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

如果尚未在系统上安装jwcrypto库,请下载它。 例如,下面的命令创建一个令牌,该令牌在5秒钟后到期。如您所见,Istio首先成功使用该令牌对请求进行身份验证,但5秒后拒绝了它们:

$ 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 1; done 200 200 200 200 200 401 401 401 401 401

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

需要一个有效的令牌 要拒绝没有有效令牌的请求,请添加带有规则的授权策略,该规则指定DENY对没有请求主体的请求的操作,如notRequestPrincipals: ["*"]以下示例所示。仅当提供有效的JWT令牌时,请求主体才可用。因此,该规则拒绝没有有效令牌的请求。

$ 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

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

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

每个路径要求有效令牌 要优化每个主机,路径或方法的令牌需求的授权,请将授权策略更改为仅要求JWT on /headers。该授权规则生效后,请求$INGRESS_HOST:$INGRESS_PORT/headers失败并显示错误代码403。例如,对所有其他路径的请求成功$INGRESS_HOST:$INGRESS_PORT/ip。

$ 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

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

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

清理第3部分 删除身份验证策略:

$ kubectl -n istio-system delete requestauthentication jwt-example

删除授权政策:

$ kubectl -n istio-system delete authorizationpolicy frontend-ingress

删除令牌生成器脚本和密钥文件:

$ rm -f ./gen-jwt.py ./key.pem

如果您不打算探索任何后续任务,则只需删除测试名称空间即可删除所有资源。

$ kubectl delete ns foo bar legacy

Last updated