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

Kubernetes運用実務

トークンの寿命とオーディエンスを扱う

TT Labで続きを見る

目標

Podのアイデンティティがどのように発行され、どのような有効期間と対象を持つのかを自分で確認し、不要なトークンはそもそも渡さない設定を適用します。

なぜ重要なのか

コンテナが突破されたとき、攻撃者が最初に探すのが/var/run/secrets/kubernetes.io/serviceaccountのトークンです。そのため、このモジュールの核心となる問いは2つあります。このPodは本当にトークンが必要なのか、そしてそのトークンはいつ期限切れになるのか。1.24より前の方式は、どちらの問いにも悪い答えを持っていました。アカウントを作成すると有効期限のないトークンが自動的に作られ、Podは必要かどうかにかかわらず、それをマウントされていました。現在は、Podが受け取るトークンが有効期間を持ち、kubeletが更新し、automountServiceAccountToken: falseでそもそもオフにすることもできます。そこにaudienceが加わると、トークンが「どこで使うのか」まで含まれ、Vaultに渡すために発行したトークンがほかの場所で再利用されるのを防ぎます。この仕組みがそのままクラウドのワークロードアイデンティティにつながり、静的なキーそのものをなくす設計になります。最後に忘れないでください。アイデンティティを作っても、権限が生じるわけではありません。権限は依然としてRBACが決めます。

ステップ

  1. ネームスペースsa-labを作成し、その中にServiceAccountpaymentsを作成してください。このアカウントには、自動生成されたSecretが付いていてはいけません。そして/root/ops/sa/out/sa-note.txtに、最近はなぜSecretが自動的に作られないのかを1行で書いてください(1.24以降に短期トークンへ変わった背景や、期限切れ・有効期間のような表現が含まれている必要があります)。
  2. sa-labにPodpayments-apiを作成し、spec.serviceAccountNameをpaymentsに指定してください。デフォルト設定なら、サービスアカウントのトークンを含むprojectedボリュームが自動的に付きます。
  3. ServiceAccountlockedをautomountServiceAccountToken: falseで作成してください。そしてPodhardenedを作成し、spec.serviceAccountNameはlocked、spec.automountServiceAccountTokenはfalseにしてください。このPodには、トークンボリュームが1つも付いていてはいけません。
  4. paymentsアカウントのトークンを、有効期間1時間(3600秒)、対象vaultで発行し、そのJWTのペイロードをデコードしたJSONを/root/ops/sa/out/token.jsonに保存してください。このJSONでは、subがsystem:serviceaccount:sa-lab:paymentsであり、audが空でなく、expとiatの差が約3600秒で、kubernetes.ioクレームが含まれている必要があります。
  5. sa-labにPodprojected-demoを作成してください。spec.serviceAccountNameはpaymentsにし、projectedボリュームにserviceAccountTokenソースを置いて、audience: vault、expirationSeconds: 3600、path: vault-tokenを指定してください。1つ目のコンテナは、このボリュームを/var/run/secrets/vaultのパスにマウントする必要があります。
  6. sa-labにSecretpayments-legacy-tokenを作成してください。typeはkubernetes.io/service-account-token、アノテーションkubernetes.io/service-account.nameはpaymentsです。そして/root/ops/sa/out/legacy-note.txtに、この方式がなぜ推奨されないのかを、60バイト以上、2文程度で書いてください(有効期限がないという点と、取り消し・ローテーションの負担が含まれている必要があります)。
  7. paymentsアカウントには、最小権限だけを与えてください。sa-labでConfigMapをgetとlistできて、Secretを読み取ったりConfigMapを削除したりはできない状態にする必要があります。権限は、paymentsをサブジェクトとするRoleBindingで紐づけてください。
  8. /root/ops/sa/out/sa-audit.jsonを作成してください。podsは配列で、sa-labの実際のPodの数と正確に一致している必要があります。各要素は、name、service_account、automountの3つのフィールドを持ちます。payments-apiのservice_accountはpaymentsで、hardenedのautomountはfalseで、service_accountがdefaultであるPodは1つもあってはいけません。最後に、recommendations配列に改善の提案を2つ以上書いてください。

参考

