AWS裏側探検：Private SubnetのEC2がIAMロールを手に入れる驚きの仕組み

# 1. はじめに
## 背景
先日、S3のVPCエンドポイントを経由してEC2インスタンスからS3上のファイルにアクセスする検証を行いました。詳細はこちらの記事をご参照ください。

https://qiita.com/dennis_wang/items/e881f2d502b1a0addfbe#22-%E3%82%B2%E3%83%BC%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A4%E5%9E%8B%E3%82%A8%E3%83%B3%E3%83%89%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88%E3%82%92%E6%A4%9C%E8%A8%BC

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/9fd550ce-d00b-fd79-cd76-bac6b4e4c063.png)


## 疑問点
この検証を通じて、以下の疑問が生じました：
- EC2インスタンスがS3バケットにアクセスするためには、適切な権限を持つ`IAM Role`が必要です。
- `IAM Role`の認証情報は、`AWS Security Token Service (STS)`によって提供されます。
- `STS`を含む多くのAWSサービスは、AWSの独自のグローバルネットワークインフラストラクチャ上に存在し、通常はVPC外にあります。
- AWSサービスへのアクセスには、一般的に以下の2つの方法があります：
    - パブリックエンドポイント：インターネット経由でアクセス
    - VPCエンドポイント：AWS内部ネットワークを使用してプライベートにアクセス
- 上の図のように今回の検証では、<font color="red">EC2インスタンスはプライベートサブネットに配置されており、インターネットへの直接アクセスはできません</font>。
- S3へのアクセスはS3 VPCエンドポイントを経由して行われていますが、<font color="red">`STS`アクセス用のVPCエンドポイントは別途作成されていません</font>。

これらの状況を踏まえると、以下の疑問が浮かび上がります：
- <font color="red">プライベートサブネットに配置されたEC2インスタンスが、インターネットアクセスもできないし、`STS`のVPCエンドポイントも持たない状況で、どのようにして`IAM Role`の認証情報を取得できているのでしょうか</font>？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/c5f7f37c-d25b-8ecd-27f7-9b63818c7ace.png)


この疑問を解消するために、以下の点について詳しく調査し説明していきます：
- EC2インスタンスプロファイル、`IAM Role`、`STS`の関係
- プライベートサブネットのEC2インスタンスから`IAM Role`の認証情報を取得する仕組み

# 2. 関連コンポーネントの関係
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/6580099a-7dd1-5104-cdaf-d25f11a67107.png)

## 2.1 EC2インスタンスと`IAM Role`
- EC2インスタンス作成時に、`IAM Role`を関連付けることができます。インスタンス作成時にロールを設定しなくても、インスタンス作成後に関連付けることも可能です。
- この`IAM Role`には、EC2インスタンスが必要とするAWSリソースへのアクセス権限が定義されています。

## 2.2 EC2インスタンスプロファイルと`IAM Role`の関係
1. インスタンスプロファイルの役割:主な目的は、EC2インスタンスに安全に`IAM Role`の権限を付与する
2. IAMロールとの関係:
    - <font color="red">EC2インスタンスに直接`IAM Role`を関連付けることはできません</font>。代わりに、<font color="red">インスタンスプロファイルを通じて`IAM Role`をEC2インスタンスに関連付けます</font>
   - インスタンスプロファイルは、<font color="red">EC2インスタンスと`IAM Role`を結び付けるための箱</font>です
   - インスタンスプロファイルとIAMロールの関係は<font color="red">一対一</font>です。
3. 自動作成:
   - AWSマネジメントコンソールから<font color="red">`IAM Role`を作成すると、同じ名前のインスタンスプロファイルが自動的に作成</font>されるため、ユーザーがインスタンスプロファイルを意識する必要がありません
4. 認証情報の取得:
   - EC2インスタンスは、インスタンスプロファイルを通じて、関連付けられた`IAM Role`の一時的な認証情報を取得

要するに、インスタンスプロファイルはEC2インスタンスと`IAM Role`の間の橋渡しの役割を果たし、EC2インスタンスがAWSリソースにアクセスするための安全な方法を提供

https://qiita.com/sakai00kou/items/a4b96dcfa6bb3e656cd9

https://www.beex-inc.com/blog/20240103_instanceProfile

