[UPDATE] IMDSv2を強制するIAMポリシーを検証してみた #reinvent

IMDSv2の利用を強制するIAMポリシーについて検証しました。アカウント全体で強制する等の利用方法ができないことに注意が必要です。また、強制することにより生まれる課題についても確認してください。
2019.12.23

こんにちは、臼田です。

みなさん、IAMポリシー絞ってますか?(挨拶

今回はre:Invent2019周辺で発表されたIMDSv2(インスタンスメタデータサービスv2)の機能について調査した内容を共有します。IMDSv2ってなんぞやという方は下記をご参照ください。

[待望のアプデ]EC2インスタンスメタデータサービスv2がリリースされてSSRF脆弱性等への攻撃に対するセキュリティが強化されました!

IMDSv2を強制するIAMポリシー

下記ユーザーガイドにて新規のEC2インスタンスを作成する際にIMDSv2を強制するIAMポリシーについて記載があります。

Configuring the Instance Metadata Service - Amazon Elastic Compute Cloud

このポリシーはユーザーやロール等に対して適用され、ポリシーが割り当てられたIAMに対してのみ強制することができます。現状ではアカウントに対して一律で強制する手法が無いことに注意してください。

動作の具体的な内容としてはec2:MetadataHttpTokensというコンディションが新しく追加されたため、これをrequiredに設定していない場合の起動をDenyしている形となります。

やってみた

実際にやってみます。

ポリシー作成

作成するポリシーは下記のとおりです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowAllRunInstances",
            "Effect": "Allow",
            "Action": "ec2:RunInstances",
            "Resource": "*"
        },
        {
            "Sid": "DenyIfImdsV2IsNotUsed",
            "Effect": "Deny",
            "Action": "ec2:RunInstances",
            "Resource": "arn:aws:ec2:*:*:instance/*",
            "Condition": {
                "StringNotLike": {
                    "ec2:MetadataHttpTokens": "required"
                }
            }
        }
    ]
}

2019/12/23時点では上述したユーザーガイドとは内容が異なりますが、ユーザーガイドの内容ではうまく動作しなかったためサポートへ問い合わせて、正しく動くポリシーについて共有いただきました。このポリシーで動作を確認しています。

違いは14行目のResourceについてinstanceを指定しているところです。このコンディションはinstanceリソースタイプにのみ動作するため、このような指定が必要になるようです。

作成したポリシーと、ついでにAmazonEC2ReadOnlyAccessをアタッチしたIAMユーザー or ロールを用意します。

通常の起動

まずは普通にRunInstanceしてみます。

$ aws ec2 run-instances --image-id ami-068a6cefc24c301d2 --count 1 --instance-type t2.micro --key-name mykey --security-group-ids sg-00000000 --subnet-id subnet-00000000

An error occurred (UnauthorizedOperation) when calling the RunInstances operation: You are not authorized to perform this operation. Encoded authorization failure message: b1JDKyXInk7QvhrOZy62x6kjxLDLWB0nd5ZIl41Mn6f5gw1LwSMWlvWqZMRpBYTIz1QpEGg2gf70te4JIPeV_Z2x_L4UGttjqKxhPVT59T7Rb69jLfJF7YApAcxnth9tyvONiTTXlZ7ii7-iyyBHIxHVvW-F-OfJi3Cc-yfn-DIaZl_YXSjpcvcuPCsLbOFBF4rWuGPEFkEPOeGwko4AL5cYwwQBLZipm5zQuokQ02Dj-xY7ID4TozJz4WFbTCniU3cX0IE5XNxMZYZsPAJnUQuQnDCAlP6MTkdIePJgzwZAGIGk5lX0x2so06z83_UF9AJAZZUaMZkZnhyrJv1H-w96G5tJYzyDXZ0jn6ErnrtJkcvLfRRxWM70JKtiYE-I981cdbNf0lWIywXhe0lkUFJageP5gub365Jo8A8v9qoAAQYUkE33Ftni6kmKOHgEp-kEpFyLK0rmHihoXJ7Ec9mIPfj3Frn61MKtYUch0QRdzRFI0WkgTA-g5jcsOm3TOfmiVcK5Me1brX3kUJlBhbEVb5edFw6FLlFe2kjFHYVeIYWohln7kAVIv09UT5OWdDOarKpE88b9WXY3CWLVJPKLDLXcDSLrqzsPIksrvv9K_DJtiAS4v_5t6Az7TATl8U7ScBq2E73kY8yytWKYXpmIu7pJ7hpjWD6yrIbMhKKnhOYrE0e7LSr1I4QvB7qIc3NGGweqG0tH4Zam4r9SepgpPLTHftJsuRErAPjthRvq0R6kUxqpRWE7wiQazT5ISMmb-4acBBpO4qSRd77g9U5snXx4Lwiruc6Lr7JND9AL5xFrnmg