専用のサービスアカウントを作成する

ネームスペースsa-labを作成し、その中にServiceAccountpaymentsを作成してください。このアカウントには、自動生成されたSecretが付いていてはいけません。そして/root/ops/sa/out/sa-note.txtに、最近はなぜSecretが自動的に作られないのかを1行で書いてください(1.24以降に短期トークンへ変わった背景や、期限切れ・有効期間のような表現が含まれている必要があります)。

最近のバージョンでは、アカウントを作成してもSecretはついてきません。なぜそのように変わったのかを、1行でまとめておいてください。

Podに専用のアカウントを指定する

sa-labにPodpayments-apiを作成し、spec.serviceAccountNameをpaymentsに指定してください。デフォルト設定なら、サービスアカウントのトークンを含むprojectedボリュームが自動的に付きます。

Podの仕様でアカウントを指定するフィールドの名前を確認してください。指定すると、トークンボリュームが自動的に付きます。

トークンの自動マウントをオフにする

ServiceAccountlockedをautomountServiceAccountToken: falseで作成してください。そしてPodhardenedを作成し、spec.serviceAccountNameはlocked、spec.automountServiceAccountTokenはfalseにしてください。このPodには、トークンボリュームが1つも付いていてはいけません。

同じフィールドを、Podにもアカウントにも書けます。オフにしたなら、ボリューム一覧にトークンが残っていてはいけません。

短期トークンを発行してペイロードを読み解く

paymentsアカウントのトークンを、有効期間1時間(3600秒)、対象vaultで発行し、そのJWTのペイロードをデコードしたJSONを/root/ops/sa/out/token.jsonに保存してください。このJSONでは、subがsystem:serviceaccount:sa-lab:paymentsであり、audが空でなく、expとiatの差が約3600秒で、kubernetes.ioクレームが含まれている必要があります。

JWTはドットで区切られた3つの要素で、真ん中がペイロードです。base64urlなので、パディングを合わせないとデコードできません。有効期間は、2つの時刻の差で確認してください。

対象が指定されたprojectedトークンを使う

sa-labにPodprojected-demoを作成してください。spec.serviceAccountNameはpaymentsにし、projectedボリュームにserviceAccountTokenソースを置いて、audience: vault、expirationSeconds: 3600、path: vault-tokenを指定してください。1つ目のコンテナは、このボリュームを/var/run/secrets/vaultのパスにマウントする必要があります。

トークンに対象を埋め込むと、その対象でのみ有効です。ファイル名とマウントパスを正確に合わせてください。

レガシーな長期トークンのSecretを作成してリスクを整理する

sa-labにSecretpayments-legacy-tokenを作成してください。typeはkubernetes.io/service-account-token、アノテーションkubernetes.io/service-account.nameはpaymentsです。そして/root/ops/sa/out/legacy-note.txtに、この方式がなぜ推奨されないのかを、60バイト以上、2文程度で書いてください(有効期限がないという点と、取り消し・ローテーションの負担が含まれている必要があります)。

特定のタイプとアノテーションがあって初めて、コントローラーがトークンを埋めてくれます。この方式の本当の問題は、便利さではなく有効期限がないという点です。

アカウントに最小権限だけを与える

paymentsアカウントには、最小権限だけを与えてください。sa-labでConfigMapをgetとlistできて、Secretを読み取ったりConfigMapを削除したりはできない状態にする必要があります。権限は、paymentsをサブジェクトとするRoleBindingで紐づけてください。

アカウントを作成することと、権限を与えることは別です。必要なものだけを開き、残りは確認して塞がれているか検証してください。

アカウントの使用状況を監査する

/root/ops/sa/out/sa-audit.jsonを作成してください。podsは配列で、sa-labの実際のPodの数と正確に一致している必要があります。各要素は、name、service_account、automountの3つのフィールドを持ちます。payments-apiのservice_accountはpaymentsで、hardenedのautomountはfalseで、service_accountがdefaultであるPodは1つもあってはいけません。最後に、recommendations配列に改善の提案を2つ以上書いてください。

Podの数は、実際のクラスターと正確に一致している必要があります。defaultアカウントを使うPodが1つでもあってはいけません。