【AWS】Amazon EKSクラスターへのアクセス制御方法について(EKS API / ConfigMap)

## はじめに
実務においてEKSクラスターへのアクセス制御について理解が浅いと感じたため、深堀りしていこうと思います。

EKSクラスターへのアクセス制御については`ConfigMap`と`EKS API`という二つの方法が存在します。
2023年12月18日のアップデートがあるまでは`ConfigMap`による制御のみでした。
しかし、アップデートしたことにより、より簡素化された`EKS API`という方式でKubernetesクラスターへアクセスできることとなりました。

この`ConfigMap`と`EKS API`の違いについてまとめた上でEKS APIのアクセスについて解説します。

## 結論
いきなり結論です。
### ConfigMap
AWS IAMユーザまたはロールとKubernetes上のユーザやグループとマッピングを行い、EKSクラスターからEKSリソースへのアクセスをする必要がある。

### EKS API
AWSのIAMユーザやロールに対してKubernetesの権限を設定することができる。


## ConfigMapとEKS APIの違いについて
### ConfigMapとは
Kubernetes APIにアクセスするための認証/認可では`aws-auth`を使用し、AWSのIAMユーザやロールとKubernetesのユーザやロールをマッピングして権限設定する必要があります。
図で表すと以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379725/2affd6a2-1b4f-005d-2a8d-d258271e9898.png)

1. AWS IAMユーザまたはロールがkubeconfigファイルを使用してEKSで稼働している`kube-apiserver`へリクエスト
2. kube-apiserverはリクエストを処理する前にリクエストしてきたユーザやロールを認可する前に、`kube-system`のネームスペースに存在する`aws-auth`ConfigMapを参照
3. ConfigMapに基づいて、IAMユーザまたはロールをKubernetesのユーザやグループにマッピング
4. マッピングされたKubernetesユーザやグループが適用され、`kube-apiserver`がリクエストに対して適切な権限を確認し、AWS リソースへの操作を実行


### EKS APIとは
AWSで以下4つの権限を用意しており、これらの権限をIAMユーザまたはロールにアタッチすることで、EKSクラスターへのアクセスを制御します。

:::note 
- AmazonEKSClusterAdminPolicy <font color="DarkGray">(EKSサービスそのもののリソースに対する管理権限: EKSクラスターの作成、更新、削除、クラスターに関連するIAMロールやポリシーの管理、EKSサービスに関連する他のAWSリソースの管理が可能となる)</font>
- AmazonEKSAdminPolicy <font color="DarkGray">(EKSクラスター内部での管理者権限に相当し、全てのネームスペースでのリソース作成、更新、削除、クラスター設定の変更、リソースの追加及び削除などが可能となる)</font>
- AmazonEKSEditPolicy 
- AmazonEKSViewPolicy
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379725/441bb63c-39cd-e1e9-259a-82bbee4bb56c.png)


## 動作確認

### クラスター構築準備
Amazon Linux2023のAMIを使用したEC2インスタンスを起動し、EKSクラスターを作成します。
<font color="DeepPink">※EC2インスタンスの作成方法については省略します。</font>

以下のAWS公式ドキュメントを参照しながらeksctl[^1]をダウンロードします。


1.eksctlの最新リリースをダウンロードする
```bash:bash
$ curl --silent --location "https://github.com/weaveworks/eksctl/releases/latest/download/eksctl_$(uname -s)_amd64.tar.gz" | tar xz -C /tmp
```

2.ダウンロードしたバイナリを/usr/local/binに移動する
```bash:bash
$ sudo mv /tmp/eksctl /usr/local/bin
```

3.ダウンロード確認
```bash:bash
$ eksctl version
```

4.kubectlインストール (https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/install-kubectl.html)

```bash:bash
$ curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.30.0/2024-05-12/bin/linux/amd64/kubectl
```

5.チェックサム確認
```bash:bash
$ curl -O https://s3.us-west-2.amazonaws.com/amazon-eks/1.30.0/2024-05-12/bin/linux/amd64/kubectl.sha256
```

6.バイナリへの実行アクセスを許可する
```bash:bash
$ chmod +x ./kubectl
```

7.バイナリをPATHのフォルダにコピーする
```bash:bash
$ mkdir -p $HOME/bin && cp ./kubectl $HOME/bin/kubectl && export PATH=$HOME/bin:$PATH
```

8.シェルを開いたときに設定されるようにシェルの初期化ファイルに$HOME/binパスを追加
```bash:bash
$ echo 'export PATH=$HOME/bin:$PATH' >> ~/.bashrc
```

