Instance Metadata Service Version 2 (IMDSv2)：インスタンスメタデータサービスv2の設定と挙動確認

Instance Metadata Service Version 2 (IMDSv2)：インスタンスメタデータサービスv2の設定と挙動確認の作業メモ

# 本記事の内容

1. Instance Metadataとは
1. Instance Metadata Service Version 2 を使う理由
1. AWS-CLI実行環境準備
1. 既存インスタンスのIMDSv2強制設定
1. IMDSv2強制設定時の挙動確認

# 1. Instance Metadataとは

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-instance-metadata.html
>インスタンスメタデータは、インスタンスに関するデータで、実行中のインスタンスを設定または管理するために使用します。インスタンスメタデータは、ホスト名、イベント、およびセキュリティグループなどのカテゴリに分けられます。

https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html
>次のいずれかのメソッドを使って、実行中のインスタンスからインスタンスメタデータにアクセスできます。
>
> - インスタンスメタデータサービスバージョン 1 (IMDSv1) – リクエスト/レスポンスメソッド
> - インスタンスメタデータサービスバージョン 2 (IMDSv2) – セッション志向メソッド
>
>インスタンスメタデータサービスバージョン 2 の仕組み
>
>IMDSv2は、セッション志向リクエストを使用します。セッション志向リクエストを使用して、セッション期間 (1 秒～6 時間) を定義するセッショントークンを作成します。指定した期間中、それ以降のリクエストに同じセッショントークンを使用できます。指定した期間が期限切れになった後、将来のリクエストに使用する新しいセッショントークンを作成する必要があります。

# 2. Instance Metadata Service Version 2 を使う理由

2019年7月29、米金融大手のCapital Oneにて不正アクセスにより、1億人以上の個人情報が漏洩。
>Information on the Capital One Cyber Incident
>https://www.capitalone.com/facts2019/  

情報漏洩の原因は以下の通り  

- 攻撃者がWAFを経由してEC2のインスタンスメタデータにアクセスし、IAM Roleの認証情報(S3へのアクセス)を取得。
- IMDSv1は、メタデータへの接続に認証は行われない。
- 取得したIAM Roleの認証情報を利用し、Capital OneのS3にアクセスし、情報を取得。

一方、IMDSv2は、メタデータへのアクセスに、事前に取得したTokenを必須とする。
IMDSv2の使い方と、セキュリティ面でのメリットは以下の通り。

##### 使い方

- PUTリクエストを使って、6 時間 (21,600 秒) のセッショントークンを作成する
- セッショントークンヘッダーをTOKENという名前の変数に保管する
- トークンを使って最上位メタデータアイテムをリクエストする

##### メリット
- ほとんどのWAFはPUTリクエストを許容していない為、WAFを経由した外部からのアクセスが成功する可能性が低い。
- メタデータレスポンスのホップリミットを短くし複数ホストを経由した取得を防止できる。(1に設定すれば、CapitalOneで発生したような、WAFを経由したメタデータ取得を阻止できる)
- PUTリクエストは、X-Forwarded-Forヘッダーが含まれている場合、拒否する。(プロキシサーバでは、通常X-Forwarded-Forヘッダーを付与する)


# 3. AWS-CLI実行環境準備

インスタンス構築/設定変更のコマンドを発行するためのAWS-CLI実行用インスタンス(cli_instance)を準備

```console:cli_instance環境(AWS-CLI発行元環境情報)
[root@ip-10-0-0-74 ~]# cat /etc/system-release
Amazon Linux release 2 (Karoo)
[root@ip-10-0-0-74 ~]# 
[root@ip-10-0-0-74 ~]# uname -a
Linux ip-10-0-0-74.ap-northeast-1.compute.internal 4.14.193-149.317.amzn2.x86_64 #1 SMP Thu Sep 3 19:04:44 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
[root@ip-10-0-0-74 ~]# 
[root@ip-10-0-0-74 ~]# aws --version 
aws-cli/1.18.107 Python/2.7.18 Linux/4.14.193-149.317.amzn2.x86_64 botocore/1.17.31
[root@ip-10-0-0-74 ~]# 
[root@ip-10-0-0-74 ~]# curl http://169.254.169.254/latest/meta-data/instance-type/
t3.small
[root@ip-10-0-0-74 ~]# 
```

IMDSv2稼働確認用インスタンス「test_instance」構築

```console:cli_instance環境(run-instances実行)
[root@ip-10-0-0-74 ~]# aws ec2 run-instances \
> --image-id ami-0ce107ae7af2e92b5 \
> --instance-type t2.nano \
> --key-name key_file \
> --monitoring Enabled=false \
> --placement AvailabilityZone=ap-northeast-1a \
> --subnet-id subnet-03d74b8d6ab6c39f2 \
> --associate-public-ip-address \
> --security-group-ids sg-0a00ecb871bb15fb3 \
> --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=test_instance}]'
```

構築したインスタンス(test_instance)デフォルトのインスタンスメタデータ設定の確認

```console:cli_instance環境(describe-instances実行)
[root@ip-10-0-0-74 ~]# aws ec2 describe-instances \
> --filters "Name=tag:Name,Values=test_instance" \
> --query Reservations[*].Instances[*].[MetadataOptions]
[
    [
        [
            {
                "State": "applied", 
                "HttpEndpoint": "enabled", 
                "HttpTokens": "optional", 
                "HttpPutResponseHopLimit": 1
            }
        ]
    ]
]
[root@ip-10-0-0-74 ~]# 
```

