Kubernetes RBAC完全ガイド:最小権限アクセスのためのRole、ClusterRole、ServiceAccount設定

Security tutorial - IT technology blog
Security tutorial - IT technology blog

すべてが壊れかけたあの日

以前、チームのジュニアDevOpsエンジニアがCI/CDパイプラインをデプロイしたとき、ビルドPodがクラスター全体のノードを一覧表示・作成・削除できる権限を持っていた。数ヶ月間は問題なく動いていた——しかしある日、誰かが誤ってクリーンアップスクリプトを実行し、本番ワークロードが丸ごと消え去った。原因は?誰もきちんとスコープを設定しなかったため、ServiceAccountにcluster-admin権限が付与されていたのだ。

たった一つのインシデント。3時間の復旧作業。非常に気まずいポストモーテム。

この経験から、私が今や鉄則として扱っていることがある:Kubernetesでは、制限しないものは必ずいつか手痛いしっぺ返しを食らう。このガイドでは、前提知識なし、曖昧な説明なしで、RBACを正しくセットアップする方法を丁寧に解説する。

なぜ過剰な権限付与が繰り返されるのか

原因はほぼ必ず、次の三つのどれかに行き着く:

  • スピード優先のセキュリティ軽視 — その場の問題を解決するためにcluster-adminを付与する
  • オーナーシップの不明確さ — ServiceAccountが実際に何を必要としているか誰も把握していないため、すべての権限を与えてしまう
  • デフォルト設定のミス — namespace内のdefault ServiceAccountに、意図せず必要以上の権限が積み重なっていく

KubernetesはV1.6以降、RBACをデフォルトで有効にして出荷されている。しかし、有効であることと、適切に設定されていることは別物だ。APIサーバーは、Podに全namespaceのSecretへの完全な書き込みアクセスを与えるRoleBindingを、警告もプロンプトもガードレールも一切なく喜んで受け付けてしまう。

Kubernetes RBACの仕組み

RBACシステム全体は4つのオブジェクトで構成されている。まずこれを理解すれば、残りはすべて自然に繋がってくる:

  • Role — 単一のnamespace内の権限を定義する
  • ClusterRole — クラスター全体、またはnamespaceをまたいで再利用可能な権限を定義する
  • RoleBinding — Role(またはClusterRole)を、一つのnamespace内のユーザー・グループ・ServiceAccountに付与する
  • ClusterRoleBinding — ClusterRoleをクラスター全体に付与する(namespace制限なし)

ServiceAccountとは、PodがAPIサーバーに対して認証するために使うIDに過ぎない。それ単体では何もできない——Bindingを紐付けて初めて権限が存在する。SSH・Kubernetes・データベースへのアクセスをより広範に一元管理したい場合は、Teleportによるアクセス制御の集約も有効な選択肢だ。

私が最も助けられた考え方:Roleを「職務記述書」、Bindingを「署名済みの雇用契約書」と捉えるとわかりやすい。

よくある3つのパターン(そのうち2つの問題点)

パターン1:cluster-adminをそのまま使う

最も抵抗のない道。組み込みのcluster-admin ClusterRoleをServiceAccountにバインドして、さっさと仕事を進める。

# 本番環境ではやらないこと
kubectl create clusterrolebinding my-app-admin \
  --clusterrole=cluster-admin \
  --serviceaccount=production:my-app

クラスターへの完全な制御権を持つ。何でも作成・削除・変更できる。目の前の問題は解決できるが、そのPodにexecできる人間はクラスターを実質的に所有することになる。コンテナが一つ侵害されれば、ゲームオーバーだ。Linuxのsudoersで全員にroot権限を付与するのと同じ過ちを、Kubernetesで繰り返しているに過ぎない。

パターン2:組み込みClusterRoleを使う

Kubernetesには約50の組み込みRoleが同梱されている。汎用的なものとしてはvieweditadminが使いやすい。cluster-adminよりはマシだが、それでも大半のワークロードが必要とする範囲より広い。

# マシだが、大抵はまだ広すぎる
kubectl create rolebinding my-app-viewer \
  --clusterrole=view \
  --serviceaccount=production:my-app \
  --namespace=production

viewはほとんどのリソースへの読み取り専用アクセスを付与する。editは書き込みアクセスも追加される。クイックセットアップには便利だが——アプリが触れることのないリソースまで一括でまとめてしまっており、不要な権限はすべてリスクになる。

パターン3:最小権限によるカスタムRole

事前の作業量は増える。それでも毎回やる価値がある。

どのverb(getlistwatchcreateupdatedelete)が、どのリソースに、どのnamespaceで適用されるかを正確に定義する。それ以上は何も追加しない。何か問題が起きたとき——そして問題は必ずいつか起きる——爆発半径を実際に制限できる唯一のアプローチがこれだ。

RBACセットアップ:ステップバイステップ

具体的な例として、PodとNodeのメトリクスを読み取るが、何も変更すべきでない監視アプリケーションを使って説明しよう。

ステップ1:専用のNamespaceとServiceAccountを作成する

kubectl create namespace monitoring
kubectl create serviceaccount metrics-reader -n monitoring

実際のワークロードにdefault ServiceAccountを使わないこと。専用のServiceAccountを使えば、監査がシンプルになり、権限のスコープが明確になる——何が何にバインドされているか常に把握できる。

ステップ2:最小権限のRoleを定義する

