身份驗證策略

此任務涵蓋您在啟用、設定和使用 Istio 身份驗證策略時可能需要執行的主要活動。在身份驗證概述中找到更多關於基礎概念的資訊。

開始之前

$ istioctl install --set profile=default

設定

我們的範例使用兩個命名空間 foobar,其中包含兩個服務 httpbincurl,兩者都使用 Envoy 代理程式執行。我們還使用了第二個 httpbincurl 執行個體,它們在 legacy 命名空間中沒有 sidecar 執行。如果您想在嘗試任務時使用相同的範例,請執行以下操作

ZipZipZipZipZipZip
$ 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/curl/curl.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/curl/curl.yaml@) -n bar
$ kubectl create ns legacy
$ kubectl apply -f @samples/httpbin/httpbin.yaml@ -n legacy
$ kubectl apply -f @samples/curl/curl.yaml@ -n legacy

您可以透過從命名空間 foobarlegacy 中的任何 curl Pod,使用 curl 發送 HTTP 請求到 httpbin.foohttpbin.barhttpbin.legacy 來驗證設定。所有請求都應該成功並返回 HTTP 程式碼 200。

例如,以下是一個檢查從 curl.barhttpbin.foo 的連線能力的指令:

$ kubectl exec "$(kubectl get pod -l app=curl -n bar -o jsonpath={.items..metadata.name})" -c curl -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=curl -n ${from} -o jsonpath={.items..metadata.name})" -c curl -n ${from} -- curl -s "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "curl.${from} to httpbin.${to}: %{http_code}\n"; done; done
curl.foo to httpbin.foo: 200
curl.foo to httpbin.bar: 200
curl.foo to httpbin.legacy: 200
curl.bar to httpbin.foo: 200
curl.bar to httpbin.bar: 200
curl.bar to httpbin.legacy: 200
curl.legacy to httpbin.foo: 200
curl.legacy to httpbin.bar: 200
curl.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:"

自動相互 TLS

預設情況下,Istio 會追蹤已遷移到 Istio 代理的伺服器工作負載,並配置客戶端代理自動將相互 TLS 流量發送到這些工作負載,以及將純文字流量發送到沒有 Sidecar 的工作負載。

因此,所有具有代理的工作負載之間的流量都使用相互 TLS,您無需執行任何操作。例如,取得對 httpbin/header 的請求的回應。當使用相互 TLS 時,代理會將 X-Forwarded-Client-Cert 標頭注入到後端的上游請求中。該標頭的存在證明使用了相互 TLS。例如:

$ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl -s http://httpbin.foo:8000/headers -s | jq '.headers["X-Forwarded-Client-Cert"][0]' | sed 's/Hash=[a-z0-9]*;/Hash=<redacted>;/'
  "By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=<redacted>;Subject=\"\";URI=spiffe://cluster.local/ns/foo/sa/curl"

當伺服器沒有 Sidecar 時,X-Forwarded-Client-Cert 標頭不存在,這表示請求是純文字的。

$ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -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/v1
kind: PeerAuthentication
metadata:
  name: "default"
  namespace: "istio-system"
spec:
  mtls:
    mode: STRICT
EOF

此對等驗證策略將工作負載配置為僅接受使用 TLS 加密的請求。由於它沒有為 selector 欄位指定值,因此該策略適用於網格中的所有工作負載。

再次執行測試指令:

$ for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=curl -n ${from} -o jsonpath={.items..metadata.name})" -c curl -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "curl.${from} to httpbin.${to}: %{http_code}\n"; done; done
curl.foo to httpbin.foo: 200
curl.foo to httpbin.bar: 200
curl.foo to httpbin.legacy: 200
curl.bar to httpbin.foo: 200
curl.bar to httpbin.bar: 200
curl.bar to httpbin.legacy: 200
curl.legacy to httpbin.foo: 000
command terminated with exit code 56
curl.legacy to httpbin.bar: 000
command terminated with exit code 56
curl.legacy to httpbin.legacy: 200

您會看到請求仍然成功,除了來自沒有代理的客戶端 curl.legacy 到具有代理的伺服器 httpbin.foohttpbin.bar 的請求。這是預期的,因為現在嚴格要求相互 TLS,但沒有 Sidecar 的工作負載無法符合此要求。

清理第 1 部分

移除在此會話中新增的全域驗證策略:

$ kubectl delete peerauthentication -n istio-system default

