认证策略

本任务涵盖了您在启用、配置和使用 Istio 认证策略时可能需要做的主要工作。更多基本概念介绍请查看认证总览

开始之前

  • 理解 Istio 认证策略双向 TLS 认证相关概念。
  • 在 Kubernetes 集群中安装 Istio 并禁用全局双向 TLS (例如,使用安装步骤提到的 demo 配置文件,或者设置 global.mtls.enabled 安装选项为 false )。

设置

我们的示例用到两个命名空间 foobar,以及两个服务 httpbinsleep,这两个服务都带有 Envoy sidecar proxy 一起运行。我们也会用到两个运行在 legacy 命名空间下不带 sidecar 的 httpbinsleep 实例。如果您想要使用相同的示例尝试任务,执行如下命令:

ZipZipZipZipZipZip

  1. $ kubectl create ns foo
  2. $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n foo
  3. $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n foo
  4. $ kubectl create ns bar
  5. $ kubectl apply -f <(istioctl kube-inject -f @samples/httpbin/httpbin.yaml@) -n bar
  6. $ kubectl apply -f <(istioctl kube-inject -f @samples/sleep/sleep.yaml@) -n bar
  7. $ kubectl create ns legacy
  8. $ kubectl apply -f @samples/httpbin/httpbin.yaml@ -n legacy
  9. $ kubectl apply -f @samples/sleep/sleep.yaml@ -n legacy

您可以在命名空间 foobarlegacy 下的任意 sleep pod 中使用 curl 发送一个 HTTP 请求给 httpbin.foohttpbin.barhttpbin.legacy 来验证。所有请求应该都成功返回 HTTP 代码 200。

例如,这里的一个从 sleep.barhttpbin.foo 的检查可达性的命令:

  1. $ 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"
  2. 200

这个单行命令可以方便地遍历所有可达性组合:

  1. $ 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
  2. sleep.foo to httpbin.foo: 200
  3. sleep.foo to httpbin.bar: 200
  4. sleep.foo to httpbin.legacy: 200
  5. sleep.bar to httpbin.foo: 200
  6. sleep.bar to httpbin.bar: 200
  7. sleep.bar to httpbin.legacy: 200
  8. sleep.legacy to httpbin.foo: 200
  9. sleep.legacy to httpbin.bar: 200
  10. sleep.legacy to httpbin.legacy: 200

您还应该要验证系统中是否有默认的网格认证策略,可执行如下命令:

  1. $ kubectl get policies.authentication.istio.io --all-namespaces
  2. No resources found.
  1. $ kubectl get meshpolicies.authentication.istio.io
  2. NAME AGE
  3. default 3m

最后同样重要的是,验证示例服务没有应用 destination rule。您可以检查现有 destination rule 中的 host: 值并确保它们不匹配。例如:

  1. $ kubectl get destinationrules.networking.istio.io --all-namespaces -o yaml | grep "host:"
  2. host: istio-policy.istio-system.svc.cluster.local
  3. host: istio-telemetry.istio-system.svc.cluster.local

您可能会看到 destination rules 配置了除上面显示以外的其他 hosts,这依赖于 Istio 的版本。但是,应该没有 destination rules 配置 foobarlegacy 命名空间中的 hosts,也没有配置通配符 *

自动双向 TLS

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

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

  1. $ 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
  2. "X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=<redacted>"

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

  1. $ 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

全局启用 Istio 双向 TLS

设置一个启用双向 TLS 的网格范围的认证策略,提交如下 mesh authentication policy

  1. $ kubectl apply -f - <<EOF
  2. apiVersion: "authentication.istio.io/v1alpha1"
  3. kind: "MeshPolicy"
  4. metadata:
  5. name: "default"
  6. spec:
  7. peers:
  8. - mtls: {}
  9. EOF

网格认证策略使用通用认证策略 API,它定义在集群作用域 MeshPolicy CRD 中。

该策略规定网格上的所有工作负载只接收使用 TLS 的加密请求。如您所见,该认证策略的类型为:MeshPolicy。策略的名字必须是 default,并且不含 targets 属性(目的是应用到网格中所有服务上)。

这时候,只有接收方配置使用双向 TLS。如果您在 Istio services 之间执行 curl 命令(即,那些带有 sidecars 的服务),由于客户端仍旧使用纯文本,所有请求都会失败并报 503 错误。

  1. $ 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
  2. sleep.foo to httpbin.foo: 503
  3. sleep.foo to httpbin.bar: 503
  4. sleep.bar to httpbin.foo: 503
  5. sleep.bar to httpbin.bar: 503

