EC2にIAMロールを割り当て、クロスアカウントアクセスをする（EC2、Fluentd、Kinesis）

## 概要

あるアカウント内のEC2から別アカウントにあるKinesisへデータを入れる必要があったので、その方法を調べた。（主にIAM権限の話）

セキュリティ上の理由からEC2にアクセスキーを置かず、EC2に付与するIAMロールの機能を使う。

## 実現したいアーキテクチャ

EC2(+Fluenetd) → Kinesis（別アカウント）→ EC2など（別アカウント）

この記事では、EC2からKinesisに入れるまでの部分を対象としている。


## Fluentdのプラグインはクロスアカウントに対応している

AWS Labsが提供している、このプラグインを使う。https://github.com/awslabs/aws-fluent-plugin-kinesis

READMEに下記のようにある。

```
assume_role_credentials
Typically, you use AssumeRole for cross-account access or federation.

<match *>
  @type kinesis_streams

  <assume_role_credentials>
    role_arn          ROLE_ARN
    role_session_name ROLE_SESSION_NAME
  </assume_role_credentials>
</match>
```

クロスアカウントアクセスに対応しているのは分かったが、具体的に何を設定すべきか。そして、必要なIAM権限は何か。（そもそもアクセスキーを使わずに、IAMロールで設定可能か）


## AWS CLIからまずはできるように

FluentdからKinesisにデータを入れる前に、AWS CLIからクロスアカウントアクセスができることを確かめ、必要なIAM権限を調べる。

ここからは便宜上、EC2を置くアカウントを「アカウントA」、Kinesisを置くアカウントを「アカウントB」とする。

### アカウントBの設定
まずは、アカウントBの方から設定をしていく。

#### Kinesis作成
検証に使うので「Kinesis Data Stream」を作成しておく。悩むことは特に無いはず。

#### IAMロール作成
IAMロールを作成する。ここで作成したロールをアカウントAのEC2が（間接的に）使う。

マネジメントコンソールから作ると楽。途中で「外部ID」を聞かれるが、これは適当な値を設定しておく。（今回はテストなので無くても問題ない）
`ロールの作成 → 「別のAWSアカウント」→ アカウントAのIDを入力（信頼関係） → IAMポリシーの設定など`

作成するIAMロールには、Kinesisの権限をつける。

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "kinesis:*",
            "Resource": "*"
        }
    ]
}
```

作成したロールを確認すると、「信頼関係」は下記のようになっているはず。"sts:ExternalId"は、「外部ID」として指定した文字列。

```
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::[アカウントAのアカウントID]:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "foo"
        }
      }
    }
  ]
}
```

ここで設定したことをまとめると、アカウントBにアカウントAのEC2が使うロールを作成した。（このロールをEC2が直接使うわけではない。アカウントAのEC2は、STSのAssumeRoleという機能を通して、このロールを引き受ける）

### アカウントAの設定
#### IAMロール作成
EC2に割り当てるIAMロールを作成する。必要な権限は下記。Resourceはアスタリスクにしてもいいかも。

```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "アカウントBで作成したロールのARN"
        }
    ]
}
```

ここでは、アカウントBで作成したロールを使う（引き受ける）ことができる権限を与えている。（詳しくは、「AssumeRole」でググるといろいろ出てくる）

また、作成後「信頼関係」を確認すると、「信頼されたエンティティ：ec2.amazonaws.com」となっているはず。「このロールを引き受けることができるのはEC2ですよ」という意味。

#### EC2作成

ふつうにEC2を作成する。IAMロールは、先程作成した"sts:AssumeRole"を許可したものを割り当てる。

### AWS CLIから確認する

下記のコマンドを実行する。"--role-sessoin-name"の値は今回は何でも良い。（詳しくは公式ドキュメント参照）　"--external-id"は、アカウントBでロールを作成する際に指定した「外部ID」を指定する。

```
$ aws sts assume-role --role-arn [アカウントBに作成したロールのARN] --role-session-name test --external-id foo
```

このコマンドの返り値は、こんな感じ。（もろもろの値は伏せている）

```
{
    "AssumedRoleUser": {
        "AssumedRoleId": "...", 
        "Arn": "arn:aws:sts::..."
    }, 
    "Credentials": {
        "SecretAccessKey": "...", 
        "SessionToken": "...", 
        "Expiration": "...", 
        "AccessKeyId": "..."
    }
}
```

この情報を環境変数に指定する。

```
$ export AWS_ACCESS_KEY_ID=...
$ export AWS_SECRET_ACCESS_KEY=...
$ export AWS_SESSION_TOKEN=...
```

こうすることで、アカウントBに作成したロールのもとで、AWS CLIが実行できる。

```
$ aws --region ap-northeast-1 kinesis list-streams
```

このコマンドで、アカウントBに作成したKinesisのストリーム名が帰ってくる。

### 注意

"aws sts assume-role..."のコマンドの結果を、`~/.aws/credentials｀に書いてもうまくいかなかった。（IAMユーザのアクセスキーを書くみたいに設定した）