`"HttpTokens": "optional" -> IMDSv1/IMDSv2が両方利用可能`
`"HttpTokens": "required" -> IMDSv2のみ利用可能(IMDSv2の強制化)`


##### "HttpTokens": "optional"状態での挙動確認(IMDSv1/IMDSv2が両方利用可能)

`以下は新規に構築した「test_instance」環境上で実行`

```console:test_instance環境（IMDSv1実行）
[root@ip-10-0-0-68 ~]# curl http://169.254.169.254/latest/meta-data/
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
identity-credentials/
instance-action
instance-id
instance-life-cycle
instance-type
local-hostname
local-ipv4
mac
metrics/
network/
placement/
profile
public-hostname
public-ipv4
public-keys/
reservation-id
security-groups
services/
[root@ip-10-0-0-68 ~]# 
```

```console:test_instance環境（IMDSv2実行）
[root@ip-10-0-0-68 ~]# 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" -v http://169.254.169.254/latest/meta-data/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    56  100    56    0     0   9333      0 --:--:-- --:--:-- --:--:-- 11200
*   Trying 169.254.169.254...
* TCP_NODELAY set
* Connected to 169.254.169.254 (169.254.169.254) port 80 (#0)
> GET /latest/meta-data/ HTTP/1.1
> Host: 169.254.169.254
> User-Agent: curl/7.61.1
> Accept: */*
> X-aws-ec2-metadata-token: AQAAAO6n081baEIWdrfpILhc9Egt4kTm0HSpUftcYqvJSR-NKewL6A==
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Accept-Ranges: bytes
< Content-Length: 313
< Content-Type: text/plain
< Date: Sat, 26 Sep 2020 14:45:29 GMT
< Last-Modified: Sat, 26 Sep 2020 14:28:58 GMT
< X-Aws-Ec2-Metadata-Token-Ttl-Seconds: 21600
< Connection: close
< Server: EC2ws
< 
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
identity-credentials/
instance-action
instance-id
instance-life-cycle
instance-type
local-hostname
local-ipv4
mac
metrics/
network/
placement/
profile
public-hostname
public-ipv4
public-keys/
reservation-id
security-groups
* Closing connection 0
services/
[root@ip-10-0-0-68 ~]#
```

# 4. 既存インスタンスのIMDSv2強制設定

`以下はAWS-CLI実行環境「cli_instance」環境上で実行`

```console:cli_instance環境
[root@ip-10-0-0-74 ~]# # test_instanceのインスタンスID取得
[root@ip-10-0-0-74 ~]# aws ec2 describe-instances \
> --filters "Name=tag:Name,Values=test_instance" \
> --query Reservations[*].Instances[*].[InstanceId]
[
    [
        [
            "i-034109c70aaa4b055"
        ]
    ]
]
[root@ip-10-0-0-74 ~]# 
[root@ip-10-0-0-74 ~]# # IMDSv2強制設定の実行("HttpTokens": "required")
[root@ip-10-0-0-74 ~]# aws ec2 modify-instance-metadata-options \
> --instance-id i-034109c70aaa4b055 \
> --http-tokens required \
> --http-put-response-hop-limit 1 \
> --http-endpoint enabled
{
    "InstanceId": "i-034109c70aaa4b055", 
    "InstanceMetadataOptions": {
        "State": "pending", 
        "HttpEndpoint": "enabled", 
        "HttpTokens": "required", 
        "HttpPutResponseHopLimit": 1
    }
}
[root@ip-10-0-0-74 ~]#
```

# 5. IMDSv2強制設定時の挙動確認

##### "HttpTokens": "required"状態での挙動確認(IMDSv2のみ利用可能(IMDSv2の強制化))

`以下は新規に構築した「test_instance」環境上で実行`

```console:test_instance環境（IMDSv1実行）
[root@ip-10-0-0-68 ~]# curl http://169.254.169.254/latest/meta-data/
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
	"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
 <head>
  <title>401 - Unauthorized</title>
 </head>
 <body>
  <h1>401 - Unauthorized</h1>
 </body>
</html>
[root@ip-10-0-0-68 ~]#
```

`IMDSv2強制化されているため、IMDSv1の方法では取得できない`


```console:test_instance環境（IMDSv2実行）
[root@ip-10-0-0-68 ~]# 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" -v http://169.254.169.254/latest/meta-data/
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    56  100    56    0     0   9333      0 --:--:-- --:--:-- --:--:-- 11200
*   Trying 169.254.169.254...
* TCP_NODELAY set
* Connected to 169.254.169.254 (169.254.169.254) port 80 (#0)
> GET /latest/meta-data/ HTTP/1.1
> Host: 169.254.169.254
> User-Agent: curl/7.61.1
> Accept: */*
> X-aws-ec2-metadata-token: AQAAAO6n081aj7u-yzdFHoD8zll2ZKiNfVS79OXp1qJDwgzw7-96gA==
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Accept-Ranges: bytes
< Content-Length: 313
< Content-Type: text/plain
< Date: Sat, 26 Sep 2020 15:01:45 GMT
< Last-Modified: Sat, 26 Sep 2020 14:28:58 GMT
< X-Aws-Ec2-Metadata-Token-Ttl-Seconds: 21600
< Connection: close
< Server: EC2ws
< 
ami-id
ami-launch-index
ami-manifest-path
block-device-mapping/
events/
hostname
identity-credentials/
instance-action
instance-id
instance-life-cycle
instance-type
local-hostname
local-ipv4
mac
metrics/
network/
placement/
profile
public-hostname
public-ipv4
public-keys/
reservation-id
security-groups
* Closing connection 0
services/
[root@ip-10-0-0-68 ~]#
```