TT Lab
はじめる
学ぶ 学習パス コース

ICA — Istio認定アソシエイト

アイデンティティはIPではなくサービスアカウントだ

TT Labで続きを見る

一言でいうと

Istioでワークロードのアイデンティティは、IPでもPod名でもなく、KubernetesのServiceAccountです。この事実1つから、mTLS、認可ポリシー、そしてほとんどのポリシーのデバッグが派生します。

なぜ必要なのか

クラスターの内部は、しばしば信頼できるネットワークとして扱われますが、実際には、Pod 1つが乗っ取られただけで、残りのすべてに平文でアクセスできてしまう、フラットなネットワークです。ゼロトラストは、この前提を覆します。問題は、これをアプリケーションのコードで実装しようとすると、すべてのサービスに、証明書の発行・更新、相手の検証、権限チェックのロジックを入れなければならないという点です。言語スタックが複数ある組織で、これを一貫して維持することは、事実上不可能です。

そこでIstioは、3つのこと(暗号化、ワークロードの認証、認可)をインフラの層に下ろしました。その土台がアイデンティティの体系です。IPはPodが再起動すると変わり、スプーフィングも可能ですが、ServiceAccountに基づくアイデンティティは、証明書に刻まれて、ハンドシェイクで暗号学的に検証されます。

どう動くのか

アイデンティティは、SPIFFE標準の形式に従います。

spiffe://cluster.local/ns/payments/sa/payments-api
         트러스트 도메인   네임스페이스  서비스어카운트

証明書の発行の流れで覚えておくべき点は、秘密鍵がPodを離れないことです。Podの中のistio-agentが鍵のペアを作り、CSRだけをistiodに送ります。istiodは、ServiceAccountのトークンでリクエスト元を検証したあと、SPIFFE IDをSANに刻んだ証明書を署名して返します。証明書はデフォルトで24時間有効で、寿命の半分ほどが過ぎた時点から、agentが自動で再発行を受けます。EnvoyはSDSで証明書を受け取るので、更新にPodの再起動やコネクションのドレインは必要ありません。

PeerAuthenticationは、「入ってくるトラフィックにmTLSをどう要求するか」を決めます。PERMISSIVE(両方を受け入れる、デフォルト)、STRICT(mTLSのみ)、DISABLEの3つで、狭い範囲が常に勝ちます。ワークロード > ネームスペース > メッシュ全体の順です。メッシュ全体のポリシーは、ルートネームスペース(通常はistio-system)に、名前defaultで置きます。ヘルスチェックや、メッシュの外のPrometheusがスクレイプするポート1つだけに例外を設けたいなら、portLevelMtlsを使います。

AuthorizationPolicyの評価順序は、試験にほぼ必ず出ます。

1. CUSTOM  → 외부 인가기가 거부하면 즉시 거부
2. DENY    → 하나라도 매칭되면 즉시 거부
3. ALLOW   → 대상 워크로드에 ALLOW 정책이 하나도 없으면 허용(기본 개방)
             ALLOW 정책이 하나라도 있으면 매칭되어야 허용(기본 거부로 전환)

最後の行が、核心となる性質です。ALLOWポリシーを1つ追加した瞬間に、そのワークロードは許可リストモードに反転します。この性質を利用して、spec: {}という空のポリシー1つで、ネームスペースのデフォルト拒否を作ります。「ALLOWポリシーは存在するが、マッチするルールがない」という意味なので、何も通過できません。

もう1つ、よく引っかかる落とし穴があります。principalsはmTLSの証明書から得られるので、平文のリクエストにはprincipalが空で、絶対にマッチしません。PERMISSIVEは、平文とmTLSの両方を受け入れます。平文のリクエストにprincipalがないという意味であって、PERMISSIVEのすべてのリクエストにアイデンティティがないという意味ではありません。mTLSのリクエストは、このモードでもprincipalを持てるので、ポリシーのデバッグでは、実際の接続方式も一緒に確認してください。STRICTは、平文を受け付けないという別の決定です。

RequestAuthenticationは、エンドユーザーのトークン(JWT)を検証します。ここで最もよくある誤解は、このリソースだけでは、トークンのないリクエストがそのまま通過するという点です。「JWTがあるなら有効でなければならない」ことだけを強制するからです。トークンの必須化にはAuthorizationPolicyも必要ですが、単にJWT条件のALLOWをもう1つ追加してはいけません。複数のALLOWは和集合です。既存のordersのアイデンティティのALLOWがあれば、JWTのないordersがそのポリシーで通過し、JWTだけを検査するALLOWは、ほかのアイデンティティやパスまで許可してしまう可能性があります。

2つの条件を一緒に求めるには、同じALLOWルールの同じsourceにprincipalsとrequestPrincipalsを入れるか、既存の最小権限のALLOWを維持したまま、JWTの主体がないリクエストをnotRequestPrincipals: ["*"]のDENYで防ぎます。同じsourceのフィールドはAND、異なるfromの項目やALLOWポリシーはORだという違いを覚えておいてください。DENYは、HTTPの属性がないTCPのリクエストも捕まえられるので、適用するポートを明示する必要があります。次のラボは、HTTPの8080ポートだけを対象にします。

失敗コードも、原因をすべて教えてくれるわけではありません。隔離したIstio 1.31.0の実験では、別のaudienceのJWTは403とaudience拒否のメッセージ、アイデンティティやパスの認可の失敗は403とRBAC拒否のメッセージでした。401/403の数字1つの代わりに、レスポンスの本文と、適用されたポリシー、検証された主体を一緒に確認してください。

ポリシーを本番に上げるときは、istio.io/dry-run: "true"アノテーションを先に付けます。評価だけを行い、実際にはブロックしないまま、「有効にしていたら何がブロックされたか」を、ログとメトリクスで見せてくれます。拒否の一覧が意図と一致したときに、アノテーションを外すのが、安全な順序です。

現場での姿

筆者のホームラボでは、ノード間のPodのトラフィックを、WireGuardで透過的に暗号化しています。cilium statusにはEncryption: Wireguard [cilium_wg0 (Port: 51871, Peers: 2)]と出力され、Modules HealthはOK 92 / Degraded 0です。

ここで、試験にも出る重要な区別が生まれます。WireGuardはノード間の伝送区間の暗号化であり、IstioのmTLSは、ワークロードのアイデンティティの証明が付いた、アプリケーション層の暗号化です。 前者は「回線を盗聴しても読めない」ことを保証しますが、「誰が送ったのか」は証明しません。AuthorizationPolicyのprincipalsが求めているのは、後者です。2つの層を重ねて使うこともできますが、どちらが暗号化の責任を持つのかを、チームとして1つに決めておかないと、二重のポリシーでデバッグの地獄が口を開けます。

同じクラスターで得たもう1つの感覚は、L7の判定のログの形です。ポリシー違反のリクエストはDROPPEDと、それに対する403の応答はFORWARDEDと記録されました。IstioのRBAC拒否も、まったく同じで「接続は成立し、403が返ってきた」形です。接続自体ができない問題と、絶対に混同しないでください。

次のラボですること

kwok APIに専用のServiceAccountとワークロードの仕様を保存したあと、メッシュ全体のSTRICTと、ワークロード単位のポート例外を書き、デフォルト拒否 → 最小権限のALLOW → 管理経路のDENY(dry-run) → JWTの必須化の順にポリシーを積み上げます。

このラボでは、実際のコンテナやEnvoyは実行されません。マニフェストの設計と、実際のトラフィックの検証を区別してください。

公式ドキュメント