配置客户端,您需要设置 destination rules 来使用双向 TLS。也可以使用多 destination rules,为每个合适的服务(或命名空间)都配置一个。不过,更方便地方式是创建一个规则使用通配符 * 匹配所有服务,因此这也和网格范围的认证策略作用等同。

  1. $ kubectl apply -f - <<EOF
  2. apiVersion: "networking.istio.io/v1alpha3"
  3. kind: "DestinationRule"
  4. metadata:
  5. name: "default"
  6. namespace: "istio-system"
  7. spec:
  8. host: "*.local"
  9. trafficPolicy:
  10. tls:
  11. mode: ISTIO_MUTUAL
  12. EOF
  • 从 Istio 1.1 开始,只有客户端命名空间,服务端命名空间和 global 命名空间(默认是 istio-system)中的 destination rules 会按顺序提供给服务。
  • Host 值 *.local 限制只与集群中的服务匹配,而不是外部服务。同时注意,destination rule 的名字或命名空间没有做限制。
  • ISTIO_MUTUAL TLS 模式下,Istio 将根据密钥和证书(例如客户端证书,密钥和 CA 证书)的内部实现为它们设置路径。

别忘了 destination rules 也可用于非授权原因例如设置金丝雀发布,不过要适用同样的优先顺序。因此,如果一个服务不管什么原因要求一个特定的 destination rule —— 例如,配置负载均衡 —— 这个规则必须包含一个简单的 ISTIO_MUTUAL 模式的 TLS 块,否则它将会被网格或者命名空间范围的 TLS 设置覆盖并使 TLS 失效。

重新执行上述测试命令,您将看到所有 Istio 服务间的请求现在都成功完成。

  1. $ 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
  2. sleep.foo to httpbin.foo: 200
  3. sleep.foo to httpbin.bar: 200
  4. sleep.bar to httpbin.foo: 200
  5. sleep.bar to httpbin.bar: 200

从非 Istio 服务到 Istio 服务的请求

非 Istio 服务,例如 sleep.legacy 没有 sidecar,所以它不能将要求的 TLS 连接初始化到 Istio 服务。这会导致从 sleep.legacyhttpbin.foo 或者 httpbin.bar 的请求失败:

  1. $ 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
  2. sleep.legacy to httpbin.foo: 000
  3. command terminated with exit code 56
  4. sleep.legacy to httpbin.bar: 000
  5. command terminated with exit code 56

由于 Envoy 拒绝纯文本请求的方式,您将会在这个例子中看到 curl 返回 56 代码(接收网络数据失败)。

这个按预期工作,而且很不幸,没有解决办法,除非降低对这些服务的认证条件要求。

从 Istio 服务到非 Istio 服务的请求

尝试从 sleep.foo (或者 sleep.bar) 发送请求给 httpbin.legacy。您将看到请求失败,因为 Istio 按照指示在 destination rule 中配置了客户端使用双向 TLS,但是 httpbin.legacy 没有 sidecar,所以它处理不了。

  1. $ 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
  2. sleep.foo to httpbin.legacy: 503
  3. sleep.bar to httpbin.legacy: 503

为了解决这个问题,我们可以为 httpbin.legacy 添加一个 destination rule 覆盖 TLS 设置。例如:

  1. $ kubectl apply -f - <<EOF
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: DestinationRule
  4. metadata:
  5. name: "httpbin-legacy"
  6. namespace: "legacy"
  7. spec:
  8. host: "httpbin.legacy.svc.cluster.local"
  9. trafficPolicy:
  10. tls:
  11. mode: DISABLE
  12. EOF

在您添加了 destination rule 后再次测试,确保它能通过:

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

这个 destination rule 在服务端(httpbin.legacy)的命名空间中,因此它优先于定义在 istio-system 中的全局 destination rule。

请求从 Istio 服务到 Kubernetes API server

Kubernetes API server 没有 sidecar,所以来自 Istio 服务的请求如 sleep.foo 将会失败,这跟发送请求给任何非 Istio 服务有相同的问题。

  1. $ TOKEN=$(kubectl describe secret $(kubectl get secrets | grep default-token | cut -f1 -d ' ' | head -1) | grep -E '^token' | cut -f2 -d':' | tr -d ' \t')
  2. $ 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"
  3. 000
  4. command terminated with exit code 35

