Ingress Sidecar TLS 终止

在常规的 Istio 网格部署中,下游请求的 TLS 终止是在 Ingress Gateway 处执行的。 虽然这可以满足大多数使用场景,但对于某些场景(如网格中的 API 网关),Ingress Gateway 并不是必需的。此任务展示了如何消除 Istio Ingress Gateway 引入的额外跃点, 并让与应用程序一起运行的 Envoy Sidecar 对来自服务网格外部的请求执行 TLS 终止。

用于此任务的示例 HTTPS 服务是一个简单的 httpbin 服务。 在以下步骤中,您将在服务网格中部署 httpbin 服务并对其进行配置。

以下信息描述了一个实验性功能,仅用于评估。

准备工作

  • 按照安装指南中的说明设置 Istio ,启用实验功能 ENABLE_TLS_ON_SIDECAR_INGRESS

    1. $ istioctl install --set profile=default --set values.pilot.env.ENABLE_TLS_ON_SIDECAR_INGRESS=true
  • 创建一个 test 命名空间,在其中部署目标 httpbin 服务。确保为该命名空间启用 Sidecar 注入。

    1. $ kubectl create ns test
    2. $ kubectl label namespace test istio-injection=enabled

启用全局 mTLS

应用以下 PeerAuthentication 策略,对网格中的所有工作负载实现 mTLS 流量。

  1. $ kubectl -n test apply -f - <<EOF
  2. apiVersion: security.istio.io/v1
  3. kind: PeerAuthentication
  4. metadata:
  5. name: default
  6. spec:
  7. mtls:
  8. mode: STRICT
  9. EOF

为外部暴露的 httpbin 端口禁用 PeerAuthentication

在 httpbin Service 的端口处禁用 PeerAuthentication,在 Sidecar 处执行入口 TLS 终止。请注意,这里是 httpbin Service 的 targetPort,专门用于与外部通信。

  1. $ kubectl -n test apply -f - <<EOF
  2. apiVersion: security.istio.io/v1
  3. kind: PeerAuthentication
  4. metadata:
  5. name: disable-peer-auth-for-external-mtls-port
  6. namespace: test
  7. spec:
  8. selector:
  9. matchLabels:
  10. app: httpbin
  11. mtls:
  12. mode: STRICT
  13. portLevelMtls:
  14. 9080:
  15. mode: DISABLE
  16. EOF

生成 CA 证书、服务器证书/密钥和客户端证书/密钥

对于此任务,您可以使用自己喜欢的工具来生成证书和密钥。下面的命令使用 openssl

  1. $ #CA is example.com
  2. $ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=example Inc./CN=example.com' -keyout example.com.key -out example.com.crt
  3. $ #Server is httpbin.test.svc.cluster.local
  4. $ openssl req -out httpbin.test.svc.cluster.local.csr -newkey rsa:2048 -nodes -keyout httpbin.test.svc.cluster.local.key -subj "/CN=httpbin.test.svc.cluster.local/O=httpbin organization"
  5. $ openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 1 -in httpbin.test.svc.cluster.local.csr -out httpbin.test.svc.cluster.local.crt
  6. $ #client is client.test.svc.cluster.local
  7. $ openssl req -out client.test.svc.cluster.local.csr -newkey rsa:2048 -nodes -keyout client.test.svc.cluster.local.key -subj "/CN=client.test.svc.cluster.local/O=client organization"
  8. $ openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key -set_serial 1 -in client.test.svc.cluster.local.csr -out client.test.svc.cluster.local.crt

为证书和密钥创建 Kubernetes Secret

  1. $ kubectl -n test create secret generic httpbin-mtls-termination-cacert --from-file=ca.crt=./example.com.crt
  2. $ kubectl -n test create secret tls httpbin-mtls-termination --cert ./httpbin.test.svc.cluster.local.crt --key ./httpbin.test.svc.cluster.local.key

部署测试服务 httpbin

当创建 httpbin Deployment 时,我们需要在该 Deployment 中使用 userVolumeMount 注解来为 istio-proxy Sidecar 挂载证书。请注意,之所以需要此步骤是因为 Istio Sidecar 目前不支持 credentialName 配置。

  1. sidecar.istio.io/userVolume: '{"tls-secret":{"secret":{"secretName":"httpbin-mtls-termination","optional":true}},"tls-ca-secret":{"secret":{"secretName":"httpbin-mtls-termination-cacert"}}}'
  2. sidecar.istio.io/userVolumeMount: '{"tls-secret":{"mountPath":"/etc/istio/tls-certs/","readOnly":true},"tls-ca-secret":{"mountPath":"/etc/istio/tls-ca-certs/","readOnly":true}}'