針對每個命名空間或工作負載啟用相互 TLS

命名空間範圍的策略

要變更特定命名空間內所有工作負載的相互 TLS,請使用命名空間範圍的策略。策略的規範與網格範圍策略的規範相同,但您要在 metadata 下指定它適用的命名空間。例如,以下對等驗證策略為 foo 命名空間啟用嚴格的相互 TLS:

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

由於此策略僅適用於 foo 命名空間中的工作負載,因此您應該只看到從沒有 Sidecar 的客戶端 (curl.legacy) 到 httpbin.foo 的請求開始失敗。

$ for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=curl -n ${from} -o jsonpath={.items..metadata.name})" -c curl -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "curl.${from} to httpbin.${to}: %{http_code}\n"; done; done
curl.foo to httpbin.foo: 200
curl.foo to httpbin.bar: 200
curl.foo to httpbin.legacy: 200
curl.bar to httpbin.foo: 200
curl.bar to httpbin.bar: 200
curl.bar to httpbin.legacy: 200
curl.legacy to httpbin.foo: 000
command terminated with exit code 56
curl.legacy to httpbin.bar: 200
curl.legacy to httpbin.legacy: 200

針對每個工作負載啟用相互 TLS

要為特定工作負載設定對等驗證策略,您必須配置 selector 區段並指定與所需工作負載匹配的標籤。例如,以下對等驗證策略為 httpbin.bar 工作負載啟用嚴格的相互 TLS:

$ cat <<EOF | kubectl apply -n bar -f -
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: "httpbin"
  namespace: "bar"
spec:
  selector:
    matchLabels:
      app: httpbin
  mtls:
    mode: STRICT
EOF

再次執行探測指令。如預期,從 curl.legacyhttpbin.bar 的請求開始因為相同的原因而失敗。

$ for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=curl -n ${from} -o jsonpath={.items..metadata.name})" -c curl -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "curl.${from} to httpbin.${to}: %{http_code}\n"; done; done
curl.foo to httpbin.foo: 200
curl.foo to httpbin.bar: 200
curl.foo to httpbin.legacy: 200
curl.bar to httpbin.foo: 200
curl.bar to httpbin.bar: 200
curl.bar to httpbin.legacy: 200
curl.legacy to httpbin.foo: 000
command terminated with exit code 56
curl.legacy to httpbin.bar: 000
command terminated with exit code 56
curl.legacy to httpbin.legacy: 200
...
curl.legacy to httpbin.bar: 000
command terminated with exit code 56

要針對每個連接埠微調相互 TLS 設定,您必須配置 portLevelMtls 區段。例如,以下對等驗證策略要求所有連接埠都使用相互 TLS,除了連接埠 8080

$ cat <<EOF | kubectl apply -n bar -f -
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: "httpbin"
  namespace: "bar"
spec:
  selector:
    matchLabels:
      app: httpbin
  mtls:
    mode: STRICT
  portLevelMtls:
    8080:
      mode: DISABLE
EOF
  1. 對等驗證策略中的連接埠值是容器的連接埠。
  2. 只有在連接埠繫結到服務時,您才能使用 portLevelMtls。否則,Istio 會忽略它。
$ for from in "foo" "bar" "legacy"; do for to in "foo" "bar" "legacy"; do kubectl exec "$(kubectl get pod -l app=curl -n ${from} -o jsonpath={.items..metadata.name})" -c curl -n ${from} -- curl "http://httpbin.${to}:8000/ip" -s -o /dev/null -w "curl.${from} to httpbin.${to}: %{http_code}\n"; done; done
curl.foo to httpbin.foo: 200
curl.foo to httpbin.bar: 200
curl.foo to httpbin.legacy: 200
curl.bar to httpbin.foo: 200
curl.bar to httpbin.bar: 200
curl.bar to httpbin.legacy: 200
curl.legacy to httpbin.foo: 000
command terminated with exit code 56
curl.legacy to httpbin.bar: 200
curl.legacy to httpbin.legacy: 200

策略優先順序

特定於工作負載的對等驗證策略優先於命名空間範圍的策略。如果您新增一個策略以停用 httpbin.foo 工作負載的相互 TLS,您可以測試此行為。請注意,您已建立一個命名空間範圍的策略,該策略為 foo 命名空間中的所有服務啟用相互 TLS,並觀察到從 curl.legacyhttpbin.foo 的請求失敗(請參閱上文)。