再次,我们通过覆盖 API server (kubernetes.default) 的 destination rule 来纠正它。

  1. $ kubectl apply -f - <<EOF
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: DestinationRule
  4. metadata:
  5. name: "api-server"
  6. namespace: istio-system
  7. spec:
  8. host: "kubernetes.default.svc.cluster.local"
  9. trafficPolicy:
  10. tls:
  11. mode: DISABLE
  12. EOF

当您安装 Istio 并启用双向 TLS 时,这个规则,会跟全局认证策略和上述 destination rule 一起被自动注入到系统中。

重新执行上述测试命令确认在规则添加后会返回 200:

  1. $ TOKEN=$(kubectl describe secret $(kubectl get secrets | grep default-token | cut -f1 -d ' ' | head -1) | grep -E '^token' | cut -f2 -d':' | tr -d ' \t')
  2. $ 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"
  3. 200

清除部分 1

删除在场景中添加的全局认证策略和 destination rules:

  1. $ kubectl delete meshpolicy default
  2. $ kubectl delete destinationrules httpbin-legacy -n legacy
  3. $ kubectl delete destinationrules api-server -n istio-system
  4. $ kubectl delete destinationrules default -n istio-system

为每个命名空间或者服务启用双向 TLS

除了为您的整个网格指定一个认证策略,Istio 也支持您为特定的命名空间或者服务指定策略。一个命名空间范围的策略优先级高于网格范围的策略,而服务范围的策略优先级更高。

命名空间范围的策略

下述示例展示为命名空间 foo 中的所有服务启用双向 TLS 的策略。如你所见,它使用的类型是 Policy 而不是 MeshPolicy,在这个案例中指定命名空间为 foo。如果您没有指定命名空间的值,策略将会应用默认命名空间。

  1. $ kubectl apply -f - <<EOF
  2. apiVersion: "authentication.istio.io/v1alpha1"
  3. kind: "Policy"
  4. metadata:
  5. name: "default"
  6. namespace: "foo"
  7. spec:
  8. peers:
  9. - mtls: {}
  10. EOF

网格范围的策略 类似,命名空间范围的策略必须命名为 default,并且没有限制任何具体服务(没有 targets 部分)。

添加相应的 destination rule:

  1. $ kubectl apply -f - <<EOF
  2. apiVersion: "networking.istio.io/v1alpha3"
  3. kind: "DestinationRule"
  4. metadata:
  5. name: "default"
  6. namespace: "foo"
  7. spec:
  8. host: "*.foo.svc.cluster.local"
  9. trafficPolicy:
  10. tls:
  11. mode: ISTIO_MUTUAL
  12. EOF

Host *.foo.svc.cluster.local 限制只匹配 foo 命名空间中的服务。

由于这些策略和 destination rule 只应用于命名空间 foo 中的服务,您应该会看到只有从没有 sidecar 的客户端(sleep.legacy) 到 httpbin.foo 的请求开始失败。

  1. $ 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
  2. sleep.foo to httpbin.foo: 200
  3. sleep.foo to httpbin.bar: 200
  4. sleep.foo to httpbin.legacy: 200
  5. sleep.bar to httpbin.foo: 200
  6. sleep.bar to httpbin.bar: 200
  7. sleep.bar to httpbin.legacy: 200
  8. sleep.legacy to httpbin.foo: 000
  9. command terminated with exit code 56
  10. sleep.legacy to httpbin.bar: 200
  11. sleep.legacy to httpbin.legacy: 200

特定服务策略

您也可以为特定服务设置认证策略和 destination rule。执行这个命令为 httpbin.bar 服务设置另一个策略。

  1. $ cat <<EOF | kubectl apply -n bar -f -
  2. apiVersion: "authentication.istio.io/v1alpha1"
  3. kind: "Policy"
  4. metadata:
  5. name: "httpbin"
  6. spec:
  7. targets:
  8. - name: httpbin
  9. peers:
  10. - mtls: {}
  11. EOF

