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

ICA — Istio認定アソシエイト

ゼロトラストのポリシーを積み上げる

TT Labで続きを見る

目標

mTLSの強制と認可ポリシーを順番に積み上げながら、評価順序(CUSTOM → DENY → ALLOW)と、「ALLOWが1つでもあるとデフォルト拒否に反転する」という性質を、マニフェストで体得します。

なぜ重要なのか

メッシュのセキュリティ導入が失敗する理由は、ほとんどの場合、順序を飛ばすことです。平文の送信元を整理せずにSTRICTを有効にすると、サイドカーのないクライアントがただちに切断され、コールグラフを知らないままデフォルト拒否を敷くと、何を開けるべきか分からないまま障害が起きます。

特に忘れやすい事実が1つあります。principalsはmTLSの証明書から得られるので、PERMISSIVE状態の平文のリクエストにはprincipalが空で、そうなるとどんなprincipalsの条件にもマッチしません。「ポリシーは間違いなく合っているのに403」の、最もよくある原因です。そのため、このラボは、mTLS → デフォルト拒否 → 最小権限 → セーフティネット → エンドユーザー認証の順序を、そのままたどります。

このラボは、kwokベースのAPIオブジェクト・マニフェスト設計のラボです。KubernetesのオブジェクトはAPIに保存されますが、実際のpaymentsコンテナ・Envoy・JWT検証サーバーが実行されるわけではありません。Istioのポリシーは/root/ica-security/の下にファイルとして作成し、HTTPの応答や暗号化の動作は採点しません。例のIdPのアドレスには接続しません。

ステップ8は、既存のALLOWを維持したまま、8080ポートのJWTのないリクエストにDENYを適用する設計です。別のJWTのALLOWは、2つの条件のANDではなく、許可経路のORを作るので使いません。ほかのポートでのJWTの必須化や、メトリクスへのアクセスの許可を保証するポリシーではありません。

