スナップショットからのAMI 作成時に IMDSv2 のデフォルトを設定出来るようになりました

2022.10.04

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

いわさです。

AMI から EC2 インスタンスを作成する際に IMDSv2 のみを有効化出来ますが、今回のアップデートでRegisterImageを使ってイメージ作成する際に IMDSv2 のみをデフォルト有効化させるオプションが追加されました。

説明を見てみてもわかったような、わからないような。
とりあえず使ってみましょう。

AMI の作成はインスタンスから作成する CreateImage とスナップショットから作成する RegisterImage があります。
今回のアップデートでは RegsterImage にてオプションが指定出来るようになったようです。

CreateImage で試してみる

まずはデフォルトで提供されている AMI から EC2 を作成してみます。

ちなみに、EC2 作成時にコンソールの高度な設定から IMDSv2 の強制化やホップ制限の変更が可能です。
指定しなかった場合のデフォルトは IMDSv1 と IMDSv2 どちらも使えてホップ数は 1 です。

作成されたインスタンスから AMI を作成します。
特に指定するオプションは見当たらないですね。

create-imageへは今回のオプションが追加されていません。

AMI を作成しました。

また、今回のアップデートでdescribe-imagesの出力にImdsSupportが追加されていますので使ってみましょう。(v1.25.86 以上から利用可能)

% aws-v1 --version                                    
aws-cli/1.25.86 Python/3.9.8 Darwin/21.6.0 botocore/1.27.85
% aws-v1 ec2 describe-images --owners self --profile hoge
{
    "Images": [
        {
            "Architecture": "x86_64",
            "CreationDate": "2022-10-04T03:22:16.000Z",
            "ImageId": "ami-004f196718e096957",
            "ImageLocation": "123456789012/hoge1004image",
            "ImageType": "machine",
            "Public": false,
            "OwnerId": "123456789012",
            "PlatformDetails": "Linux/UNIX",
            "UsageOperation": "RunInstances",
            "State": "available",
            "BlockDeviceMappings": [
                {
                    "DeviceName": "/dev/xvda",
                    "Ebs": {
                        "DeleteOnTermination": true,
                        "SnapshotId": "snap-09f818236215c396b",
                        "VolumeSize": 8,
                        "VolumeType": "gp2",
                        "Encrypted": false
                    }
                }
            ],
            "EnaSupport": true,
            "Hypervisor": "xen",
            "Name": "hoge1004image",
            "RootDeviceName": "/dev/xvda",
            "RootDeviceType": "ebs",
            "SriovNetSupport": "simple",
            "VirtualizationType": "hvm"
        }
    ]
}

ImdsSupportが確認出来ませんでした。

スナップショットからイメージ作成(コンソール)

次にスナップショットからイメージを作成するregister-imageを試してみましょう。

こちらも特に設定は見当たらないですね。

先程と同様にdescribe-imagesで確認してみましたがオプションは設定されていませんでした。
以下によるとregister-imageでオプションを指定出来るのですが、本日時点でコンソールからは設定出来ないようですね。

ちなみに、コンソールから指定した際の CloudTrail ログは以下のようにオプションが追加されていない状態でした。

{
:
    "eventName": "RegisterImage",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "AWS Internal",
    "userAgent": "AWS Internal",
    "requestParameters": {
        "name": "hoge1004snapshot",
        "description": "hoge1004snapshot",
        "architecture": "x86_64",
        "rootDeviceName": "/dev/sda1",
        "blockDeviceMapping": {
            "items": [
                {
                    "deviceName": "/dev/sda1",
                    "ebs": {
                        "snapshotId": "snap-09f818236215c396b",
                        "volumeSize": 8,
                        "deleteOnTermination": true,
                        "volumeType": "gp2"
                    }
                }
            ]
        },
        "enaSupport": true
    },
:
}

スナップショットからイメージ作成(AWS CLI)

では本命というか AWS CLI からregister-imageを使ってみましょう。

% aws-v1 ec2 register-image \
    --name hoge1004snapshot2 \
    --root-device-name /dev/sda1 \
    --architecture x86_64 \
    --block-device-mappings DeviceName=/dev/sda1,Ebs="{SnapshotId=snap-09f818236215c396b,VolumeSize=8,VolumeType=gp2}" \
    --imds-support v2.0 \
    --profile hoge
{
    "ImageId": "ami-0b00d81b063096582"
}

AMI が作成されたら、先程と同じようにdescribe-imagesで確認してみます。

