プライベートサブネットに配置した EC2 の AWS Cloud9 環境で AWS Managed Temporary Credentials を使用してもなぜか S3 向けの AWS CLI が成功した

プライベートサブネットに Cloud9 環境を構築したい場合は AMTC を無効化してインスタンスプロファイルを使用するのがセオリーだと思いますが、S3 向けのコマンドだけは AMTC を使用して成功しました。だから何だっていう話ではあります。

コンバンハ、千葉(幸)です。

タイトルの繰り返しになりますが、プライベートサブネットに配置した AWS Cloud9 環境で AWS Managed Temporary Credentials(AMTC)を使用してもなぜか S3 向けの AWS CLI が成功しました。

言いたいことは読んで文字の如くなのですが、このままだと意味を理解できる方は少ないかもしれません。順を追って説明します。

最終的に「なんでできるんだろう……わからん。」という結論に至る話です。

まとめ

  • 本来プライベートサブネットにある AWS Cloud9 環境では AMTC を使用しての AWS API 実行はできない
    • AWS ドキュメントにその旨 記載がある
  • そのはずなのになぜか S3 のコマンドだけは実行できる
  • 先日のアップデートで対応した?情報求ム

AWS Cloud9 環境における IAM 権限の使用

前提の部分をおさらいしておきます。

AWS Cloud9 はクラウドベースの統合開発環境(IDE)です。ローカルコンピュータの Web ブラウザで実行されている AWS Cloud9 IDE から AWS Cloud9 環境に接続します。AWS Cloud9 環境は裏側で EC2 インスタンスやオンプレミスのサーバーといったコンピューティングリソースからも接続されています。

arch

What is AWS Cloud9? - AWS Cloud9 より)

AWS Cloud9 環境は以下のいずれかのタイプに分類されます。(以降、Cloud9 環境と EC2 環境をあまり厳密に区別せず文中で使用します。)

  • EC2 環境(直接接続)
  • EC2 環境(インバウンドなし、Systems Manager 経由)
  • SSH 環境(任意の既存のサーバ)

このうち、二番目のインバウンドなしの EC2 環境のみプライベートサブネットに配置できます。

また、AWS Cloud9 環境でクレデンシャルを使用する方法として以下があります。以下表での「 EC2 環境」は、直接接続/インバウンドなし の区別はありません。

方式 一時/永続 EC2 環境 SSH 環境 備考
AMTC の使用 一時 不可 EC2 環境のデフォルト
インスタンスプロファイルの使用 一時 不可 EC2 にアタッチされたものを使用
アクセスキーの使用 永続 EC2 環境では非推奨

たとえば Cloud9 IDE 上で AWS CLI を実行したい、となった場合には上記のいずれかを使用して必要な権限を確保する必要があります。

このうち AMTC は、Cloud9 環境に接続する AWS エンティティ(IAM ユーザーなど)とほぼ同等の権限を持つ一時クレデンシャルである、と考えると分かりやすいかと思います。もう少し正確に言うと AMTC では一部の制限を除きほとんどのアクションに対する許可が与えられており、AMTC と「AWS エンティティが持つパーミッション」の両方で許可が与えられている場合のみアクションが成功する、という挙動となります。

AMTC における一部の制限としては IAM や Cloud9 サービスの一部のアクションが該当するほか、送信元 IP アドレスによって受ける制限もあります。サービスエンドポイントに対して通信を行う送信元 IP アドレスが Cloud9 環境が持つそれでない場合、アクションは拒否されます。

プライベートサブネットに配置された EC2 の Cloud9 環境の場合、サービスエンドポイントに到達するためには NAT ゲートウェイか VPC エンドポイントを経由する必要があるため、常にこの制限に抵触します。そのため、プライベートサブネットにある Cloud9 環境では AMTC を使用できない、という記述がドキュメントにあります。

Currently, if your environment’s EC2 instance is launched into a private subnet, you can't use AWS managed temporary credentials to allow the EC2 environment to access an AWS service on behalf of an AWS entity (an IAM user, for example).

……と、前提となる話はここまでです。

ここに書いてある通り使えないはずなのに、 S3 だけコマンド通る……!という話をこれからします。

やってみた

Cloud9 環境の作成

今回は以下の構成で Cloud9 環境を作成しました。