ステップ

  1. ネームスペースica-securityを作成し、ラベルistio-injection=enabledを付けてください。
  2. ネームスペースica-securityに、ServiceAccount payments-apiとorders-apiを作成してください。
  3. 同じネームスペースに、Deployment payments-apiをデプロイしてください。Podのラベルはapp=payments-api、serviceAccountNameはpayments-api、イメージはnginx:1.27-alpineです。そして、Service payments-apiを、ポート8080(名前http)と9090(名前http-metrics)の2つで作成してください。
  4. /root/ica-security/pa-mesh.yamlにPeerAuthenticationを作成してください。名前default、ネームスペースistio-system、mtls.mode: STRICTで、selectorは置きません。続けて、/root/ica-security/pa-workload.yamlに、ネームスペースica-security、selector app: payments-api、mtls.mode: STRICTでありながら、portLevelMtlsでポート9090だけがPERMISSIVEのポリシーを作成してください。
  5. /root/ica-security/ap-deny-all.yamlにAuthorizationPolicyを作成してください。名前default-deny、ネームスペースica-security、specは空のオブジェクトです。
  6. /root/ica-security/ap-allow-orders.yamlにAuthorizationPolicyを作成してください。selector app: payments-api、action: ALLOWで、ルール1つに、from.source.principalsはcluster.local/ns/ica-security/sa/orders-api、to.operation.methodsはPOST、to.operation.pathsは/v1/chargesです。
  7. /root/ica-security/ap-deny-admin.yamlにAuthorizationPolicyを作成してください。selector app: payments-api、action: DENYで、ルールはto.operation.pathsに/admin/*が1つだけで、fromは置きません。metadata.annotationsにistio.io/dry-run: "true"を付けてください。
  8. /root/ica-security/ra-jwt.yamlにRequestAuthenticationを作成してください。名前payments-api-jwt、ネームスペースica-security、selector app: payments-api、jwtRulesは1つで、issuer https://idp.example.com/、jwksUri https://idp.example.com/.well-known/jwks.json、audiences [payments-api]です。続けて、/root/ica-security/ap-require-jwt.yamlに、名前require-jwt、同じネームスペースとselector、action: DENYのAuthorizationPolicyを作成してください。ルール1つのfrom.source.notRequestPrincipalsは["*"]、同じルールのto.operation.portsは["8080"]です。追加のfrom・to・rulesの条件とdry-runは置きません。ステップ6の最小権限のALLOWは、そのまま維持してください。

参考

セキュリティのラボ用のネームスペースを作成する

ネームスペースica-securityを作成し、ラベルistio-injection=enabledを付けてください。

サイドカーがなければ、mTLSも認可もかけられません。自動注入のラベルを付けてください。

ワークロードごとの専用ServiceAccountを作成する

ネームスペースica-securityに、ServiceAccount payments-apiとorders-apiを作成してください。

SPIFFE IDの最後の欄がServiceAccountです。複数のワークロードがdefaultを共有すると、ポリシーで互いを区別できません。

ServiceAccountを付けたワークロードをデプロイする

同じネームスペースに、Deployment payments-apiをデプロイしてください。Podのラベルはapp=payments-api、serviceAccountNameはpayments-api、イメージはnginx:1.27-alpineです。そして、Service payments-apiを、ポート8080(名前http)と9090(名前http-metrics)の2つで作成してください。

PodのspecにserviceAccountNameを明示して初めて、そのアイデンティティで証明書が発行されます。Serviceには、アプリケーションのポートとメトリクスのポートを一緒に開けてください。

メッシュ全体のSTRICTとポートの例外

/root/ica-security/pa-mesh.yamlにPeerAuthenticationを作成してください。名前default、ネームスペースistio-system、mtls.mode: STRICTで、selectorは置きません。続けて、/root/ica-security/pa-workload.yamlに、ネームスペースica-security、selector app: payments-api、mtls.mode: STRICTでありながら、portLevelMtlsでポート9090だけがPERMISSIVEのポリシーを作成してください。

メッシュ全体のポリシーは、ルートネームスペースに決まった名前で置き、selectorは付けません。selectorが付いた瞬間に、ワークロードのポリシーになります。例外のポートは、portLevelMtlsで狭く指定してください。

ネームスペースのデフォルト拒否を作成する

/root/ica-security/ap-deny-all.yamlにAuthorizationPolicyを作成してください。名前default-deny、ネームスペースica-security、specは空のオブジェクトです。

ルールが1つもないALLOWポリシーが、そのまま全面拒否です。specフィールド自体を抜いてはいけず、空の状態で置く必要があります。

コールグラフのとおりにだけ開ける

/root/ica-security/ap-allow-orders.yamlにAuthorizationPolicyを作成してください。selector app: payments-api、action: ALLOWで、ルール1つに、from.source.principalsはcluster.local/ns/ica-security/sa/orders-api、to.operation.methodsはPOST、to.operation.pathsは/v1/chargesです。

principalsは、トラストドメインから始まるパスの形式です。メソッドとパスは、to.operationの下に入ります。このステップでは、ServiceAccountの条件だけを使ってください。

管理用のパスをDENYでもう一重に防ぐ

/root/ica-security/ap-deny-admin.yamlにAuthorizationPolicyを作成してください。selector app: payments-api、action: DENYで、ルールはto.operation.pathsに/admin/*が1つだけで、fromは置きません。metadata.annotationsにistio.io/dry-run: "true"を付けてください。

DENYはALLOWより先に評価されるので、ALLOWのミスを防ぐセーフティネットになります。送信元とは無関係に防ぐ必要があるので、fromは置かないでください。本番に上げる前の段階なので、シャドウ評価のアノテーションも付けます。

エンドユーザーのトークンの必須化

/root/ica-security/ra-jwt.yamlにRequestAuthenticationを作成してください。名前payments-api-jwt、ネームスペースica-security、selector app: payments-api、jwtRulesは1つで、issuer https://idp.example.com/、jwksUri https://idp.example.com/.well-known/jwks.json、audiences [payments-api]です。続けて、/root/ica-security/ap-require-jwt.yamlに、名前require-jwt、同じネームスペースとselector、action: DENYのAuthorizationPolicyを作成してください。ルール1つのfrom.source.notRequestPrincipalsは["*"]、同じルールのto.operation.portsは["8080"]です。追加のfrom・to・rulesの条件とdry-runは置かないでください。ステップ6の最小権限のALLOWは、そのまま維持してください。

RequestAuthenticationはトークンの検証です。別のALLOWは既存の許可と合わさるので、必須化ではありません。JWTの主体がないリクエストをDENYで先に拒否し、対象ポートは文字列の8080に限定してください。