失敗しました。想定どおりです。

ちなみに失敗したログを読むにはsts:DecodeAuthorizationMessage権限が必要になります。詳細は下記を参照。

Management Consoleの権限不足エラーをデコードする

IMDSv2強制起動

それではIMDSv2を必須にして起動してみます。

$ aws ec2 run-instances --image-id ami-068a6cefc24c301d2 --count 1 --instance-type t2.micro --key-name mykey --security-group-ids sg-00000000 --subnet-id subnet-00000000 --metadata-options HttpTokens=required,HttpEndpoint=enabled
{
    "Groups": [],
    "Instances": [
        {
            "AmiLaunchIndex": 0,
            "ImageId": "ami-068a6cefc24c301d2",
            "InstanceId": "i-00xxxxxxxxxxxxxxx",
            "InstanceType": "t2.micro",
            "KeyName": "mykey",
            "LaunchTime": "2019-12-22T21:36:46.000Z",
...省略...
            "MetadataOptions": {
                "State": "pending",
                "HttpTokens": "required",
                "HttpPutResponseHopLimit": 1,
                "HttpEndpoint": "enabled"
            }
        }
    ],
    "OwnerId": "000000000000",
    "ReservationId": "r-0xxxxxxxxxxxxxxx"
}

無事起動しました。

--metadata-optionsでは、modify-instance-metadata-optionsと同じようにHttpTokens=requiredだけでなくHttpEndpoint=enabledも必要になる点が注意です。

起動したインスタンスにアクセスしてみる

無事起動できたので、インスタンスにアクセスしてみました。

$ ssh -i ~/.ssh/mykey.pem ec2-user@54.92.124.85
ec2-user@54.92.124.85: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).

アクセスができませんでした。通常ならローカル側keyの設定による問題であることが多いですが、コンソールから起動した同じAMIの別インスタンスでは問題なくアクセスできました。

該当のインスタンスは最新のAmazon Linux 2を利用しています。

起動時のシステムログを確認してみます。

[  134.892685] cloud-init[3076]: Dec 22 21:39:26 cloud-init[3076]: cc_write_metadata.py[WARNING]: there is no identity dataset
[  134.897935] cloud-init[3076]: Dec 22 21:39:26 cloud-init[3076]: util.py[WARNING]: Running module write-metadata (<module 'cloudinit.config.cc_write_metadata' from '/usr/lib/python2.7/site-packages/cloudinit/config/cc_write_metadata.pyc'>) failed
         Starting Hostname Service...
[[32m  OK  [0m] Started Hostname Service.
[[1;31mFAILED[0m] Failed to start Initial cloud-init job (metadata service crawler).
See 'systemctl status cloud-init.service' for details.

なんとなく予想はついていましたが、起動する際にメタデータサービスへのアクセスが失敗していて、これが原因で鍵の設定が正しく行われていないようです。おそらく、まだAmazon Linux 2はIMDSv2を利用した初期起動に対応していないと考えられます。この内容もサポートへ問い合わせています。

まとめ

IMDSv2を強制したIAMポリシーについて動作検証を行いました。

強制すること自体は新しいコンディションを利用することにより実現できることを確認しました。

しかしながら、(おそらく)AMI側でも起動時のIMDSv2利用に対応していないと行けないため、この設定が利用できるタイミングはもう少し先かもしれません。別AMIでは試していないので、他のOSなどではうまく動作するかもしれません。あるいはゴールデンAMIに公開鍵を埋め込んでおけば行けるかもしれません。

現実的には起動時だけでなく稼働中にIMDSを利用するアプリケーションがIMDSv2に対応していないといけないため、すぐにこれを強制することはまだ現実的ではありません。公式ブログでもv1からv2への移行方法につて言及されており、新しくインスタンスに追加されたMetadataNoTokenというCloudWatchメトリクスをモニタリングしつつ、完全にこれがなくなってから移行することを推奨しています。

また、IMDSv2を利用することでセキュリティは向上しますが、確実にSSRF等の攻撃を防ぐことができるわけではないことも認識して利用する必要があります。詳細は徳丸先生のブログをご確認ください。

すぐにIMDSv2強制の恩恵を受けることは簡単ではありませんが、挑戦したい方は是非参考にしてください。