ロールを作れるなら管理者になれるのか
目標
低い権限のServiceAccountでRBACの権限昇格を試み、apiserverの権限昇格防止(escalate/bind)が 実際にその試みを防ぐことを観察します。そして、正当な管理者だけが権限を渡せること、インパーソネーションも 別に保護される権限であることを確認して、レポートに残します。
なぜ重要なのか
RBACで最も危険なミスは、「ロールを作成できる権限」を与えたことが、実質的に「何にでもなれる権限」を 与えたのと同じになってしまう場合です。もしロールの作成権限だけで、自分にシークレットの権限を持つロールを作成して付けられる のなら、そのServiceAccountは、すなわちクラスターの管理者です。
Kubernetesは、これをapiserverのレベルで防ぎます。自分が現在持っていない権限を含むロールを作成したり、 バインドしたりすることはできません(権限昇格の防止)。そのため、ロールの作成権限を与えても、それがすぐには特権の拡大に つながりません。この境界がコードではなくapiserverの承認の段階にあること、そしてimpersonateのような 動詞がなぜ別に危険な動詞として扱われるのかを、自分の目で確かめることが、このラボの核心です。
ステップ
- ネームスペース
kcsa-privとServiceAccountlowprivを作成します。 - lowprivに、ロール・ロールバインディングの作成権限だけを与えるRole
rolemakerをバインドします。 - 管理者として、標的のClusterRole
secret-admin(シークレットのget/list)を作成します。 - lowprivになりすましてシークレット権限のロールを作成しようとして、拒否されることを確認し、保存します。
- lowprivになりすましてsecret-adminを自分にバインドしようとして、拒否されることを確認し、保存します。
- 管理者が正式にsecret-adminをlowprivにバインドして、今度はシークレットを読めるようにします。
- インパーソネーションの権限がないServiceAccount
auditor2が、他人になりすませないことを確認します。 - 何が止められ、何が通ったかを
report.txtに記録します。
参考
kubectl auth can-i <동사> <리소스> --as=<주체>で、管理者権限から別の主体の権限を確認します(プレースホルダーは動詞、リソース、主体です)。- 自己昇格の試みは失敗が正常なので、コマンドの後ろに
2>&1を付けて、標準エラー出力までファイルに受け取ってください。 - 拒否メッセージの「is attempting to grant RBAC permissions not currently held」が、権限昇格防止の証拠です。
- 公式ドキュメント: RBAC認可・ RBACのグッドプラクティス。
低い権限の主体を準備する
ネームスペースkcsa-privと、その中にServiceAccountlowprivを作成してください。まだ何の権限も与えません。
ServiceAccountは、ネームスペース内の主体(subject)です。createを--dry-run=clientで生成してapplyすれば、何度実行しても安全です。このステップでは、lowprivにどのロールもバインドしません。
ロールを作成する権限だけを握らせる
Rolerolemakerをネームスペースkcsa-privに作成してください。apiGroupsはrbac.authorization.k8s.io、resourcesはrolesとrolebindings、verbsはcreate、get、listです。そしてこのRoleを、ServiceAccountlowprivにRoleBinding(rolemaker-binding)で結び付けます。lowprivはロール・ロールバインディングを作成できますが、シークレットの権限は持っていない必要があります。
RBACは、ロール(権限の束)とロールバインディング(主体への接続)に分かれています。kubectl create role・create rolebindingで、--verb・--resource・--serviceaccountを使います。lowprivは、このステップ以降、ロールの作成権限は得ますが、シークレットに対する権限はやはり持っていないという点を覚えておいてください。次のステップ以降の核心です。
魅力的な標的: シークレットの読み取り権限
管理者権限でClusterRolesecret-adminを作成してください。コアグループのsecretsに対するgetとlistの権限です。以降のステップで、lowprivがこの権限を手に入れようと試みる標的です。
ClusterRoleは、ネームスペースに縛られない権限の束です。kubectl create clusterroleで、--verb・--resourceを使います。このステップは、デフォルトのkubeconfig(cluster-admin)でそのまま作成すればかまいません。
持っていない権限を自分に与えようとして阻止される
次に、lowprivになりすまして(--as=system:serviceaccount:kcsa-priv:lowpriv)、ネームスペースkcsa-privにシークレットのget権限を含むRolesneakyを作成しようと試みてください。lowprivはシークレットの権限を持たないため、apiserverはこの作成を拒否するはずです。拒否メッセージをそのまま/root/kcsa-priv/escalate-denied.txtに保存します。
RBACには、権限昇格防止のルールがあります。自分が持っていない権限を含むロールは作成できません。ロールを作成する権限(ステップ2)があっても、このルールは別に働きます。コマンドが失敗するのが正常なので、標準エラー出力までまとめてファイルに受け取ってください(2>&1)。メッセージに「forbidden」と「not currently held」が見えるはずです。
標的の権限を自分に結び付けようとして阻止される
lowprivになりすまして(--as=system:serviceaccount:kcsa-priv:lowpriv)、ClusterRolesecret-adminを自分(lowpriv)に結び付けるRoleBindinggrabを、ネームスペースkcsa-privに作成しようと試みてください。これも、lowprivが持っていない権限を渡すことになるので、拒否されるはずです。拒否メッセージを/root/kcsa-priv/bind-denied.txtに保存します。
lowprivはロールバインディングを作成できます(ステップ2)。しかし、自分が持っていない権限を含むロールをバインドするには、その権限をすでに持っているか、対象のロールに対するbind権限を持っている必要があります。どちらもないので、apiserverが阻止します。失敗が正常なので、2>&1でエラーまで受け取ってください。
管理者が正式に権限を渡す
今度は管理者権限(デフォルトのkubeconfig)で、ClusterRolesecret-adminをServiceAccountlowprivに結び付けるRoleBindinggrantedを、ネームスペースkcsa-privに作成します。これでlowprivは、kcsa-privでシークレットをgetできる必要があります。
境界は、lowprivの「ロールバインディングを作成する能力」ではなく、apiserverの権限昇格の検査でした。すでにその権限を持つ管理者がバインディングを作成すれば通過します。--clusterroleと--serviceaccountでバインディングを作成し、auth can-i get secrets --as=<lowpriv>がyesに変わるかを確認してください。
他人になりすます権限も別に守られている
ネームスペースkcsa-privにServiceAccountauditor2を作成します(インパーソネーションの権限は与えません)。auditor2が別のユーザーになりすませる(impersonate users)かを確認して、権限のない主体はインパーソネーションができないことを確かめます。
インパーソネーション(--as)は、それ自体が別のRBAC権限(users/groups/serviceaccountsリソースに対するimpersonate動詞)です。権限を与えていないServiceAccountは、auth can-i impersonate users --as=<그 SA>がnoである必要があります(プレースホルダーはそのSAです)。管理者(デフォルトのkubeconfig)はyesであることと対比してみてください。
何が止められ、何が通ったかを帳簿に
観察結果を/root/kcsa-priv/report.txtに、正確に4行で書いてください。self-escalate-role=blocked、self-bind-clusterrole=blocked、admin-grant=allowed、impersonate-without-verb=blockedです。値は、実際のクラスターの動作と一致している必要があります。
採点ツールは、この4行をクラスターでもう一度確認します。lowprivでの新たな自己昇格の試みが失敗するか、管理者のバインディングのあとにlowprivがシークレットをgetできるか、auditor2がインパーソネーションをできないかです。推測で書かず、前のステップで見た結果をそのまま書き写してください。