添加一个 destination rule:

  1. $ cat <<EOF | kubectl apply -n bar -f -
  2. apiVersion: "networking.istio.io/v1alpha3"
  3. kind: "DestinationRule"
  4. metadata:
  5. name: "httpbin"
  6. spec:
  7. host: "httpbin.bar.svc.cluster.local"
  8. trafficPolicy:
  9. tls:
  10. mode: ISTIO_MUTUAL
  11. EOF
  • 本示例中,我们 在元数据中指定命名空间而是将它放在命令行上(-n bar),这也有相同的作用。
  • 认证策略和 destination rule 的名字没有限制。为了简单起见,本示例使用服务本身的名字。

再次,执行探查命令。跟预期一样,从 sleep.legacyhttpbin.bar 的请求开始失败因为同样的问题。

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

如果我们在命名空间 bar 中还有其它服务,我们应该会看到请求它们的流量将不会受到影响。除了添加更多服务来演示这个行为,我们也可以稍微编辑策略将其应用到一个具体端口:

  1. $ cat <<EOF | kubectl apply -n bar -f -
  2. apiVersion: "authentication.istio.io/v1alpha1"
  3. kind: "Policy"
  4. metadata:
  5. name: "httpbin"
  6. spec:
  7. targets:
  8. - name: httpbin
  9. ports:
  10. - number: 1234
  11. peers:
  12. - mtls: {}
  13. EOF

对 destination rule 也做相应修改:

  1. $ cat <<EOF | kubectl apply -n bar -f -
  2. apiVersion: "networking.istio.io/v1alpha3"
  3. kind: "DestinationRule"
  4. metadata:
  5. name: "httpbin"
  6. spec:
  7. host: httpbin.bar.svc.cluster.local
  8. trafficPolicy:
  9. tls:
  10. mode: DISABLE
  11. portLevelSettings:
  12. - port:
  13. number: 1234
  14. tls:
  15. mode: ISTIO_MUTUAL
  16. EOF

这个新策略将只应用在 httpbin 服务的 1234 端口。结果,双向 TLS 在 8000 端口上会再次失效而来自 sleep.legacy 的请求将会恢复正常工作。

  1. $ 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"
  2. 200

策略优先级

为了演示特定服务策略比命名空间范围的策略优先级高,您可以像下面一样为 httpbin.foo 添加一个禁用双向 TLS 的策略。 注意您已经为所有在命名空间 foo 中的服务创建了命名空间范围的策略来启用双向 TLS 并观察到从 sleep.legacyhttpbin.foo 的请求都会失败(如上所示)。

  1. $ cat <<EOF | kubectl apply -n foo -f -
  2. apiVersion: "authentication.istio.io/v1alpha1"
  3. kind: "Policy"
  4. metadata:
  5. name: "overwrite-example"
  6. spec:
  7. targets:
  8. - name: httpbin
  9. EOF

添加 destination rule:

  1. $ cat <<EOF | kubectl apply -n foo -f -
  2. apiVersion: "networking.istio.io/v1alpha3"
  3. kind: "DestinationRule"
  4. metadata:
  5. name: "overwrite-example"
  6. spec:
  7. host: httpbin.foo.svc.cluster.local
  8. trafficPolicy:
  9. tls:
  10. mode: DISABLE
  11. EOF

重新执行来自 sleep.legacy 的请求,您应该又会看到请求成功返回 200 代码,证明了特定服务策略覆盖了命名空间范围的策略。

  1. $ 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"
  2. 200

清除部分 2

删除上面步骤中创建的策略和 destination rules:

  1. $ kubectl delete policy default overwrite-example -n foo
  2. $ kubectl delete policy httpbin -n bar
  3. $ kubectl delete destinationrules default overwrite-example -n foo
  4. $ kubectl delete destinationrules httpbin -n bar

终端用户认证

为了体验这个特性,您需要一个有效的 JWT。该 JWT 必须和您用于该 demo 的 JWKS 终端对应。在这个教程中,我们使用来自 Istio 代码基础库的 JWT testJWKS endpoint 同时,为了方便,通过 ingressgateway 暴露 httpbin.foo(更多细节,查看 ingress 任务)。

  1. $ kubectl apply -f - <<EOF
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: Gateway
  4. metadata:
  5. name: httpbin-gateway
  6. namespace: foo
  7. spec:
  8. selector:
  9. istio: ingressgateway # use Istio default gateway implementation
  10. servers:
  11. - port:
  12. number: 80
  13. name: http
  14. protocol: HTTP
  15. hosts:
  16. - "*"
  17. EOF
  1. $ kubectl apply -f - <<EOF
  2. apiVersion: networking.istio.io/v1alpha3
  3. kind: VirtualService
  4. metadata:
  5. name: httpbin
  6. namespace: foo
  7. spec:
  8. hosts:
  9. - "*"
  10. gateways:
  11. - httpbin-gateway
  12. http:
  13. - route:
  14. - destination:
  15. port:
  16. number: 8000
  17. host: httpbin.foo.svc.cluster.local
  18. EOF