使用以下命令部署带有 userVolumeMount 配置的 httpbin 服务:

  1. $ kubectl -n test apply -f - <<EOF
  2. apiVersion: v1
  3. kind: ServiceAccount
  4. metadata:
  5. name: httpbin
  6. ---
  7. apiVersion: v1
  8. kind: Service
  9. metadata:
  10. name: httpbin
  11. labels:
  12. app: httpbin
  13. service: httpbin
  14. spec:
  15. ports:
  16. - port: 8443
  17. name: https
  18. targetPort: 9080
  19. - port: 8080
  20. name: http
  21. targetPort: 9081
  22. selector:
  23. app: httpbin
  24. ---
  25. apiVersion: apps/v1
  26. kind: Deployment
  27. metadata:
  28. name: httpbin
  29. spec:
  30. replicas: 1
  31. selector:
  32. matchLabels:
  33. app: httpbin
  34. version: v1
  35. template:
  36. metadata:
  37. labels:
  38. app: httpbin
  39. version: v1
  40. annotations:
  41. sidecar.istio.io/userVolume: '{"tls-secret":{"secret":{"secretName":"httpbin-mtls-termination","optional":true}},"tls-ca-secret":{"secret":{"secretName":"httpbin-mtls-termination-cacert"}}}'
  42. sidecar.istio.io/userVolumeMount: '{"tls-secret":{"mountPath":"/etc/istio/tls-certs/","readOnly":true},"tls-ca-secret":{"mountPath":"/etc/istio/tls-ca-certs/","readOnly":true}}'
  43. spec:
  44. serviceAccountName: httpbin
  45. containers:
  46. - image: docker.io/kennethreitz/httpbin
  47. imagePullPolicy: IfNotPresent
  48. name: httpbin
  49. ports:
  50. - containerPort: 80
  51. EOF

配置 httpbin 以启用外部 mTLS

这是此功能的核心步骤。使用 Sidecar API 配置入口 TLS 设置。TLS 模式可以是 SIMPLEMUTUAL,本示例使用 MUTUAL

  1. $ kubectl -n test apply -f - <<EOF
  2. apiVersion: networking.istio.io/v1
  3. kind: Sidecar
  4. metadata:
  5. name: ingress-sidecar
  6. namespace: test
  7. spec:
  8. workloadSelector:
  9. labels:
  10. app: httpbin
  11. version: v1
  12. ingress:
  13. - port:
  14. number: 9080
  15. protocol: HTTPS
  16. name: external
  17. defaultEndpoint: 0.0.0.0:80
  18. tls:
  19. mode: MUTUAL
  20. privateKey: "/etc/istio/tls-certs/tls.key"
  21. serverCertificate: "/etc/istio/tls-certs/tls.crt"
  22. caCertificates: "/etc/istio/tls-ca-certs/ca.crt"
  23. - port:
  24. number: 9081
  25. protocol: HTTP
  26. name: internal
  27. defaultEndpoint: 0.0.0.0:80
  28. EOF

验证

现在已经部署和配置了 httpbin 服务器,启动两个客户端来测试网格内部和外部的端到端连接:

  1. 在与 httpbin 服务相同的命名空间(test)中的内部客户端(curl),已注入 Sidecar。
  2. 在 default 命名空间(即服务网格外部)中的外部客户端(curl)。
  1. $ kubectl apply -f samples/curl/curl.yaml
  2. $ kubectl -n test apply -f samples/curl/curl.yaml

运行以下命令以验证一切都已启动并正在运行,并且配置正确。

  1. $ kubectl get pods
  2. NAME READY STATUS RESTARTS AGE
  3. curl-557747455f-xx88g 1/1 Running 0 4m14s
  1. $ kubectl get pods -n test
  2. NAME READY STATUS RESTARTS AGE
  3. httpbin-5bbdbd6588-z9vbs 2/2 Running 0 8m44s
  4. curl-557747455f-brzf6 2/2 Running 0 6m57s
  1. $ kubectl get svc -n test
  2. NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
  3. httpbin ClusterIP 10.100.78.113 <none> 8443/TCP,8080/TCP 10m
  4. curl ClusterIP 10.110.35.153 <none> 80/TCP 8m49s