Cloud9 環境のタイプはインバウンドなしの EC2 環境(Systems Manager 経由)で、プライベートサブネットに配置します。プライベートサブネットは NAT Gateway 向けのルートを持ち、 VPC エンドポイントは何もアタッチされていない状態です。

Cloud9 環境の作成はいくつかのステップに分かれるため、順番に見ていきます。

ステップ 1

まずはステップ 1 で環境名と説明の入力です。今回は環境名のみ指定して先に進みます。

Cloud9

ステップ 2

続いてステップ 2 で詳細設定を行います。

環境タイプは先述の通りインバウンドなしの EC2 環境を選択します。SSH 環境以外を選択した場合、対応する EC2 インスタンスが新規作成されます。(裏側で CloudFormation が動きます。)

Cloud9-8641809

↑インスタンスのタイプやプラットフォームも併せて指定します。

続いて自動停止の時間の指定や、 Cloud9 に関連する IAM ロールの確認(自動的に選択されてここでは変更できない)を行います。

Cloud9-8641861

ロールやインスタンスプロファイルの概要は以下の通りです。

# リソース名 概要
1 AWSServiceRoleForAWSCloud9 Cloud9 環境の作成などに使用されるサービスリンクロール
2 AWSCloud9SSMAccessRole Cloud9 環境の EC2 インスタンスが引き受ける IAM ロール
3 AWSCloud9SSMInstanceProfile #2 の IAM ロールに関連づくインスタンスプロファイル

ステップ 2 の最後に、配置する VPC ・サブネットを指定します。

Cloud9-8641903

「 AMTC はプライベートサブネットでは使用できない」という注意書きはここでも確認できますね。

ステップ 3

最後のステップ 3 でこれまでの設定値を確認し、環境の作成を実行します。

Cloud9-8641958

Cloud9 IDE の画面に遷移し、数分後にアクセスできるようになります。

Cloud9-8641995

Cloud9 環境上での AWS CLI の実行(失敗)

Cloud9 環境の作成が完了し、マネジメントコンソールに接続している IAM ロール cm-chiba.yukihiro として Cloud9 IDE に接続できました。

cm-chiba.yukihiro には IAM ポリシーAdministratorAccessをアタッチしています。(つまりほとんど何でもできる)

さっそく環境上で AWS CLI をいくつか叩いてみます。

まずは何かと叩きたくなるaws sts get-caller identityです。

エラーが出ました。

cm-chiba.yukihiro:~/environment $ aws sts get-caller-identity 
An error occurred (InvalidClientTokenId) when calling the GetCallerIdentity operation: The security token included in the request is invalid

コンソール上では AMTC による制限を受けていることを示すメッセージが表示されています。

Cloud9AMTC

If you get an InvalidTokenId error and you are using AWS managed temporary credentials, your access might be restricted. For more information, see Temporary managed credentials.

そのほか参照系のコマンドをいくつか叩いてみますが、同様にエラーとなります。(2回目以降はメッセージがちょっと違いますね。)

# EC2
cm-chiba.yukihiro:~/environment $ aws ec2 describe-instances

An error occurred (AuthFailure) when calling the DescribeInstances operation: AWS was not able to validate the provided access credentials

# RDS                         
cm-chiba.yukihiro:~/environment $ aws rds describe-db-instances

An error occurred (InvalidClientTokenId) when calling the DescribeDBInstances operation: The security token included in the request is invalid

# IAM
cm-chiba.yukihiro:~/environment $ aws iam list-users

An error occurred (InvalidClientTokenId) when calling the ListUsers operation: The security token included in the request is invalid

Cloud9 接続後に特に設定をしていないのでデフォルトの AMTC が使用されており、プライベートサブネット上にある Cloud9 環境では制限を受けている、という部分を確認できました。

CloudTrail でのイベントの確認

上記で実行したコマンドはいずれも CloudTrail に記録されていませんでした。

ちなみに今回の環境とは異なるパブリックな EC2 環境で実行した場合、以下のように記録が確認できます。

