KubernetesのClusterRoleをClusterRoleBindingしたときとRoleBindingしたときの挙動の違い

# はじめに

KubernetesのRBACを勉強しているときに、ClusterRoleをClusterRoleBindingすることもできるし、RoleBindingすることもできると知った。
その違いがいまいちピンと来なかったので、実際に動かして確認してみることにした。

# シナリオ

今回はviewというデフォルトで用意されているClusterRoleを、ClusterRoleBindingした場合とRoleBindingした場合の違いについて確認する。
[ユーザー向けRole](https://kubernetes.io/ja/docs/reference/access-authn-authz/rbac/#%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E5%90%91%E3%81%91role)

具体的な手順は以下である。
1. appleとbananaというNamespaceを作成する
2. viewをClusterRoleBindingで紐づけたapple-crbというPodを作成する
3. 同じくviewをRoleBindingで紐づけたapple-rbというPodを作成する
4. banana Namespaceに確認用のPodを作成する
5. apple-crbとapple-rbのそれぞれからkubectlコマンドを実行し、appleのNamespace内やbananaのNamespace内のPodがどう見えるのか確認する

# 確認環境

|Component|Version|
|---|---|
| PC | M1 MacBook Pro |
| Docker Desktop | 4.34.3 |
| kind | 0.24.0 |
| Kubernetes | v1.31.0 |

# 環境構築

## Kuberneteクラスタ

まずは動作確認に必要なKubernetesクラスタを構築する。

```yaml:kind-sandbox.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: sandbox
nodes:
- role: control-plane
- role: worker
```

kindをインストールした状態で以下のコマンドを実行すれば、1,2分くらいでKubernetesクラスタを構築することができる。
```bash
kind create cluster --config kind-sandbox.yaml
```

## Namespace

apple,bananaという2つのNamespaceを作成する。

```yaml:ns.yaml
kind: Namespace
apiVersion: v1
metadata:
  name: apple
  labels:
    name: apple
---
kind: Namespace
apiVersion: v1
metadata:
  name: banana
  labels:
    name: banana
```

## ServiceAccount

apple-crbというPodに割り当てるServiceAccountであるapple-crb-saとapple-rbというPodに割り当てるServiceAccountであるapple-rb-saを作成する。

```yaml:sa.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: apple-crb-sa
  namespace: apple
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: apple-rb-sa
  namespace: apple
```

## ClusterRoleBinding

apple-crb-saにはClusterRoleBindingでClusterRoleであるviewを割り当てる。

```yaml:crb.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: crb-view
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: view
subjects:
- kind: ServiceAccount
  name: apple-crb-sa
  namespace: apple
```

## RoleBinding

一方、apple-rb-saにはRoleBindingでClusterRoleであるviewを割り当てる。
ポイントは.metadata.namespaceでappleのNamespaceを指定していることである。
これにより、このRoleBindingはappleのNamespaceに対してのみ有効であることを示す。

```yaml:rb.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: rb-view
  namespace: apple
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: view
subjects:
- kind: ServiceAccount
  name: apple-rb-sa
```

## Deployment

最後に、apple-crb-saを割り当てたPodであるapple-crb、apple-rb-saを割り当てたPodであるapple-rb、確認対象であるbananaのNamespaceで実行するPodであるbananaをDeploymentで作成する。

```yaml:deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: apple
  name: apple-crb
  namespace: apple
spec:
  replicas: 1
  selector:
    matchLabels:
      app: apple-crb
  template:
    metadata:
      labels:
        app: apple-crb
    spec:
      serviceAccountName: apple-crb-sa
      containers:
      - name: apple-kubectl
        image: bitnami/kubectl:latest
        command: ["sh", "-c", "while true; do sleep 3600; done"]
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: apple
  name: apple-rb
  namespace: apple
spec:
  replicas: 1
  selector:
    matchLabels:
      app: apple-rb
  template:
    metadata:
      labels:
        app: apple-rb
    spec:
      serviceAccountName: apple-rb-sa
      containers:
      - name: apple-kubectl
        image: bitnami/kubectl:latest
        command: ["sh", "-c", "while true; do sleep 3600; done"]
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: banana
  name: banana
  namespace: banana
spec:
  replicas: 1
  selector:
    matchLabels:
      app: banana
  template:
    metadata:
      labels:
        app: banana
    spec:
      containers:
      - name: busybox
        image: busybox:1.37
        command: ["sh", "-c", "while true; do sleep 3600; done"]
```

# 動作確認

では、ClusterRoleBindingとRoleBindingの挙動の違いを確認していく。

## viewをClusterRoleBindingした場合

### apple Namespace内のPod情報の取得

apple Namespace内のPod情報は取得できます。

```bash
❯ k -n apple exec apple-crb-6644f9f5fb-grkrj -it -- /bin/bash
I have no name!@apple-crb-6644f9f5fb-grkrj:/$ kubectl -n apple get po
NAME                         READY   STATUS    RESTARTS   AGE
apple-crb-6644f9f5fb-grkrj   1/1     Running   0          13h
apple-rb-fd9c9f785-k6nvd     1/1     Running   0          13h
I have no name!@apple-crb-6644f9f5fb-grkrj:/$ 
```

### banana Namespace内のPod情報の取得

banana Namespace内のPod情報も取得できます。

```bash
I have no name!@apple-crb-6644f9f5fb-grkrj:/$ kubectl -n banana get po
NAME                      READY   STATUS    RESTARTS   AGE
banana-7d4d466457-9gvmk   1/1     Running   0          13h
I have no name!@apple-crb-6644f9f5fb-grkrj:/$ 
```

## apple-rb

### apple Namespace内のPod情報の取得

apple Namespace内のPod情報は取得できます。

```bash
❯ k -n apple exec apple-rb-fd9c9f785-k6nvd -it -- /bin/bash 
I have no name!@apple-rb-fd9c9f785-k6nvd:/$ kubectl -n apple get po
NAME                         READY   STATUS    RESTARTS   AGE
apple-crb-6644f9f5fb-grkrj   1/1     Running   0          13h
apple-rb-fd9c9f785-k6nvd     1/1     Running   0          13h
I have no name!@apple-rb-fd9c9f785-k6nvd:/$ 
```

### banana Namespace内のPod情報の取得

しかし、banana Namespace内のPod情報は取得できませんでした。
banana Namespaceでのpodsのlistが許可されていないためです。

```bash
I have no name!@apple-rb-fd9c9f785-k6nvd:/$ kubectl -n banana get po
Error from server (Forbidden): pods is forbidden: User "system:serviceaccount:apple:apple-rb-sa" cannot list resource "pods" in API group "" in the namespace "banana"
I have no name!@apple-rb-fd9c9f785-k6nvd:/$ 
```

# まとめ

ClusterRoleBindingは文字通りKubernetesクラスタ全体への権限をbindするのに対し、RoleBindingはnamespaceに限定した権限をbindする。
今回の動作確認によって、その違いを実例をもとに確認することができた。