9.以下yamlファイルでクラスターを作成する (既存VPCおよびサブネットを指定しないとVPC, Subnetなど全てゼロから構築されてしまうため、自身で使用しているVPCやサブネットを使用する場合には明示的に指定します)
```bash:bash
$ eksctl create cluster -f cluster.yml
```
```yaml:sandbox-cluster.yml
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: sandbox-cluster
  region: ap-northeast-1

vpc:
  id: vpc-xxxxxxxxxxxxxxx # 自身のVPC IDに置き換えてください
  subnets:
    private:
      ap-northeast-1a:
        id: subnet-xxxxxxxxxxxxxxx # 自身のサブネットIDに置き換えてください
      ap-northeast-1c:
        id: subnet-xxxxxxxxxxxxxxx # 自身のサブネットIDに置き換えてください

fargateProfiles:
  - name: fp-default
    selectors:
      - namespace: default
      - namespace: kube-system
```

10.kubeconfigファイルを作成し、EKSクラスターとのやり取りをできるようにする
```bash:bash
$ aws eks update-kubeconfig --region ap-northeast-1 --name sandbox-cluster
```
kubeconfigは`~/.kube/config`に作成されます。

### クラスター構築後
クラスター構築後のアクセスポリシーを確認してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379725/1d6510b1-2ec4-670d-145c-d746b1e47759.png)

`eksctl-sandbox-cluster-clus-FargatePodExecutionRole`はEKSが自動的に作成したIAMロールです。
このロールはEKS Fargate Podがクラスター内で実行される際に使用されるIAMロールで、PodがECRからコンテナイメージを取得したり、その他のAWSサービスにアクセスしたりするための権限です。

`Administrator`のプリンシパルには`AmazonEKSClusterAdminPolicy`がアタッチされ、EKSクラスター内の管理者権限が付与されます。

ただし、このままではEKSクラスターとはまだやりとりできません。
冒頭で説明したように、EKSクラスターのAPIにリクエストを送る際にはkubeconfigファイルを作成し、kubeconfigファイルを使用してリクエストする必要があります。

**再掲**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379725/441bb63c-39cd-e1e9-259a-82bbee4bb56c.png)

試しに、kubeconfigファイルを作成しない状態でkubectlコマンドでEKSクラスター(kube-apiserver)へリクエストを投げてみましょう。

```bash:kubectl
$ kubectl get nodes

E0701 05:46:57.542348    4454 memcache.go:265] couldn't get current server API group list: Get "https://D7D0E33219BB97104A4A840CB72A87DD.gr7.ap-northeast-1.eks.amazonaws.com/api?timeout=32s": dial tcp: lookup D7D0E33219BB97104A4A840CB72A87DD.gr7.ap-northeast-1.eks.amazonaws.com on 10.0.0.2:53: no such host
E0701 05:46:57.544274    4454 memcache.go:265] couldn't get current server API group list: Get "https://D7D0E33219BB97104A4A840CB72A87DD.gr7.ap-northeast-1.eks.amazonaws.com/api?timeout=32s": dial tcp: lookup D7D0E33219BB97104A4A840CB72A87DD.gr7.ap-northeast-1.eks.amazonaws.com on 10.0.0.2:53: no such host
E0701 05:46:57.545485    4454 memcache.go:265] couldn't get current server API group list: Get "https://D7D0E33219BB97104A4A840CB72A87DD.gr7.ap-northeast-1.eks.amazonaws.com/api?timeout=32s": dial tcp: lookup D7D0E33219BB97104A4A840CB72A87DD.gr7.ap-northeast-1.eks.amazonaws.com on 10.0.0.2:53: no such host
E0701 05:46:57.546774    4454 memcache.go:265] couldn't get current server API group list: Get "https://D7D0E33219BB97104A4A840CB72A87DD.gr7.ap-northeast-1.eks.amazonaws.com/api?timeout=32s": dial tcp: lookup D7D0E33219BB97104A4A840CB72A87DD.gr7.ap-northeast-1.eks.amazonaws.com on 10.0.0.2:53: no such host
E0701 05:46:57.548003    4454 memcache.go:265] couldn't get current server API group list: Get "https://D7D0E33219BB97104A4A840CB72A87DD.gr7.ap-northeast-1.eks.amazonaws.com/api?timeout=32s": dial tcp: lookup D7D0E33219BB97104A4A840CB72A87DD.gr7.ap-northeast-1.eks.amazonaws.com on 10.0.0.2:53: no such host
Unable to connect to the server: dial tcp: lookup D7D0E33219BB97104A4A840CB72A87DD.gr7.ap-northeast-1.eks.amazonaws.com on 10.0.0.2:53: no such host
```
上記のように"サーバーに接続できません"といったエラーが返されます。

kubeconfigファイルを作成して再度アクセスしてみます。


