ServiceAccountとRBAC【kubernetes】

## 概要

kubernetesのServiceAccount・RBAC周りについて勉強したので、学んだことと手を動かしてやってみたことについてまとめました。

## kubernetesのアカウント

kubernetesにはアカウント(ユーザー)が2種類あります。

- ServiceAccount
  - kubernetesリソースの1つ
  - Podが他のリソースを操作するための認証に使用される
- UserAccount
  - kubernetesによる管理対象ではない
  - クラスターと同じレベルの存在で、例えばEKSの場合はIAMとリンクする

## ServiceAccountとRBAC

### RBACとは

kubernetesはリソースへのアクセス制御の方法として、RBAC(Role Based Access Control)が利用出来ます。
RBACはアクセス制御をユーザーのロール(役割)をベースに行う方法です。
kubernetesではServiceAccountに対して認可を行う際はRBACを使うことが推奨されています。

### ServiceAccountとは

ServiceAccountはNamespaceに紐づくkubernetesのリソースです。
Namespaceは作成時に、紐づくServiceAccountがデフォルトで１つ作成されます。
nameは`default`で、リソースの作成時にServiceAccountが指定されていない場合はそのnamespaceのdefaultが自動的に割り当てられます。

```bash
# 確認例
# 全てのnamespaceにdefaultサービスアカウントがあることが分かる

$ kubectl get ns
$ kubectl get sa -A | head -1 && kubectl get sa -A | grep " default"
```

なので、Namespaceに紐づくリソースには全てServiceAccountが紐づきます。

```bash
# 確認例

$ kubectl get pod -o yaml | grep " serviceAccount:" # Namespaceに紐づくリソース
$ kubectl get node -o yaml | grep " serviceAccount:" # 紐づかないリソース
```

ServiceAccountのnameはNamespace毎にユニークである必要がありますが、クラスター単位では被っても大丈夫です。

### ServiceAccountとRole

ServiceAccountはそれだけではリソースに対する操作を行う権限を持ちません。
RBACではどんなユーザー(ServiceAccount)か、ではなくユーザーがどんなロールを持っているかで権限が決まります。
kubernetesにはロールに関わるリソースはRole・ClusterRole・RoleBinding・ClusterRoleBindingの４つがあります。

#### Role・ClusterRoleとは

Role・ClusterRoleはどちらもリソースに対する操作権限・ルールについて定義したものです。
さらにClusterRoleはNamespaceに属さない(Clusterスコープ)リソースの操作のルールを定義することが出来ます。
Roleはそれ自体がNamespaceに属し、ClusterRoleは属しません。

#### RoleBinding・ClusterRoleBindingとは

RoleBinding・ClusterRoleBindingとはRole・ClusterRoleとユーザーやグループ、ServiceAccountを紐付けるためのリソースです。
ClusterRoleBindingは、ClusterRoleを定義することでそのルールがクラスター全体のリソースに対して適用されます。
RoleBindingはRoleと同じくそれ自体がNamespaceに属し、ClusterRoleBindingは属しません。

#### デフォルトのClusterRole

ClusterRoleはデフォルトのざっくりとした権限を持つ物がいくつか用意されており、ClusterRoleBindingで設定されている物もあります。

| name          | 設定されたClusterRoleBinding | 説明                                                         |
| ------------- | ---------------------------- | ------------------------------------------------------------ |
| cluster-admin | system:masters group         | クラスター内の全てのリソース・全てのNamespaceに対して全ての操作が可能なスーパーユーザーの役割を持つ。RoleBindingに設定した場合はそのNamespaceに対する全ての操作が可能になる。 |
| admin         | None                         | RoleBindingで設定し、Namespace内で使用されることを目的としたロール。Namespaceのほとんどのリソースに対する読み取り/書き込みアクセスが可能になる。ResourceQuotaとそのNamespace自体へのアクセスは不可。 |
| edit          | None                         | Namespaceのほとんどのリソースに対する読み取り/書き込みアクセスが可能になる。Role/RoleBindingの操作は不可。Namespace内のServiceAccountとしてPodを実行出来るため、任意のServiceAccountのAPIアクセスレベルを取得するために使用出来る。 |
| view          | None                         | Namespaceのほとんどのリソースに対する読み取りアクセスが可能になる。Role/RoleBindingの操作は不可で、さらにSecretの操作も許可されていない。Secretを読み取り可能にすると、ServiceAccountの認証情報にアクセスし任意のServiceAccountとしてAPIにアクセス出来るようになるため。(特権の昇格) |



