kubectlの適切なクラスタ接続設定を考えてみた

# はじめに
業務でkubectlでのEKSクラスタへの接続設定を定期的に変更するのですが、その運用が適切でなく、先日クラスタに誰もアクセスできない状態になるというインシデントがありました。
その経緯や解決策の紹介と、あわせてクラスタアクセスの認証の仕組みについてなど、忘備録としてまとめたいと思います。

# クラスタのユーザ認証・認可の仕組み
## コンポーネント
クラスタのユーザ認証管理は、下記コンポーネントから成り立っています。
- kubeconfig:
ユーザがクラスタ内のAPIサーバと通信するために必要な情報を定義しており、これを接続元(ユーザのkubectl実行環境)に設定することで、クラスタに接続できるようになります。
kubeconfigは下記awsコマンドの実行により自動生成され、~/.kube/configに配置されます。
```
$ aws eks update-kubeconfig --region {region-code} --name {my-cluster}
```
- aws-auth: 
クラスタのkube-system namespace内に予め配置されているConfigMapです。IAMプリンシパル(IAMユーザ・ロール)が所属するグループを定義することで、クラスタへの操作権限を設定します。具体的には、操作権限自体はRBAC(ClusterRoleBinding・ClusterRole)により定義されており、aws-authでIAMプリンシパルに対し設定したGroupに紐づいているRBACの権限が反映されます。

## クラスタ作成者のアクセス権限
下記にあるように、aws-authの設定とは関係なく、クラスタを作成したIAMプリンシパルにはsystem:mastersのGroupの操作権限でクラスタにアクセスできます。
> Amazon EKS クラスターを作成すると、このクラスターを作成する IAM プリンシパルには、Amazon EKS コントロールプレーンのクラスターロールベースアクセスコントロール (RBAC) 設定で、system:masters グループの許可が自動的に付与されます。

system:mastersのGroupは下記RBACに紐づいています。またこのRBACはデフォルトで存在しているもので、スーパーユーザーとして任意のリソースで任意のアクションを実行できる設定になっています。
```
$ kubectl get -n kube-system clusterrolebinding cluster-admin -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata: 
　・・・
  name: cluster-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: Group
  name: system:masters
```

```
$ kubectl get -n kube-system clusterrole cluster-admin -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
　・・・
  name: cluster-admin
rules:
- apiGroups:
  - '*'
  resources:
  - '*'
  verbs:
  - '*'
- nonResourceURLs:
  - '*'
  verbs:
  - '*'
```

## ユーザ認証・認可処理の流れ
ユーザがクラスタに接続する際のユーザ認証・認可処理の流れは下記のとおりです。
![流れ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521220/a831c2e1-d014-daf2-4dfb-9dd632aa2fa0.png)


[1] ユーザによりkubectlが実行され、kubeconfigに記載されているクラスタエンドポイントやトークン取得コマンドを取得
[2] トークン取得コマンドを実行し、生成したトークンを使いクラスタ内のAPIサーバへアクセス
[3] IAMがトークン認証をし、正しいIAMユーザであることを確認
[4] aws-auth(ConfigMap)を取得し、IAMユーザに紐づいているRBACを確認
[5] RBACを取得し、ユーザの操作権限を確認
[6] 要求されたkubectlの操作を許可or拒否する


# クラスタに誰もアクセスできない状態になった原因・解決策・再発防止策
## 原因
クラスタ作成後、aws-authにクラスタ運用者を下記のように追加していました。
```yaml
apiVersion: v1
data:
  mapRoles: 
    ・・・
  mapUsers: |
    - groups:
      - system:masters
      userarn: arn:aws:iam::XXXXXXXXXXXX:user/user1
    - groups:
      - system:masters
      userarn: arn:aws:iam::XXXXXXXXXXXX:user/user2
kind: ConfigMap
metadata: 
  ・・・
```

また、クラスタ運用者追加を手動で行う運用をしていました。（下記コマンドを実行すると、aws-authファイルの編集画面に遷移します）
```
$ kubectl edit -n kube-system configmap/aws-auth
```

今回のインシデントが起きた発端は、手動でaws-authの編集行っている中で、文法を誤った状態で保存してしまったことです。それにより、まずaws-authに登録していた全IAMユーザがクラスタにアクセスできない状態になってしまいました。
また、クラスタを作成したIAMプリンシパルの情報を記録していない状態でしたので、クラスタに誰もアクセスできない状態になってしまいました。

## 解決策
幸い、クラスタを作成したIAMプリンシパルを特定することで、aws-authを修正し解決に至りました。
ですが、調査に長時間要してしまい、クラスタを作成したIAMプリンシパルを記録しておくことの重要性を痛感しました。

聞くと他チームでも、クラスタを作成したユーザが退職したためIAMユーザを削除しており、同様のインシデントが発生したことがあったようです。
この場合の解決策としては、クラスタを作成したIAMプリンシパルと同じARNになるように作成しなおし、そのIAMプリンシパルでクラスタにアクセスするというものです。EKSクラスタはIAMプリンシパルをARNで認識しているため、アクセスキーの値などが変わっても、同一のIMAプリンシパルとして認識されるようです。


# 再発防止策
## 1. IAMロールを使いクラスタを作成する
IAMユーザでクラスタを作成すると、部署移動や退職等の理由でIAMユーザを削除しており、なおかつARNが特定できないなど、クラスタに誰もアクセスできない状況に陥る可能性が高いです。
ですので、そもそもクラスタ作成時に、専用のIAMロールを使うようにすることで、チーム内の認識合わせがしやすくなります。

## 2. IAMロールにクラスタへのアクセス許可をする
下記にもある通り、1と同じ理由で、aws-authの設定についても、IAMユーザではなくIAMロールに対し設定するように運用を改善しました。
> クラスターを作成したユーザーでない場合は、以降の手順を実行して、他の IAM プリンシパルのクラスターへのアクセスを有効にする必要があります。IAM のベストプラクティスでは、ユーザーではなくロールに許可を付与することが推奨されています。

## 3. aws-authをyamlファイル管理にする
ヒューマンエラーを最小限にするため、kubectl editを使った手動での変更はしないのが一番です。下記のように、yamlファイル管理にすること、eksctlを使うことで、aws-authを安全に修正するような運用に改善しました。
```yaml:cluster.yaml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
（省略）
iamIdentityMappings:
- arn: arn:aws:iam::XXXXXXXXXXXX:role/administrator-role
  groups:
    - system:masters
  username: administrator
```
```
$ eksctl create iamidentitymapping -f cluster.yaml
```

# おわりに
今回は、クラスタの接続設定管理に関するインシデントの紹介にあわせ、再発防止策として自分なりのベストプラクティスを紹介しました。
かなり基礎的かつ地味な内容だと思いますが、そのような運用こそおろそかにならないように気を付けていきたなと思いました。

# 参考
https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/add-user-role.html

https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/create-kubeconfig.html

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

https://eksctl.io/usage/iam-identity-mappings/