はじめに
実務のAWS環境において、「本番アカウントのS3バケットにあるデータを、分析用アカウントのECSやLambdaから読み込みたい」「開発アカウントからステージング用のS3バケットにビルド成果物をアップロードしたい」といった、クロスアカウントでのS3共有が必要になるケースは非常に多く存在します。
しかし、アクセス制御の設定を誤ると、機密データの漏洩や、アップロードしたファイルの所有権が原因でファイルが読み込めないといったトラブルの原因になります。この記事では、AWSのセキュリティベストプラクティスに則り、2つのアカウント間で安全かつ確実にS3バケットを共有するための設定手順をステップバイステップで解説します。
前提知識/必要な理由
AWSにおいて異なるアカウント(アカウントAとアカウントB)の間でS3バケットを共有する場合、原則として「アクセス権を提供する側(アカウントAのバケットポリシー)」と「アクセスする側(アカウントBのIAMポリシー)」の両方で明示的にアクセスを許可する必要があります。
クロスアカウントアクセスを実現する方法には「IAMロールへのスイッチ」もありますが、以下のような理由から、S3バケットポリシーとIAMポリシーを組み合わせた直接アクセス方式が実務で広く採用されています。
- アプリケーションやECSタスク、Lambda関数などのプログラムが、追加の認証処理(AssumeRole)を行うことなく、自身のIAMロールの権限のまま直接S3バケットを操作できるため、設計がシンプルになる。
- データ転送や書き込みの処理速度・エラーハンドリングにおいて、ロール切り替えのオーバーヘッドを削減できる。
具体的な設定手順・設計方法
ここでは、データを保持する側を「アカウントA(111111111111)」、アクセスするアプリケーションやユーザーが存在する側を「アカウントB(222222222222)」として解説します。バケット名は shared-data-bucket-example とします。
ステップ1:アカウントA(S3バケット所有側)での設定
まずは、バケットポリシーを設定して、アカウントBからのアクセスを許可します。
- AWSマネジメントコンソールでアカウントAにログインし、S3コンソールを開きます。
- 対象のS3バケットを選択し、「アクセス許可」タブをクリックします。
- 「バケットポリシー」の編集を開き、以下のJSONポリシーを貼り付けて保存します。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowAccountBToAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::222222222222:root"
},
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::shared-data-bucket-example",
"arn:aws:s3:::shared-data-bucket-example/*"
]
}
]
}
※ Principal で相手アカウントの root を指定することで、アカウントB側でどのIAMユーザー/ロールに権限を割り当てるかの制御を、アカウントBの管理者に委譲することができます。
ステップ2:アカウントB(アクセス側)での設定
次に、アカウントBのIAMポリシーを設定し、アクセスするリソース(EC2、Lambda、またはIAMユーザーなど)に付与します。
- アカウントBにログインし、IAMコンソールを開きます。
- 「ポリシー」メニューから「ポリシーの作成」を選択し、JSONタブに以下のポリシーを入力します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListBucket"
],
"Resource": "arn:aws:s3:::shared-data-bucket-example"
},
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject"
],
"Resource": "arn:aws:s3:::shared-data-bucket-example/*"
}
]
}
- ポリシーを作成後、実際にS3へアクセスするEC2のインスタンスプロフィール(IAMロール)や、Lambda関数の実行ロールにこのポリシーをアタッチします。
ステップ3:AWS CLIでのアクセス確認
設定が完了したら、アカウントBの環境から正しくアクセスできるかを確認します。ここではAWS CLIを使用します。
※AWS CLIが手元の環境に導入されていない場合は、事前に公式のインストーラー、またはPython環境であれば以下のpipコマンドを使用してインストールしてください。
pip install awscli
準備ができたら、アカウントBの認証情報を用いて、以下のコマンドを実行します。
# バケット内のファイル一覧を表示するテスト
aws s3 ls s3://shared-data-bucket-example
# テストファイルのアップロード
aws s3 cp test.txt s3://shared-data-bucket-example/test.txt
エラーが発生せずに実行できれば、クロスアカウントアクセスの設定は成功です。
実務での注意点
1. S3オブジェクト所有権(Object Ownership)の落とし穴
デフォルトの設定では、アカウントBがアップロードしたファイルの所有者は「アカウントB」になります。そのため、バケット所有者であるアカウントAがそのファイルを読み込めないというトラブルが多発します。
これを防ぐため、アカウントAのS3バケット設定の「オブジェクト所有者」で「バケット所有者の強制(Bucket owner enforced)」を有効にしてください。これにより、アカウントBがアップロードしたファイルであっても、自動的にアカウントAが所有者となり、アクセス権の問題が解消されます。
2. KMSによる暗号化が有効な場合の設定
S3バケットがデフォルトの「SSE-S3」ではなく、AWS KMSのカスタマーマネージドキー(SSE-KMS)で暗号化されている場合、アカウントBはS3ポリシーだけでなくKMSキーの利用権限(キーポリシー)も必要になります。KMSのキーポリシーにもアカウントBからの kms:Decrypt や kms:GenerateDataKey を許可する設定を追加してください。
まとめ
AWSのクロスアカウント接続は難しく感じられますが、「双方のアカウントでの明示的な許可」という原則を理解すれば、シンプルに設計可能です。実務で設定する際は、不要な権限(s3:* など)を避け、必要なアクション(GetObject や PutObject など)に絞り込む「最小特権の原則」を徹底しましょう。また、オブジェクト所有権の設定を有効にすることを忘れないようにしてください。