Ingress Sidecar TLS 终止

在常规 Istio 网格部署中,下游请求的 TLS 终止在入口网关处执行。尽管这满足了大多数用例,但对于某些用例(例如网格中的 API 网关),入口网关并非必需。此任务演示了如何消除 Istio 入口网关引入的额外跳跃,并让与应用程序一起运行的 Envoy sidecar 为来自服务网格外部的请求执行 TLS 终止。

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

开始之前

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

    $ istioctl install --set profile=default --set values.pilot.env.ENABLE_TLS_ON_SIDECAR_INGRESS=true
    
  • 创建目标 httpbin 服务将部署到的测试命名空间。确保为命名空间启用 sidecar 注入。

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

启用全局 mTLS

应用以下 PeerAuthentication 策略以要求网格中所有工作负载的 mTLS 流量。

$ kubectl -n test apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: default
spec:
  mtls:
    mode: STRICT
EOF

禁用外部公开的 httpbin 端口的对等身份验证

禁用执行入口 TLS 终止的 sidecar 端 httpbin 服务的端口上的PeerAuthentication。请注意,这是 httpbin 服务的targetPort,应专门用于外部通信。

$ kubectl -n test apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: disable-peer-auth-for-external-mtls-port
  namespace: test
spec:
  selector:
    matchLabels:
      app: httpbin
  mtls:
    mode: STRICT
  portLevelMtls:
    9080:
      mode: DISABLE
EOF

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

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

$ #CA is example.com
$ 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
$ #Server is httpbin.test.svc.cluster.local
$ 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"
$ 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
$ #client is client.test.svc.cluster.local
$ 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"
$ 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

为证书和密钥创建 k8s 密钥

$ kubectl -n test create secret generic httpbin-mtls-termination-cacert --from-file=ca.crt=./example.com.crt
$ 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 部署时,我们需要在部署中使用userVolumeMount注释来挂载 istio-proxy sidecar 的证书。请注意,此步骤仅在 Istio 目前不支持 sidecar 配置中的credentialName时才需要。

sidecar.istio.io/userVolume: '{"tls-secret":{"secret":{"secretName":"httpbin-mtls-termination","optional":true}},"tls-ca-secret":{"secret":{"secretName":"httpbin-mtls-termination-cacert"}}}'
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服务

$ kubectl -n test apply -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: httpbin
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
    service: httpbin
spec:
  ports:
  - port: 8443
    name: https
    targetPort: 9080
  - port: 8080
    name: http
    targetPort: 9081
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
      annotations:
        sidecar.istio.io/userVolume: '{"tls-secret":{"secret":{"secretName":"httpbin-mtls-termination","optional":true}},"tls-ca-secret":{"secret":{"secretName":"httpbin-mtls-termination-cacert"}}}'
        sidecar.istio.io/userVolumeMount: '{"tls-secret":{"mountPath":"/etc/istio/tls-certs/","readOnly":true},"tls-ca-secret":{"mountPath":"/etc/istio/tls-ca-certs/","readOnly":true}}'
    spec:
      serviceAccountName: httpbin
      containers:
      - image: docker.io/kennethreitz/httpbin
        imagePullPolicy: IfNotPresent
        name: httpbin
        ports:
        - containerPort: 80
EOF

配置 httpbin 以启用外部 mTLS

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

$ kubectl -n test apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: Sidecar
metadata:
  name: ingress-sidecar
  namespace: test
spec:
  workloadSelector:
    labels:
      app: httpbin
      version: v1
  ingress:
  - port:
      number: 9080
      protocol: HTTPS
      name: external
    defaultEndpoint: 0.0.0.0:80
    tls:
      mode: MUTUAL
      privateKey: "/etc/istio/tls-certs/tls.key"
      serverCertificate: "/etc/istio/tls-certs/tls.crt"
      caCertificates: "/etc/istio/tls-ca-certs/ca.crt"
  - port:
      number: 9081
      protocol: HTTP
      name: internal
    defaultEndpoint: 0.0.0.0:80
EOF

验证

现在 httpbin 服务器已部署并配置,启动两个客户端以测试从网格内部和外部进行的端到端连接

  1. 一个内部客户端(curl)与 httpbin 服务位于相同的命名空间(test)中,并注入了 sidecar。
  2. 一个外部客户端(curl)位于默认命名空间(即服务网格外部)。
$ kubectl apply -f samples/curl/curl.yaml
$ kubectl -n test apply -f samples/curl/curl.yaml

运行以下命令以验证一切是否已启动并运行,以及是否已正确配置。

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

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

$ istioctl proxy-config secret httpbin-5bbdbd6588-z9vbs.test
RESOURCE NAME                                                           TYPE           STATUS     VALID CERT     SERIAL NUMBER                               NOT AFTER                NOT BEFORE
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
default                                                                 Cert Chain     ACTIVE     true           329492464719328863283539045344215802956     2022-02-15T09:55:46Z     2022-02-14T09:53:46Z
ROOTCA                                                                  CA             ACTIVE     true           204427760222438623495455009380743891800     2032-02-07T16:58:00Z     2022-02-09T16:58:00Z
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 上的内部网格连接

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

验证端口 8443 上的外部到内部网格连接

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

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

现在外部 curl 客户端可以使用这些证书了,您可以使用以下命令验证其与内部 httpbin 服务的连接。

$ 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"
server: istio-envoy
date: Mon, 24 Oct 2022 09:05:31 GMT
content-type: text/html; charset=utf-8
access-control-allow-origin: *
access-control-allow-credentials: true
content-length: 0
x-envoy-upstream-service-time: 4
x-envoy-decorator-operation: ingress-sidecar.test:9080/*

除了通过入口端口 8443 验证外部 mTLS 连接外,还必须验证端口 8080 是否不接受任何外部 mTLS 流量。

$ 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"
curl: (56) Recv failure: Connection reset by peer
command terminated with exit code 56

清理双向 TLS 终止示例

  1. 删除已创建的 Kubernetes 资源

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

    $ 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 \
        client.test.svc.cluster.local.crt client.test.svc.cluster.local.key client.test.svc.cluster.local.csr
    
  3. 从您的集群中卸载 Istio

    $ istioctl uninstall --purge -y
    
这些信息是否有用?
您是否有任何改进建议?

感谢您的反馈!