別アカウントの EC2 インスタンスにクレデンシャルをセットしてクロスアカウントでないアクセスをしてみた
コンバンハ、千葉(幸)です。
突然ですが問題です。
以下のような構成で EC2 インスタンスから S3 にアクセスを行うとします。EC2 インスタンスと S3 バケットは別々のアカウントに属しています。
S3 バケットはパブリックアクセスを許可しておらず、何らか認証情報を使用してアクセスが必要な状態です。
この時、この構成を「クロスアカウントでのアクセス」と呼ぶでしょうか?また、その理由も合わせて考えてみてください。
答えはちょっと後に出てきます。クロスアカウントって何だっけ……というのを考えてみるのが本エントリの趣旨です。
AWS のクロスアカウントアクセスとは何か
AWS におけるクロスアカウントアクセスとは何を指すのかを考えます。明確に定義された記述はありませんが、アクションの実行元のプリンシパル(エンティティ)と実行先の AWS リソースが別のアカウントに属している場合のアクセスを指すと考えればよいでしょう。
以下のような構成はわかりやすくクロスアカウントアクセスです。アクションが成功するためにはプリンシパルと AWS リソース、双方のパーミッションで許可が与えられている必要があります。
一方で、一度対向のアカウントにある IAM ロールを引き受けてからリソースにアクセスする、という一連の流れをクロスアカウントアクセスと呼ぶこともあります。
ここでのプロセスを分解すると以下のようになります。
- 別アカウントにある IAM ロールを引き受ける(クロスアカウントアクセス)
- IAM ロールを引き受けたセッションでリソースにアクセスする(同一アカウントアクセス)
マネジメントコンソールでスイッチロールと呼ばれる仕組みを使用しているのであれば、こちらのパターンのクロスアカウントアクセスを行なっていることになります。IAM ロールはプリンシパルでありながら同時に実行先の AWS リソースでもある、ちょっと特殊な存在です。そんな所が好きです。
別アカウントの EC2 からのアクセスはクロスアカウントアクセスか
ここで冒頭の問題に戻ります。果たしてこの構成はクロスアカウントアクセスなのかどうか、というのが観点です。
結果からいうと「この情報だけでは判断できない(どちらもあり得る)」なのですが、「クロスアカウントアクセスである」と考えた方も多いのではないでしょうか。
先程のおさらいをすると、クロスアカウントアクセスとはアクションの実行元のプリンシパル(エンティティ)と実行先の AWS リソースが別のアカウントに属している場合を指します。
「クロスアカウントアクセスである」と考えた方は、暗黙的に以下のような構成を思い浮かべたのではないかと思います。
EC2 インスタンスに IAM ロール(インスタンスプロファイル)をアタッチし、その認証情報を使用するという構成です。永続的な認証方法をセットする必要がなく、一時的な認証情報を IAM ロール経由で取得することになるため、ベストプラクティスに則った構成です。
一方で、永続的なアクセスキーを使用する構成も可能です。
別アカウント(ここでは実行先の S3 バケットのアカウントと同一のアカウント)の IAM ユーザーが発行した永続的なアクセスキーを EC2 インスタンスにセットアップすれば、あくまで同一アカウントでのアクセスとしてアクションを実行できます。
今回はたまたま実行環境が別アカウントの EC2 インスタンスであったためややこしくなりますが、アクセスキーをローカルの操作端末にセットアップした場合を考えれば、「同一アカウントでのアクセスである」ということがわかりやすくなるかと思います。
別アカウントの EC2 インスタンスからクロスアカウントじゃないアクセスをやってみた
今回は以下の構成で行います。
- AWS アカウント
000000000000
に存在する IAM ユーザーChibayuki
でアクセスキーを発行 - AWS アカウント
999999999999
に存在する EC2 インスタンスには元々 IAM ロールがアタッチされているが、上記のアクセスキーを AWS CLI プロファイルで設定して上書き - AWS アカウント
000000000000
に存在する S3 バケットchibayuki-test
に同一アカウントアクセス
同一アカウントアクセスであるため、プリンシパルか AWS リソースどちらかで許可が与えられていればアクセスは成功します。今回は S3 バケットポリシー側でのみ許可を与えます。
IAM ユーザーの作成
IAM ユーザーChibayuki
を作成し、IAM ポリシーは何もアタッチしていない状態にしました。アクセスキーを払い出しておきます。
S3 バケットの作成
以下のバケットポリシーを持つ S3 バケットを作成しました。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::000000000000:user/Chibayuki" }, "Action": "s3:*", "Resource": [ "arn:aws:s3:::chibayuki-test", "arn:aws:s3:::chibayuki-test/*" ] } ] }
プリンシパルとして IAM ユーザーChibayuki
を指定しているため、当該ユーザーに IAM ポリシーがアタッチされていなくともアクションが成功する状態です。
EC2 インスタンスでの作業
ここまで見てきた IAM ユーザーや S3 バケットとは別アカウントにある EC2 インスタンスで作業していきます。EC2 インスタンスには IAM ロールをアタッチしており、Systems Manager セッションマネージャーで接続ができる状態です。
接続し、アクセスキーのセットアップ前に AWS CLI の実行主体を確認してみます。
sh-4.2$ aws sts get-caller-identity { "Account": "999999999999", "UserId": "AROAQ3BIIH735AQHQPIOW:i-047875da17caa9cc2", "Arn": "arn:aws:sts::999999999999:assumed-role/AmazonSSMRoleForInstancesQuickSetup/i-047875da17caa9cc2" }
↑ EC2 インスタンスにアタッチされている IAM ロールを引き受けたセッションが実行主体であることがわかります。この状態のまま S3 バケットchibayuki-test
にアクセスを試みるのであればクロスアカウントアクセスとなり、IAM ロールと S3 バケットの双方で許可が必要です。
今回は S3 バケット側で許可が与えられていないため、S3 へのアクセスは拒否されました。
sh-4.2$ aws s3 cp /tmp/test.txt s3://chibayuki-test upload failed: ../../tmp/test.txt to s3://chibayuki-test/test.txt An error occurred (AccessDenied) when calling the PutObjectoperation: Access Denied
ここで、AWS CLI のプロファイルをセットアップします。IAM ユーザーChibayuki
のアクセスキー/シークレットアクセスキーを入力します。
sh-4.2$ aws configure AWS Access Key ID [None]: AKIAXXXXXXXXXXXX AWS Secret Access Key [None]: Ez+xYYYYYYYYYYYYYYYYYY Default region name [None]: ap-northeast-1 Default output format [None]: json
AWS CLI プロファイルの方が優先度が高く解釈されるため、以降の AWS CLI コマンドは(特に指定しない限り)IAM ユーザーChibayki
として実行されます。
sh-4.2$ aws sts get-caller-identity { "Account": "000000000000", "UserId": "AIDARXXXXXXXXXXXXXXXX", "Arn": "arn:aws:iam::000000000000:user/Chibayuki" }
この状態で S3 バケットchibayuki-test
にアクセスを試みると、問題なく成功しました。
sh-4.2$ aws s3 cp /tmp/test.txt s3://chibayuki-test upload: ../../tmp/test.txt to s3://chibayuki-test/test.txt sh-4.2$ aws s3 ls chibayuki-test 2021-10-25 11:43:05 0 test.txt
クロスアカウントかどうかを判断する上で「実行環境がどこか」は関係なく、「どのアカウントの認証情報を使用するか」が意識すべき点であると分かりました。
EC2 インスタンスが存在するアカウント側では制御できないのか?
上記の試行では権限の制御は IAM ユーザーと S3 バケットでのみ行われます。EC2 インスタンスにアタッチされた IAM ロールの権限をどういじっても、「同一アカウントアクセス」を制御することはできません。
悪意を持ったユーザーが EC2 インスタンスに侵入し自身のアカウントのアクセスキーをそこでセットアップした場合、機密情報が外部の S3 バケットに Put されてしまうということも起こり得ます。
認証情報は切り替えができますので、「インスタンスプロファイルの権限を使用し S3 バケットからデータを Get し、アクセスキーを使用して外部の S3 バケットへ Put」ということもできてしまいます。
この事象を、EC2 インスタンスが存在するアカウント側で防ぐ手立てはないのでしょうか?
あります。VPC エンドポイントです。
S3 向けの VPC エンドポイントはエンドポイントポリシーを設定できます。そのポリシーの中でアクセス可能なリソースとして必要最小限の S3 バケットを指定していれば、外部の S3 バケットに Put されるという事態は防げます。
S3 向けの通信が VPC エンドポイントを向くように設定されていれば、そこでブロックできます。(基本的にはサブネットルートテーブルでの制御になります。)
仮に EC2 インスタンスに侵入されても、そのユーザーは VPC 周りの設定変更が可能な権限は有していないはずです。VPC エンドポイントが、プリンシパル / アクション実行先の AWS リソースとは別の防御レイヤとして働くことが期待できます。(EC2 インスタンスにアタッチされた IAM ロールに VPC の変更権限がついていた場合は突破されてしまいます。)
どこまでのセキュリティレベルを求めたいかを考え、各種ポリシーを設計するとよいでしょう。
終わりに
別アカウントの EC2 インスタンスからクロスアカウントでないアクセスを試みてみました。
それは EC2 インスタンス上で使用する認証情報として自アカウントのクレデンシャルを使用することで実現しました。クロスアカウントかどうかに実行環境は関係なく、プリンシパルと実行先 AWS リソースがアカウントを跨いでいるかどうかで考える、というのを覚えておいてください。
クロスアカウントってなんだっけ……となった時の参考になれば幸いです。
以上、 チバユキ (@batchicchi) がお送りしました。