[アップデート] リージョンごとにデフォルトIMDSを設定できるようになりました

IMDSv2利用促進に繋がりそう
2024.03.26

IMDSv2のみに明示するのが面倒

こんにちは、のんピ(@non____97)です。

皆さんはIMDSv2のみに明示するのが面倒だなと思ったことはありますか? 私はあります。

起動するEC2インスタンスのIMDSのどのバージョンを使用するかはAMIの設定次第です。もし、IMDSのデフォルト設定が定義されていないAMIを利用する場合、IMDSv2のみとする際にはEC2インスタンス起動時に明示的に指定する必要がありました。

今回、アップデートによりリージョンごとにデフォルトのIMDSを設定できるようになりました。

事前にデフォルトのIMDSの設定をしていれば、EC2インスタンスを起動する際に都度指定する必要がなくなります。

実際に試してみたので紹介します。

いきなりまとめ

  • リージョン毎にIMDSに関連する以下についてのデフォルト設定を定義できるようになった
    • IMDSの有効/無効
    • IMDSのバージョン
    • メタデータでインスタンスのタグ情報のアクセス可否
    • メタデータのレスポンスホップ数の上限
  • いずれもデフォルト値であり、定義した値に強制するものではない
    • 起動時に明示的に値を設定すれば、そちらの値が使われる
  • CloudFormationで設定する場合はカスタムリソースを使用する必要がある

やってみた

アカウント設定をしていない場合

実際に試してみます。

まずはアカウント設定をしていない場合です。

Amazon Linux 2とAmazon Linux 2023のAMIを確認します。

$ aws ec2 describe-images --image-ids ami-033a1ebf088e56e81 ami-0c101f26f147fa7fd
{
    "Images": [
        {
            "Architecture": "x86_64",
            "CreationDate": "2024-03-21T02:25:43.000Z",
            "ImageId": "ami-0c101f26f147fa7fd",
            "ImageLocation": "amazon/al2023-ami-2023.4.20240319.1-kernel-6.1-x86_64",
            "ImageType": "machine",
            "Public": true,
            "OwnerId": "137112412989",
            "PlatformDetails": "Linux/UNIX",
            "UsageOperation": "RunInstances",
            "State": "available",
            "BlockDeviceMappings": [
                {
                    "DeviceName": "/dev/xvda",
                    "Ebs": {
                        "DeleteOnTermination": true,
                        "Iops": 3000,
                        "SnapshotId": "snap-04819fa0d2620618b",
                        "VolumeSize": 8,
                        "VolumeType": "gp3",
                        "Throughput": 125,
                        "Encrypted": false
                    }
                }
            ],
            "Description": "Amazon Linux 2023 AMI 2023.4.20240319.1 x86_64 HVM kernel-6.1",
            "EnaSupport": true,
            "Hypervisor": "xen",
            "ImageOwnerAlias": "amazon",
            "Name": "al2023-ami-2023.4.20240319.1-kernel-6.1-x86_64",
            "RootDeviceName": "/dev/xvda",
            "RootDeviceType": "ebs",
            "SriovNetSupport": "simple",
            "VirtualizationType": "hvm",
            "BootMode": "uefi-preferred",
            "DeprecationTime": "2024-06-19T02:26:00.000Z",
            "ImdsSupport": "v2.0"
        },
        {
            "Architecture": "x86_64",
            "CreationDate": "2024-03-20T02:57:50.000Z",
            "ImageId": "ami-033a1ebf088e56e81",
            "ImageLocation": "amazon/amzn2-ami-kernel-5.10-hvm-2.0.20240318.0-x86_64-gp2",
            "ImageType": "machine",
            "Public": true,
            "OwnerId": "137112412989",
            "PlatformDetails": "Linux/UNIX",
            "UsageOperation": "RunInstances",
            "State": "available",
            "BlockDeviceMappings": [
                {
                    "DeviceName": "/dev/xvda",
                    "Ebs": {
                        "DeleteOnTermination": true,
                        "SnapshotId": "snap-09e76fd13c67fa768",
                        "VolumeSize": 8,
                        "VolumeType": "gp2",
                        "Encrypted": false
                    }
                }
            ],
            "Description": "Amazon Linux 2 Kernel 5.10 AMI 2.0.20240318.0 x86_64 HVM gp2",
            "EnaSupport": true,
            "Hypervisor": "xen",
            "ImageOwnerAlias": "amazon",
            "Name": "amzn2-ami-kernel-5.10-hvm-2.0.20240318.0-x86_64-gp2",
            "RootDeviceName": "/dev/xvda",
            "RootDeviceType": "ebs",
            "SriovNetSupport": "simple",
            "VirtualizationType": "hvm",
            "DeprecationTime": "2025-07-01T00:00:00.000Z"
        }
    ]
}