## 2.3 インスタンスメタデータサービス（`IMDS`）
### 2.3.1 基本情報
- EC2インスタンス内部で利用可能な<font color="red">HTTPサービス</font>
- 通常、IPアドレス<font color="red">169.254.169.254</font>で提供
- インスタンスに関する情報や`IAM Role`の一時的な認証情報を提供

### 2.3.2 IMDSの仕組み
`IMDS`のIPアドレスは、<font color="red">物理的なサーバーやデバイスを指すものではなく、AWSのインフラストラクチャによって提供される仮想的なエンドポイント</font>です。
- リンクローカルアドレス
    - `169.254.169.254`は、リンクローカルアドレスと呼ばれる特殊なIPアドレスです。
    - このアドレスは、<font color="red">ローカルネットワークセグメント内でのみ有効で、インターネットにルーティングされません</font>。

-  AWSハイパーバイザーの役割
<font color="red">AWSの仮想化レイヤー（ハイパーバイザー）が、このアドレスへのリクエストをインターセプトし、適切な応答</font>を返します。

-  EC2インスタンス内での動作
EC2インスタンス内からこのアドレスにアクセスすると、<font color="red">リクエストは実際にはインスタンスの外に出ることなく、AWSのインフラストラクチャによって処理</font>されます。


# 3. EC2インスタンスからIAM Roleの認証情報を取得する仕組み
## 3.1 認証情報の取得プロセス
EC2インスタンスが`IAM Role`の認証情報を取得するプロセスは以下の通りです。
1. **リクエストの開始**
   - EC2インスタンス内のアプリケーションやAWS SDKが`IMDS`にリクエストを送信

2. **IMDSの処理**
   - `IMDS`がリクエストを受け取り、`STS`サービスに対して`AssumeRole`リクエスト

3. **`STS`処理**
   - `STS`サービスは、`IAM Role`に基づいて<font color="red">一時的な認証情報</font>を生成し、`IMDS`に返す
   - これらの認証情報は通常、数時間後に期限切れになり、自動的に更新(<font color="red">有効期限はデフォルトでは1時間、最大で12時間まで延長することが可能</font>)
   - AWS CLIやAPIを使用する場合、`duration-seconds`パラメータを指定することで、1時間から12時間の間で任意の有効期限を設定可能

4. **認証情報の提供**
   - `IMDS`が生成された認証情報をアプリケーションに提供

## 3.2 重要なポイント

- **内部通信**: `IMDS`はAWSの内部インフラストラクチャの一部として機能し、`STS`サービスとの通信にAWSの内部ネットワークを使用します。この通信はパブリックインターネットを経由せず、AWSの管理下にある特殊なネットワーク経路を使用

- **VPCエンドポイントの不要性**: <font color="red">`IMDS`と`STS`の通信はAWS内部で行われるため、VPCエンドポイントを明示的に作成する必要はありません</font>

- **透過的な処理**: EC2インスタンス上のアプリケーションやSDKからは、この内部通信プロセスは透過的です。<font color="red">アプリケーションは`IMDS`にリクエストを送信するだけで、残りの処理はAWSインフラストラクチャによって自動的に処理</font>されます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/36390c1f-ef90-53d6-0797-e2310ba935d5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/8c22c664-5561-6487-d5fc-2024bd1ddebb.png)

```
private subnetにあるEC2から以下のコマンドでSTSに直接アクセスしようとしても返事が来ない：
$ aws sts get-caller-identity
```

## 3.3 セキュリティと効率性
このプロセスにおいて、EC2インスタンスプロファイル、`IMDS`、`IAM Role`、`STS`サービスが連携して機能します。これにより：
1. EC2インスタンスが安全かつ効率的に必要な認証情報を取得可能
2. AWSリソースへのアクセスが可能になる
3. EC2インスタンス上に長期的な認証情報を保存する必要がなくなり、セキュリティが向上


# 4. EC2から`IMDS`をアクセスしてみる
`IMDS`には`IDMSv1`と`IDMSv2`という2つのバージョンがあります。

| 特徴 | IMDSv1 | IMDSv2 |
|------|--------|--------|
| 認証 | 不要 | <font color="red">時間制限</font>のあるセッショントークンが必要 |
| セキュリティ | 低 | 高 |
| 設定の複雑さ | 簡単 | やや複雑 |
| AWS推奨 | ❌ | ⭕ |

