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

CKS — Kubernetesセキュリティスペシャリスト

RBACを狭めトークンを遮断する

TT Labで続きを見る

目標

クラスターで過剰な権限を持つロールを自分で探し出し、最小権限のRoleに置き換えたうえで、その結果をAPIサーバーに問い合わせて確認します。ServiceAccountトークンがPodに自動で入る経路もすべて閉じます。

なぜ重要なのか

RBACは「誰が何をできるか」を決めますが、それを人が読んで検証するのは非常に困難です。ClusterRole 1つにresources: ["*"]が混ざっていると、そのロールは今後追加されるCRDまで全部含むことになり、その事実に誰も気づきません。そのため、ハードニングの実際の作業は、「ポリシーをうまく書くこと」ではなく、「いま何が許可されているかを一覧にすること」から始まります。

2つ目の軸は認証情報です。Podはデフォルトでトークンを受け取ります。アプリケーションがAPIを使わなくても受け取ります。Podが1つ破られると、攻撃者はすぐにクラスターの認証情報を1つ手に入れることになります。automountServiceAccountToken: falseは1行ですが、侵害時に被害が広がる範囲を大きく減らします。

ステップ

  1. ネームスペースcks-rbacを作成し、その中にServiceAccountreport-saを作成してください。
  2. クラスター内のすべてのClusterRoleのうち、rules[].verbsに*が含まれるものの名前だけを選んで辞書順に並べ替え、/root/cks-rbac-hardening/wildcard-roles.txtに1行に1つずつ保存してください。
  3. ClusterRolecks-pod-readerを作成してください。apiGroupsはコアグループ(空文字列)1つ、resourcesはpodsとpods/log、verbsはget・list・watchの3つです。どのフィールドにも*があってはいけません。
  4. クラスター内のすべてのClusterRoleのうち、escalate、bind、impersonateのいずれか1つでもverbsに持つものの名前だけを並べ替えて、/root/cks-rbac-hardening/danger-verb-roles.txtに保存してください。
  5. cks-rbacネームスペースに、RoleBindingreport-sa-pod-readerでreport-saにClusterRolecks-pod-readerをバインドしてください。そのあと、次の2つの質問の答え(yesまたはno)を順番に/root/cks-rbac-hardening/can-i.txtに2行で保存します。1行目はreport-saがcks-rbacでpodsをlistできるか、2行目はreport-saがcks-rbacでsecretsをgetできるかです。
  6. cks-rbacにPodreport(イメージnginx:1.27-alpine)を作成してください。serviceAccountNameはreport-sa、PodスペックのautomountServiceAccountTokenはfalseです。
  7. cks-rbacにSecretreport-sa-tokenを作成してください。タイプはkubernetes.io/service-account-token、アノテーションkubernetes.io/service-account.nameの値はreport-saです。さらに、Podtoken-app(イメージnginx:1.27-alpine、serviceAccountNamereport-sa)を作成し、トークンはprojectedボリュームで受け取ってください。ボリューム名はapi-token、serviceAccountTokenソースのaudienceはapi、expirationSecondsは3600、pathはtokenです。
  8. cks-rbacのdefaultServiceAccountにautomountServiceAccountToken: falseを設定し、defaultのSAがこのネームスペースでpodsをlistできないこと(no)を、/root/cks-rbac-hardening/default-sa-can-i.txtに1行で保存してください。

参考

ネームスペースと専用のServiceAccount

ネームスペースcks-rbacを作成し、その中にServiceAccountreport-saを作成してください。

kubectl create serviceaccountで作成します。ネームスペースを先に作成しておかないと、SAの作成は失敗します。

ワイルドカードのClusterRoleを探し出す

クラスター内のすべてのClusterRoleのうち、rules[].verbsに*が含まれるものの名前だけを選んで辞書順に並べ替え、/root/cks-rbac-hardening/wildcard-roles.txtに1行に1つずつ保存してください。

kubectl get clusterrole -o jsonをjqで走査し、rulesのうちverbsに*が含まれるものを選びます。結果は名前だけを、1行に1つずつ保存します。

絞り込んだClusterRoleを書く

ClusterRolecks-pod-readerを作成してください。apiGroupsはコアグループ(空文字列)1つ、resourcesはpodsとpods/log、verbsはget・list・watchの3つです。どのフィールドにも*があってはいけません。

kubectl create clusterrole --resource= --verb=でひな形を作れます。ワイルドカードは、apiGroups・resources・verbsのどこにも使いません。

危険な動詞を持つロールを選び出す

クラスター内のすべてのClusterRoleのうち、escalate、bind、impersonateのいずれか1つでもverbsに持つものの名前だけを並べ替えて、/root/cks-rbac-hardening/danger-verb-roles.txtに保存してください。

escalate、bind、impersonateの3つの動詞のうち、どれか1つでも持つClusterRoleが対象です。ステップ2と同じ方法で、名前だけを並べ替えて保存します。

auth can-iで結果を確認する

cks-rbacネームスペースに、RoleBindingreport-sa-pod-readerでreport-saにClusterRolecks-pod-readerをバインドしてください。そのあと、次の2つの質問の答え(yesまたはno)を順番に/root/cks-rbac-hardening/can-i.txtに2行で保存します。1行目はreport-saがcks-rbacでpodsをlistできるか、2行目はreport-saがcks-rbacでsecretsをgetできるかです。

--as=system:serviceaccount:<네임스페이스>:<이름>(プレースホルダーはネームスペースとServiceAccountの名前です)の形式で、サブジェクトになりすまします。出力はyesまたはnoの1行だけなので、そのままファイルに集めれば構いません。

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

cks-rbacにPodreport(イメージnginx:1.27-alpine)を作成してください。serviceAccountNameはreport-sa、PodスペックのautomountServiceAccountTokenはfalseです。

automountServiceAccountTokenはPodスペックの最上位(specの直下)のフィールドです。コンテナの中ではありません。

手動のトークンSecretとprojectedトークン

cks-rbacにSecretreport-sa-tokenを作成してください。タイプはkubernetes.io/service-account-token、アノテーションkubernetes.io/service-account.nameの値はreport-saです。さらに、Podtoken-app(イメージnginx:1.27-alpine、serviceAccountNamereport-sa)を作成し、トークンはprojectedボリュームで受け取ってください。ボリューム名はapi-token、serviceAccountTokenソースのaudienceはapi、expirationSecondsは3600、pathはtokenです。

手動のトークンSecretは、タイプがkubernetes.io/service-account-tokenで、kubernetes.io/service-account.nameアノテーションで持ち主を指定します。もう一方は、PodのprojectedボリュームにserviceAccountTokenソースを置き、audienceと有効期限を付けます。

default ServiceAccountを無力化する

cks-rbacのdefaultServiceAccountにautomountServiceAccountToken: falseを設定し、defaultのSAがこのネームスペースでpodsをlistできないこと(no)を、/root/cks-rbac-hardening/default-sa-can-i.txtに1行で保存してください。

kubectl patch serviceaccount default -n <네임스페이스> -p '...'(プレースホルダーはネームスペースです)で、1行で終わります。そのあと、defaultのSAが本当に何の権限も持たないことをcan-iで確認してください。