パブリック環境から実行した場合

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROAQ3BIIH7XXXXXXXXXX:cm-chiba.yukihiro",
        "arn": "arn:aws:sts::012345678910:assumed-role/cm-chiba.yukihiro/cm-chiba.yukihiro",
        "accountId": "012345678910",
        "accessKeyId": "ASIAQ3BIIHXXXXXXXXXX",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROAQ3BIIH7XXXXXXXXXX",
                "arn": "arn:aws:iam::012345678910:role/cm-chiba.yukihiro",
                "accountId": "012345678910",
                "userName": "cm-chiba.yukihiro"
            },
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2021-09-10T10:36:20Z",
                "mfaAuthenticated": "true"
            }
        }
    },
    "eventTime": "2021-09-10T11:26:42Z",
    "eventSource": "ec2.amazonaws.com",
    "eventName": "DescribeInstances",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "3.112.221.xx", # EC2 環境のパブリック IP アドレス
    "userAgent": "aws-cli/1.19.112 Python/2.7.18 Linux/4.14.243-185.433.amzn2.x86_64 botocore/1.20.112",
    "requestParameters": {
        "instancesSet": {},
        "filterSet": {}
    },
    "responseElements": null,
    "requestID": "d92ee874-9c6f-45ce-beef-cdb55a6d95fd",
    "eventID": "26d864aa-f012-4f51-a56b-63b14a5dca31",
    "readOnly": true,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "012345678910",
    "eventCategory": "Management"
}

Cloud9 環境への接続に使用している AWS エンティティ(今回は IAM ロール)がプリンシパルとして記録されます。

プライベートな環境での実行は(エラーが記録されるでもなく)記録自体がされていなかったことから、API リクエストそのものが正常に行われていないことが予想されます。

Cloud9 環境上での aws s3 コマンドの実行(成功)

ようやく今回のエントリの本題です。先ほどと同じ条件で、S3 に関するコマンドを叩いていきます。

まずは個人的に手癖で叩く率ナンバーワンのaws s3 lsコマンドです。

問題なく S3 バケット一覧がリストされました。何でだ。

Cloud9AMTC-1271385

バケットを指定して実行しても問題なくオブジェクトがリストされます。何でだ。

Cloud9AMTC-1271450

aws s3は高レベルコマンドと呼ばれ S3 API を抽象化して使用できるものですが、より API に近い API レベルコマンド(aws s3api )もあります。そちらも試してみます。

こちらも問題なく成功します。そうだろうと思っていました。

Cloud9AMTC-1272133

CloudTrail でのイベントの確認

今回実行した S3 コマンドは、ListBucketのみ CloudTrail 上で確認できました。(オブジェクトに対するデータイベントの記録は明示的に有効化する必要があり、わたしの環境ではそれを行なっていないため。)

ハイライト部から、以下が確認できます。

  • プリンシパルは Cloud9 コンソールに接続している IAM ロール cm-chiba.yukihiro であること
  • 送信元 IP アドレスは Cloud9 環境が経由する NAT Gateway が持つものであること
{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROAQ3BIIH7XXXXXXXXXX:cm-chiba.yukihiro",
        "arn": "arn:aws:sts::012345678910:assumed-role/cm-chiba.yukihiro/cm-chiba.yukihiro",
        "accountId": "012345678910",
        "accessKeyId": "ASIAQ3BIIH732EAOASPZ",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROAQ3BIIH7XXXXXXXXXX",
                "arn": "arn:aws:iam::012345678910:role/cm-chiba.yukihiro",
                "accountId": "012345678910",
                "userName": "cm-chiba.yukihiro"
            },
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2021-09-10T10:36:20Z",
                "mfaAuthenticated": "true"
            }
        }
    },
    "eventTime": "2021-09-10T10:55:22Z",
    "eventSource": "s3.amazonaws.com",
    "eventName": "ListBuckets",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "35.76.192.xx",
    "userAgent": "[aws-cli/1.19.112 Python/2.7.18 Linux/4.14.241-184.433.amzn2.x86_64 botocore/1.20.112]",
    "requestParameters": {
        "Host": "s3.ap-northeast-1.amazonaws.com"
    },
    "responseElements": null,
    "additionalEventData": {
        "SignatureVersion": "SigV4",
        "CipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
        "bytesTransferredIn": 0,
        "AuthenticationMethod": "AuthHeader",
        "x-amz-id-2": "AUnspZNxHoXGKvOwsEm4+QTq42c0e3pwUWWuQ+yuHgjGykb5vDP8hwnm6waijPQ8H9RyWMd/CJw=",
        "bytesTransferredOut": 1914
    },
    "requestID": "FGYTABWQZQGWQZ16",
    "eventID": "96fa785d-87b9-4103-8cf0-a6b558f67b24",
    "readOnly": true,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "012345678910",
    "eventCategory": "Management"
}

