権限はなぜ静かに膨らむのか
一言でいうと
RBACはデフォルトで権限昇格を防ぎますが、escalate・bind・impersonateの3つの動詞と、自動でマウントされるServiceAccountトークンが、その防御線を迂回します。ハードニングとは、この4つの穴を塞ぐ作業です。
なぜ必要なのか
RBACには組み込みの保護機構があります。ユーザーがRoleやRoleBindingを作成するには、そのRoleに含まれるすべての権限をすでに持っていなければなりません。このルールのおかげで、「権限の低い人が自分に高い権限を与える」経路が塞がれます。問題は、この保護を明示的に突破する動詞が存在することです。
| 動詞 | 何を突破するか | 実務での対応 |
|---|---|---|
escalate |
自分が持っていない権限をRoleに追加できる | プラットフォーム管理者のみ。監査ログは必須 |
bind |
自分が持っていない権限のRoleをバインドできる | ClusterRoleBindingの作成権限そのものを制限 |
impersonate |
他のユーザー・グループ・SAになりすませる | 対象をresourceNamesで固定し、監査する |
ここにsystem:mastersグループがあります。このグループのメンバーは、RBACのチェックをまるごと迂回します。kubeconfigが1つ流出すれば、それで終わりです。break-glass手順専用にしておき、普段は誰もこのグループに入っていないようにします。
どう動くのか
2つ目の軸は、ServiceAccountトークンです。Podはデフォルトで、自分のネームスペースのdefaultServiceAccountで実行され、そのトークンは/var/run/secrets/kubernetes.io/serviceaccount/tokenに自動でマウントされます。アプリケーションがKubernetes APIを使わなくてもマウントされます。つまり、Podが1つ破られると、攻撃者はすぐにクラスターAPIの認証情報を1つ手に入れることになります。
オフにする方法は2か所あります。ServiceAccountオブジェクトのautomountServiceAccountToken: falseと、Podスペックの同じフィールドです。Podレベルの設定がServiceAccountレベルの設定をオーバーライドします。そのため、実務では両方に書きます。defaultSAをオフにしておけば、新しいワークロードが誤ってトークンを受け取る経路がなくなり、必要なワークロードだけが専用のSAを作って明示的にオンにします。
トークンの形も変わりました。以前はSAごとに有効期限のないトークンSecretが自動生成され、そのSecretを読める人は永久に有効な認証情報を手に入れていました。今はTokenRequest APIで、有効期間と対象(audience)が付いたトークンを発行し、Podにはprojected volumeのserviceAccountTokenソースとして注入されます。手動で作成するkubernetes.io/service-account-tokenSecretは今でも作れますが、有効期限がないため、本当に必要なときだけ使います。
3つ目は検証です。RBACは目で読んで検証するのが困難です。kubectl auth can-iに--as=system:serviceaccount:<네임스페이스>:<이름>(プレースホルダーはネームスペースとServiceAccountの名前です)を付けると、そのサブジェクトが実際に何をできるのかをAPIサーバーが直接答えてくれます。設計ではなく結果を確認できる唯一の方法です。
現場での姿
ポリシーエンジンを導入した組織でよく起きるP0障害が1つあります。OPA GatekeeperのValidatingWebhookがfailurePolicy: Failで登録されている状態で、Gatekeeper Podがすべて停止すると、APIサーバーがWebhookの応答を受け取れず、対象リソースの作成・変更をすべて拒否します。ポリシーを守ろうとして、クラスターへのデプロイが丸ごと止まってしまうのです。応急処置はWebhook設定オブジェクトを削除することで、根本的な対応は、本番でfailurePolicy: Ignoreを使いつつ、Gatekeeperのダウンを必ずアラートで捉えることです。権限を絞る仕組みが可用性の事故の原因になりうるという事実が、ハードニング設計の現実的な制約です。
筆者のホームラボでコントロールプレーンを1台から3台に増やしたときも、認証情報の寿命が問題でした。kubeadm init phase upload-certsは、CA鍵の一式を暗号化してクラスター内のSecretとしてアップロードし、certificate-keyはその暗号文を復号する対称鍵です。このSecretは2時間後に自動削除されます。CA鍵が暗号化された状態であってもクラスター内に存在する時間を最小限にしようという設計であり、同じ原理がSAトークンにもそのまま当てはまります。認証情報は、隠すよりも寿命を短くするほうが、ほとんどの場合に効果的です。
次のラボですること
ワイルドカードを持つClusterRoleをクラスターから自分で探し出して一覧にし、危険な動詞を持つロールを別に抜き出します。次に、絞り込んだRoleを作成してServiceAccountにバインドし、auth can-i --as=で結果を確認します。最後に、PodとdefaultServiceAccountの両方で、トークンの自動マウントをオフにします。