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

KCSA — Kubernetesセキュリティアソシエイト

アカウントを作り直しても権限が残るのはなぜか

TT Labで続きを見る

目標

実際のbearerトークンで、audience・認証・認可を分離し、SAを同じ名前で再作成したときの、古いトークンと名前に基づくバインディングの動作を比較します。

なぜ重要なのか

権限の取り消しと、認証情報の失効は、同じことではありません。アカウント名だけを見て対応が完了したと判断すると、新しい身元に既存の権限が再び適用されることがあります。 管理者の代理リクエストではなく、TokenRequest・TokenReviewと、CAを検証する実際のHTTPSのGETで確認します。 個人のk3s VMの中の、合成ConfigMapだけを使います。本番のkubeconfigや実際のシークレットを持ち込まないでください。 環境の準備に数分かかることがあります。55分のラボで、さらに必要なら期限切れの前に延長してください。VM・ファイル・トークンは、セッションが終了すると回収されます。 必要な観測だけを事前にダウンロードし、トークンの原文はダウンロードも共有もしないでください。

用意されている環境とツール

入力のJSONだけを自分で書きます。トークンの発行結果は、ヘルパーが/opt/fixtures/kcsa-identity-tokensの中に0600で保管し、原文は出力しません。 これらのファイルは個人のラボの内部の材料であり、編集したりレポートに貼り付けたりする答案ではありません。手動で発行したトークンのリクエスト上の有効期間は3時間で、サーバーのレスポンスで実際の有効期限を確認します。 内部の基準線/opt/fixtures/kcsa-identity-context.jsonと/opt/fixtures/kcsa-identity-lab-context.jsonは、ヘルパーが管理します。削除・編集しないでください。 actは、指定された変更だけを行います。captureは、観測の条件を確認したあと、/root/kcsa-identityにJSONを保存します。 observeは現在の観測だけを出力し、gradeはリソースや学習者のファイルを変更しません。TokenReviewはトークンの検証APIであり、保存型のオブジェクトの作成ではありません。

ステップ

  1. capture 1で/root/kcsa-identity/baseline.jsonを保存してください。kcsa-identityのreader SA、identity-client Pod、named-config-reader Role・RoleBindingと、lesson-policy ConfigMapのUIDを調べます。PodとSAの自動トークンマウントはfalseで、ほかのチームのkcsa-identity-otherの正常なアクセスを維持する必要があります。
  2. /root/kcsa-identity/old-request.jsonにauthentication.k8s.io/v1のTokenRequestを書いてください。spec.audiencesは["labhub-kcsa-api"]、expirationSecondsは10800、boundObjectRefはapiVersion=v1、kind=Pod、name=identity-clientと、ステップ1で観測した実際のPodのUIDです。act 2で発行し、capture 2でbound-token.jsonにハッシュ・クレーム・検証された身元を記録します。トークンの原文は出力しないでください。
  3. capture 3で/root/kcsa-identity/scoped-request.jsonを保存してください。元のbearerトークンで、自分のlesson-policyの取得が200、ほかのチームの取得が403であることを確認します。管理者の証明書や--asによるリクエストを、この証拠の代わりにはしません。
  4. /root/kcsa-identity/report-request.jsonに、ステップ2と同じTokenRequestを書いてください。ただしaudiencesを["labhub-kcsa-report"]に変更します。act 4とcapture 4でaudience.jsonを保存します。このトークンのAPIのGETは401、デフォルトのTokenReviewはfalse、レポートのaudienceを明示したTokenReviewはtrueである必要があります。
  5. /root/kcsa-identity/deny-role.jsonにrbac.authorization.k8s.io/v1のRoleを書いてください。名前はnamed-config-reader、ネームスペースはkcsa-identity、rulesは空の配列です。act 5とcapture 5でrbac-revoked.jsonを保存します。バインディングとSAのUIDは維持され、元のトークンの認証はtrue、取得は403である必要があります。
  6. /root/kcsa-identity/restore-role.jsonに同じRoleを書いてください。ただしrulesに、apiGroups=[""]、resources=["configmaps"]、resourceNames=["lesson-policy"]、verbs=["get"]のルールを1つ入れます。act 6でRoleを復元し、元のSAをUIDの前提条件付きで削除して、同じ名前で再作成します。capture 6でuid-revoked.jsonを保存してください。既存のPod・バインディングのUIDは維持され、SAのUIDは変更され、古いトークンは期限切れの前に認証false・401である必要があります。
  7. /root/kcsa-identity/new-request.jsonに、ステップ2と同じAPIのaudience・有効期間・元のPodのUIDのTokenRequestを書いてください。act 7とcapture 7でnew-identity.jsonを保存します。新しいトークンのsubは同じですがSAのUIDは異なり、既存のバインディングで取得が200、古いトークンは引き続き401である必要があります。
  8. act 8で、調べた元のRoleBindingだけを、UID・resourceVersionの前提条件を使って削除してください。capture 8で/root/kcsa-identity/closed.jsonを保存します。新しいトークンの認証true・取得403、古いトークンの401、ほかのチームの身元・ConfigMapのUIDの維持と、正常な取得200をあわせて確認します。

参考