Amazon Linux 2023の方にはImdsSupportv2.0という設定がありますが、Amazon Linux 2にはImdsSupportというプロパティはありません。

Amazon Linux 2のAMIを使ってEC2インスタンスを起動させます。その際にメタデータのバージョンは選択の状態にしておきます。

メタデータのバージョンを選択で起動

起動してきたEC2インスタンスのIMDSを確認すると、IMDSv2がOptionalとなっていました。つまりはIMDSv2のみではありません。IMDSv1も使用できる状態です。

IMDSv2がOptionalになっていることを確認

ちなみに、AWS CLIから確認すると、以下のとおりです。

$ aws ec2 describe-instances \
	--instance-id i-02859717ec0cd97d9 \
	--query "Reservations[*].Instances[*].MetadataOptions"
[
    [
        {
            "State": "applied",
            "HttpTokens": "optional",
            "HttpPutResponseHopLimit": 2,
            "HttpEndpoint": "enabled",
            "HttpProtocolIpv6": "disabled",
            "InstanceMetadataTags": "disabled"
        }
    ]
]

マネジメントコンソールからアカウントのデフォルトIMDSを設定

それではマネジメントコンソールからアカウントのデフォルトIMDSを設定します。

EC2のコンソールからデータ保護とセキュリティをクリックします。

データ保護とセキュリティ

デフォルトIMDSの設定を確認できました。デフォルトだと全てNo preferenceになっていますね。IMDSのバージョンだけでなく、そもそもIMDSを有効にするか、メタデータでタグ情報にアクセスさせるか、メタデータレスポンスのホップ数も指定できるようです。

IMDS デフォルト

管理をクリックしてMetadata versionV2 only (token required)に変更します。

v2 onlyに設定

設定が変更されたことを確認します。

設定がされたことを確認

この状態で、先ほどと全く同じAMIと設定でAmazon Linux 2を起動します。

起動後にIMDSを確認するとIMDSv2がRequiredになっていました。やったぜ。

IMDSv2がRequiredになっていることを確認

AWS CLIでは以下のとおりです。

$ aws ec2 describe-instances \
	--instance-id i-02399237a7d5ac8ad \
	--query "Reservations[*].Instances[*].MetadataOptions"
[
    [
        {
            "State": "applied",
            "HttpTokens": "required",
            "HttpPutResponseHopLimit": 2,
            "HttpEndpoint": "enabled",
            "HttpProtocolIpv6": "disabled",
            "InstanceMetadataTags": "disabled"
        }
    ]
]

なお、デフォルトIMDS設定はあくまでデフォルト値です。「起動時に指定したIMDSの設定とマッチしていなければ拒否する」という代物ではありません。

AMI、デフォルトIMDS設定、起動パラメーターでどのような設定をした場合に、最終的にどの値になるかは以下のとおりです。

