Memo

メモ > サーバ > サービス: AWS > S3バケットに他AWSアカウントからのアクセスを許可

S3バケットに他AWSアカウントからのアクセスを許可
以下のAWSアカウントがあるとする。 account1(111111111111) account2(222222222222) このとき、account1アカウントが所持しているS3バケットを、account2アカウントから読み書きできるように設定する。 ■account1にて設定 ※外部AWSアカウント「222222222222」用に、S3を操作するためのロールを作成する。 ※ロールに割り当てる許可ポリシーは、検証なら「AmazonS3FullAccess」でもいいが、実案件ではできるだけ権限を絞るといい。 以下の例では、特定バケットに限定して操作を許可している。 ポリシー ExternalExampleDevelopPolicy として以下を作成。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ … 許可したい操作を指定。 "s3:ListBucket", "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": [ … 許可したいバケットを指定。 "arn:aws:s3:::example-linkage-develop", "arn:aws:s3:::example-linkage-develop/*" ] } ] }
ロール ExternalExampleDevelopRole として以下を作成 信頼されたエンティティタイプ: AWSアカウント AWSアカウント: 別のAWSアカウント アカウントID: 222222222222 … 許可したいAWSアカウントを指定。 オプション: 外部IDを要求する … 必須では無いが推奨されている。 外部ID: Abcd1234 … 必須では無いが推奨されている。 許可ポリシー: ExternalExampleDevelopPolicy … 上で作成したポリシーを指定。 参考までに、作成後に ExternalExampleDevelopRole の「信頼関係」を確認すると、以下のJSONを確認できる。 「Principal」の部分が、別のAWSアカウントを信頼するための指定。 外部IDを指定しなかった場合、Conditionの部分は「"Condition": {}」となる。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::222222222222:root" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "Abcd1234" } } } ] }
■account2にて設定 ポリシー ExampleDevelopPolicy として以下を作成。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::111111111111:role/ExternalExampleDevelopRole" … 上で作成したロールの情報(アカウントIDとロール名)を指定する } ] }
ロール「ExampleDevelopRole」を作成し、上で作成した「ExampleDevelopPolicy」を割り当てる。 さらにEC2「Example-Develop-Web1(i-0e6194f055556b51b)」に対して、上で作成した「ExampleDevelopRole」を割り当てる。 上で設定したEC2にSSHで接続し、以下のとおりコマンドを実行する。(外部IDを指定しなかった場合、コマンドから「--external-id "Abcd1234"」部分は省く。) AssumeRole に必要な認証情報(アクセスキー、シークレットキー、セッショントークン)が返される。
$ aws sts assume-role --role-arn "arn:aws:iam::111111111111:role/ExternalExampleDevelopRole" --role-session-name "TestSession" --external-id "Abcd1234" … 上で作成したロールの情報(アカウントID、ロール名、外部ID)を指定する { "AssumedRoleUser": { "AssumedRoleId": "AROAXXXXXXXXXXXXX5CGE:TestSession", "Arn": "arn:aws:sts::111111111111:assumed-role/ExternalExampleDevelopRole/TestSession" }, "Credentials": { "SecretAccessKey": "G/u8iH7jXXXXXXXXXXXXXXXXXXXXXXXXzuYZ+BWH", "SessionToken": "FwoGZXIvYXdzEID//////////wEaDCa6rWxdIvTnBtc1MCKvAdsYLpWCFGwVeHNa3wY81UI8m4rhfWwqXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXZjnF1Q0o/aCtsQYyLeltX4vBYejF6L6CbWk+nMrXkJ9Nv7DFb7W6vCOc59g9/Qj7tqRwWn2Tetopyg==", "Expiration": "2024-04-26T07:58:05Z", "AccessKeyId": "ASIAXXXXXXXXXXXX4C76" } }
上記内容をもとに、~/.aws/credentials に認証情報を設定する。
$ mkdir ~/.aws $ vi ~/.aws/credentials
以下のとおり設定する。
[cross-account] aws_access_key_id = ASIAXXXXXXXXXXXX4C76 … 取得したAccessKeyId。 aws_secret_access_key = G/u8iH7jXXXXXXXXXXXXXXXXXXXXXXXXzuYZ+BWH … 取得したSecretAccessKey。 aws_session_token = FwoGZXIvYXdzEID//////////wEaDCa6rWxdIvTnBtc1MCKvAdsYLpWCFGwVeHNa3wY81UI8m4rhfWwqXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXZjnF1Q0o/aCtsQYyLeltX4vBYejF6L6CbWk+nMrXkJ9Nv7DFb7W6vCOc59g9/Qj7tqRwWn2Tetopyg== … 取得したSessionToken。
以下のとおり接続する。
$ export AWS_PROFILE=cross-account $ aws sts get-caller-identity --query 'Arn' | awk '{ print substr($1, index($1, "/")) }' | awk '{ sub("/", ""); sub("\"", ""); print }' ExternalExampleDevelopRole/TestSession
以下でS3の内容を参照できる。
$ aws s3 ls example-linkage-develop $ aws s3 ls example-linkage-develop/images/
以降は以下で接続できる。 ただし、取得したセッショントークンの有効期限は1時間となっている。(延長可能だが最大で12時間まで。)
$ export AWS_PROFILE=cross-account $ aws s3 ls example-linkage-develop/images/
■PHPからの接続 以下のようにアクセスキー、シークレットキー、セッショントークンを指定することで、PHPからも接続できる。 ただし上記のとおり、この情報には有効期限がある。(一定時間が経過すると「400 Bad Request」「ExpiredToken」「The provided token has expired.」のエラーになる。)
<?php require_once 'vendor/autoload.php'; use Aws\S3\S3Client; use Aws\S3\Exception\S3Exception; try { // アクセスキーとシークレットアクセスキーを指定して接続 $client = new S3Client([ 'credentials' => [ 'key' => 'ASIAXXXXXXXXXXXX4C76', 'secret' => 'G/u8iH7jXXXXXXXXXXXXXXXXXXXXXXXXzuYZ+BWH', 'token' => 'FwoGZXIvYXdzEID//////////wEaDCa6rWxdIvTnBtc1MCKvAdsYLpWCFGwVeHNa3wY81UI8m4rhfWwqXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXZjnF1Q0o/aCtsQYyLeltX4vBYejF6L6CbWk+nMrXkJ9Nv7DFb7W6vCOc59g9/Qj7tqRwWn2Tetopyg==', ], 'region' => 'ap-northeast-1', 'version' => 'latest', ]); // バケットとディレクトリを指定して取得 $result = $client->listObjects([ 'Bucket' => 'example-linkage-develop', 'Prefix' => 'images/', 'Delimiter' => '/', ]); // ファイルを表示 foreach ($result['Contents'] as $content) { echo $content['Key'] . '(' . $content['LastModified'] . ')' . '<br>'; } } catch (S3Exception $e) { exit('S3Exception: ' . $e->getMessage()); } catch (Exception $e) { exit('Exception: ' . $e->getMessage()); }
■PHPからの接続(改修版) 以下のようにすると、STSを使って都度認証情報を取得する。これにより、有効期限の問題を回避できる。 ただし「EC2インスタンスでプログラムを実行していて、そのインスタンスに適切なIAMロールが割り当てられている」必要がある。
<?php require 'vendor/autoload.php'; use Aws\Sts\StsClient; use Aws\S3\S3Client; // STS Clientの初期化 $stsClient = new StsClient([ 'version' => 'latest', 'region' => 'ap-northeast-1', ]); try { // AssumeRole APIをコール $result = $stsClient->assumeRole([ 'RoleArn' => 'arn:aws:iam::111111111111:role/ExternalExampleDevelopRole', 'RoleSessionName' => 'TestSession', 'DurationSeconds' => 3600, // トークンの有効期限(秒) 'ExternalId' => 'Abcd1234', // 外部ID ]); // 取得した認証情報 $credentials = $result->get('Credentials'); // 認証情報を使って新しいクライアントを作成 $client = new S3Client([ 'version' => 'latest', 'region' => 'ap-northeast-1', 'credentials' => [ 'key' => $credentials['AccessKeyId'], 'secret' => $credentials['SecretAccessKey'], 'token' => $credentials['SessionToken'], ] ]); // バケットとディレクトリを指定して取得 $result = $client->listObjects([ 'Bucket' => 'example-linkage-develop', 'Prefix' => 'images/', 'Delimiter' => '/', ]); // ファイルを表示 foreach ($result['Contents'] as $content) { echo $content['Key'] . '(' . $content['LastModified'] . ')' . '<br>'; } } catch (Exception $e) { exit('Exception: ' . $e->getMessage()); }
■バケットポリシーの設定 ※ロールIDを変更しても変化なかったので、本当に設定できているかは改めて検証したい。 ただし最初に設定したポリシー ExternalExampleDevelopPolicy とロール ExternalExampleDevelopRole によってアクセス元を限定しているので、ここでの設定はあまり意味が無いような気はする。 最後に、AWSアカウントaccount1にて、S3バケットexample-linkage-developに対して、バケットポリシーを設定する。
$ export AWS_PROFILE=cross-account $ aws sts get-caller-identity { "Account": "111111111111", "UserId": "AROAXXXXXXXXXXXXX5CGE:TestSession", "Arn": "arn:aws:sts::111111111111:assumed-role/ExternalExampleDevelopRole/TestSession" } 「UserId」の値のうち、「:」より前がロールID、「:」より後ろがセッション名となっている。 この場合、以下のとおりバケットポリシーを設定する。 { "Version": "2012-10-17", "Statement": [ { "Effect": "Deny", "Principal": "*", "Action": "s3:*", "Resource": "arn:aws:s3:::example-linkage-develop/*", "Condition": { "StringNotLike": { "aws:userId": [ "AROAXXXXXXXXXXXXX5CGE:*" … ロールIDを指定 ] } } } ] }
これで完了。 特定の IAM ロールのみアクセスできる S3 バケットを実装する際に検討したあれこれ | DevelopersIO https://dev.classmethod.jp/articles/s3-bucket-acces-to-a-specific-role/#toc-7 ■メモ 受け入れ側でポリシーを変更すると、アクセスする側で再接続が必要みたい。 ただし「PHPからの接続(改修版)」の方法なら問題無いはず。 以下は参考ページ。 AWSのIAM Roleについて整理してみた #AWS - Qiita https://qiita.com/NMRchan/items/7d5adc44bed2b53f0347 IAMユーザにIAMロールをアタッチする話 #AWS - Qiita https://qiita.com/yukitsuboi/items/bf033c9d321dccdeedd4 IAMロール徹底理解 〜 AssumeRoleの正体 | DevelopersIO https://dev.classmethod.jp/articles/iam-role-and-assumerole/ AssumeRole について DiveDeep する - サーバーワークスエンジニアブログ https://blog.serverworks.co.jp/assume-role-divedeep 一時的なセキュリティ認証情報のリクエスト - AWS Identity and Access Management https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_temp_request.html#api_assumero...

Advertisement