Nodeのメトリクスを読み取るにはクラスター全体へのアクセスが必要なため、namespace単位のRoleではなくClusterRoleが必要になる:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: metrics-reader
rules:
  - apiGroups: [""]
    resources: ["pods", "nodes", "nodes/metrics"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["metrics.k8s.io"]
    resources: ["pods", "nodes"]
    verbs: ["get", "list"]
kubectl apply -f metrics-reader-clusterrole.yaml

注目すべき点が三つある:

  • apiGroups: [""] はコアAPIグループ(Pod、Node、Service、ConfigMapなど)を対象にしている
  • getlistwatchのみ——createupdatedeleteは含まない
  • リソースはワイルドカードではなく明示的に列挙されている

ステップ3:RoleをServiceAccountにバインドする

Nodeのメトリクスにはクラスター全体の可視性が必要なため、ここではClusterRoleBindingが適切だ:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: metrics-reader-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: metrics-reader
subjects:
  - kind: ServiceAccount
    name: metrics-reader
    namespace: monitoring
kubectl apply -f metrics-reader-binding.yaml

ステップ4:PodにServiceAccountを割り当てる

apiVersion: v1
kind: Pod
metadata:
  name: metrics-collector
  namespace: monitoring
spec:
  serviceAccountName: metrics-reader
  containers:
    - name: collector
      image: your-metrics-image:latest

ステップ5:ServiceAccountが実際にできることを確認する

何かが壊れてから首を傾けるのではなく、デプロイ前にこれを実行しよう:

# 「yes」が返るべき
kubectl auth can-i list pods \
  --as=system:serviceaccount:monitoring:metrics-reader

# 「no」が返るべき
kubectl auth can-i delete pods \
  --as=system:serviceaccount:monitoring:metrics-reader

# 権限セット全体を確認する
kubectl auth can-i --list \
  --as=system:serviceaccount:monitoring:metrics-reader

二番目のコマンドがyesを返した場合、Roleの権限が広すぎる。ポストモーテムの場で修正するのではなく、今すぐ直そう。

ステップ6:不要な場合はオートマウントを無効にする

KubernetesはデフォルトですべてのPodにServiceAccountトークンを自動マウントする。PodがKubernetes APIを直接呼び出さないなら、そのトークンはファイルシステム上に置かれた攻撃対象面に過ぎない:

apiVersion: v1
kind: ServiceAccount
metadata:
  name: metrics-reader
  namespace: monitoring
automountServiceAccountToken: false

あるいは、Pod単位で無効にすることもできる:

spec:
  automountServiceAccountToken: false

ディスク上にトークンがなければ、Role Bindingが存在していてもAPIサーバーへの認証手段がなくなる。多層防御の考え方だ。

見落としがちなよくあるミス

  • リソースやverbへのワイルドカード使用resources: ["*"]はすべてへのアクセスを付与する。それは実質的にcluster-adminと同じだ。
  • RoleBindingで十分なところにClusterRoleBindingを使う — アプリが一つのnamespaceしか触らないなら、RoleそのものがClusterRoleであってもBindingはそのnamespaceに限定すること
  • 既存のBindingを定期監査しないkubectl get clusterrolebindings -o wideを定期実行しよう。権限の肥大化は静かに、着実に進む。Lynisによる自動セキュリティ監査と組み合わせれば、インフラ全体の設定ドリフトを継続的に把握できる
  • auth can-iチェックをスキップする — 5分の確認作業は、2時間のインシデント対応より遥かに安上がりだ

ServiceAccountクレデンシャルについて

(Podだけでなく)人間のユーザー向けにクラスターアクセスをセットアップする際は、クレデンシャルを含むkubeconfigファイルを生成することになる。トークンやパスワードにはtoolcraft.app/ja/tools/security/password-generatorのジェネレーターを使っている——完全にブラウザ上で動作するため、サーバーには何も送信されない。セキュリティ関連のものはクライアントサイドのみで処理されることが重要だ。

RBAC設定の監査

定期的な監査を習慣にしよう。次の3つのコマンドで必要な内容のほとんどをカバーできる:

# cluster-adminを持つすべてのServiceAccountを検索する
kubectl get clusterrolebindings -o json | \
  jq '.items[] | select(.roleRef.name=="cluster-admin") | \
  {name: .metadata.name, subjects: .subjects}'

# namespace内のすべてのRoleBindingを一覧表示する
kubectl get rolebindings -n production -o wide

# 機密性の高い操作を実行できるユーザーを確認する
kubectl auth can-i create pods --all-namespaces --list

大規模なクラスターでは、rbac-lookupkubectl-who-canを使うと、この作業がかなり楽になる。静的な監査だけでなく、実行時の異常な権限行使を検知したい場合は、KubernetesにFalcoをデプロイすることで、RBACの設定だけでは防げないランタイムの脅威をカバーできる。

原則を実践に落とし込む

最小権限はチェックボックスではない。すべてのデプロイに組み込んでいく習慣だ。

新しいワークロードを追加するたびに問いかけよう:このPodが実際に呼び出す必要があるのは何か?まず何も与えない。何かが壊れたとき、その理由を明確に正当化できる場合にのみ権限を追加する。上記の監視の例はシンプルだが、実際のアプリケーションはもっと複雑になる。CRDを管理するOperator、namespace横断でデプロイするパイプライン、Secretを読み取るAdmission Webhook。アプローチはどんな場合でも変わらない。

本番クラスターのKubernetesセキュリティ調査において、RBACの設定ミスは常に上位の問題として挙げられる——ツールが難しいからではなく、最初から正しくやる時間を誰も取らなかったからだ。私のチームが経験したあのポストモーテムは、防げたはずだった。あなたのチームも防げる。

Share: