KeycloakによるKubernetesのOIDC認証を試す

## 1.はじめに
一般的に`Kubernetes`はユーザー管理の仕組みを有しておらず何かしら外部の仕組みを用いてユーザーの認証を行うことになり、`kubeadm`や`minikube`などによりバニラな`Kubernetes`クラスターを構築した場合、通常は`x509証明書`（adminの証明書が払い出される）によりユーザー認証を行うことになります。

ここでは`OIDC(OpenID Connect)IDプロバイダー`の1つである`Keycloak`でユーザー情報の管理および`OIDC認証`を行い、その認証情報（`id_token`）を用いて`Kubernetes（API）`での認証を行うことを目指します。

Keycloak
https://www.keycloak.org/

なお、`Kubernetes`における`OIDC`認証のシーケンスは公式ドキュメントで示されている以下が参考になります。

![pic0.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/e0ed76c4-cf57-1971-7074-809d1e51705c.png)

OpenID Connect Tokens
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens

## 2.前提
- `minikube v1.25.2(Kubernetes v1.23.3)`のシングルNode構成で検証した
- `Keycloak`は現時点の最新バージョンである`v18.0.0`を用いることとし、`Kubernetes(minikube)`クラスター上に構築した（一般的な`Kubernetes`クラスター上に構築するケースでもそんなに大差はない想定）
- 検証用なので`Keycloak`は以下の条件で起動した
  - `dev mode`（本来であれば`prod mode`で起動すべき）
  - `Keycloak`のデータストアはInternal DB（`H2`）を永続化して利用（本来であれば`PostgreSQL`など外部DBを利用すべき）
  - 証明書は自己証明書を利用した（本来であれば信頼できるCAで署名されたものを利用すべき）
- `Keycloak`の名前解決には`DNS`ではなく`hostsファイル`を使用した
  - `Keycloak`にはそれぞれ`kube-apiserver`とクライアントが同じホスト名でアクセスできる必要があるため暫定対処
  - 本来は`DNS`の名前解決でアクセスできるのが望ましい
- `Keycloak`のアクセスエンドポイント（`HTTP/HTTPS`）は`NodePort`とした
  - `Keycloak`にはそれぞれ`kube-apiserver`とクライアントが同じポートでアクセスできる必要があるため暫定対処
  - 本来は`ingress`や`LB`に証明書のインポートを行いTLS終端すべき

基本的に`Keycloak`公式ドキュメントに沿っているが、必要に応じて設定を変更している
Getting started/Kubernetes
https://www.keycloak.org/getting-started/getting-started-kube

## 3.Keycloakの証明書を作成
まずは`Keycloak`が`TLS`に用いるキーペアを作成する。
この後キーペアを`Kubernetes`クラスター上にデプロイする`Pod`に`hostPath`でマウントするため`minikube VM`上で作成を行う。

`minikube`に`SSH`接続する。

```
minikube ssh
```

ディレクトリ作成

```
sudo su -
mkdir /srv/keycloak-ssl
cd /srv/keycloak-ssl 
```

証明書設定ファイルの作成

`sslcert.conf`

```
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = keycloak 
[v3_req]      
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth, clientAuth   
subjectAltName = @alt_names                 
[alt_names]
DNS.1 = keycloak                            
DNS.2 = keycloak.example.com
```

キーペアの作成

```
# openssl req -x509 -nodes -days 730 -newkey rsa:2048 -keyout tls.key -out tls.crt -config sslcert.conf -extensions 'v3_req'

ls
sslcert.conf  tls.crt  tls.key 
```

公開鍵の内容確認
`Issuer: CN`と`Subject: CN`が同じ`自己証明書`（所謂オレオレ証明書）になっていることがわかる。

```
# openssl x509 -in tls.crt --text --noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            53:e8:c5:1e:e4:6c:dd:13:59:f8:4d:81:0d:6f:56:97:06:e3:4f:f3
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN = keycloak
        Validity
            Not Before: May  8 06:27:59 2022 GMT
            Not After : May  7 06:27:59 2024 GMT
        Subject: CN = keycloak
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                ・・・
```

今回`Keycloak`のPodは`hostPath`を用いてキーペア格納フォルダをマウントするため権限を変更しておく。
※厳密には`tls.key`に`Read`権限を付与しておけば良いはず。

```
# chmod -R 777 /srv/keycloak-ssl 
```

`Master Node`への公開鍵配置

```
# mkdir -p /etc/kubernetes/ssl/
# cp tls.crt /etc/kubernetes/ssl/kc-ca.crt
# ls /etc/kubernetes/ssl/
kc-ca.crt
```

## 4.Keycloak永続化用ディレクトリの作成
`Keycloak`を`Pod`としてデプロイする際、通常は`Keycloak`内部のローカルDB（`H2`）に情報が保存され、コンテナが再起動するとそれらのデータは揮発してしまう。
本来は外部DB（`PostgreSQL`など）を保存先として指定すべきだが、今回はデータ保存先ディレクトリを`minikube VM`上のディレクトリとし、`Pod`に`hostPath`として`volumeMounts`することで対応した。

`minikube VM`上で保存先ディレクトリを作成する。

```
# mkdir -p /srv/keycloak
# chmod -R 777 /srv/keycloak
# ls -la /srv
total 0
drwxr-xr-x  4 root root  80 May  8 06:46 .
drwxr-xr-x 19 root root 500 May  8 06:24 ..
drwxrwxrwx  2 root root  40 May  8 06:46 keycloak
drwxrwxrwx  2 root root 100 May  8 06:27 keycloak-ssl
```

## 5.Keycloak構築
`minikube`の操作権限のあるクライアントから`Kubernetes`クラスタ上に`Keycloak`を`Pod`としてデプロイする。

### Keycloakのデプロイ
`Keycloak`公式ドキュメントを参考に一部`manifest`を変更し、以下の`manifest`を用いてデプロイした。

`Keycloak`起動設定留意点
- `dev mode`で起動
- `HTTPSポート`の指定（デフォルト8443）
-  `NodePort`でサービスを公開
- `TLSキーペア`の指定と格納先を`hostPath`としてマウント

`keycloak.yaml`

```yaml
apiVersion: v1
kind: Namespace
metadata:
  name: keycloak
---
apiVersion: v1
kind: Service
metadata:
  name: keycloak
  namespace: keycloak
  labels:
    app: keycloak
spec:
  ports:
  - name: http
    port: 8080
    targetPort: 8080
    nodePort: 31008 # NodePortを明示
  - name: https # HTTPアクセス用に8443ポートを公開
    port: 8443
    targetPort: 8443
    nodePort: 32084 # NodePortを明示
  selector:
    app: keycloak
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: keycloak
  namespace: keycloak
  labels:
    app: keycloak
spec:
  replicas: 1
  selector:
    matchLabels:
      app: keycloak
  template:
    metadata:
      labels:
        app: keycloak
    spec:
      containers:
      - name: keycloak
        image: quay.io/keycloak/keycloak:18.0.0
        args: ["start-dev"] # dev modeで起動
        env:
        - name: KEYCLOAK_ADMIN
          value: "admin"
        - name: KEYCLOAK_ADMIN_PASSWORD
          value: "admin"
        - name: KC_PROXY
          value: "edge"
        - name: "KC_HTTP_ENABLED" # HTTP接続有効化（ブラウザアクセス用）
          value: "true"
        - name: "KC_HTTPS_PORT" # HTTPSポート
          value: "8443"
        - name: KC_HTTPS_CERTIFICATE_FILE # 公開鍵のパス
          value: "/opt/keycloak/tls/tls.crt"
        - name: KC_HTTPS_CERTIFICATE_KEY_FILE # 秘密鍵のパス
          value: "/opt/keycloak/tls/tls.key"
        ports:
        - name: http
          containerPort: 8080
        - name: https # TLS用に8443ポートを公開
          containerPort: 8443
        readinessProbe:
          httpGet:
            path: /realms/master
            port: 8080
        volumeMounts: # 再起動してもデータが揮発しないよう暫定でhostPathにマウント
        - name: "keycloak-persistent-storage"
          mountPath: "/opt/keycloak/data"
        - name: "keycloak-ssl" # キーペア格納先
          mountPath: "/opt/keycloak/tls"
      volumes:
      - name: keycloak-persistent-storage
        hostPath:
          path: /srv/keycloak
          type: DirectoryOrCreate
      - name: keycloak-ssl # 証明書格納先
        hostPath:
          path: /srv/keycloak-ssl     
          type:  DirectoryOrCreate
```

デプロイ実行

```
# kubectl apply -f keycloak.yaml
namespace/keycloak created
service/keycloak created
deployment.apps/keycloak created

# kubectl -n keycloak get all
NAME                            READY   STATUS    RESTARTS   AGE
pod/keycloak-679b66bbd8-l2n9g   1/1     Running   0          5m16s

NAME               TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                         AGE
service/keycloak   NodePort   10.102.40.27   <none>        8080:31008/TCP,8443:32084/TCP   5m16s

NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/keycloak   1/1     1            1           5m16s

NAME                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/keycloak-679b66bbd8   1         1         1       5m16s
```


### Keycloak名前解決設定
今回は検証なので`/etc/hosts`にレコード追加して対応した。
※`Keycloak`にブラウザアクセスする端末と`minikube VM`上それぞれで追加

`minikube VM`のIPアドレス確認

```
# minikube ip
192.168.59.101
```

`/etc/hosts`

```
・・・
<minikube VM IP>	keycloak.example.com
・・・
```

## 6.Keycloakアクセス確認
`Keycloak`へのアクセス確認を行う。

### curlアクセス確認（HTTPS）
`minikube VM`から`curl`コマンドで`HTTPS`アクセスできるか確認する。
`Keycloak`は自己証明書を利用しているため、公開鍵をCAとして署名している。
そのため`TLS`するクライアント側で`Keycloak`のCA証明書（=公開鍵)を信頼することでアクセスできるようにする。

```
# curl -v --cacert /etc/kubernetes/ssl/kc-ca.crt https://keycloak.example.com:32084
```

### ブラウザアクセス確認（HTTP）
ブラウザで以下にアクセスする。
※ブラウザから直接`HTTPS`でアクセスしても`ERR_SSL_KEY_USAGE_INCOMPATIBLE`で画面表示できなかったため、`HTTP`アクセスするようにした

```
http://keycloak.example.com:31008
```

## 7.Keycloakの設定
`Keycloak`にブラウザからアクセスして以下の設定を行う。

### 管理者画面にアクセス
`ID/Password=admin`で`Administration Console`にログイン。

![pic1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/98c249c2-9ad4-7942-528c-87c333d2903f.png)
![pic2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/cac77a83-bb30-8ea1-d9fa-1311e2704c3c.png)

### Realmの作成
`Keycloak`においてテナントの概念に該当する`Realm`を作成する。
今回は`kubernetes`という名前で作成した。

![pic3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/5ca16031-9545-3997-ff4b-81b329da7ce5.png)
![pic4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/de07b375-23c7-1d4c-4947-201a66f50f9b.png)
![pic5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/21f48742-2ea8-bc97-fd31-8713a2c06f0b.png)

### ClientsとClientScopesの作成
`Keycloak`の認証受口になる`Client`とその設定に該当する`Client Scope`を作成する。

`Client`の作成
- `Client ID`: `kubernetes`
- `Access Type`: `confidential`
- `Valid Redirect URIs`: `http://*` `https://*`

![pic6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/4062719d-a04b-6754-99c5-a4e3b152fdab.png)
![pic7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/8ca807f9-3464-cfcf-5514-d354b30b11f1.png)
![pic8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/57029003-9e91-96fe-7305-0232c1231c9a.png)
![pic9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/a4da9d98-3e98-fdfc-7de5-9860127c7c3c.png)

`Client Scope`の作成
- `Client Scope`: `groups`
  - `Mappers`: `name` `groups`を追加

