金色の配線パターンが広がる基板の接写
現場の実践

Kubernetes RBACの落とし穴 複数チーム共用クラスタで権限が漏れる原因

目次を見る

複数のプロダクトチームで1つのKubernetesクラスタを共用している運用担当者に向けた内容です。新しいチームがオンボーディングするたびにRBAC(Role-Based Access Control、役割に基づくアクセス制御の仕組み)の設定を書いているなら、どこかで権限が想定より広がっていないか確認する機会にしてもらえればと思います。

監査で「他チームのワークロードに触れないことを証明してほしい」と言われて、即答できる設定になっているでしょうか。実はRBACは加算方式のため、設定ファイルを1つずつ見ても気づきにくい権限漏れが起こります。

何が起きるか

共用クラスタでよくあるのは、あるチームに割り当てたはずの権限が、気づかないうちにクラスタ全体に及んでしまう事象です。

典型例は、新しいチームをオンボーディングする際に、既存のRoleBinding(権限をnamespace内の対象者に割り当てる設定)をコピーして名前だけ変えるやり方です。コピー元がClusterRoleBinding(クラスタ全体に権限を割り当てる設定)だった場合、新チームにもクラスタ全体の権限が渡ってしまいます。

もう1つの典型例は、Podが明示的にServiceAccount(PodやCIパイプラインがAPIサーバーと通信するための識別情報)を指定しなかった場合です。namespaceのdefaultServiceAccountが自動的に使われ、そのnamespaceに強めの権限を与えていると、意図しないPodまで強い権限を持つことになります。

影響範囲は、気づいた時点ではすでに本番稼働中のPodやCIパイプラインに及んでいることが多く、監査直前に発覚すると是正に時間を取られます。権限の棚卸しを後回しにしていると、障害対応やセキュリティインシデントの根本原因調査でも「誰が何に触れたか」の特定に余計な時間がかかります。

なぜ起きるか

原因を分解すると、Kubernetes RBACが4種類のオブジェクトの組み合わせでできていることの理解不足に行き着きます。

Roleはnamespaceに閉じた権限の定義です。一方ClusterRoleはクラスタスコープのオブジェクトですが、ルールの適用範囲はバインド方法次第で変わります。

ここが混乱のもとです。ClusterRoleをRoleBindingで紐付ければ、そのnamespace内だけに権限が限定されます。逆にClusterRoleBindingで紐付けると、クラスタ全体に権限が広がります。

つまり同じClusterRoleでも、どちらのBindingと組み合わせるかで結果がまったく違います。この区別を意識せずコピー&ペーストすると、スコープごと複製してしまう事故が起きます。

さらに、権限を受け取る側(subject)にはUser「Group「ServiceAccountの3種類があります。UserとGroupはKubernetesのオブジェクトではなく、クライアント証明書やOIDC(OpenID Connect、外部IDプロバイダと連携する認証の仕組み)トークンのクレームから認証器がマッピングする概念上の存在です。

この違いを理解していないと、IDプロバイダ側のグループ管理とKubernetes側のRBAC設定がずれていることに気づけません。また、ServiceAccountを明示しないPodは自動的にdefaultを使うため、namespaceのdefaultServiceAccountに強い権限を与えていないかも見落とされがちな点です。

自分のクラスタが該当するか確認する方法

まず、クラスタ全体に効いているClusterRoleBindingを一覧で洗い出します。

kubectl get clusterrolebindings -o custom-columns=NAME:.metadata.name,ROLE:.roleRef.name,SUBJECTS:.subjects[*].name

この出力でcluster-admin(組み込みの最上位権限ロール)が紐付いているsubjectを確認します。クラスタオペレーターや限られたコントローラー以外に紐付いていたら要注意です。

次に、特定のnamespaceでdefaultServiceAccountに何が許可されているか確認します。

kubectl auth can-i --list --as=system:serviceaccount:team-payments:default -n team-payments

getやlist以外にdeployments作成やsecrets閲覧などが許可されていたら、意図しないPodが強い権限を持つ可能性があります。

さらに、各namespaceのRoleBindingが参照しているroleRef種別も確認します。

kubectl get rolebindings -A -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,KIND:.roleRef.kind,ROLE:.roleRef.name

KINDがClusterRoleになっている場合、そのClusterRoleの中身がnamespace単位で問題ない粒度か、中身まで見直す必要があります。

対策の手順

確認で問題が見つかった場合の是正手順です。

1. 再利用可能なClusterRoleをテンプレート化する: チームごとに権限定義をコピーするのではなく、namespace-developerのような共通ClusterRoleを1つ定義し、各チームのnamespaceにRoleBindingで紐付けます。deployments、pods、pods/logなど必要なverbsだけを列挙します。

2. subjectはGroupで管理する: RoleBindingのsubjectにはIDプロバイダのグループクレームと一致するGroupを指定します。入退社やチーム異動はIdP側の変更だけで反映され、Kubernetes側の設定変更が不要になります。

3. ClusterRoleBindingは最小限に絞る: クラスタ全体の権限が本当に必要なのは、クラスタオペレーターと一部のコントローラーだけです。それ以外の用途でClusterRoleBindingを使っていたら、RoleBindingへの置き換えを検討します。

4. default ServiceAccountには何も与えない: namespaceごとに用途別のServiceAccountを明示的に作成し、CIパイプライン用、アプリケーション用で権限を分けます。defaultは権限ゼロのまま維持するのが安全です。

5. BindingをGitで管理しレビュー対象にする: RoleBindingやClusterRoleBindingのYAMLをGitリポジトリで管理し、変更時はプルリクエストでレビューします。監査時にも変更履歴がそのまま証跡になります。

対策を適用したら、再度kubectl auth can-i --listで意図した範囲に権限が収まっているか確認します。特に他チームのnamespaceを指定して--asで確認し、権限が及んでいないことを実際に試すところまでやっておくと安心です。

まとめ

共用クラスタのRBACは、ClusterRoleとRoleBindingの組み合わせ次第でスコープが変わる点が最大の落とし穴です。

まずkubectl get clusterrolebindingsでcluster-admin相当の権限がどこまで広がっているか棚卸しし、kubectl auth can-i --list --asで各ServiceAccountやGroupの実効権限を確認するところから始めてみてください。

defaultServiceAccountへの権限付与がないか、RoleBindingのroleRefがClusterRoleを参照している箇所の中身が妥当かも合わせて点検すると、監査対応だけでなく障害時の権限調査もしやすくなります。

参考

Locking Down Kubernetes RBAC in a Multi-Tenant Cluster

この記事について: 本記事は AI を活用して作成し、forva AI 編集部が内容を確認・監修しています。

AI 駆動開発のご相談は forva AI へ。まずはお気軽にどうぞ。