#### マニフェストファイルの例

ServiceAccountに対してRoleまたはClusterRoleを、RoleBindingまたはClusterRoleBindingを使って紐づけることで、ServiceAccountの権限は設定されます。
以下は同一のnamespaceのDeploymentに対して、get・list・watchの操作可能なPodを作成するマニフェストファイルの例です。

```yaml
kind: ServiceAccount
metadata:
  name: test-sa
  namespace: default
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: test-role
rules:
- apiGroups: ["apps"]
  resources: ["deployments"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: test-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: role
  name: test-role
subjects:
- kind: ServiceAccount
  name: test-sa
  namespace: default
--- 
apiVersion: v1
kind: Pod
metadata:
  name: test-pod
spec:
  serviceAccountName: test-sa
  containers:
  - name: kubectl-container
    image: lachlanevenson/k8s-kubectl:v1.18.2
    command: ["sleep","86400"]
```

今回の場合はRole・RoleBindingをそれぞれClusterRole・ClusterRoleBindingに変更すると、作成されるPodの権限は「全てのnamespaceのDeploymentに対してget・list・watchの操作可能」に変更されます。

## やってみた

### defaultのServiceAccountには権限があるのか？

defalutのServiceAccountに何か権限があるのか(ロールがあるのか)、
ややこしいですが、`defalut/defalut`(defalutのnamespaceの、defalutのServiceAccount)に絞って確認してみます。

```bash
# 確認方法

$ kubectl get RoleBinding -o wide | grep "default/default"
# なし
$ kubectl get ClusterRoleBinding -o wide | grep "default/default"
# なし
```

何も権限がないようです。

### Clusterスコープのリソースの操作の許可をするClusterRoleをRoleBindingで設定するとどうなるのか？

Namespaceに属さない(Clusterスコープ)のリソースであるNodeの操作を許可するClusterRoleをRoleBindingに設定してみます。
この場合はどんな挙動が起きるのでしょうか。

```yaml
# test-sa-2.yaml

kind: ServiceAccount
metadata:
  name: test-sa-2
  namespace: default
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: test-role-2
rules:
- apiGroups: [""]
  resources: ["nodes"]
  verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: test-rolebinding-2
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: role
  name: test-role-2
subjects:
- kind: ServiceAccount
  name: test-sa-2
  namespace: default
--- 
apiVersion: v1
kind: Pod
metadata:
  name: test-pod-2
spec:
  serviceAccountName: test-sa-2
  containers:
  - name: kubectl-container-2
    image: lachlanevenson/k8s-kubectl:v1.18.2
    command: ["sleep","86400"]
   
```

このマニフェストファイルでリソースを作成し、Podの権限はどうなるのか確認してみます。

```bash
$ kubectl apply -f test-sa-2.yaml
# 表示略。全てのリソースがちゃんと作成された


$ kubectl auth can-i list nodes --as=system:serviceaccount:default:test-sa-2
Warning: resource 'nodes' is not namespace scoped
yes
# auth can-iで確認してみると、nodesはnamespaceスコープのリソースだからWarningだけどyesとのこと


$ kubectl exec -it test-pod-2 -- sh
~ # kubectl get nodes
nodes is forbidden: User "system:serviceaccount:default:test-sa-2" cannot list resource "nodes" in API group "" at the cluster scope
command terminated with exit code 1
# 作成したPodでexec -itしてClusterRoleで許可した操作を実行してみたところ、やはりクラスタースコープの操作は実行できないとのこと
```

矛盾した設定でもリソースの作成自体は行えるようでした。
つまり`kubectl apply`の時点では気付けない可能性があり、注意すべき点かもしれません。

## 参考文献
- [Kubernetes Documentation(公式)](https://kubernetes.io/docs/home/)
- [Kubernetes完全ガイド](www.amazon.co.jp/dp/B08FZX8PYW)