PERMISSIVEからSTRICTまで押し上げる
目標
mTLSポリシーの3つの範囲と優先順位を実際に手で扱い、稼働中のメッシュを切らずにSTRICTへ引き上げる手順を、計画として残します。
なぜ重要なのか
STRICTへの切り替えは、技術的にはフィールド1つを変える作業ですが、運用では最も事故の多い作業の1つです。理由は単純で、平文で入ってきていた経路が、予告なしにすべて切れるからです。サイドカーのないcron、メッシュの外のバッチ、カスタムプローブは、ダッシュボードにあまり現れず、STRICTを有効にした瞬間に障害として表面化します。そのため定石は、PERMISSIVEのままにして、平文トラフィックの送信元を全数調査したうえで、狭い範囲から引き上げることです。
もう1つ、必ず身につけるべきなのが方向の区別です。PeerAuthenticationは受け取る側(インバウンド)を、DestinationRuleのtrafficPolicy.tlsは送る側(アウトバウンド)を扱います。この2つがずれると接続が失敗しますが、アプリケーションのログには単なる503としか見えません。しかも、この組み合わせはistioctl analyzeが検出してくれません。以前は専用のアナライザーがありましたが、今のバージョンのアナライザー一覧にはありません。そのため、2つのリソースをセットで並べて、人が自分で突き合わせる習慣が、そのままセーフティネットになります。
このラボ環境では実際のハンドシェイクが起こらないため、採点はポリシーの名前・範囲・フィールドと、静的分析の結果を見ます。
ステップ
開始前の準備: ラボのPodはラボごとに新しく起動するため、前のラボのメッシュ設定は残っていません。kubectl get crd peerauthentications.security.istio.ioの結果が空なら、istioctl manifest generate --set profile=minimal > /root/istio/manifest.yamlを実行したあと、kubectl apply -f /root/istio/manifest.yamlを2回実行してください。このマニフェストはistio-systemネームスペースも一緒に作りますが、ステップ2でメッシュ全体のポリシーを置く場所なので、必ず存在している必要があります。そのあと、kubectl create ns mesh-lab && kubectl label ns mesh-lab istio-injection=enabledで作業用のネームスペースを準備してください。
/root/istio/mtls/pa-permissive.yamlを作成してください。ドキュメントが1つだけのPeerAuthenticationで、metadata.nameはdefault、metadata.namespaceはmesh-lab、spec.mtls.modeはPERMISSIVEで、selectorは置かないでください(ネームスペース全体のポリシーです)。作成したら適用してください。istio-systemネームスペースに、名前がdefaultのPeerAuthenticationをspec.mtls.mode: STRICTで、selectorなしで適用してください。そして、/root/istio/mtls/out/scope-note.txtに、メッシュ全体・ネームスペース・ワークロードの3つの層のうち、より狭く具体的な範囲が優先されるというルールを書いてください。mesh-labにpaymentsワークロードを作成してください。ServiceAccountpayments、Servicepayments(ポート名httpは8080、http-metricsは9090)、Deploymentpayments(Podラベルapp: payments、serviceAccountName: payments)です。そのあと、PeerAuthenticationlegacy-metricsをmesh-labに作成してください。selector.matchLabels.appはpayments、spec.mtls.modeはSTRICTで、portLevelMtlsにはポート9090だけをmode: DISABLEで置いてください。mesh-labにDestinationRulepayments-mtlsを作成してください。spec.hostはpayments、spec.trafficPolicy.tls.modeはISTIO_MUTUALです。そして、/root/istio/mtls/out/direction-note.txtに、PeerAuthenticationは受け取る側(インバウンド)を、DestinationRuleは送る側(クライアント/アウトバウンド)を扱うという違いを書いてください。/root/istio/mtls/identity.txtに、paymentsワークロードのSPIFFEアイデンティティをちょうど1行で書いてください:spiffe://cluster.local/ns/mesh-lab/sa/payments。そして、/root/istio/mtls/out/identity-note.txtに、メッシュの身元はIPアドレスではなくサービスアカウントであるという要点を書いてください。mesh-labにRequestAuthenticationjwt-paymentsを作成してください。selector.matchLabels.appはpayments、jwtRules[0].issuerはhttps://idp.labhub.example/、jwtRules[0].jwksUriはhttps://idp.labhub.example/.well-known/jwks.jsonです。そして、/root/istio/mtls/out/jwt-note.txtに、このリソースだけではトークンのないリクエストを防げず、AuthorizationPolicyが一緒に必要であることを書いてください。mesh-labのdefaultPeerAuthenticationをSTRICTに変えて適用してください。その状態で、spec.trafficPolicy.tls.mode: DISABLEのDestinationRule(例:payments-plaintext)を適用し、istioctl analyze -n mesh-labの結果を/root/istio/mtls/out/analyze-conflict.txtに保存してください。このバージョンのアナライザー一覧には、mTLSの組み合わせの競合を見るアナライザーがないため、出力にその競合は現れません。何と何がずれているのかを、1行で追記して記録に残してください。そのあと、そのDestinationRuleを削除するかISTIO_MUTUALに直して競合をなくし、もう一度分析して/root/istio/mtls/out/analyze-fixed.txtに保存してください。mesh-labにtls.mode: DISABLEのDestinationRuleが1つも残ってはいけません。/root/istio/mtls/migration.mdを400バイト以上で書いてください。文書の中で、PERMISSIVEと観測(メトリクス/モニタリング)の話が、STRICTという単語よりも先(上の行)に出てくる必要があり、ネームスペース単位の段階的な適用と、ロールバック(元に戻す)方法を含める必要があります。最後に、mesh-labのdefaultPeerAuthenticationがSTRICTの状態で締めくくってください。
参考
- ラボのPodはラボごとに新しく起動するため、前のラボのクラスター状態は残っていません。そのため、メッシュ設定をマニフェストとして残しておくことが、そのまま再現可能性になります。セキュリティポリシーは特にそうです。手で有効にしたSTRICTは、クラスターが消えると一緒に消えますが、リポジトリにあるポリシーは、次のクラスターでもそのまま再現されます。
- ステップ8で、文書のタイトルに
STRICTを先に書くと、順序の検査に引っかかります。タイトルは「mTLS切り替え計画」のようにして、本文を、ステップ0(観測、PERMISSIVEを維持)→ ステップ1(平文の送信元の除去)→ ステップ2(ネームスペース単位のSTRICT)→ ステップ3(メッシュ全体のSTRICT)→ ロールバックの順に書いてください。 - 実際の運用では、平文の割合を
istio_requests_totalのconnection_security_policyラベルで見ます。この環境にはメトリクスがありませんが、計画書には、その判断の根拠を書いておくのが適切です。 - ステップ3のDeploymentは、
/opt/lab/fixtures/istio/inject-target.yamlをコピーして、serviceAccountNameだけを追加すると楽です。 - よくある間違い1: ネームスペース全体のポリシーにselectorを付けること。selectorが付いた瞬間にワークロード単位のポリシーになり、残りのワークロードには適用されません。
- よくある間違い2: メッシュ全体のポリシーを、任意のネームスペースに置くこと。ルートネームスペース(
istio-system)に、defaultという名前で置いて初めて、メッシュ全体のポリシーになります。
ネームスペースの既定ポリシーをPERMISSIVEにする
/root/istio/mtls/pa-permissive.yamlを作成してください。ドキュメントが1つだけのPeerAuthenticationで、metadata.nameはdefault、metadata.namespaceはmesh-lab、spec.mtls.modeはPERMISSIVEで、selectorは置かないでください(ネームスペース全体のポリシーです)。作成したら適用してください。
ネームスペース全体にかかるポリシーは、名前が決まっていて、selectorを置きません。切り替えは、平文と暗号文の両方を受け付ける状態から始めます。
メッシュ全体の既定値をSTRICTに引き上げる
istio-systemネームスペースに、名前がdefaultのPeerAuthenticationをspec.mtls.mode: STRICTで、selectorなしで適用してください。そして、/root/istio/mtls/out/scope-note.txtに、メッシュ全体・ネームスペース・ワークロードの3つの層のうち、より狭く具体的な範囲が優先されるというルールを書いてください。
メッシュ全体のポリシーは、ルートネームスペースに置きます。そして、3つの層の範囲のうち、どれが勝つのかをメモにまとめてください。
1つのワークロードの1つのポートだけを例外として開ける
mesh-labにpaymentsワークロードを作成してください。ServiceAccount payments、Service payments(ポート名httpは8080、http-metricsは9090)、Deployment payments(Podラベルapp: payments、serviceAccountName: payments)です。そのあと、PeerAuthentication legacy-metricsをmesh-labに作成してください。selector.matchLabels.appはpayments、spec.mtls.modeはSTRICTで、portLevelMtlsにはポート9090だけをmode: DISABLEで置いてください。
例外は、できるだけ狭くします。ワークロードはselectorで選び、既定はSTRICTのままにして、問題のあるポートだけを別に指定します。
送る側のmTLSを宣言する
mesh-labにDestinationRule payments-mtlsを作成してください。spec.hostはpayments、spec.trafficPolicy.tls.modeはISTIO_MUTUALです。そして、/root/istio/mtls/out/direction-note.txtに、PeerAuthenticationは受け取る側(インバウンド)を、DestinationRuleは送る側(クライアント/アウトバウンド)を扱うという違いを書いてください。
受け取る側と送る側は、別のリソースが扱います。証明書を直接指定せず、メッシュが発行したものを使うモードが別にあります。
ワークロードの身元を書いてみる
/root/istio/mtls/identity.txtに、paymentsワークロードのSPIFFEアイデンティティをちょうど1行で書いてください: spiffe://cluster.local/ns/mesh-lab/sa/payments。そして、/root/istio/mtls/out/identity-note.txtに、メッシュの身元はIPアドレスではなくサービスアカウントであるという要点を書いてください。
3つの要素が順番に入ります。そして、その身元の根拠となるオブジェクトが、クラスターに実際に存在している必要があります。
エンドユーザーのJWT検証ポリシーを作成する
mesh-labにRequestAuthentication jwt-paymentsを作成してください。selector.matchLabels.appはpayments、jwtRules[0].issuerはhttps://idp.labhub.example/、jwtRules[0].jwksUriはhttps://idp.labhub.example/.well-known/jwks.jsonです。そして、/root/istio/mtls/out/jwt-note.txtに、このリソースだけではトークンのないリクエストを防げず、AuthorizationPolicyが一緒に必要であることを書いてください。
署名を検証するには、発行者と公開鍵の場所の両方が必要です。そして、このリソースだけでは何を防げないのかを、メモに書いてください。
STRICTとDISABLEの競合を作って解消する
mesh-labのdefault PeerAuthenticationをSTRICTに変えて適用してください。その状態で、spec.trafficPolicy.tls.mode: DISABLEのDestinationRule(例: payments-plaintext)を適用し、istioctl analyze -n mesh-labの結果を/root/istio/mtls/out/analyze-conflict.txtに保存してください。このバージョンのアナライザー一覧には、mTLSの組み合わせの競合を見るアナライザーがないため、出力にその競合は現れません。何と何がずれているのかを、1行で追記して記録に残してください。そのあと、そのDestinationRuleを削除するかISTIO_MUTUALに直して競合をなくし、もう一度分析して/root/istio/mtls/out/analyze-fixed.txtに保存してください。mesh-labにtls.mode: DISABLEのDestinationRuleが1つも残ってはいけません。
受け取る側が暗号化を要求しているのに、送る側が平文に固執すると、接続は失敗します。分析結果は、直す前と直した後に分けて残し、ずれた設定は1つも残さないでください。
段階的な切り替え計画を書いて切り替えを終える
/root/istio/mtls/migration.mdを400バイト以上で書いてください。文書の中で、PERMISSIVEと観測(メトリクス/モニタリング)の話が、STRICTという単語よりも先(上の行)に出てくる必要があり、ネームスペース単位の段階的な適用と、ロールバック(元に戻す)方法を含める必要があります。最後に、mesh-labのdefault PeerAuthenticationがSTRICTの状態で締めくくってください。
文書の中で単語が出てくる順序が、そのまま段階の順序です。観測とPERMISSIVEを先に、STRICTを後に書き、元に戻す方法も入れてください。