eksctlを使わずterraformを使ってIAM Roles for Service Accounts

https://aws.amazon.com/jp/blogs/news/introducing-fine-grained-iam-roles-service-accounts/
最近EKSにServiceAccountに紐付いたrole arnでIAM権限を付与する仕組みが実装されました
非の打ち所のないような公式ドキュメントで、eksctlを使う場合は上のドキュメントを読んでいただければ完璧に理解できると思います、この記事ではeksctlではなくterraformを使って `IAM Roles for Service Accounts` を使用する手順を記載します
* 東京リージョンで作成することを想定しています

source code: https://github.com/keiSunagawa/qiita/tree/master/eks-sa-2-iam

## そもそも何ができるようになったの？
従来のEKSではWorkerNodeのIAM roleがPod内に引き継がれる仕様でした
つまり、Pod内のアプリケーションからAWSリソースへアクセスするための権限を、対象のPodがデプロイされる可能性のある全てのWorkerNode自身につける必要がありました
これを回避するためにPod自身に(正確にはServiceAccountに)IAM roleを付与する方法が今回追加されました
* ちなみに以前から [kiam](https://github.com/uswitch/kiam) などを使えば同様のことができたそうですが、僕はよく知らないので説明しません

## OpenID Connect
今回追加された `IAM Roles for Service Accounts` を構成する要素の一つ
OAuth2.0の仕様を拡張し、`IDトークン` と呼ばれる情報を取得できるようにした認可/認証のための仕様
`IAM Roles for Service Accounts` ではこれに則って認可/認証を実装しているため
`OpenID Connect` という用語がドキュメントやコンソールにちらほら出てきます
ただし、今回の機能を使う上でほとんどEKSとIAM(及びSTS)の間で完結している話なのでユーザはあまり気にしなくても良かったりします

## 環境構築
冒頭で貼った `source code` に沿って説明します

- VPC ~ EKS ~ EC2
  この辺は通常通りなので特に説明をしません
- IAM  
worker & master用のロールとServiceAccount用のロールを付与します
 - worker & master用のロール  
   通常通り、EKSに必要なポリシー以外は付与しません
 - ServiceAccount用のロール
   `assume_role_policy` がworkerの付与するロールと違っているので注意 
   あとで付与するPodのためにCloudWatchLogsのFullAccessを付与します

```sa-iam.tf
resource "aws_iam_role" "eks_log_role" {
  name = "${local.prefix}_eks_log_role"

  assume_role_policy = <<EOS
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
          "Federated": "${aws_iam_openid_connect_provider.for_eks.arn}"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "${aws_iam_openid_connect_provider.for_eks.url}:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}
EOS

resource "aws_iam_role_policy_attachment" "eks_log_role_policy" {
  policy_arn = "arn:aws:iam::aws:policy/CloudWatchFullAccess"
  role       = "${aws_iam_role.eks_log_role.name}"
}
```
 - IAM OpenID Connect Provider  
   EKSをidP(OpenID Connectで言う認証を行うためのサーバ)とするための設定を作成します
  thumbprintはidPのドメインの証明書から生成しますが、リージョンが `ap-northeast-1` の場合はソースそのままで問題ありません(もちろん証明書が差し替わった場合は変更になります)  
  自分で作成したい場合は以下の手順が参照できます  
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_providers_create_oidc_verify-thumbprint.html 

```oidcp.tf
resource "aws_iam_openid_connect_provider" "for_eks" {
  url = "${aws_eks_cluster.main_eks.identity.0.oidc.0.issuer}"

  client_id_list = [
    "sts.amazonaws.com"
  ]
  thumbprint_list = ["9e99a48a9960b14926bb7f3b02e22da2b0ab7280"]
}
```

## マニフェストファイル
- ServiceAccount  
  `annotations` に環境構築で作ったarnを記載します、このアノテーションをみてEKSは認証方式を判断します(厳密に言うとSTS認証方式向けの環境変数と値のファイルを作成します)  

```service-account.yml
apiVersion: v1
kind: ServiceAccount
metadata:
  annotations:
    eks.amazonaws.com/role-arn: <your role arn>
  name: fluentd
  namespace: kube-system
```
- DaemonSet  
 特に特筆することはない、通常のDaemonSetです、上記で作ったServiceAccountを紐付けます  
 今回はCloudWatchにfluentdでログを転送するアプリケーションをデプロイすることにします

## Let's Deploy

```
$ cd play-ground/eks-sa-2-iam/terraform

$ terraform init && terraform apply

$ vim ~/.kube/config # 接続先情報の編集

$ terraform output eks_configmap | kubectl apply -f -

$ cd ../k8s

$ vim service-account.yml # metadata.annotations.eks.amazonaws.com/role-arnを作成したServiceAccount用roleのarnで書き換え

$ kubectl apply -f .
```

fluentdよりCloudWatchにログが流れていれば成功です

## 確認
```
$ kube get pods fluentd-k8sd5 -n kube-system -o yaml
```

podの情報を取得すると、マニフェストファイルに記載しておらず、EKSによって自動で付与された環境変数 `AWS_ROLE_ARN` `AWS_WEB_IDENTITY_TOKEN_FILE` やvolumeの `aws-iam-token` などを確認することができます
aws client(およびsdk)はこれらの情報を使い、STS経由で認証/認可を行うことができます