![pic10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/654f7325-0e35-ae6d-e870-057d2aebcf7b.png)
![pic11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/881f2a09-7f47-bca1-7b89-52c4e7c9eebf.png)
![pic12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/2f80229b-024d-3b9c-b235-6ce0018cfcfe.png)
![pic13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/c7291785-4a2b-629d-125e-33f3ccd1f251.png)
![pic14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/d9a49526-ad81-4c55-4aea-dc12a537c9db.png)
![pic15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/ce4c6602-c43b-98cb-5e30-4dde2a948574.png)
![pic16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/b31fbbdd-2cba-ffb3-ac54-b5b0ba546704.png)
![pic17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/399e1984-ee2e-a0cf-f408-359b7f5d295e.png)

`Client`の`Client Scopes`に`groups`を追加

![pic18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/c77a4053-fcef-a871-5e0f-4384df99dc36.png)
![pic19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/20967395-05dd-16a2-f61e-4bd33871d1ef.png)
![pic20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/fb964bc4-ec1d-7cec-53b9-931b21f60d11.png)

`Client`の`Credentials`から`Secret`取得

![pic21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/cd382743-81b6-8959-9d3e-8a63f00dc698.png)

### ユーザーとグループの作成
認証に用いるユーザーとグループを作成する。

`administrators`、`developers`というグループを作成。
これらのグループに対して後程`RBAC`により`Kubernetes`クラスターに対する権限を付与する
- `administrators`: `Kubernetes`クラスターの管理者権限を持つグループ
- `developers`: `Kubernetes`クラスターに対して特定の権限のみしか持たないグループ

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

![pic22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/7c37e71c-3d60-33f8-149f-784198cc2765.png)
![pic23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/379e56ff-46ae-81b2-4b11-8dfcb65ee9c9.png)
![pic24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/e81e6690-f600-a4bf-61f8-d2b4b60a7576.png)

`admin-user`、`dev-user`というユーザーをそれぞれ作成。
- `admin-user`: `administrators`グループに所属
- `dev-user`: `developers`グループに所属

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

![pic25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/46b2626b-280a-b83c-9f8c-0ee1829a2260.png)
![pic26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/ce590acc-e4a2-5a6a-d8e0-6593000ca5be.png)
![pic27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/8119b85c-dc57-8d6a-3be8-f1dac1248ecc.png)

### 確認

以下コマンドにより`Keycloak`に認証を行い`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=<Keycloakで作成したユーザー名>" -d "password=<Keycloakで作成したユーザーパスワード>" https://keycloak.example.com:32084/realms/kubernetes/protocol/openid-connect/token | jq .

・・・
{
  "access_token": ・・・,
  "expires_in": 300,
  "refresh_expires_in": 1800,
  "refresh_token": ・・・
  "id_token": ・・・,
  "not-before-policy": 0,
  "session_state": ・・・,
  "scope": "openid profile email groups"
}
```

以下コマンドにより各`token`の検証が行える。

- `exp`: `token`有効期限（UNIX時間）
- `iat`: `token`発行時間（UNIX時間）
- `active`: `token`の有効有無

```
# curl -k --user "kubernetes:<client=kubernetesのsecret>" -d "token=<token>" https://keycloak.example.com:32084/realms/kubernetes/protocol/openid-connect/token/introspect | jq .

・・・
{
  "exp": 1651997178,
  "iat": 1651996878,
  ・・・
  "active": true
}
```

## 8.kube-apiserverの設定
`kube-apiserver`が`Keycloak`を用いて`OIDC認証`を行うための設定を行う。
`minikube VM`にアクセスし、`kube-apiserver`の`manifest`に起動OPおよび`Keycloak`CA証明書（=公開鍵)格納先を`volumeMounts`する。
なお、`kube-apiserver`は`Static Pod`として起動しているため`manifest`変更後に自動的に再デプロイされる。

`/etc/kubernetes/manifests/kube-apiserver.yaml`

```yaml
・・・
spec:
  containers:
  - command:
    - kube-apiserver
    ・・・
    - --oidc-issuer-url=https://keycloak.example.com:32084/realms/kubernetes
    - --oidc-client-id=kubernetes
    - --oidc-username-claim=name
    - --oidc-groups-claim=groups
    - --oidc-ca-file=/etc/kubernetes/ssl/kc-ca.crt
    ・・・
    volumeMounts:
    ・・・
    - mountPath: /etc/kubernetes/ssl
      name: keycloak-ca-certificates
      readOnly: true
  ・・・
  volumes: 
  ・・・
  - hostPath:
      path: /etc/kubernetes/ssl
      type: DirectoryOrCreate
    name: keycloak-ca-certificates
status: {} 
```

