最小権限でロールを設計する
目標
ロールとバインディングを自分で作成して最小権限を設計し、kubectl auth can-iで権限の境界を検証する習慣を身に付けます。
なぜ重要なのか
RBACで最も多くの人が混同するのは、スコープを決めるのはロールではなくバインディングだという点です。同じClusterRoleでも、ClusterRoleBindingで紐づければクラスター全体、RoleBindingで紐づければそのネームスペースでのみ有効です。この性質のおかげで、共通の権限のまとまりをClusterRoleとして一度だけ定義し、チームごとに自分のネームスペースで再利用できます。2つ目の落とし穴はサブリソースです。podsとpods/log、pods/execは別々のリソース名なので、権限が自動的についてくることはありません。ログだけを読むボットに、シェルへの接続権限までついていかないようにするには、この区別が必要です。最後に、権限は書くものではなく判定されるものなので、マニフェストを目で読む代わりに、impersonationで問い合わせてください。yesだけを確認せず、noも一緒に確認して初めて、権限が必要以上に広くないかがわかります。
ステップ
- ネームスペース
rbac-labを作成し、その中にサービスアカウントapp-reader、inspector、log-botの3つを作成してください。 rbac-labにRolepod-readerを作成してください。ルールは1つで、apiGroupsはコアグループ(空文字列)、resourcesはpods、verbsはget、list、watchの3つです。ワイルドカード*は、どこにも使ってはいけません。rbac-labにRoleBindingread-podsを作成してください。roleRefはkindRole、namepod-readerで、サブジェクトはkindServiceAccount、nameapp-reader、namespacerbac-labです。app-readerにimpersonationして権限を確認し、結果を/root/ops/rbac/out/can-i.txtに保存してください。このファイルには、yesの応答とnoの応答の両方が含まれている必要があります。確認する項目は、最低でも次の3つです。rbac-labでのPod一覧の取得(yes)、rbac-labでのPodの削除(no)、defaultネームスペースでのPod一覧の取得(no)です。- ClusterRole
node-viewerを作成してください。resourcesにnodesが含まれ、すべてのルールのverbsはget、list、watchだけである必要があります。続けてClusterRoleBindingnode-viewer-bindingで、このロールをrbac-labのapp-readerに与えてください。結果として、app-readerはノードを取得できて、削除はできない状態になっている必要があります。 - ClusterRole
ns-inspectorを作成してください(ConfigMapを読み取れる必要があります)。そしてrbac-labにRoleBindinginspect-hereを作成し、roleRef.kindをClusterRole、nameをns-inspectorとし、サブジェクトはrbac-labのinspectorにしてください。結果として、inspectorはrbac-labではConfigMapを読み取れますが、defaultでは読み取れない状態になっている必要があります。 rbac-labにRolelog-readerを作成してください。resourcesにはpods/logだけを入れ、podsは入れないでください。このロールをlog-botにRoleBindingで紐づけてください。結果として、log-botはpods/logを読み取れて、pods自体は読み取れず、pods/execも使えない状態になっている必要があります。/root/ops/rbac/out/rbac-audit.jsonを作成してください。フィールドは4つです。cluster_admin_bindingsは、roleRef.nameがcluster-adminであるClusterRoleBindingの名前の配列で、実際のクラスターの数と正確に一致している必要があり、デフォルトのバインディングであるcluster-adminが含まれている必要があります。wildcard_rolesは*を使うロール名の配列(1つ以上)、secret_readersはsecretsを扱うロール名の配列(1つ以上)、least_privilege_violationsは最小権限の違反と判断した項目の配列です。
参考
- サービスアカウントの正式なサブジェクト名は、
system:serviceaccount:<네임스페이스>:<이름>です。kubectl auth can-i <동사> <리소스> -n <네임스페이스> --as=<주체>の形で問い合わせてください。 kubectl create role/create clusterrole/create rolebindingに--dry-run=client -o yamlを付けると、正しいマニフェストの骨組みが得られます。--serviceaccount=<네임스페이스>:<이름>を使うと、サブジェクトのnamespaceフィールドが自動的に埋まります。- ステップ8は、手で数えるとほぼ間違えます。
kubectl get clusterrolebindings -o json | jqで抽出して作成してください。ワイルドカードのロールは、kubectl get roles,clusterroles -A -o jsonから、verbsやresourcesに*があるものを絞り込めばよいです。 - よくある間違い1:
apiGroupsに"v1"や"core"を書いてしまうことです。コアグループの名前は空文字列です。 - よくある間違い2: ステップ7で
podsとpods/logを一緒に入れてしまうことです。そうするとPod自体も読み取れるようになり、サブリソースを分離する練習になりません。 - よくある間違い3: ステップ5でClusterRoleを作成して、RoleBindingで紐づけてしまうことです。ノードはネームスペースがないので、RoleBindingではアクセス権限が生まれません。
- ラボのPodはラボごとに新しく起動するので、前のラボで作ったクラスターの状態は残っていません。ネームスペースとサービスアカウントは、このラボの中で自分で作成してください。運用手順を記憶ではなくランブックとマニフェストとして残すべき理由が、まさにここにあります。
ネームスペースとサービスアカウントを準備する
ネームスペースrbac-labを作成し、その中にサービスアカウントapp-reader、inspector、log-botの3つを作成してください。
サブジェクトがなければ、権限を与えられません。サービスアカウントはネームスペースに属し、名前だけで作成できます。
読み取り専用のRoleを作成する
rbac-labにRolepod-readerを作成してください。ルールは1つで、apiGroupsはコアグループ(空文字列)、resourcesはpods、verbsはget、list、watchの3つです。ワイルドカード*は、どこにも使ってはいけません。
コアリソースのAPIグループの名前が何かを確認してください。読み取り系の動詞は3つで、ワイルドカードは使いません。
Roleをサービスアカウントにバインドする
rbac-labにRoleBindingread-podsを作成してください。roleRefはkindRole、namepod-readerで、サブジェクトはkindServiceAccount、nameapp-reader、namespacerbac-labです。
サブジェクトがサービスアカウントの場合は、名前だけでは足りません。どのネームスペースのアカウントなのかまで書く必要があります。
impersonationで権限の境界を確認する
app-readerにimpersonationして権限を確認し、結果を/root/ops/rbac/out/can-i.txtに保存してください。このファイルには、yesの応答とnoの応答の両方が含まれている必要があります。確認する項目は、最低でも次の3つです。rbac-labでのPod一覧の取得(yes)、rbac-labでのPodの削除(no)、defaultネームスペースでのPod一覧の取得(no)です。
サービスアカウントの正式な名前の形式を思い出してください。できることとできないことの両方を確認して初めて、境界が見えてきます。
クラスタースコープのリソース用のClusterRoleを作成する
ClusterRolenode-viewerを作成してください。resourcesにnodesが含まれ、すべてのルールのverbsはget、list、watchだけである必要があります。続けてClusterRoleBindingnode-viewer-bindingで、このロールをrbac-labのapp-readerに与えてください。結果として、app-readerはノードを取得できて、削除はできない状態になっている必要があります。
ノードはネームスペースのないリソースなので、Roleでは扱えません。クラスター全体に適用するには、バインディングもクラスタースコープである必要があります。
ClusterRoleをRoleBindingで紐づけてみる
ClusterRolens-inspectorを作成してください(ConfigMapを読み取れる必要があります)。そしてrbac-labにRoleBindinginspect-hereを作成し、roleRef.kindをClusterRole、nameをns-inspectorとし、サブジェクトはrbac-labのinspectorにしてください。結果として、inspectorはrbac-labではConfigMapを読み取れますが、defaultでは読み取れない状態になっている必要があります。
スコープを決めるのは、ロールの種類ではなくバインディングの種類です。同じロールがどのネームスペースでだけ有効になるのかを確認してください。
サブリソースだけを開くロールを作成する
rbac-labにRolelog-readerを作成してください。resourcesにはpods/logだけを入れ、podsは入れないでください。このロールをlog-botにRoleBindingで紐づけてください。結果として、log-botはpods/logを読み取れて、pods自体は読み取れず、pods/execも使えない状態になっている必要があります。
ログは、Podとは別のリソース名を持ちます。上位のリソースまで一緒に開けてしまうと、このステップの意味がなくなります。
権限の監査レポートを作成する
/root/ops/rbac/out/rbac-audit.jsonを作成してください。フィールドは4つです。cluster_admin_bindingsは、roleRef.nameがcluster-adminであるClusterRoleBindingの名前の配列で、実際のクラスターの数と正確に一致している必要があり、デフォルトのバインディングであるcluster-adminが含まれている必要があります。wildcard_rolesは*を使うロール名の配列(1つ以上)、secret_readersはsecretsを扱うロール名の配列(1つ以上)、least_privilege_violationsは最小権限の違反と判断した項目の配列です。
手で数えず、クラスターから抽出してください。cluster-adminのバインディングの数は、実際の値と正確に一致している必要があります。