在以下命令中,将 httpbin-5bbdbd6588-z9vbs 替换为 httpbin Pod 的名称。

  1. $ istioctl proxy-config secret httpbin-5bbdbd6588-z9vbs.test
  2. RESOURCE NAME TYPE STATUS VALID CERT SERIAL NUMBER NOT AFTER NOT BEFORE
  3. file-cert:/etc/istio/tls-certs/tls.crt~/etc/istio/tls-certs/tls.key Cert Chain ACTIVE true 1 2023-02-14T09:51:56Z 2022-02-14T09:51:56Z
  4. default Cert Chain ACTIVE true 329492464719328863283539045344215802956 2022-02-15T09:55:46Z 2022-02-14T09:53:46Z
  5. ROOTCA CA ACTIVE true 204427760222438623495455009380743891800 2032-02-07T16:58:00Z 2022-02-09T16:58:00Z
  6. file-root:/etc/istio/tls-ca-certs/ca.crt Cert Chain ACTIVE true 14033888812979945197 2023-02-14T09:51:56Z 2022-02-14T09:51:56Z

在 8080 端口上验证内部网格连通性

  1. $ export INTERNAL_CLIENT=$(kubectl -n test get pod -l app=curl -o jsonpath={.items..metadata.name})
  2. $ kubectl -n test exec "${INTERNAL_CLIENT}" -c curl -- curl -IsS "http://httpbin:8080/status/200"
  3. HTTP/1.1 200 OK
  4. server: envoy
  5. date: Mon, 24 Oct 2022 09:04:52 GMT
  6. content-type: text/html; charset=utf-8
  7. access-control-allow-origin: *
  8. access-control-allow-credentials: true
  9. content-length: 0
  10. x-envoy-upstream-service-time: 5

在 8443 端口上验证外部到内部网格的连通性

要验证来自外部客户端的 mTLS 流量,首先将 CA 证书和客户端证书/密钥复制到在 default 命名空间中运行的 curl 客户端。

  1. $ export EXTERNAL_CLIENT=$(kubectl get pod -l app=curl -o jsonpath={.items..metadata.name})
  2. $ kubectl cp client.test.svc.cluster.local.key default/"${EXTERNAL_CLIENT}":/tmp/
  3. $ kubectl cp client.test.svc.cluster.local.crt default/"${EXTERNAL_CLIENT}":/tmp/
  4. $ kubectl cp example.com.crt default/"${EXTERNAL_CLIENT}":/tmp/ca.crt

现在证书可用于外部 curl 客户端,您可以使用以下命令验证该客户端到内部 httpbin 服务的连通性。

  1. $ kubectl exec "${EXTERNAL_CLIENT}" -c curl -- curl -IsS --cacert /tmp/ca.crt --key /tmp/client.test.svc.cluster.local.key --cert /tmp/client.test.svc.cluster.local.crt -HHost:httpbin.test.svc.cluster.local "https://httpbin.test.svc.cluster.local:8443/status/200"
  2. server: istio-envoy
  3. date: Mon, 24 Oct 2022 09:05:31 GMT
  4. content-type: text/html; charset=utf-8
  5. access-control-allow-origin: *
  6. access-control-allow-credentials: true
  7. content-length: 0
  8. x-envoy-upstream-service-time: 4
  9. x-envoy-decorator-operation: ingress-sidecar.test:9080/*

除了通过入口端口 8443 验证外部 mTLS 连通性之外,验证端口 8080 不接受任何外部 mTLS 流量也很重要。

  1. $ kubectl exec "${EXTERNAL_CLIENT}" -c curl -- curl -IsS --cacert /tmp/ca.crt --key /tmp/client.test.svc.cluster.local.key --cert /tmp/client.test.svc.cluster.local.crt -HHost:httpbin.test.svc.cluster.local "http://httpbin.test.svc.cluster.local:8080/status/200"
  2. curl: (56) Recv failure: Connection reset by peer
  3. command terminated with exit code 56

清理双向 TLS 终止示例

  1. 移除创建的 Kubernetes 资源:

    1. $ kubectl delete secret httpbin-mtls-termination httpbin-mtls-termination-cacert -n test
    2. $ kubectl delete service httpbin curl -n test
    3. $ kubectl delete deployment httpbin curl -n test
    4. $ kubectl delete namespace test
    5. $ kubectl delete service curl
    6. $ kubectl delete deployment curl
  2. 删除证书和私钥:

    1. $ rm example.com.crt example.com.key httpbin.test.svc.cluster.local.crt httpbin.test.svc.cluster.local.key httpbin.test.svc.cluster.local.csr \
    2. client.test.svc.cluster.local.crt client.test.svc.cluster.local.key client.test.svc.cluster.local.csr
  3. 将 Istio 从集群内卸载:

    1. $ istioctl uninstall --purge -y