获取 ingress IP

  1. $ export INGRESS_HOST=$(kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

执行一个查询测试

  1. $ curl $INGRESS_HOST/headers -s -o /dev/null -w "%{http_code}\n"
  2. 200

现在,为 httpbin.foo 添加一个要求配置终端用户 JWT 的策略。下面的命令假定 httpbin.foo 没有特定服务策略(如果您执行了清除所述的操作,就会是这样)。您可以执行 kubectl get policies.authentication.istio.io -n foo 进行确认。

  1. $ cat <<EOF | kubectl apply -n foo -f -
  2. apiVersion: "authentication.istio.io/v1alpha1"
  3. kind: "Policy"
  4. metadata:
  5. name: "jwt-example"
  6. spec:
  7. targets:
  8. - name: httpbin
  9. origins:
  10. - jwt:
  11. issuer: "testing@secure.istio.io"
  12. jwksUri: "https://raw.githubusercontent.com/istio/istio/master/security/tools/jwt/samples/jwks.json"
  13. principalBinding: USE_ORIGIN
  14. EOF

之前相同的 curl 命令将会返回 401 错误代码,由于服务器结果期望 JWT 却没有提供:

  1. $ curl $INGRESS_HOST/headers -s -o /dev/null -w "%{http_code}\n"
  2. 401

附带上上面生成的有效 token 将返回成功:

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

为了观察 JWT 验证的其它方面,使用脚本 gen-jwt.py 生成新 tokens 带上不同的发行人、受众、有效期等等进行测试。这个脚本可以从 Istio 库下载:

  1. $ wget https://raw.githubusercontent.com/istio/istio/master/security/tools/jwt/samples/gen-jwt.py
  2. $ chmod +x gen-jwt.py

您还需要 key.pem 文件:

  1. $ wget https://raw.githubusercontent.com/istio/istio/master/security/tools/jwt/samples/key.pem

下载 jwcrypto 库,如果您还没有在您的系统上安装的话。

例如,下述命令创建一个 5 秒钟过期的 token。如您所见,Istio 使用这个 token 刚开始认证请求成功,但是 5 秒后拒绝了它们。

  1. $ TOKEN=$(./gen-jwt.py ./key.pem --expire 5)
  2. $ 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
  3. 200
  4. 200
  5. 200
  6. 200
  7. 200
  8. 401
  9. 401
  10. 401
  11. 401
  12. 401

您也可以给一个 ingress gateway 添加一个 JWT 策略(例如,服务 istio-ingressgateway.istio-system.svc.cluster.local)。 这个常用于为绑定到这个 gateway 的所有服务定义一个 JWT 策略,而不是单独的服务。

按路径要求的终端用户认证

终端用户认证可以基于请求路径启用或者禁用。如果您想要让某些路径禁用认证就非常有用,例如,用于健康检查或者状态报告的路径。 您也可以为不同的路径指定不同的 JWT。

按路径要求的终端用户认证在 Istio 1.1 中是一个实验性的特性并 推荐在生产环境中使用。

为指定路径禁用终端用户认证

修改 jwt-example 策略禁用路径 /user-agent 的终端用户认证:

  1. $ cat <<EOF | kubectl apply -n foo -f -
  2. apiVersion: "authentication.istio.io/v1alpha1"
  3. kind: "Policy"
  4. metadata:
  5. name: "jwt-example"
  6. spec:
  7. targets:
  8. - name: httpbin
  9. origins:
  10. - jwt:
  11. issuer: "testing@secure.istio.io"
  12. jwksUri: "https://raw.githubusercontent.com/istio/istio/master/security/tools/jwt/samples/jwks.json"
  13. trigger_rules:
  14. - excluded_paths:
  15. - exact: /user-agent
  16. principalBinding: USE_ORIGIN
  17. EOF

确认 /user-agent 路径允许免 JWT tokens 访问:

  1. $ curl $INGRESS_HOST/user-agent -s -o /dev/null -w "%{http_code}\n"
  2. 200

确认不带 JWT tokens 的非 /user-agent 路径拒绝访问:

  1. $ curl $INGRESS_HOST/headers -s -o /dev/null -w "%{http_code}\n"
  2. 401

为指定路径启用终端用户认证

修改 jwt-example 策略启用路径 /ip 的终端用户认证:

  1. $ cat <<EOF | kubectl apply -n foo -f -
  2. apiVersion: "authentication.istio.io/v1alpha1"
  3. kind: "Policy"
  4. metadata:
  5. name: "jwt-example"
  6. spec:
  7. targets:
  8. - name: httpbin
  9. origins:
  10. - jwt:
  11. issuer: "testing@secure.istio.io"
  12. jwksUri: "https://raw.githubusercontent.com/istio/istio/master/security/tools/jwt/samples/jwks.json"
  13. trigger_rules:
  14. - included_paths:
  15. - exact: /ip
  16. principalBinding: USE_ORIGIN
  17. EOF

确认不带 JWT tokens 的非 /ip 路径允许访问:

  1. $ curl $INGRESS_HOST/user-agent -s -o /dev/null -w "%{http_code}\n"
  2. 200

确认不带 JWT tokens 的 /ip 路径拒绝访问:

  1. $ curl $INGRESS_HOST/ip -s -o /dev/null -w "%{http_code}\n"
  2. 401

确认带有效 JWT token 的 /ip 路径允许访问:

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

带双向 TLS 的终端用户认证

终端用户认证和双向 TLS 可以共用。修改上面的策略定义双向 TLS 和终端用户 JWT 认证:

  1. $ cat <<EOF | kubectl apply -n foo -f -
  2. apiVersion: "authentication.istio.io/v1alpha1"
  3. kind: "Policy"
  4. metadata:
  5. name: "jwt-example"
  6. spec:
  7. targets:
  8. - name: httpbin
  9. peers:
  10. - mtls: {}
  11. origins:
  12. - jwt:
  13. issuer: "testing@secure.istio.io"
  14. jwksUri: "https://raw.githubusercontent.com/istio/istio/master/security/tools/jwt/samples/jwks.json"
  15. principalBinding: USE_ORIGIN
  16. EOF

添加一个 destination rule:

  1. $ kubectl apply -f - <<EOF
  2. apiVersion: "networking.istio.io/v1alpha3"
  3. kind: "DestinationRule"
  4. metadata:
  5. name: "httpbin"
  6. namespace: "foo"
  7. spec:
  8. host: "httpbin.foo.svc.cluster.local"
  9. trafficPolicy:
  10. tls:
  11. mode: ISTIO_MUTUAL
  12. EOF

如果您已经启用网格范围或者命名空间范围的 TLS,那么 host httpbin.foo 已经被这些 destination rule 覆盖。 因此,您不需要添加这个 destination rule 。另外,您仍然需要添加 mtls 段到认证策略,因为特定服务策略将完全覆盖网格范围(或者命名空间范围)的策略。

修改这些后,从 Istio 服务,包括 ingress gateway,到 httpbin.foo 的流量将使用双向 TLS。上述测试命令将仍然会正常工作。给定正确的 token,从 Istio 服务直接到 httpbin.foo 的请求也会正常工作:

  1. $ TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/master/security/tools/jwt/samples/demo.jwt -s)
  2. $ 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"
  3. 200

然而,来自非 Istio 服务,使用纯文本的请求将会失败:

  1. $ 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"
  2. 000
  3. command terminated with exit code 56

清除部分 3

  1. 删除认证策略:

    1. $ kubectl -n foo delete policy jwt-example
  2. 删除 destination rule:

    1. $ kubectl -n foo delete destinationrule httpbin
  3. 如果您不打算研究后续任务,您只需简单删除测试命名空间即可删除所有资源:

    1. $ kubectl delete ns foo bar legacy

相关内容

双向 TLS 迁移

阐述如何将 Istio 服务逐步迁移至双向 TLS 通信模式。

安全

描述 Istio 的授权与认证功能。

Istio 2020——为了商用

Istio 在 2020 年的愿景声明及路线图。

移除跨 pod Unix domain socket

一种更安全的秘密管理方式。

DNS 证书管理

在 Istio 中配置和管理 DNS 证书。

Istio v1beta1 授权策略概述

Istio v1beta1 授权策略的设计原则、基本概述及迁移操作。