お疲れ様。最近、クラウドネイティブなインフラの設計レビューをしていて、一番頭が痛くなるのがKubernetesの権限周りのガバナンス不足なんだよね。
「とりあえず動くようにする」という魔力に負けて、ClusterRole に resources: ["*"] や verbs: ["*"] なんていうワイルドカードをぶち込んでいるYAMLを見ると、セキュリティチーフとしては血の気が引く。あれは、自宅の玄関の鍵を開け放して、リビングの真ん中に「どうぞご自由にお金を持っていってください」と札を立てているようなものだからね。
今回は、Kubernetes環境におけるRBAC(Role-Based Access Control)の罠、特に過剰な権限付与が招く地獄と、それを現場でどうねじ伏せるかについて、実戦の知見を交えて解説しよう。
—
1. 攻撃者はどう狙う?ワイルドカードと特権昇格の悪夢
Kubernetesの設計において、開発チームの利便性を優先するあまり、次のような ClusterRole を作成してしまうケースが後を絶たない。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: overly-permissive-developer
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
この設定の何が危険か。もし、この強力な権限を持つサービスアカウントやユーザーのトークンが、万が一アプリケーションの脆弱性(例えば、不完全なSSRFやコンテナのRCE)を通じて外部に漏洩したとしよう。
攻撃者は、Kubernetes APIサーバーへ直接リクエストを飛ばし、集群全体のマスターノードを含むすべてのリソースを掌握する。さらに厄介なのが、Kubernetes特有の特権昇格(Privilege Escalation)を招く2つの動詞、escalate と bind だ。
escalate と bind という名のパンドラの箱
escalate権限: ユーザーが、自分が持っている権限よりも強力な権限を持つRoleやClusterRoleを作成・変更できるようになる。つまり、実質的に「全権限への昇格チケット」を自分で発行できるようになる。bind権限: 既存の強力なロール(例えばcluster-admin)を、任意のサービスアカウントやユーザーに紐付ける(Bindingする)ことを許可する。
これらがワイルドカード(*)や不適切なスコープ設定によって付与されていると、攻撃者は瞬時にクラスタの管理者権限(cluster-admin)を手に入れ、すべてのシークレット(DBのパスワードや外部APIの認証情報)を抜き取り、マイニング用のコンテナをばら撒く。インシデントレスポンスの現場で、これをやられたら復旧には数日〜数週間、下手をすれば企業の社会的信用が吹き飛ぶ。
—
2. 最小権限の原則(PoLP)に基づく設計アプローチ
では、現場のエンジニアとしてどう防ぐべきか。答えはシンプルで、「必要最小限のNamespaceに、必要最小限のリソースと動詞だけを許可する」ことだ。
1. ClusterRoleの乱用をやめる: クラスタ全体に影響する操作が本当に必要なシステムコンポーネント以外は、原則として Role と RoleBinding を使い、Namespaceスコープに閉じ込める。
2. 動詞(Verbs)を厳選する: ["*"] は絶対に使わない。本当に get, list, watch だけ景観確認のために必要なのか、あるいは create, update が必要なのかを精査する。
3. escalate と bind を剥奪する: 通常の開発者や一般的なアプリケーション用サービスアカウントに、これらの権限を与える正当な理由は存在しない。
—
3. 【実践】セキュアなRBACマニフェスト実装例
それでは、具体的なコードで示そう。以下は、特定のNamespace(例: production-app)において、アプリケーションの監視やヘルスチェックを行うためのサービスアカウントに対し、必要最低限の権限(Podの参照および特定のConfigMapの更新のみ)を安全に付与するKubernetesマニフェストのサンプルだ。
このYAMLは、そのままコピーして検証環境に適用できる。
apiVersion: v1
kind: Namespace
metadata:
name: production-app
---
# アプリケーション監視・運用のための専用サービスアカウント
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-operator-sa
namespace: production-app
labels:
security.example.com/hardened: "true"
---
# Namespaceスコープに限定したRoleの定義
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-operator-role
namespace: production-app
rules:
# 1. Podの状態確認(get, list, watch)のみを許可。deleteやcreateは含まない。
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
# 2. ConfigMapは動的な設定変更のため update を許可するが、削除(delete)や作成(create)は禁止する。
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["app-runtime-config"] # 特定のConfigMapだけに操作を限定
verbs: ["get", "list", "update"]
# ※ 注意: apiGroupsに "*", resourcesに "*", verbsに "escalate" や "bind" は絶対に含まない。
---
# RoleとServiceAccountを紐付けるRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-operator-binding
namespace: production-app
subjects:
- kind: ServiceAccount
name: app-operator-sa
namespace: production-app
roleRef:
kind: Role
name: app-operator-role
apiGroup: rbac.authorization.k8s.io
この設定のポイントは、resourceNames を使って操作できるリソースの名称まで限定している点だ。これにより、万が一サービスアカウントの権限が悪用されても、影響範囲を特定の ConfigMap 1つにまで絞り込むことができる。
—
4. 現場のセキュリティチェックリスト
リリース前のCI/CDパイプラインや、K8sマニフェストのレビュー時には、必ず以下の項目をチェックしてほしい。
- [ ]
ClusterRoleまたはRoleのrules[*].verbsに*が含まれていないか? - [ ]
apiGroupsやresourcesに*を指定して、意図しないリソースへのアクセスを許していないか? - [ ] 開発者向け、あるいはアプリ用のロールに
escalateやbind権限が混入していないか? - [ ] クラスタ全体を見渡す権限が必要な場合を除き、すべて
RoleとRoleBinding(Namespaceスコープ)で設計されているか? - [ ]
kube-benchなどのツールや、OPA/Gatekeeper、Kyvernoなどのポリシーエンジンを使って、過剰な権限を持つマニフェストのデプロイを自動検知・ブロックする仕組みを入れているか?
セキュリティは「面倒くさい」と感じた瞬間からほころびが生じる。しかし、インフラの根幹であるKubernetesの権限管理を怠ると、一撃で会社が傾くレベルの踏み台事故につながる。
「動けばいいや」の精神を捨て、今日から君のプロジェクトのRBACを厳格に締め直してくれ。頼んだよ!
コメント