% aws-v1 ec2 describe-images --owners self --profile hoge
{
    "Images": [
        {
            "Architecture": "x86_64",
:
            "SriovNetSupport": "simple",
            "VirtualizationType": "hvm",
            "ImdsSupport": "v2.0"
        }
    ]
}

おお、今度はImdsSupportが指定されていました。
どうやら指定されているときだけdescribe-imagesで確認出来るようですね。

AMI からインスタンスを作成

目的の AMI を作成することが出来たのでインスタンスを起動してみましょう。

メタデータ設定は冒頭と同様でデフォルトのままでいきます。
今まではこの設定だと IMDSv1 も使えてホップ数は 1 がデフォルトでしたね。

[ec2-user@ip-172-31-9-69 ~]$ curl http://169.254.169.254/latest/meta-data/placement/availability-zone
<?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>

IMDSv1 が禁止されていますね。

インスタンスの MetadataOptions がどうなっているのかを確認してみましょう。

% aws ec2 describe-instances --profile hoge | jq '.Reservations[].Instances[] | { InstanceId: .InstanceId, MetadataOptions: .MetadataOptions}'
{
  "InstanceId": "i-0cb822c6cd4d706fb",
  "MetadataOptions": {
    "State": "applied",
    "HttpTokens": "optional",
    "HttpPutResponseHopLimit": 1,
    "HttpEndpoint": "enabled",
    "HttpProtocolIpv6": "disabled",
    "InstanceMetadataTags": "disabled"
  }
}
{
  "InstanceId": "i-0d8263c0c915c82a4",
  "MetadataOptions": {
    "State": "applied",
    "HttpTokens": "required",
    "HttpPutResponseHopLimit": 2,
    "HttpEndpoint": "enabled",
    "HttpProtocolIpv6": "disabled",
    "InstanceMetadataTags": "disabled"
  }
}

このようにHttpsTokensHttpPutResponseHopLimitがデフォルトと変わっていることが確認出来ました。

明示的に指定した場合上書きされる

ちなみに AMI からインスタンス作成をする際に以下のように指定したらどうなるでしょうか。

{
  "InstanceId": "i-073b28a211b8b5439",
  "MetadataOptions": {
    "State": "applied",
    "HttpTokens": "optional",
    "HttpPutResponseHopLimit": 1,
    "HttpEndpoint": "enabled",
    "HttpProtocolIpv6": "disabled",
    "InstanceMetadataTags": "disabled"
  }
}

上書きされましたね。
あくまでもデフォルト時の挙動を指定出来るオプションということです。

IMDSv2 強制ポリシーは今までどおりで良さそう

一点気になったのが IAM で IMDSv2 を強制するポリシーを使う方法があるのですが、そちらとの兼ね合いはどうなるでしょうか。

CloudTrail の RunInstances イベントでは以下のようにhttpTokensrequiredが指定されていました。

{
:
    "responseElements": {
:
        "instancesSet": {
            "items": [
                {
:
                    "metadataOptions": {
                        "state": "pending",
                        "httpTokens": "required",
                        "httpPutResponseHopLimit": 2,
                        "httpEndpoint": "enabled",
                        "httpProtocolIpv4": "enabled",
                        "httpProtocolIpv6": "disabled",
                        "instanceMetadataTags": "disabled"
                    },
:}

そのためポリシーを適用した上で、明示的にインスタンス作成時に指定しなくても、今回の方法で作成した AMI であればインスタンス作成に成功しました。

さいごに

本日はスナップショットからのAMI 作成時に IMDSv2 のデフォルトを設定出来るようになったので試してみました。

まずは検証日時点ではコンソールからは利用出来ずに API 経由 でのみ設定出来るオプションであることと、create-imageでは利用出来ずregister-imageで使えるということがわかりました。

IMDSv1 をデフォルトで使えなくする点はセキュリティの観点ということでわかりやすいですが、ホップ制限が 2 になる点はコンテナ環境向けに以下が関係しているようです。

コンテナ環境では、ホップ制限が 1 の場合、コンテナへの到達は余分なネットワークホップと見なされるため、IMDSv2 応答は返されません。IMDSv1 へのフォールバックプロセスとその結果として生じる遅延を回避するために、コンテナ環境でホップ制限を 2 に設定することをお勧めします。

ユースケースは少し限られそうですが、起動時やあるいは起動後に手動でメタデータオプションを更新・指定した点が省略出来るようになるアップデートでした。

また、デフォルトの挙動を変更するのみで手動で IMDSv1 を有効化は出来るので強制化するオプションではないことを理解し、今までと同様に強制化している運用環境では IAM ポリシーでの制御を引き続き利用するのが良さそうです。