KeycloakのOIDC認証を利用してKubernetesでテナント制御を行う

## はじめに

※本記事は前回投稿した[「KeycloakによるKubernetesのOIDC認証を試す」](https://qiita.com/mochizuki875/items/96b55ff7f3b6536aa9e5)という記事の続きになります。

`Keycloak`で作成したグループに対して`Kubernetes`の特定`Namespace`のみに対する操作権限を与えることで、テナント制御を実現する。

イメージは以下の通り。
![pic29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/e78155fb-4f08-0b6a-20b7-a019459cfe89.png)

## Keycloakの設定

`Keycloak`の`Administration Console`にログインしてユーザーとグループを作成する。

### グループの作成

`group-a`、`group-b`というグループをそれぞれ作成する。

- `group-a`: `Kubernetes`クラスターの`Namespace=tenant-a`（後程作成）に対して管理者権限を持つグループ
- `group-b`: `Kubernetes`クラスターの`Namespace=tenant-b`（後程作成）に対して管理者権限を持つグループ

※キャプチャでは`group-a`の作成しか示していないが`group-b`についても同様に作成する。

![pic30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/e0b9d421-1c2c-7405-5282-075e2ce655ef.png)
![pic31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/0e329b49-2177-fce6-ba7b-22bafd2b3c82.png)


### ユーザーの作成

`user-a`、`user-b`というユーザーをそれぞれ作成する。

- `user-a`: `group-a`グループに所属
- `user-b`: `group-b`グループに所属

※キャプチャでは`user-a`の作成しか示していないが`user-b`についても同様に作成する。
※パスワードは任意の値を設定（ここではP@ssw0rdという値を設定した）

![pic32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/8b8ed9a1-bd10-3762-dfdc-c84554702ddf.png)
![pic33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/d2bd32f1-2c34-c0c1-635d-c979ccb2b066.png)
![pic34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/247c9ccd-3e3f-2b9b-ec96-b8397c3c48e6.png)

## Kubernetesクラスターの設定

以下の`manifest`を用いて`Namespace`,`Role`,`RoleBinding`を作成。
※リソースの作成は`Cluster-Role=cluster-admin`相当の権限を持つユーザーで実施した。

`tenant-a.yaml`

```yaml
kind: Namespace
apiVersion: v1
metadata:
  name: tenant-a
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tenant-a
  namespace: tenant-a
rules:
- apiGroups:
  - '*'
  resources:
  - '*'
  verbs:
  - '*'
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tenant-a-rb
  namespace: tenant-a
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: tenant-a
subjects:
- kind: Group
  name: "group-a"
  apiGroup: rbac.authorization.k8s.io
```

`tenant-b.yaml`

```yaml
kind: Namespace
apiVersion: v1
metadata:
  name: tenant-b
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tenant-b
  namespace: tenant-b
rules:
- apiGroups:
  - '*'
  resources:
  - '*'
  verbs:
  - '*'
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: tenant-b-rb
  namespace: tenant-b
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: tenant-b
subjects:
- kind: Group
  name: "group-b"
  apiGroup: rbac.authorization.k8s.io
```

リソースの作成

```
# kubectl apply -f tenant-a.yaml
namespace/tenant-a created
role.rbac.authorization.k8s.io/tenant-a created
rolebinding.rbac.authorization.k8s.io/tenant-a-rb created

# kubectl apply -f tenant-b.yaml
namespace/tenant-b created
role.rbac.authorization.k8s.io/tenant-b created
rolebinding.rbac.authorization.k8s.io/tenant-b-rb created
```

## 動作確認

ここでは`user-a`について動作確認を行っているが、`user-b`についても同様。

### 認証

`group-a`に所属する`user-a`で認証し、`id-token`と`refresh-token`を取得する。

```
# curl -k -d "grant_type=password" -d "scope=openid" -d "client_id=kubernetes" -d "client_secret=<client=kubernetesのsecret>" -d "username=user-a" -d "password=<Keycloakで作成したuser-aのパスワード>" https://keycloak.example.com:32084/realms/kubernetes/protocol/openid-connect/token | jq .
```

取得した`id-token`と`refresh-token`を用いて`kubeconfig`の設定を行う。

```
# kubectl config set-credentials user-a \
    "--auth-provider=oidc" \
    "--auth-provider-arg=idp-issuer-url=https://keycloak.example.com:32084/realms/kubernetes" \
    "--auth-provider-arg=client-id=kubernetes" \
    "--auth-provider-arg=idp-certificate-authority=<keycloak公開鍵のパス>" \
    "--auth-provider-arg=client-secret=<client=kubernetesのsecret>" \
    "--auth-provider-arg=id-token=<id-token>" \
    "--auth-provider-arg=refresh-token=<refresh-token>"
    
# kubectl config set-context user-a@<kubeconfigで定義されているk8sクラスタ名> --cluster=<kubeconfigで定義されているk8sクラスタ名> --user=user-a

# kubectl config use-context user-a@<kubeconfigで定義されているk8sクラスタ名>
Switched to context "user-a@<kubeconfigで定義されているk8sクラスタ名>".
```

### クラスターアクセス

`user-a`で`Namespace=tenant-a`にアクセスし、諸々の操作が行えることを確認。

```
tenant-aにPodの作成
# kubectl run nginx-a --image=nginx --restart=Never -n tenant-a
pod/nginx-a created

tenant-aのリソース参照
# kubectl get all -n tenant-a
NAME          READY   STATUS    RESTARTS   AGE
pod/nginx-a   1/1     Running   0          87m
```

`user-a`で`Namespace=tenant-b`にアクセスし、諸々の操作が行えないことを確認。

```
# kubectl get all -n tenant-b
Error from server (Forbidden): pods is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#a user" cannot list resource "pods" in API group "" in the namespace "tenant-b"
Error from server (Forbidden): replicationcontrollers is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#a user" cannot list resource "replicationcontrollers" in API group "" in the namespace "tenant-b"
Error from server (Forbidden): services is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#a user" cannot list resource "services" in API group "" in the namespace "tenant-b"
Error from server (Forbidden): daemonsets.apps is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#a user" cannot list resource "daemonsets" in API group "apps" in the namespace "tenant-b"
Error from server (Forbidden): deployments.apps is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#a user" cannot list resource "deployments" in API group "apps" in the namespace "tenant-b"
Error from server (Forbidden): replicasets.apps is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#a user" cannot list resource "replicasets" in API group "apps" in the namespace "tenant-b"
Error from server (Forbidden): statefulsets.apps is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#a user" cannot list resource "statefulsets" in API group "apps" in the namespace "tenant-b"
Error from server (Forbidden): horizontalpodautoscalers.autoscaling is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#a user" cannot list resource "horizontalpodautoscalers" in API group "autoscaling" in the namespace "tenant-b"
Error from server (Forbidden): cronjobs.batch is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#a user" cannot list resource "cronjobs" in API group "batch" in the namespace "tenant-b"
Error from server (Forbidden): jobs.batch is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#a user" cannot list resource "jobs" in API group "batch" in the namespace "tenant-b"
```

---

## 宣伝

Twitterやってます。
よかったらフォローいただけると嬉しいです。

@mochizuki875
プロになりたいITエンジニア Kubernetesが好き
a fox, not a raccoon dog.
https://twitter.com/mochizuki875