```bash:awscli
# kubeconfigファイルの作成
# 以下AWS CLIコマンドでKubernetes Configファイルを作成
$ aws eks update-kubeconfig --name <cluster_name> --region <region>

Updated context arn:aws:eks:ap-northeast-1:{AccountID}:cluster/sandbox-cluster in /home/ec2-user/.kube/config

# 再度アクセスしてみます
$ kubectl get nodes
# リクエストに対するレスポンスが返ってきました -> 現状Nodeを作成しているわけではないので"No resorces found"となります
No resources found

```

kubeconfigファイルを作成すると~/.kube配下にconfigファイルが作成され、認証情報が登録されます。
```
$ cat ~/.kube/config
```
一部マスクしますが、certificate-authority-data以下に記載されている(マスクしている箇所)箇所がKubernetesクラスターのAPIサーバ(kube-apiserver)にアクセスする際のサーバ証明書を検証するためのCA証明書情報です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379725/9dbbe0c0-4bcd-f754-39b8-3ff4cb5fe094.png)

## EKSクラスターを作成したIAMユーザ以外をアクセスエントリに追加する場合

クラスターの[アクセス]タブから[アクセスエントリの作成]ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379725/11ea5757-7930-6ce0-a084-9dc6d75f14f9.png)

IAMプリンシパルに登録したいIAMプリンシパルARNを選択します。
ユーザが利用するIAMプリンシパルにはタイプを[スタンダード]に設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379725/801f46fe-f6aa-ebf9-cf8c-4a80b7fbf407.png)

ユーザ名については自動生成が推奨のため空白のままにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379725/27a6a537-1f23-6e90-2bc3-7fa37124abeb.png)

アクセスポリシーを追加 - オプションでポリシー名[AmazonEKSClusterAdminPolicy] (EKSクラスターを作成したIAMユーザと同じポリシー)を追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/379725/4ccf0c64-aec3-93a4-5be1-052b3e7a4bb1.png)

これでポリシーの追加が追加されます。

AWS CLIでアクセスエントリーに追加されたことを確認します。
```bash:aws cli
$ aws eks list-access-entries --cluster-name sandbox-cluster
{
    "accessEntries": [
        "arn:aws:iam::xxxxxxxxxxxx:role/eksctl-sandbox-cluster-clus-FargatePodExecutionRole-dMNVotqRuHbj",
        "arn:aws:iam::xxxxxxxxxxxx:user/Administrator",
        "arn:aws:iam::xxxxxxxxxxxx:user/develop-user"
    ]
}
```

## CloudFormationでの書き方

```yaml
EKSAccessEntryAdmin:
   Type: AWS::EKS::AccessEntry
   Properties:
     AccessPolicies: 
      - AccessScope:
           Type: 'cluster'
         PolicyArn: 'arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy'
     ClusterName: !Ref EKSCluster
     PrincipalArn: !Sub "arn:aws:iam::${AWS::AccountId}:role/${Prefix}-${Env}-role"
```
CloudFormationでIAMアクセスエントリと記述する場合、IAMロールごとに記述する必要があるため、
同じアクセスポリシーを複数のIAMロールに適用する場合でも、それぞれ`Type: AWS::EKS::AccessEntry`
として記述する必要があります。

以下のようなイメージです。

```yaml
EKSAccessEntryAdmin1:
   Type: AWS::EKS::AccessEntry
   Properties:
     AccessPolicies: 
      - AccessScope:
           Type: 'cluster'
         PolicyArn: 'arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy'
     ClusterName: !Ref EKSCluster
     PrincipalArn: !Sub "arn:aws:iam::${AWS::AccountId}:role/${Prefix}-${Env}-role-a"

EKSAccessEntryAdmin2:
   Type: AWS::EKS::AccessEntry
   Properties:
     AccessPolicies: 
      - AccessScope:
           Type: 'cluster'
         PolicyArn: 'arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy'
     ClusterName: !Ref EKSCluster
     PrincipalArn: !Sub "arn:aws:iam::${AWS::AccountId}:role/${Prefix}-${Env}-role-b"
```


## クラスターの削除
諸々確認が終わり、もうクラスターは必要ないという場合には以下のコマンドでクラスターの削除をすることができます。

```bash:bash
$ eksctl delete cluster --name <clustername>
```


## 最後に
今回はEKSクラスターへのアクセスEKS API/ConfigMapについての違いを理解し、EKS APIによるアクセスを行ってみました。
本記事内ではEKSクラスターを作成したユーザにおけるEKSクラスターのアクセスについての検証ができませんでしたが、別途機会を設けて試してみたいと思っています。


## 参考
https://techblog.ap-com.co.jp/entry/2024/03/27/104530


[^1]:EKS専用のコマンドラインツールであり、EKSクラスターを構築することができます。