(ちなみに NAT Gateway はこちら。グローバル IP アドレスが、上記で確認できる送信元 IP アドレスと一致しています。)

NATGateway

Cloud9 環境上での aws s3 コマンドの実行(失敗)

Trail の結果から Cloud9 に接続している IAM ロール cm-chiba.yukihiro (と AMTC )が使用されていることは確認できましたが、念の為 Deny を加えたときの挙動を確認しておきます。

以下の IAM ポリシーを cm-chiba.yukihiro にアタッチします。S3 に関する操作を全て拒否するものです。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Deny",
            "Action": "s3:*",
            "Resource": "*"
        }
    ]
}

その状態で Cloud9 IDE 上で aws s3 lsを叩くと、エラーが発生します。

Cloud9AMTC-1273939

AMTC による制限を受けていた場合のメッセージとは異なり、アクセスが明示的に Deny されていることが読み取れます。

cm-chiba.yukihiro:~/environment $ aws s3 ls

An error occurred (AccessDenied) when calling the ListBuckets operation: Access Denied

CloudTrail でのイベントの確認

イベントを確認すると、プリンシパルや送信元 IP アドレスは成功時と変わらず、その上でアクセスが拒否されていることが分かります。

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROAQ3BIIH7XXXXXXXXXX:cm-chiba.yukihiro",
        "arn": "arn:aws:sts::012345678910:assumed-role/cm-chiba.yukihiro/cm-chiba.yukihiro",
        "accountId": "012345678910",
        "accessKeyId": "ASIAQ3BIIH7335RIXL7Z",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROAQ3BIIH7XXXXXXXXXX",
                "arn": "arn:aws:iam::012345678910:role/cm-chiba.yukihiro",
                "accountId": "012345678910",
                "userName": "cm-chiba.yukihiro"
            },
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2021-09-10T10:36:20Z",
                "mfaAuthenticated": "true"
            }
        }
    },
    "eventTime": "2021-09-10T11:38:34Z",
    "eventSource": "s3.amazonaws.com",
    "eventName": "ListBuckets",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "35.76.192.xx",
    "userAgent": "[aws-cli/1.19.112 Python/2.7.18 Linux/4.14.241-184.433.amzn2.x86_64 botocore/1.20.112]",
    "errorCode": "AccessDenied",
    "errorMessage": "Access Denied",
    "requestParameters": {
        "Host": "s3.ap-northeast-1.amazonaws.com"
    },
    "responseElements": null,
    "additionalEventData": {
        "SignatureVersion": "SigV4",
        "CipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
        "bytesTransferredIn": 0,
        "AuthenticationMethod": "AuthHeader",
        "x-amz-id-2": "LTZbIVB52pZn4IlZCf6D64BkdxWnz76sF5bgZTTzilefLyiaZJB9nby+a7p5yxn3LBFnxw/lbAc=",
        "bytesTransferredOut": 243
    },
    "requestID": "54TKJ6V6V42RGGQS",
    "eventID": "82cc3a5f-0981-4906-8b9f-330340bacc0e",
    "readOnly": true,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "012345678910",
    "eventCategory": "Management"
}

番外編:AMTC の無効化

本筋から外れますが、AMTC を使用せずにインスタンスプロファイルを使用するパターンを試してみます。

Cloud9 コンソールから AMTC を無効化します。

Cloud9AMTC-1274299

その後にaws sts get-caller-identityを実行すると、インスタンスプロファイル(に紐づく IAM ロール)を使用していることが見て取れます。

cm-chiba.yukihiro:~/environment $ aws sts get-caller-identity 
{
    "Account": "012345678910", 
    "UserId": "AROAQ3BIIH732TG4DM2N4:i-07856803079a58378", 
    "Arn": "arn:aws:sts::012345678910:assumed-role/AWSCloud9SSMAccessRole/i-07856803079a58378"
}

aws ec2 describe-instancesを単純に実行するとリージョンの指定が必要、と怒られます。リージョンを指定して実行すると、権限不足によるエラーが発生します。