Launch parameter Account level default AMI default Resulting instance configuration
V2 only (token required) No preference V2 only V2 only
V2 only (token required) V2 only V2 only V2 only
V2 only (token required) V1 or V2 V2 only V2 only
V1 or V2 (token optional) No preference V2 only V1 or V2
V1 or V2 (token optional) V2 only V2 only V1 or V2
V1 or V2 (token optional) V1 or V2 V2 only V1 or V2
Not set No preference V2 only V2 only
Not set V2 only V2 only V2 only
Not set V1 or V2 V2 only V1 or V2
V2 only (token required) No preference null V2 only
V2 only (token required) V2 only null V2 only
V2 only (token required) V1 or V2 null V2 only
V1 or V2 (token optional) No preference null V1 or V2
V1 or V2 (token optional) V2 only null V1 or V2
V1 or V2 (token optional) V1 or V2 null V1 or V2
Not set No preference null V1 or V2
Not set V2 only null V2 only
Not set V1 or V2 null V1 or V2

抜粋 : Configure the instance metadata options - Amazon Elastic Compute Cloud

AWS CLIでアカウントのデフォルトIMDSを設定

もちろん、AWS CLIで設定変更することも可能です。

デフォルトIMDSの設定を確認する際にはget-instance-metadata-defaultsを叩きます。

$ aws ec2 get-instance-metadata-defaults
{
    "AccountLevel": {
        "HttpTokens": "required"
    }
}

HttpTokensrequiredなので、IMDSv2のみがデフォルトになっていそうです。また、No preferenceの設定は出力されないようです。

デフォルトIMDSの設定を変更する際にはmodify-instance-metadata-defaultsを叩きます。

試しにIMDSの有効化と、IMDSのバージョンの指定を解除します。

$ aws ec2 modify-instance-metadata-defaults \
    --http-tokens no-preference \
    --http-endpoint enabled
{
    "Return": true
}

$ aws ec2 get-instance-metadata-defaults
{
    "AccountLevel": {
        "HttpEndpoint": "enabled"
    }
}

問題なく設定できましたね。

CloudFormation Custom ResourceでアカウントのデフォルトIMDSを設定

このデフォルトIMDSはリージョン単位の設定です。また、大量のAWSアカウントがある場合はそれを一つづつ設定するのはかなり骨が折れます。

そんな時にはCloudFormation StackSetsを使いたいとことです。

しかし、2024/3/26時点でデフォルトIMDSはCloudFormationでサポートされていません。そのため、カスタムリソースで作成をする必要があります。

AWS CDKを使ってデフォルトIMDSでIMDSv2のみを明示的に指定する場合は、以下のように定義できます。

./lib/imdsv2-stack.ts

import * as cdk from "aws-cdk-lib";
import { Construct } from "constructs";

export class Imdsv2Stack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    new cdk.custom_resources.AwsCustomResource(
      this,
      "ModifyInstanceMetadataDefaults",
      {
        onCreate: {
          service: "EC2",
          action: "modifyInstanceMetadataDefaults",
          parameters: {
            HttpTokens: "required",
          },
          physicalResourceId: cdk.custom_resources.PhysicalResourceId.of(
            "ModifyInstanceMetadataDefaults"
          ),
        },
        onDelete: {
          service: "EC2",
          action: "modifyInstanceMetadataDefaults",
          parameters: {
            HttpTokens: "no-preference",
          },
        },
        policy: cdk.custom_resources.AwsCustomResourcePolicy.fromStatements([
          new cdk.aws_iam.PolicyStatement({
            actions: ["ec2:ModifyInstanceMetadataDefaults"],
            resources: ["*"],
          }),
        ]),
        installLatestAwsSdk: true,
        logRetention: cdk.aws_logs.RetentionDays.ONE_WEEK,
      }
    );
  }
}

今回はinstallLatestAwsSdktrueと指定して、最新のAWS SDKをインストールさせています。これは裏側で作成できるLambda関数にプリインストールされているAWS SDK for JavaScript v3にはModifyInstanceMetadataDefaultsCommandがまだ存在していなかったためです。

installLatestAwsSdktrueにしない状態でnpx cdk deployでデプロイすると、以下のように怒られました。

18:18:13 | CREATE_FAILED        | Custom::AWS           | ModifyInstanceMetadataDefaults0E27DBD7
Received response status [FAILED] from custom resource. Message returned: Unable to find command named: ModifyInstanceMetadataDefaultsCommand for action:
ModifyInstanceMetadataDefaults in service package @aws-sdk/client-ec2 (RequestId: ca445099-6fb1-4ab0-b3b1-825ae4a38a49)

