Alibaba Cloud Service Mesh (ASM) は、連携して機能する 2 つの Istio リソースを通じて JWT 認証を処理します。
-
RequestAuthentication は、トークンの検証方法 (信頼する発行者と署名キー) を定義します。無効なトークンを保持するリクエストは拒否しますが、トークンをまったく保持しないリクエストは許可します。
-
AuthorizationPolicy は、トークンの有無、クレーム、またはリクエストパスに基づいてリクエストを許可または拒否するかどうかというアクセス決定を適用します。
RequestAuthentication 単独では、未認証リクエストをブロックしません。トークンを要求するには、AuthorizationPolicy と組み合わせて使用します。
ASM がサポートする JWT アルゴリズム
サポートされるアルゴリズムは、ご利用の ASM インスタンスバージョンによって異なります。
-
v1.13 より前: RSA のみ
-
v1.13 以降: 以下にリストされているすべての標準アルゴリズム
| アルゴリズムファミリー | サポートされるアルゴリズム |
|---|---|
| RSA | RS256, RS384, RS512 |
| RSA-PSS | PS256, PS384, PS512 |
| ECDSA | ES256, ES384, ES512 |
| HMAC | HS256, HS384, HS512 |
| Edwards-curve | EdDSA |
jwksUri パラメーターの使用方法
注: jwksUri には ASM v1.13 以降が必要です。
RequestAuthentication リソースの jwksUri フィールドは、JWT を検証するために使用される公開鍵をどこからフェッチするかを ASM に指示します。その設定方法は、JSON Web Key Set (JWKS) エンドポイントがどこでホストされているかによって異なります。
-
管理対象クラスター内 —
jwksUriをクラスター内のサービスアドレスに直接指定します。 -
マネージドクラスター外 -- ASM が外部エンドポイントに到達できるように、まず ServiceEntry を作成します。
以下の例では、Istio プロジェクトの サンプル JWT と サンプル JWKS を使用します。
マネージドクラスター内の JWKS エンドポイント
この例では、Container Service for Kubernetes (ACK) クラスター内の nginx-proxy サービスが、ポート 80 の /get_jwks で JWKS を提供します。完全な jwksUri は http://nginx-proxy.{Namespace}.svc.cluster.local:80/get_jwks です。
-
JWKS エンドポイントが到達可能であることを確認します。
curl nginx-proxy/get_jwks期待される出力:
{ "keys":[ {"e":"AQAB","kid":"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-e****","kty":"RSA","n":"xAE7eB6qugXyCAG3yhh7pkDkT65p****-P7KfIupjf59vsdo91bSP9C8H07pSAGQ****_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5****_pbhLdKXbi66GlVeK6ABZOUW3WYt****-91gVuoeJT_DwtGGcp4ignkgXfkiE****-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoU****_-4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZ****_b3y5u-YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqT****_EyxOGuHJrLsn00****"}]}有効な JSON キーセットが返された場合、エンドポイントは機能しています。
-
ASM コンソールで RequestAuthentication リソースを作成します。
-
ASM コンソールにログインします。
-
左側のナビゲーションウィンドウで、[Service Mesh] > [メッシュ管理] を選択します。
-
「メッシュ管理」ページで、対象の ASM インスタンスを見つけます。インスタンス名をクリックするか、[操作] 列の [管理] をクリックします。
-
左側のナビゲーションウィンドウで、Mesh セキュリティセンター > RequestAuthentication を選択します。
-
「RequestAuthentication」ページで、[YAML から作成] をクリックします。[名前空間] と [テンプレート] を選択し、以下の YAML を貼り付け、その後 [作成] をクリックします。
apiVersion: security.istio.io/v1beta1 kind: RequestAuthentication metadata: name: jwt-example namespace: foo spec: jwtRules: - issuer: testing@secure.istio.io jwksUri: 'http://nginx-proxy/get_jwks' selector: matchLabels: app: httpbin
-
-
sleepPod からhttpbinサービスに JWT を含むリクエストを送信して、構成をテストします:# Retrieve and decode the sample JWT TOKEN=$(curl https://raw.githubusercontent.com/istio/istio/release-1.14/security/tools/jwt/samples/demo.jwt -s) && echo "$TOKEN" | cut -d '.' -f2 - | base64 --decode - # Send a request with the JWT 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" -sS -o /dev/null \ -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n"200状態コードにより、JWT 認証が正常に動作していることを確認できます。
マネージドクラスター外の JWKS エンドポイント
JWKS エンドポイントが ASM によって管理される ACK クラスターの外部でホストされている場合、メッシュがそれに到達できるように ServiceEntry を作成します。この例では HTTP を使用します。HTTPS の場合は、オプションの手順に示すように DestinationRule を追加します。
-
外部 JWKS エンドポイントの ServiceEntry を作成します。
-
service-entry.yaml という名前のファイルを作成します。
apiVersion: networking.istio.io/v1beta1 kind: ServiceEntry metadata: name: external-svc-https namespace: foo spec: addresses: - 11.11.XX.XX # Replace with the IP address of your JWKS endpoint. endpoints: - address: 11.11.XX.XX # Replace with the IP address of your JWKS endpoint. hosts: - 11.11.XX.XX # Replace with the IP address of your JWKS endpoint. location: MESH_EXTERNAL ports: - name: http number: 80 protocol: HTTP - name: https number: 443 protocol: HTTPS resolution: STATIC -
ServiceEntry を適用します。
kubectl apply -f service-entry.yaml
-
-
(オプション) HTTPS 経由で JWKS をフェッチするには、TLS を有効にする DestinationRule を作成します。
apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: external-svc-https namespace: foo spec: host: ${The host where ServiceEntry resides} trafficPolicy: loadBalancer: simple: ROUND_ROBIN portLevelSettings: - port: number: 443 tls: mode: SIMPLE -
RequestAuthentication リソースを作成します。
-
ASM コンソールにログインします。
-
左側のナビゲーションウィンドウで、Service Mesh > Mesh 管理 を選択します。
-
[メッシュ管理] ページで対象の ASM インスタンスを見つけ、インスタンス名をクリックするか、[操作] 列の [管理] をクリックします。
-
左側のナビゲーションウィンドウで、[メッシュセキュリティセンター] > [RequestAuthentication] を選択します。
-
「RequestAuthentication」ページで、[YAML から作成] をクリックします。[名前空間] と [テンプレート] を選択し、以下の YAML を貼り付け、その後 [作成] をクリックします。
apiVersion: security.istio.io/v1beta1 kind: RequestAuthentication metadata: name: jwt-example namespace: foo spec: jwtRules: - issuer: tes****@secure.istio.io jwksUri: '${Your jwksUri}' selector: matchLabels: app: httpbin
-
-
設定をテストします。
# Send a request with a JWT from the sleep Pod to httpbin 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" -sS -o /dev/null \ -H "Authorization: Bearer $TOKEN" -w "%{http_code}\n"200の状態コードは、JWT 認証が正常に機能していることを確認します。
特定のパスの JWT 認証をスキップする方法
RequestAuthentication リソースを、action: DENY および notPaths を使用する AuthorizationPolicy と組み合わせます。RequestAuthentication はイングレスゲートウェイでトークンを検証し、AuthorizationPolicy は、除外するパスを除くすべてのパスへの認証されていないリクエストを拒否します。
次の例では、/productpage を JWT 認証から除外する一方で、/api/v1/products/0 および /api/v1/products/1 については有効な JWT の使用を必須とします。
-
Bookinfo サンプルアプリケーションをデプロイし、アクセスを検証します。
ASM インスタンスに追加された ACK クラスターに Bookinfo アプリケーションをデプロイします。詳細については、「ASM インスタンスに追加された ACK クラスターにアプリケーションをデプロイする」をご参照ください。
すべてのパスがアクセス可能であることを確認します。
curl "http://<ingress-gateway-ip>/productpage" -sS -o /dev/null -w "%{http_code}\n" curl "http://<ingress-gateway-ip>/api/v1/products/0" -sS -o /dev/null -w "%{http_code}\n" curl "http://<ingress-gateway-ip>/api/v1/products/1" -sS -o /dev/null -w "%{http_code}\n"各コマンドは、
200を返す必要があります。 -
イングレスゲートウェイに適用される RequestAuthentication リソースを作成します。
-
ASM コンソールにログインします。
-
左側のナビゲーションウィンドウで、[Service Mesh] > [メッシュ管理] を選択します。
-
[メッシュ管理] ページで目的の ASM インスタンスを見つけ、インスタンス名をクリックするか、[操作] 列の [管理] をクリックします。
-
左側のナビゲーションウィンドウで、[メッシュセキュリティセンター] > [リクエスト認証] を選択します。
-
「RequestAuthentication」ページで、[YAML から作成] をクリックします。[名前空間] と [テンプレート] を選択し、以下の YAML を貼り付け、その後 [作成] をクリックします。
apiVersion: security.istio.io/v1beta1 kind: RequestAuthentication metadata: name: jwt-example namespace: istio-system spec: jwtRules: - issuer: testing@secure.istio.io jwks: >- { "keys":[ {"e":"AQAB","kid":"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-e****","kty":"RSA","n":"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQ****_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5****_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVu****_DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoU****_-4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZ****_b3y5u-YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqT****_EyxOGuHJrLsn00****"}]} selector: matchLabels: app: istio-ingressgateway
この RequestAuthentication はイングレスゲートウェイに適用されます。JWT 署名を検証しますが、トークンなしのリクエストをブロックしません。それは AuthorizationPolicy の役割です。
-
-
AuthorizationPolicy を作成して、
/productpageを除くすべてのパスで JWT を要求します。-
ASM コンソールにログインします。
-
左側のナビゲーションウィンドウで、Service Mesh > Mesh 管理 を選択します。
-
「[メッシュ管理]」ページで、対象のASM インスタンスを見つけます。インスタンス名をクリックするか、[操作] 列の [管理] をクリックします。
-
左側のナビゲーションウィンドウで、[Mesh セキュリティセンター] > [AuthorizationPolicy] を選択します。
-
[AuthorizationPolicy] ページで、[YAML から作成] をクリックし、[名前空間] と [テンプレート] を選択し、次の YAML を貼り付けてから [作成] をクリックします。
apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: test-exclude namespace: istio-system spec: action: DENY rules: - from: - source: notRequestPrincipals: - '*' to: - operation: notPaths: - /productpage selector: matchLabels: app: istio-ingressgateway
このポリシーの仕組み: DENY ルールは、両方の条件を満たすリクエストを対象とします: (1) リクエストに有効な JWT ID がない (
notRequestPrincipals: ['*'])、および (2) リクエストが/productpageを除く任意のパスを対象としている (notPaths: [/productpage])。この結果、/productpageへの認証されていないリクエストは許可されますが、他のすべてのパスへの認証されていないリクエストは403状態コードで拒否されます。 -
-
設定を検証します。
サンプル JWT を環境変数として設定します。
export TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1****.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5p****.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9****-LS9qd_vpdLG4Tn1A15NxfCjp5f7Q****-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZz****__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCg****_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvH****_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8****JWT を含まないテストリクエスト:
curl "http://<ingress-gateway-ip>/productpage" -sS -o /dev/null -w "%{http_code}\n" # Expected: 200 (exempted path) curl "http://<ingress-gateway-ip>/api/v1/products/0" -sS -o /dev/null -w "%{http_code}\n" # Expected: 403 (no JWT, access denied) curl "http://<ingress-gateway-ip>/api/v1/products/1" -sS -o /dev/null -w "%{http_code}\n" # Expected: 403 (no JWT, access denied)有効な JWT でリクエストをテストします。
curl "http://<ingress-gateway-ip>/productpage" -H "Authorization: Bearer $TOKEN" -sS -o /dev/null -w "%{http_code}\n" # Expected: 200 curl "http://<ingress-gateway-ip>/api/v1/products/0" -H "Authorization: Bearer $TOKEN" -sS -o /dev/null -w "%{http_code}\n" # Expected: 200 curl "http://<ingress-gateway-ip>/api/v1/products/1" -H "Authorization: Bearer $TOKEN" -sS -o /dev/null -w "%{http_code}\n" # Expected: 200パス JWT なし 有効な JWT あり /productpage200(除外)200/api/v1/products/0403(拒否)200/api/v1/products/1403(拒否)200