他のアカウントへS3バケットへのアクセスを許可する

クロスアカウントと外部ID

このあたりに書いてあることを実践してみます。
外部 ID について - AWS Security Token Service
一時的なセキュリティ認証情報を使用して AWS リソースをリクエストする - AWS Security Token Service

準備

アクセス元のアカウントIDを調べます。
ここではアクセス元を999999999999としS3バケットがある方を111111111111とします。

S3バケットを持つアカウントの方にIAMにロールを作成

  • ロール名を入力

ここではcross-accountとしました。

  • アクセス元のアカウントIDを入力

f:id:yustam:20150207133008p:plain

  • ポリシーを選択

f:id:yustam:20150207133022p:plain

ここではSelectPolicyTemplateからAmazon S3 Read Only Accessを選択し、
そのまま進んでロールを作成しました。

アクセス元のアカウントからアクセスしてみる

  • アクセス元のアカウントにEC2を立ててそこからアクセスしてみます。

sshログインしたら試しにS3のバケット一覧を取得してみます。

$ ssh ec2-user@ec2-54-65-xxx-xxx.ap-northeast-1.compute.amazonaws.com -i key.pem
[ec2-user@ip-172-31-24-163 ~]$ aws s3 ls
2014-12-23 07:25:37 cf-templates-xxxxxxxxxxxxx-ap-northeast-1
2015-01-11 03:32:18 elasticbeanstalk-ap-northeast-1-999999999999
2015-01-05 14:42:54 yustam-artifact-repository
2014-11-07 15:21:07 yustam-docker-registry
2014-12-11 09:52:14 yustam-lambda-sample
2014-11-14 12:26:45 yustam-private

ロールを付与しておくとここではアクセス元アカウントのバケットが確認できます。

  • STSのAssumeRoleでセッションを作成

ここではaws-cliを使用してクロスアカウントアクセスを利用します。
sts assume-roleを使用してS3バケットアクセス用のセッションを作成します。

[ec2-user@ip-172-31-24-163 ~]$ aws sts assume-role --role-arn arn:aws:iam::111111111111:role/cross-account --role-session-name hoge
{
    "AssumedRoleUser": {
        "AssumedRoleId": "AROAJWSTZ4KKB2EXAMPLE:hoge",
        "Arn": "arn:aws:sts::111111111111:assumed-role/cross-account/hoge"
    },
    "Credentials": {
        "SecretAccessKey": "EmlBUyGcOKPI/Sdsf4RCE56sHnbo6XfJkEXAMPLE",
        "SessionToken": "AQoDYXdzECUagAKpFP2OknLKXsPLl/801n...(省略)",
        "Expiration": "2015-02-07T05:19:53Z",
        "AccessKeyId": "ASIAJ2GNOUN4ZEXAMPLE"
    }
}
  • 作成したセッションを利用してS3バケットにアクセスしてみる

先ほどのレスポンスに記載されたアクセスキー/シークレットキー/トークンを使用します。

[ec2-user@ip-172-31-24-163 ~]$ export AWS_ACCESS_KEY_ID=ASIAJ2GNOUN4ZEXAMPLE
[ec2-user@ip-172-31-24-163 ~]$ export AWS_SECRET_ACCESS_KEY=EmlBUyGcOKPI/Sdsf4RCE56sHnbo6XfJkEXAMPLE
[ec2-user@ip-172-31-24-163 ~]$ export AWS_SECURITY_TOKEN=AQoDYXdzECUagAKpFP2OknLKXsPLl/801n...(省略)
[ec2-user@ip-172-31-24-163 ~]$ aws s3 ls
2013-02-15 08:04:27 private.yustam.jp
2013-01-21 05:53:07 www.robotjohn.jp
2013-07-10 01:08:47 www.yustam.jp
2013-10-27 02:28:07 yustam.convert

S3バケットの一覧を取得するとアクセス先アカウントのバケット一覧が取得できます。


Javaから利用する場合は以下のようにAssumeRoleしてcom.amazonaws.auth.BasicSessionCredentialsを作成すればいいみたいです。

package jp.yustam.s3.ca;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicSessionCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.Bucket;
import com.amazonaws.services.securitytoken.AWSSecurityTokenService;
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient;
import com.amazonaws.services.securitytoken.model.AssumeRoleRequest;
import com.amazonaws.services.securitytoken.model.AssumeRoleResult;
import com.amazonaws.services.securitytoken.model.Credentials;
import org.junit.Test;

public class CrossAccountTest {

  static final String ROLE_ARN = "arn:aws:iam::111111111111:role/cross-account";
  static final String ROLE_SESSION_NAME = "hoge";

  @Test
  public void testAccessS3BucketOnAnotherAccount() {
    AWSSecurityTokenService sts = new AWSSecurityTokenServiceClient();
    AssumeRoleResult result = sts.assumeRole(new AssumeRoleRequest()
        .withRoleArn(ROLE_ARN)
        .withRoleSessionName(ROLE_SESSION_NAME));

    Credentials credentials = result.getCredentials();
    AWSCredentials sessionCredentials
        = new BasicSessionCredentials(credentials.getAccessKeyId(),
        credentials.getSecretAccessKey(),
        credentials.getSessionToken());
    AmazonS3 s3 = new AmazonS3Client(sessionCredentials);
    for (Bucket bucket : s3.listBuckets()) {
      System.out.println(bucket);
    }
  }

}

Javaからもっと簡単に使う(3/5更新)

Credentialsを毎回生成するのもイケてないし有効期限があるから管理めんどうだなと
思ってたらSTSAssumeRoleSessionCredentialsProviderという便利なものが既に用意されていた。

AWSCredentialsProvider cp
    = new STSAssumeRoleSessionCredentialsProvider(ROLE_ARN, ROLE_SESSION_NAME);
AmazonS3 s3 = new AmazonS3Client(cp);