`IMDSv2`では、まずトークンを取得し、そのトークンを使用してメタデータにアクセスする2段階のプロセスを利用することによって、セキュリティが大幅に向上します。AWSはセキュリティ機能が強化されている`IMDSv2`の使用を推奨しています。

EC2インスタンス作成する時に、V2のみとV1、V2両方サポートという２つの選択肢がありまして、デフォルトはV2のみです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/b781ea53-e39b-d8a6-627b-78b29aa156b1.png)

- `IMDSv1`の例:
    ```
    curl http://169.254.169.254/latest/meta-data/instance-id
    ```

- `IMDSv2`の例:
    ```
    TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` 
    
    curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id
    ```

- AWSのSDKには、基本的なメタデータ情報をサポートする便利なコマンドラインツール`ec2-metadata`も付属しています。
    ```
    $ ec2-metadata --all
    ami-id: ami-07281c2a30e5bc1ab
    ami-launch-index: 0
    ami-manifest-path: (unknown)
    ancestor-ami-ids: not available
    block-device-mapping:
             ami: xvda
             root: /dev/xvda
    instance-id: i-0c0d60ae77b6039c5
    instance-type: t3.micro
    local-hostname: ip-10-0-0-165.ap-northeast-1.compute.internal
    local-ipv4: 10.0.0.165
    kernel-id: not available
    placement: ap-northeast-1a
    product-codes: not available
    public-hostname: not available
    public-ipv4: not available
    public-keys:
    not available
    ramdisk-id: not available
    reservation-id: r-0ce0d554681d2d19d
    security-groups: ProviderVpcStack-WebSecurityGroup73AF7387-ZxdSh2HgIV3V
    user-data: #!/bin/bash
    yum update -y
    yum install -y httpd
    systemctl start httpd
    systemctl enable httpd
    ```
興味深い点として、`IMDS`は`user-data`の取得も可能であり、これを利用して任意のプロセスがスクリプトを実行できます。主要なOSでは、通常、`cloud-init`が利用可能で、`user-data`の取得と実行を処理します。
EC2インスタンス作成する時に、デフォルトとしては`IMDS`が有効になり、手動で無効化すると、以下の警告が表示される。つまり<font color="red">`IMDS`が無効になると、インスタンス初回起動する時にuser dataが実行できない</font>。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/18f78fdd-745c-1496-cbbf-7a33c3ba9ed4.png)


- 認証情報の取得について
`IMDS`がEC2インスタンスに一時的な認証情報を提供しています。これは上記の`ec2-metadata`の属性には表示されていないため、curlを使用して取得する必要があります。認証情報を取得するにはRole名が必要です。これは手動で入力するか、メタデータ自体から簡単に取得できます。
    - 例えば、以下のようにしてRole名を取得できます。
        ```
        $ ROLE_NAME=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)
        
        ```

    - そして、そのRole名を使用して実際の認証情報を取得します。
        ```
        $ curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE_NAME
        {
          "Code" : "Success",
          "LastUpdated" : "2023-05-25T23:23:40Z",
          "Type" : "AWS-HMAC",
          "AccessKeyId" : "[redacted]",
          "SecretAccessKey" : "[redacted]",
          "Token" : "[redacted]",
          "Expiration" : "2024-09-06T13:53:03Z"
        }
        ```

この呼び出しにより、`AccessKeyId`、`SecretAccessKey`、`Token`などの認証情報が返されます。これらの認証情報は、AWS CLIやSDKを使用してAWSリソースにアクセスする際に使用できます。
`Token`の存在は、これが`STS（Security Token Service）`からの一時的な認証情報であることを示しています。これにより、EC2インスタンス上に認証情報を保存する必要なく、認証を機能させることができます。


# 5. まとめ
- EC2インスタンスはIMDSを通じて`IAM Role`の認証情報を取得
- このプロセスはAWS内部で完結し、外部ネットワークアクセスは不要
- <font color="red">`IMDS`と`STS`の通信はAWS内部で行われるため、VPCエンドポイントを明示的に作成する必要はありません
- セキュリティ機能が強化されている`IMDSv2`の使用を推奨