正常にデプロイが完了した場合は、以下のように"HttpTokens": "required"となっていました。"HttpEndpoint": "enabled"となっているのは先の検証の影響です。

$ aws ec2 get-instance-metadata-defaults
{
    "AccountLevel": {
        "HttpTokens": "required",
        "HttpEndpoint": "enabled"
    }
}

Lambda関数のログは以下のとおりです。AWS SDK v3をインストールした後、ModifyInstanceMetadataDefaultsCommandを叩いていそうです。

2024-03-26T19:05:16.683+09:00	INIT_START Runtime Version: nodejs:18.v24 Runtime Version ARN: arn:aws:lambda:us-east-1::runtime:c09960ad0af4321e1a7cf013174f7c0d7169bf09af823ca2ad2f93c72ade708a
2024-03-26T19:05:16.863+09:00	START RequestId: 71f83dee-ecf7-4e2b-bd5a-da30bccca558 Version: $LATEST
2024-03-26T19:05:16.937+09:00	2024-03-26T10:05:16.937Z 71f83dee-ecf7-4e2b-bd5a-da30bccca558 INFO Installing latest AWS SDK v3: @aws-sdk/client-ec2
2024-03-26T19:07:10.695+09:00	2024-03-26T10:07:10.695Z 71f83dee-ecf7-4e2b-bd5a-da30bccca558 INFO {"RequestType":"Create","ServiceToken":"arn:aws:lambda:us-east-1:<AWSID>:function:Imdsv2Stack-AWS679f53fac002430cb0da5b7982bd22872D1-3j5lfQgw5O8A","ResponseURL":"...","StackId":"arn:aws:cloudformation:us-east-1:<AWSID>:stack/Imdsv2Stack/3ca48b50-eb58-11ee-9d41-0e3fea76f2d9","RequestId":"208d4943-a498-40a2-a6e9-2dd8ab92848f","LogicalResourceId":"ModifyInstanceMetadataDefaults0E27DBD7","ResourceType":"Custom::AWS","ResourceProperties":{"ServiceToken":"arn:aws:lambda:us-east-1:<AWSID>:function:Imdsv2Stack-AWS679f53fac002430cb0da5b7982bd22872D1-3j5lfQgw5O8A","Delete":{"service":"EC2","action":"modifyInstanceMetadataDefaults","parameters":{"HttpTokens":"no-preference"}},"InstallLatestAwsSdk":"true","Create":{"service":"EC2","action":"modifyInstanceMetadataDefaults","parameters":{"HttpTokens":"required"},"physicalResourceId":{"id":"ModifyInstanceMetadataDefaults"}}}}
2024-03-26T19:07:12.216+09:00	2024-03-26T10:07:12.216Z 71f83dee-ecf7-4e2b-bd5a-da30bccca558 INFO API response { Return: true }
2024-03-26T19:07:12.254+09:00	2024-03-26T10:07:12.254Z 71f83dee-ecf7-4e2b-bd5a-da30bccca558 INFO Responding {"Status":"SUCCESS","Reason":"OK","PhysicalResourceId":"ModifyInstanceMetadataDefaults","StackId":"arn:aws:cloudformation:us-east-1:<AWSID>:stack/Imdsv2Stack/3ca48b50-eb58-11ee-9d41-0e3fea76f2d9","RequestId":"208d4943-a498-40a2-a6e9-2dd8ab92848f","LogicalResourceId":"ModifyInstanceMetadataDefaults0E27DBD7","NoEcho":false,"Data":{"region":"us-east-1","Return":true}}
2024-03-26T19:07:12.437+09:00	END RequestId: 71f83dee-ecf7-4e2b-bd5a-da30bccca558
2024-03-26T19:07:12.437+09:00	REPORT RequestId: 71f83dee-ecf7-4e2b-bd5a-da30bccca558 Duration: 115573.00 ms Billed Duration: 115574 ms Memory Size: 128 MB Max Memory Used: 128 MB Init Duration: 178.67 ms

