AWS アクセスキーをインスタンスロールにしても一時的なキーは残る

## 結論

- インスタンスロールの中身は`sts:AssumeRole`なので一時的なキーは残る
- インスタンスロールを使う場合でもduration_secondsの時間はできる限り減らす(1時間)


## 経緯

AWSアクセスキーを使用するとgithubなどの公開されているサービスへの漏えいリスクがあり、その代わりにインスタンスロールなどの永続的なキーを発行しない仕組みを使うのが推奨されています。AWSの公式ドキュメントにも

> アカウントを保護する最善の方法の 1 つは、AWS アカウントのルートユーザー のアクセスキーを持たないことです。

との記載があります。

しかしインスタンスロールを発行するとデフォルトの信頼関係のpolicyは以下のようになります。

```json
{
  "Version": "2008-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

`sts:AssumeRole`????インスタンス内でaws api叩くときにそんなの全く意識していないのになぜ必要なのか？

と、良く分からない仕組みを使うのは怖くなったので、中でどう動いているのか調べることにしました。


## インスタンスロールは作成者と使用者が区別されるよう設計されている

インスタンスロールを使用する場合以下のような手順になります
1. `管理者`がインスタンスロールを発行する
2. `開発者`が指定されたロールでインスタンスを立ち上げる
3. インスタンス内でaws apiを発行する

ここで重要な部分は開発者がIAMロールを選択する部分です。例えばを以下のロールを許可している場合を考えましょう。
- Aロール: Admin権限
- Bロール: ec2:DescribeInstances

この場合、管理者は開発者にBロールをつけるように期待をしますが、悪い開発者は全ての権限を持つ Aロールをつけてインスタンスを立ち上げるかもしれません。

こういった事態を回避するために`Principal`があります。上記のpolicyの例だと

```json
"Principal": {
        "Service": "ec2.amazonaws.com"
      },
```
のように書かれているので、ec2からのアクセスのみを許可します。

実際に`ec2:DescribeInstances`のロールを持つインスタンスを立ち上げてどのように動くのか確認してみましょう


## IAMロールをつけたインスタンスを立ち上げる

### policyの作成

- `ec2:DescribeInstances`のみを許可しているpolicyを作成する

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "ec2:DescribeInstances",
            "Resource": "*"
        }
    ]
}
```

### IAM Roleの作成

- 上記で作成したpolicyをアタッチする
- Role作成時にec2からのアクセスを許可させる(以下のようなpolicyになります)

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

### インスタンスの立ち上げ

- ec2のインスタンスを立ち上げるときに上記で作成したroleをインスタンスロールとして立ち上げる

## 実践


上記で作成したインスタンス内で以下のコマンドを打ってみる。

```bash
aws ec2 describe-instances --region ap-northeast-1
```

`ap-northeast-1`内に存在するインスタンスが出力されます。

### `ec2.amazonaws.com`を外してみる

`ec2`の部分を`s3`に変更して再度上記の aws apiを打ってみます。

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "s3.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

もう一度 aws api

```bash
aws ec2 describe-instances --region ap-northeast-1
```

実はこれ1時間以内であれば**正常**に結果が返ってきます。

え、ec2からアクセスを拒否したはずなのになんで？ と思われるかもしれませんが、これも冒頭に書いたように一時的なアクセスキーをインスタンス内部で保持しているからです。

### アクティブなセッションを無効化する

AWSコンソール>IAM>該当のロール>セッションの無効化
からセッションの無効化を行います

<img width="1062" alt="スクリーンショット 2019-05-19 13.31.11.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114135/70fb769a-8641-1ba5-34e1-86fd983b1aed.png">

もう一度 aws api

```bash
aws ec2 describe-instances --region ap-northeast-1
```

ec2からのアクセスが拒否されているので、エラーが発生しています。

```
[ec2-user@ip-172-31-20-141 ~]$ aws ec2 describe-instances --region ap-northeast-1

An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.
```

アクティブなセッションを無効化した場合、1時間立たないとaws cliのセッション情報が上書きされないので、検証が終わったらインラインポリシーから破棄してください。

## assumeRoleがどのように動くのか確認してみる

上記の流れで殆ど理解できたと思いますが、インスタンスプロファイルを使用した時のAWS apiの処理フローとしては
1. 一時キーがあるか確認する(正しくは 実行時引数/環境変数/credential/configに情報があるかも確認します)
2. なければ一時キーを発行する
3. apiを発行する

の順番で処理を行います。この処理の２の部分がassumeRoleに当たります。

公式ドキュメントにあるように一時的な認証情報はインスタンスメタから取得します
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html#roles-usingrole-ec2instance-roles

> 認証情報とその関連する値はメタデータの iam/security-credentials/role-name カテゴリ から入手できます。アプリケーションがインスタンスメタデータから認証情報を取得する場合、アプリケーションは認証情報をキャッシュすることができます。

実際に作成したインスタンス内で以下のコマンドを打ってみると(ec2からのアクセスは許可してください)

```sh
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/${your_role}
```
すると一時キー情報が取得できます。これがインスタンスプロファイルの正体ですね

```json
{
  "Code" : "Success",
  "LastUpdated" : "2019-05-19T05:00:41Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "t",
  "SecretAccessKey" : "",
  "Token" : "",
  "Expiration" : "2019-05-19T11:35:09Z"
{
```


## assumeRoleで取得したキーがいつexpiredするの確認する

この一時キーどのくらい有効なのか結構気になりますよね。
有効時間は以下で確認可能です。
3,600 (1 時間) から 43,200 (12 時間)で選択可能です。
<img width="928" alt="スクリーンショット 2019-05-19 13.48.35.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/114135/66e3ea80-eeb7-6ef5-3c20-592490682908.png">


## 結論

インスタンスプロファイル変更したとしてもキーの漏えいリスクは残り続ける、というのは心に留めていた方が良さそうです。これに加えて

- インスタンスロールを使う場合でもduration_secondsの時間はできる限り減らす(1時間)

というのは実践した方が良さそうです。