$ cat <<EOF | kubectl apply -n foo -f -
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
  name: "overwrite-example"
  namespace: "foo"
spec:
  selector:
    matchLabels:
      app: httpbin
  mtls:
    mode: DISABLE
EOF

重新執行來自 curl.legacy 的請求,您應該會再次看到成功的返回程式碼 (200),確認特定於服務的策略會覆寫命名空間範圍的策略。

$ kubectl exec "$(kubectl get pod -l app=curl -n legacy -o jsonpath={.items..metadata.name})" -c curl -n legacy -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}\n"
200

清理第 2 部分

移除在上述步驟中建立的策略:

$ kubectl delete peerauthentication default overwrite-example -n foo
$ kubectl delete peerauthentication httpbin -n bar

終端使用者身份驗證

要試用此功能,您需要一個有效的 JWT。JWT 必須對應於您想要用於演示的 JWKS 端點。本教學課程使用來自 Istio 程式碼庫的測試權杖 JWT 測試JWKS 端點

此外,為了方便起見,請透過 Ingress Gateway 公開 httpbin.foo(如需更多詳細資訊,請參閱 Ingress 任務)。

設定閘道:

Zip
$ kubectl apply -f @samples/httpbin/httpbin-gateway.yaml@ -n foo

依照 決定 Ingress IP 和連接埠 中的指示定義 INGRESS_PORTINGRESS_HOST 環境變數。

透過閘道執行測試查詢:

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

現在,新增一個請求驗證策略,該策略要求 Ingress Gateway 使用終端使用者 JWT。

$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: RequestAuthentication
metadata:
  name: "jwt-example"
  namespace: istio-system
spec:
  selector:
    matchLabels:
      istio: ingressgateway
  jwtRules:
  - issuer: "testing@secure.istio.io"
    jwksUri: "https://raw.githubusercontent.com/istio/istio/release-1.24/security/tools/jwt/samples/jwks.json"
EOF

在它所選取的工作負載的命名空間中套用該策略,在此案例中為 Ingress Gateway。

如果您在授權標頭中提供權杖,即其隱含的預設位置,則 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.24/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 驗證的其他方面,請使用指令碼 gen-jwt.py 來產生新的權杖,以測試不同的簽發者、受眾、到期日等。該指令碼可以從 Istio 儲存庫下載:

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

您也需要 key.pem 檔案:

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

JWT 驗證具有 60 秒的時鐘偏移,這表示 JWT 權杖將比其設定的 nbf 早 60 秒生效,並在其設定的 exp 之後的 60 秒內保持有效。

例如,以下指令建立一個在 5 秒後過期的權杖。如您所見,Istio 一開始會成功使用該權杖驗證請求,但在 65 秒後會拒絕它們:

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

您也可以將 JWT 策略新增至 Ingress Gateway(例如,服務 istio-ingressgateway.istio-system.svc.cluster.local)。這通常用於為繫結到閘道的所有服務定義 JWT 策略,而不是針對個別服務。

需要有效的令牌

要拒絕沒有有效權杖的請求,請新增授權策略,其中包含一條規則,該規則指定沒有請求主體(在以下範例中顯示為 notRequestPrincipals: ["*"])的請求執行 DENY 動作。只有在提供有效的 JWT 權杖時,請求主體才可用。因此,該規則會拒絕沒有有效權杖的請求。

$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
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

針對每個路徑需要有效的令牌

要針對每個主機、路徑或方法微調使用權杖需求的授權,請變更授權策略,使其僅在 /headers 上需要 JWT。當此授權規則生效時,對 $INGRESS_HOST:$INGRESS_PORT/headers 的請求會失敗,並顯示錯誤碼 403。對所有其他路徑的請求都會成功,例如 $INGRESS_HOST:$INGRESS_PORT/ip

$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
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 部分

  1. 移除驗證策略:

    $ kubectl -n istio-system delete requestauthentication jwt-example
    
  2. 移除授權策略:

    $ kubectl -n istio-system delete authorizationpolicy frontend-ingress
    
  3. 移除權杖產生器指令碼和金鑰檔案:

    $ rm -f ./gen-jwt.py ./key.pem
    
  4. 如果您不打算探索任何後續任務,您可以直接刪除測試命名空間來移除所有資源。

    $ kubectl delete ns foo bar legacy
    
此資訊是否有用?
您有任何改進建議嗎?

感謝您的意見反應!