ちなみにStack内のリソース一覧は以下のとおりです。

スタック内のリソース一覧

npx cdk destroyでスタックを削除します。削除後にデフォルトIMDS設定を確認すると、"HttpTokens": "required"の指定が消えていました。意図したとおりです。

$ aws ec2 get-instance-metadata-defaults
{
    "AccountLevel": {
        "HttpEndpoint": "enabled"
    }
}

Lambda関数のログは以下のとおりです。

2024-03-26T19:12:24.853+09:00	START RequestId: 1355c1e9-4eba-4045-b806-44b40257dce6 Version: $LATEST
2024-03-26T19:12:25.117+09:00	2024-03-26T10:12:25.117Z 1355c1e9-4eba-4045-b806-44b40257dce6 INFO {"RequestType":"Delete","ServiceToken":"arn:aws:lambda:us-east-1:<AWSID>:function:Imdsv2Stack-AWS679f53fac002430cb0da5b7982bd22872D1-3j5lfQgw5O8A","ResponseURL":"...","StackId":"arn:aws:cloudformation:us-east-1:<AWSID>:stack/Imdsv2Stack/3ca48b50-eb58-11ee-9d41-0e3fea76f2d9","RequestId":"459c6847-e9f1-4e18-ba56-8d2355d29e7b","LogicalResourceId":"ModifyInstanceMetadataDefaults0E27DBD7","PhysicalResourceId":"ModifyInstanceMetadataDefaults","ResourceType":"Custom::AWS","ResourceProperties":{"ServiceToken":"arn:aws:lambda:us-east-1:<AWSID>:function:Imdsv2Stack-AWS679f53fac002430cb0da5b7982bd22872D1-3j5lfQgw5O8A","Delete":{"service":"EC2","action":"modifyInstanceMetadataDefaults","parameters":{"HttpTokens":"no-preference"}},"InstallLatestAwsSdk":"true","Create":{"service":"EC2","action":"modifyInstanceMetadataDefaults","parameters":{"HttpTokens":"required"},"physicalResourceId":{"id":"ModifyInstanceMetadataDefaults"}}}}
2024-03-26T19:12:25.742+09:00	2024-03-26T10:12:25.742Z 1355c1e9-4eba-4045-b806-44b40257dce6 INFO API response { Return: true }
2024-03-26T19:12:25.742+09:00	2024-03-26T10:12:25.742Z 1355c1e9-4eba-4045-b806-44b40257dce6 INFO Responding {"Status":"SUCCESS","Reason":"OK","PhysicalResourceId":"ModifyInstanceMetadataDefaults","StackId":"arn:aws:cloudformation:us-east-1:<AWSID>:stack/Imdsv2Stack/3ca48b50-eb58-11ee-9d41-0e3fea76f2d9","RequestId":"459c6847-e9f1-4e18-ba56-8d2355d29e7b","LogicalResourceId":"ModifyInstanceMetadataDefaults0E27DBD7","NoEcho":false,"Data":{"region":"us-east-1","Return":true}}
2024-03-26T19:12:25.953+09:00	END RequestId: 1355c1e9-4eba-4045-b806-44b40257dce6
2024-03-26T19:12:25.953+09:00	REPORT RequestId: 1355c1e9-4eba-4045-b806-44b40257dce6 Duration: 1099.35 ms Billed Duration: 1100 ms Memory Size: 128 MB Max Memory Used: 128 MB

IMDSv2利用促進に繋がりそう

リージョンごとにデフォルトIMDSを設定できるようになったアップデートを紹介しました。

現在ではIMDSv2のみとすることが推奨されています。こちらの設定を使うことによってデフォルトでIMDSv2のみで起動させることが可能になるため、意図せずIMDSv1を使用しているインスタンスが少なくなるのではと考えます。

IMDSv2への移行手順は以下記事が参考になります。

この記事が誰かの助けになれば幸いです。

以上、AWS事業本部 コンサルティング部の のんピ(@non____97)でした!