cm-chiba.yukihiro:~/environment $ aws ec2 describe-instances
You must specify a region. You can also configure your region by running "aws configure".
cm-chiba.yukihiro:~/environment $ aws ec2 describe-instances --region ap-northeast-1

An error occurred (UnauthorizedOperation) when calling the DescribeInstances operation: You are not authorized to perform this operation.

今回の環境が使用している IAM ロールには IAM ポリシーAWSCloud9SSMInstanceProfileのみがアタッチされており、その内容は以下の通りです。セッションマネージャー接続に必要な最小限のもののみが定義されています。

AWSCloud9SSMInstanceProfile

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssmmessages:CreateControlChannel",
                "ssmmessages:CreateDataChannel",
                "ssmmessages:OpenControlChannel",
                "ssmmessages:OpenDataChannel",
                "ssm:UpdateInstanceInformation"
            ],
            "Resource": "*"
        }
    ]
}

IAM ロールAWSCloud9SSMAccessRoleに AWS 管理ポリシーAmazonEC2FullAccessをアタッチしたのちに再度実行すると、問題なくコマンドが成功します。

cm-chiba.yukihiro:~/environment $ aws ec2 describe-instances --region ap-northeast-1
{
    "Reservations": [
        {
            "Instances": [
                {
                    "Monitoring": {
                        "State": "disabled"
                    }, 
                    "PublicDnsName": "", 
・・以下略

CloudTrail でのイベントの確認

プリンシパルが、 EC2 環境が使用する IAM ロールとなっていることが確認できます。

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "AssumedRole",
        "principalId": "AROAQ3BIIH732TG4DM2N4:i-07856803079a58378",
        "arn": "arn:aws:sts::012345678910:assumed-role/AWSCloud9SSMAccessRole/i-07856803079a58378",
        "accountId": "012345678910",
        "accessKeyId": "ASIAQ3BIIH73YNU7HLMY",
        "sessionContext": {
            "sessionIssuer": {
                "type": "Role",
                "principalId": "AROAQ3BIIH732TG4DM2N4",
                "arn": "arn:aws:iam::012345678910:role/service-role/AWSCloud9SSMAccessRole",
                "accountId": "012345678910",
                "userName": "AWSCloud9SSMAccessRole"
            },
            "webIdFederationData": {},
            "attributes": {
                "creationDate": "2021-09-10T11:30:08Z",
                "mfaAuthenticated": "false"
            },
            "ec2RoleDelivery": "2.0"
        }
    },
    "eventTime": "2021-09-10T11:48:39Z",
    "eventSource": "ec2.amazonaws.com",
    "eventName": "DescribeInstances",
    "awsRegion": "ap-northeast-1",
    "sourceIPAddress": "35.76.192.xx",
    "userAgent": "aws-cli/1.19.112 Python/2.7.18 Linux/4.14.241-184.433.amzn2.x86_64 botocore/1.20.112",
    "errorCode": "Client.UnauthorizedOperation",
    "errorMessage": "You are not authorized to perform this operation.",
    "requestParameters": {
        "instancesSet": {},
        "filterSet": {}
    },
    "responseElements": null,
    "requestID": "40da5c61-f772-46f2-8caa-529a744c3704",
    "eventID": "500af49a-7f17-45fb-938e-a05aed8a82f1",
    "readOnly": true,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "012345678910",
    "eventCategory": "Management"
}

AMTC を無効化すると、EC2 環境のインスタンスプロファイルが使用されることを確認できました。

なんで S3 だけ叩けるんだろうなぁ

プライベートサブネットに配置した AWS Cloud9 環境で AMTC を使用した AWS API 実行はできないはずなのに、なぜか S3 向けの AWS CLI が成功する、という話でした。

この事象は以下のエントリを書いた時に気づいたのですが、改めて深堀りして確認しても謎のままでした。

一つ気になる点として、上記のエントリの元となるアップデート記事に以下の文言があります。

さらに、S3 統合により VPC のパフォーマンスが向上し、……

Cloud9 環境と S3 が何かこう……いい感じに統合されることで実現されているのかなと妄想していますが、公式な情報は見つけられませんでした。何かご存知の方がいれば教えてください。

そして、EC2 環境をプライベートサブネットに配置したい場合には、難しいことを考えず AMTC を無効化してインスタンスプロファイルを使用してください。

以上、 チバユキ (@batchicchi) がお送りしました。

参考