トークンのデコードは署名の検証ではなく、管理者の--asによるリクエストは、該当するbearerトークンの受信者・有効期限の検査ではありません。 すでに進めたステップは、ファイル・構成を保存します。過去の記録を消してから、現在の状態で同じ成功の記録を作り直しません。 あとのステップに進んだあと、過去のステップは保存した時点のトークンの有効期間で検証し、現在のステップの実際の状態も別に確認します。 新しいステップを観測する前に、直前のステップの記録と入力ファイルを保存してください。ほかのチームのSA・Role・バインディング・ConfigMapと、自分のPod・Role・ConfigMapのUIDは、最後まで保存します。 ステップの準備では、ない前の入力・観測だけを作成し、現在の答えや未完成の入力は上書きしません。 ヘルパーが待機する制限に達したら、同じVMのobserveと状態を調べてください。インストールを繰り返したり、権限を広げたりして解決しません。 SAの削除は、ほかのトークンにも影響を与えることがあります。この実験を、本番での無条件の対応手順として使わないでください。 401・403は、今回の認証されたリクエストの文脈で読みます。すべての匿名リクエストが401という意味ではありません。 観測の記録は学習の資料であり、rootを統制するリモート証明や、不正行為の防止の仕組みではありません。 公式ドキュメント: ServiceAccountトークン・ 名前・ネームスペースに基づくRBAC。

基準線の身元とセキュリティ設定を調べる

capture 1で/root/kcsa-identity/baseline.jsonを保存してください。kcsa-identityのreader SA、identity-client Pod、named-config-reader Role・RoleBindingと、lesson-policy ConfigMapのUIDを調べます。PodとSAの自動トークンマウントはfalseで、ほかのチームのkcsa-identity-otherの正常なアクセスを維持する必要があります。

名前のほかに、metadata.uidを見てください。Podの自動マウントの設定と、SAの設定を、それぞれ確認します。

実際のPodのUIDにバインドしたトークンを発行する

/root/kcsa-identity/old-request.jsonにauthentication.k8s.io/v1のTokenRequestを書いてください。spec.audiencesは["labhub-kcsa-api"]、expirationSecondsは10800、boundObjectRefはapiVersion=v1、kind=Pod、name=identity-clientと、ステップ1で観測した実際のPodのUIDです。act 2で発行し、capture 2でbound-token.jsonにハッシュ・クレーム・検証された身元を記録します。トークンの原文は出力しないでください。

TokenRequestでバインドするUIDは、推測せずに、baseline.jsonのsnapshot.pod.metadata.uidから読みます。specの中には、トークンの原文は必要ありません。

実際のリクエストのネームスペースの境界

capture 3で/root/kcsa-identity/scoped-request.jsonを保存してください。元のbearerトークンで、自分のlesson-policyの取得が200、ほかのチームの取得が403であることを確認します。管理者の証明書や--asによるリクエストを、この証拠の代わりにはしません。

認証がtrueだからといって、そのリクエストを許可したという意味ではありません。実際のGETのリソース・ネームスペースを確認してください。

受信者の異なるトークンを比較する

/root/kcsa-identity/report-request.jsonに、ステップ2と同じTokenRequestを書いてください。ただしaudiencesを["labhub-kcsa-report"]に変更します。act 4とcapture 4でaudience.jsonを保存します。このトークンのAPIのGETは401、デフォルトのTokenReviewはfalse、レポートのaudienceを明示したTokenReviewはtrueである必要があります。

トークンの有効期間が残っていても、受信者が異なることがあります。レポートサーバーをデプロイする代わりに、受信者を明示した検証を比較します。

権限の取り消しと認証の維持

/root/kcsa-identity/deny-role.jsonにrbac.authorization.k8s.io/v1のRoleを書いてください。名前はnamed-config-reader、ネームスペースはkcsa-identity、rulesは空の配列です。act 5とcapture 5でrbac-revoked.jsonを保存します。バインディングとSAのUIDは維持され、元のトークンの認証はtrue、取得は403である必要があります。

Roleを削除せず、rulesだけを空にします。アカウント・バインディングのUIDが同じであるという事実と、403をあわせて見ます。

同じ名前の新しいサービスアカウント

/root/kcsa-identity/restore-role.jsonに同じRoleを書いてください。ただしrulesに、apiGroups=[""]、resources=["configmaps"]、resourceNames=["lesson-policy"]、verbs=["get"]のルールを1つ入れます。act 6でRoleを復元し、元のSAをUIDの前提条件付きで削除して、同じ名前で再作成します。capture 6でuid-revoked.jsonを保存してください。既存のPod・バインディングのUIDは維持され、SAのUIDは変更され、古いトークンは期限切れの前に認証false・401である必要があります。

Roleを無制限の権限で復元しないでください。名前が同じ新しいアカウントと、元のアカウントのUIDを比較します。

新しい身元と既存のバインディングの接続

/root/kcsa-identity/new-request.jsonに、ステップ2と同じAPIのaudience・有効期間・元のPodのUIDのTokenRequestを書いてください。act 7とcapture 7でnew-identity.jsonを保存します。新しいトークンのsubは同じですがSAのUIDは異なり、既存のバインディングで取得が200、古いトークンは引き続き401である必要があります。

バインディングのSAの主体は、名前とネームスペースです。新しいトークンの成功と、古いトークンの無効化を、同時に確認してください。

正確な権限の取り消しと影響の確認

act 8で、調べた元のRoleBindingだけを、UID・resourceVersionの前提条件を使って削除してください。capture 8で/root/kcsa-identity/closed.jsonを保存します。新しいトークンの認証true・取得403、古いトークンの401、ほかのチームの身元・ConfigMapのUIDの維持と、正常な取得200をあわせて確認します。

認証falseと、認証true・認可の失敗を区別してください。ほかのチームの正常なリクエストが、取り消しの範囲の比較基準です。