入口 Sidecar TLS 終止

在一般的 Istio 網格部署中,下游請求的 TLS 終止是在 Ingress Gateway 執行。雖然這滿足了大多數的使用案例,但對於某些情況(例如網格中的 API Gateway),Ingress Gateway 並非必要。此任務展示如何消除 Istio Ingress Gateway 引入的額外跳躍,並讓與應用程式一起執行的 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 連接埠的 PeerAuthentication

針對將在 sidecar 執行 Ingress TLS 終止的 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,設定 Ingress 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/*

除了驗證透過 Ingress 連接埠 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
    
這項資訊是否有用?
您有任何改進建議嗎?

感謝您的回饋!