## 9.RBACの作成
`Keycloak`で作成したグループに対して`RBAC`の設定を行う。
今回はそれぞれ以下のような権限設定とする。

- `administrators`: クラスターに対する全ての権限を付与（ビルドインの`ClusterRole=cluster-admin`をバインド）
- `developers`: `Namespace`と`Pod`の参照権限のみを付与（独自で`ClusterRole=developer-role`を作成してバインド）

`adminrole.yaml`

```yaml
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: administrator-crb
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: Group
  name: "administrators"
  apiGroup: rbac.authorization.k8s.io
```

`devrole.yaml`

```yaml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: developer-role
rules:
  - apiGroups: [""]
    resources: ["namespaces","pods"]
    verbs: ["get", "watch", "list"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: developer-crb
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: developer-role
subjects:
- kind: Group
  name: "developers"
  apiGroup: rbac.authorization.k8s.io
```

クラスターに適用

```
# kubectl apply -f adminrole.yaml
clusterrolebinding.rbac.authorization.k8s.io/administrator-crb created

# kubectl apply -f devrole.yaml
clusterrole.rbac.authorization.k8s.io/developer-role created
clusterrolebinding.rbac.authorization.k8s.io/developer-crb created
```

## 10.kubectlの設定
`kubectl`には`OIDC ID プロバイダー`にログインする仕組みが用意されていないため、
手動でログインおよび`kubeconfig`への`token`設定を行う。

![pic28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/246372/ee7e9b20-1658-9667-8733-a55d686be1f1.png)

`kubectl`からログインを行うためのプラグインも存在する模様
kubelogin
https://github.com/int128/kubelogin

### 手順①：Keycloakへのログイン

以下コマンドにより`Keycloak`に認証を行い`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=<Keycloakで作成したユーザー名>" -d "password=<Keycloakで作成したユーザーパスワード>" https://keycloak.example.com:32084/realms/kubernetes/protocol/openid-connect/token | jq .
```

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

```
# kubectl config set-credentials <Keycloakで作成したユーザー名> \
    "--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 <Keycloakで作成したユーザー名>@<kubeconfigで定義されているk8sクラスタ名> --cluster=<kubeconfigで定義されているk8sクラスタ名> --user=<Keycloakで作成したユーザー名>

# kubectl config use-context <Keycloakで作成したユーザー名>@<kubeconfigで定義されているk8sクラスタ名>
Switched to context "<Keycloakで作成したユーザー名>@<kubeconfigで定義されているk8sクラスタ名>".
```

上記操作を一括で実施する`shell`

```sh
#!/bin/bash

scope=openid
client_id=kubernetes
client_secret=<client=kubernetesのsecret>
username=<Keycloakで作成したユーザー名>
password=<Keycloakで作成したユーザーパスワード>
oidc_url=https://keycloak.example.com:32084/realms/kubernetes/protocol/openid-connect/token
realm_url=https://keycloak.example.com:32084/realms/kubernetes
certificate=<keycloak公開鍵のパス>
cluster=<kubeconfigで定義されているk8sクラスタ名>

### Generate Authentication token

json_data=`curl -k -d "grant_type=password" -d "scope=${scope}" -d "client_id=${client_id}" -d "client_secret=${client_secret}" -d "username=${username}" -d "password=${password}" ${oidc_url}`

id_token=`echo $json_data | jq '.id_token' | tr -d '"'`
refresh_token=`echo $json_data | jq '.refresh_token' | tr -d '"'`
access_token=`echo $json_data | jq '.access_token' | tr -d '"'`

### Print tokens

echo "ID_TOKEN=$id_token"; echo
echo "REFRESH_TOKEN=$refresh_token"; echo
echo "ACCESS_TOKEN=$access_token"; echo

### Introspect the id token

token=`curl -k --user "${client_id}:${client_secret}" -d "token=${id_token}" ${oidc_url}/introspect`
token_details=`echo $token | jq .`
echo $token_details

### Update kubectl config

kubectl config set-credentials ${username} \
    "--auth-provider=oidc" \
    "--auth-provider-arg=idp-issuer-url=${realm_url}" \
    "--auth-provider-arg=client-id=${client_id}" \
    "--auth-provider-arg=client-secret=${client_secret}" \
    "--auth-provider-arg=refresh-token=${refresh_token}" \
    "--auth-provider-arg=idp-certificate-authority=${certificate}" \
    "--auth-provider-arg=id-token=${id_token}"

### Create new context

kubectl config set-context ${username}@${cluster} --cluster=${cluster} --user=${username}

### Set current context
kubectl config use-context ${username}@${cluster} 

### Validate access with new context

kubectl get pods

```

