はじめに
Amazon EC2(以下、EC2) から Amazon S3(以下、S3) にアクセスしたい場合、VPCエンドポイントを作成する必要があります。その動作を確認したくなり、個人利用のAWS環境で動作確認をしていました。適切なVPCエンドポイントポリシー、IAMロール、S3バケットポリシーを設定しようとしたのですが、S3バケットポリシーの設定を誤り、S3バケットポリシーの修正や削除などの操作をできなくなってしまいました。
戒めとして記事を書こうと思います。
本記事は、筆者個人の見解です。個人利用の範囲で試した結果のため、参考程度に留めてください。内容に間違い等がありましたらご指摘いただけますと幸いです。
VPC、EC2の作成、S3バケット、VPCエンドポイントの作成手順は割愛します。
今回の環境
以下のような環境で動作確認しました。また、図ではVPCエンドポイントは一つだけですが、S3にはSystems Manager のセッションマネージャーを使ってアクセスするため、
- S3 VPC Endpoint (Gateway): S3アクセス用
- SSM VPC Endpoint (Interface): Systems Manager用
- SSM Messages VPC Endpoint (Interface): セッションマネージャー通信用
- EC2 Messages VPC Endpoint (Interface): EC2メッセージング用
の4つ作成しました。
構成図は、Bedrock Engineer の Diagram Generator を使って生成しました。
ポリシー周りの設定
IAMロール
今回は、EC2からセッションマネージャーを使ってS3に接続します。従って、プライベートサブネットから Systems Manager に接続できるよう、AmazonSSMManagedInstanceCore
と AmazonS3FullAccess
をアタッチしたIAMロールを作成します。
今回は一時的な検証利用なので、AmazonS3FullAccess をアタッチしましたが、厳密には最小権限の原則に則ったIAMポリシーを作成してアタッチするのが良いと思います。
VPCエンドポイントポリシー
以下のような、参照とオブジェクト作成のみを許可したポリシを設定しました。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Statement1",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::vpcendpoint-test-xxxxxxxxx1",
"arn:aws:s3:::vpcendpoint-test-xxxxxxxxx1/*"
],
"Condition": {
"IpAddress": {
"aws:VpcSourceIp": "10.0.1.0/24"
}
}
},
{
"Sid": "Statement2",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::vpcendpoint-test-xxxxxxxxx2",
"arn:aws:s3:::vpcendpoint-test-xxxxxxxxx2/*"
],
"Condition": {
"IpAddress": {
"aws:VpcSourceIp": "10.0.2.0/24"
}
}
}
]
}
S3バケットポリシーの設定
VPCエンドポイントからのアクセスのみ許可するVPCエンドポイントは、以下の通りです。
特定のVPCエンドポイントを経由したアクセスのみ、オブジェクトの参照と格納を許可することを想定していました。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::bucket-name",
"arn:aws:s3:::bucket-name/*"
],
"Condition": {
"StringEquals": {
"aws:sourceVpce": "vpce-xxxxxxxxx"
}
}
},
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::bucket-name",
"arn:aws:s3:::bucket-name/*"
],
"Condition": {
"StringNotEquals": {
"aws:sourceVpce": "vpce-xxxxxxxxx"
}
}
}
]
}
このバケットポリシーの適用は、自己責任でお願いします!!
S3バケットポリシーを適用したら・・・
急にエラーが表示されました。
S3バケットが消せなくなり、微量とはいえお金が嵩んでいく不安が頭をよぎり、焦りました…(個人利用アカウントなので、微量のお金でもチリツモになるのが怖い…)
解決方法
ふと、ルートユーザなら何とかなるかなと思いました。何とかできそうです!!
以下の記事を読んで安心できました。ありがとうございました!
ルートユーザでログインして取り急ぎバケットポリシーを削除したら、普段ログインしているアカウントで操作できるようになりました。
エラーが表示された理由
APIレスポンスを開くと、「ポリシーでDenyされているから」といった内容が書かれていました。
つまり、先述のバケットポリシーのDeny設定に何か問題がありそうです。
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::bucket-name",
"arn:aws:s3:::bucket-name/*"
],
"Condition": {
"StringNotEquals": {
"aws:sourceVpce": "vpce-xxxxxxxxx"
}
}
}
特定のVPCエンドポイントを経由していない場合は、バケットの操作をすべて("Action": "s3:*")拒否したく、明示的な拒否を適用しました。
こうすれば、特定のVPCエンドポイントを経由しない操作はされないと思って適用したのですが、マネージドコンソールからのアクセスも特定のVPCエンドポイントを経由していないため拒否されたことに気づきました。それを明示的なDenyに書いたからこうなった…というわけです。
実は、適用したバケットポリシーは、Amazon Q に聞いて提示されたアウトプットでした。プロンプトの内容は割愛しますが、プロンプトの書き方も悪かったのでしょう。「AWSのことを Amazon Q に聞いて出てきたアウトプットだからまぁー大丈夫か」と油断していたのでしょう。しかし、Amazon Q のアウトプットをあまり吟味せず、そのまま適用してしまった。これが一番の問題です!!
生成AIのアウトプットを鵜吞みにするのはやめましょう!必ず人の目で確認しましょう!!
改善方法
マネージドコンソールからのアクセスを拒否ってしまったら、メンテナンスできなくなります(言うまでもありませんが、ルートユーザで解決できてもルートユーザを日常利用するものではありません)。
特定のVPCエンドポイント以外のアクセスを拒否、または特定のIAMロールにスイッチしたユーザ以外からのアクセスを拒否するための明示的な拒否は、以下の通りです。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:ListBucket",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::bucket-name",
"arn:aws:s3:::bucket-name/*"
],
"Condition": {
"StringEquals": {
"aws:sourceVpce": "vpce-xxxxxxxxx"
}
}
},
{
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::bucket-name",
"arn:aws:s3:::bucket-name/*"
],
"Condition": {
"StringNotLike": {
"aws:userId": "XXXXXXXXXXXXXXXXXXX:*"
},
"StringNotEquals": {
"aws:sourceVpce": "vpce-xxxxxxxxx"
}
}
}
]
}
"aws:userId" にはスイッチ先のIAMロールIDが入ります。スイッチ先のIAMロールは、
aws iam get-role --role-name <ロール名>
で調べることができます。
"RoleId": "XXXXXXXXXXXXXXXXXXX",
の値が、"aws:userId" に指定する値となります。
こうすることで、特定のVPCエンドポイントを通過させたアクセス、または特定のユーザ・ロールのみにS3を操作できる権限を与えることができました。
この方法は、以下の記事を参考にさせていただきました。ありがとうございました!
まとめ
理解不十分な状態での明示的な拒否の適用、確認不十分な明示的な拒否の適用をすると、痛い目にあうことが分かりました。また、生成AIのアウトプットを鵜吞みにすると痛い目にあうことも、身をもって体験しました。
バケットポリシー、エンドポイントポリシー、IAMポリシーといった、ポリシー周りの設計の重要さ、奥深さを再確認しました。ポリシー設計は慎重にですね。
以上です。最後まで読んでいただき、ありがとうございました!