"list-streams"はアカウントA内のリソースに対して実行され、権限が無いとエラーになった。

### できなかったこと

「コマンドの結果を環境変数に設定して…」という面倒なことをしなくても、AWS CLIなら上手いことやってくれる機能がある。

> AWS CLIにおける一時キーの管理【自動クレデンシャル取得】
https://dev.classmethod.jp/cloud/aws/aws-cli-supports-assume-role-credentials-provider-and-mfa/

今回はこれができなかった。IAMユーザを作成してならできるが、EC2にIAMロールを割り当てる場合にはできないのだろうか？
今回の目的はFluentdからクロスアカウントでKinesisを使うことなので、この件は深追いしなかった。

## FluentdからKinesisにデータをいれる（クロスアカウント）

ここまで来ると、Fluentdの設定もわかってくる。今まで作成したIAMロールはそのまま使い、先ほど立てたEC2（アカウントA）からKinesis（アカウントB）にデータを入れる。

Fluentdのインストールは普通に行う。td-agent-gemコマンドで、aws-fluent-plugin-kinesisもインストールする。

Fluentdの設定を下記のようにする。

```
<source>
  @type tail
  path /tmp/test.json
  pos_file /var/log/td-agent/foo.bar.pos
  tag foo.bar
  format json
</source>

<match foo.bar>
  @type kinesis_streams
  region ap-northeast-1
  stream_name [アカウントBに作成したストリームの名前]

  <assume_role_credentials>
    role_arn [アカウントBに作成したIAMロールのARN]
    role_session_name foo  # 任意の名前
    external_id foo  # ロール作成時に指定した外部ID
  </assume_role_credentials>
</match>
```

Fluentdのリロードで設定を適用したら、"/tmp/test.json"に書き込みを行う。

```
$ echo "{ "key": "value" } >> /tmp/test.json
```

こうすると、アカウントBのKinesisにデータが入る。

この確認は、アカウントBにEC2を立てるなどして、確認できる。

（参考）確認方法
『基本的なストリームオペレーションの実行』
https://docs.aws.amazon.com/ja_jp/streams/latest/dev/fundamental-stream.html

## 参考URL
* https://dev.classmethod.jp/cloud/aws/signin-with-cross-account-access/
    * クロスアカウントアクセスの説明
* https://dev.classmethod.jp/cloud/aws/aws-cli-supports-assume-role-credentials-provider-and-mfa/
    * AWS CLIからAssumeRoleを使う手順が分かりやすい
* https://docs.aws.amazon.com/sdkforruby/api/Aws/STS/Client.html
    * role_session_nameに何を指定すべき？
* https://dev.classmethod.jp/cloud/aws/iam-role-and-assumerole/
    * IAMロール、AssumeRoleの解説
* https://docs.fluentd.org/v0.12/articles/install-by-rpm
    * Fluentdのインストール
* https://github.com/awslabs/aws-fluent-plugin-kinesis
    * Kinesisにデータを入れるプラグイン