## 11.動作確認
`Keycloak`で作成したユーザーを用いて`Kubernetes`クラスターへのアクセスを確認する。

### admin-user
クラスターに対して全ての操作が可能

```
# kubectl -n kube-system get all
NAME                                   READY   STATUS    RESTARTS      AGE
pod/coredns-64897985d-dn9c4            1/1     Running   0             125m
pod/etcd-minikube                      1/1     Running   0             125m
pod/kube-apiserver-minikube            1/1     Running   0             15m
pod/kube-controller-manager-minikube   1/1     Running   0             125m
pod/kube-proxy-gbw5v                   1/1     Running   0             125m
pod/kube-scheduler-minikube            1/1     Running   0             125m
pod/storage-provisioner                1/1     Running   4 (15m ago)   125m

NAME               TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)                  AGE
service/kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   125m

NAME                        DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE
daemonset.apps/kube-proxy   1         1         1       1            1           kubernetes.io/os=linux   125m

NAME                      READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/coredns   1/1     1            1           125m

NAME                                DESIRED   CURRENT   READY   AGE
replicaset.apps/coredns-64897985d   1         1         1       125m
```

### dev-user
許可されていないリソースの参照はエラーになる。

```
# kubectl -n kube-system get all
NAME                               READY   STATUS    RESTARTS      AGE
coredns-64897985d-dn9c4            1/1     Running   0             128m
etcd-minikube                      1/1     Running   0             128m
kube-apiserver-minikube            1/1     Running   0             17m
kube-controller-manager-minikube   1/1     Running   0             128m
kube-proxy-gbw5v                   1/1     Running   0             128m
kube-scheduler-minikube            1/1     Running   0             128m
storage-provisioner                1/1     Running   4 (18m ago)   128m
Error from server (Forbidden): replicationcontrollers is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "replicationcontrollers" in API group "" in the namespace "kube-system"
Error from server (Forbidden): services is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "services" in API group "" in the namespace "kube-system"
Error from server (Forbidden): daemonsets.apps is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "daemonsets" in API group "apps" in the namespace "kube-system"
Error from server (Forbidden): deployments.apps is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "deployments" in API group "apps" in the namespace "kube-system"
Error from server (Forbidden): replicasets.apps is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "replicasets" in API group "apps" in the namespace "kube-system"
Error from server (Forbidden): statefulsets.apps is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "statefulsets" in API group "apps" in the namespace "kube-system"
Error from server (Forbidden): horizontalpodautoscalers.autoscaling is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "horizontalpodautoscalers" in API group "autoscaling" in the namespace "kube-system"
Error from server (Forbidden): cronjobs.batch is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "cronjobs" in API group "batch" in the namespace "kube-system"
Error from server (Forbidden): jobs.batch is forbidden: User "https://keycloak.example.com:32084/realms/kubernetes#user dev" cannot list resource "jobs" in API group "batch" in the namespace "kube-system"
```

## つづき
[KeycloakのOIDC認証を利用してKubernetesでテナント制御を行う](https://qiita.com/mochizuki875/items/79b087f60906b90e6f43)

## 参考
Keycloak/Getting started/Kubernetes
https://www.keycloak.org/getting-started/getting-started-kube

Kubernetes/Authenticating
https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens

How to authenticate user with Keycloak OIDC Provider in Kubernetes
https://middlewaretechnologies.in/2022/01/how-to-authenticate-user-with-keycloak-oidc-provider-in-kubernetes.html

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

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