AWS
S3
IAM

【AWS】AWSアカウントをまたいでS3 Bucket間コピーする(実践編)【クロスアカウント】

はじめに

下記の記事で説明した構成パターンについて、一部、複雑なものがあります。
この記事は、当該構成の具体的な設定内容・挙動を説明することを目的とします。

【AWS】AWSアカウントをまたいでS3 Bucket間コピーする(解説編)【クロスアカウント】
https://qiita.com/tmiki/items/cef01fa5d34c55dbbeae

想定構成

  • ファイルコピー元AWSアカウント
    • AWSアカウントID: 111111111111111
    • S3 Bucket: inter-bucket-copy-src
    • IAM User: src-user
    • IAM Role: src-role
  • ファイルコピー先AWSアカウント
    • AWSアカウントID: 999999999999999
    • S3 Bucket: inter-bucket-copy-dst
    • IAM User: dst-user
    • IAM Role: dst-role

実践

Type2: コピー元S3 BucketにBucket Policyを付与する

下記と同等の内容です。
https://qiita.com/tmiki/items/cef01fa5d34c55dbbeae#type2-%E3%82%B3%E3%83%94%E3%83%BC%E5%85%83s3-bucket%E3%81%ABbucket-policy%E3%82%92%E4%BB%98%E4%B8%8E%E3%81%99%E3%82%8B%E3%82%88%E3%82%8A%E8%A4%87%E9%9B%91

まずは構成をおさらいしましょう。
type2.PNG

ファイルコピー元AWSアカウントのAWSリソース設定

S3 Bucket: inter-bucket-copy-src

Bucket Policyは以下のようになります。
dst-roleの権限でコピー操作を行うこととなりますので、当該Roleからの参照権限が必要になります。

BucketPolicy
{
    "Version": "2012-10-17",
    "Statement": {
        "Sid": "SourceBucketPolicy-1",
        "Effect": "Allow",
        "Principal": {
            "AWS": "arn:aws:iam::999999999999999:role/dst-role"
        },
        "Action": [
            "s3:ListBucket",
            "s3:GetObject"
        ],
        "Resource": [
            "arn:aws:s3:::inter-bucket-copy-src",
            "arn:aws:s3:::inter-bucket-copy-src/*"
        ]
    }
}

IAM User: arn:aws:iam::111111111111111:user/src-user

Permissionsは以下のように、AssumeRoleを実行する権限さえあれば十分です。
対象のResourceには、具体的なRole名を指定することもできますが、「*」を指定した方が管理しやすいと思います。

permissions
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

IAM Role: arn:aws:iam::111111111111111:role/src-role

※このケースでは利用しません

ファイルコピー先AWSアカウントのAWSリソース設定

S3 Bucket: inter-bucket-copy-dst

※Bucket Policyは不要

IAM Role: arn:aws:iam::999999999999999:role/dst-role

PermissionsとTrust Relationshipsは以下のようになります。

  • Permissions

コピー元S3 Bucketへの参照権限、およびコピー先S3 Bucketへの書込権限が全て必要です。

permissions
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::inter-bucket-copy-src",
        "arn:aws:s3:::inter-bucket-copy-src/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::inter-bucket-copy-dst/*"
      ]
    }
  ]
}
  • Trust Relationships

src-userにAssumeRoleさせるための設定を追加します。

TrustRelationships
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111111111111111:user/src-user"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

手順

手順を確認していきます。

  1. src-userは、dst-roleをAssumeRoleするようAPIを発行する。
  2. temporary credentialsを取得する。temporary credentialsはdst-roleの権限でAWS APIを発行できる。
  3. temporary credentialsを利用してPUT Object - Copyを発行する。
  4. inter-bucket-copy-src上のファイルをinter-bucket-copy-dstにコピーする。

以下から、具体的なコマンド、AWS APIのレスポンス等を詳しく見ていきます。

1. src-userは、dst-roleをAssumeRoleするようAPIを発行する。

項番1,2は併せて説明します。

2. temporary credentialsを取得する。temporary credentialsはdst-roleの権限でAWS APIを発行できる。

まず、適当なLinuxマシンを用意し、AWS CLIをインストールします。
src-userのAWS Credentialsを準備し、AWS CLIにセットします。

$ aws configure
AWS Access Key ID [None]: AKIA****************
AWS Secret Access Key [None]: duOb********************************n6mM
Default region name [None]: ap-northeast-1
Default output format [None]: json

AssumeRoleを実行します。オプション「--role-session-name」の値は必須ですが、任意の文字列を指定することができます。
「AccessKeyId」「SecretAccessKey」「SesionToken」という値が返却されたことが分かります。

$ aws sts assume-role --role-arn "arn:aws:iam::999999999999999:role/dst-role" --role-session-name "you-can-put-any-string"
{
    "AssumedRoleUser": {
        "AssumedRoleId": "AROAXXXXXXXXXXXXXXXXX:you-can-put-any-string",
        "Arn": "arn:aws:sts::999999999999999:assumed-role/dst-role/you-can-put-any-string"
    },
    "Credentials": {
        "SecretAccessKey": "0hq4**********************************",
        "SessionToken": "FQoD************************************************************************************************************************************************************************************************************",
        "Expiration": "2018-04-29T12:57:34Z",
        "AccessKeyId": "ASIAXXXXXXXXXXXXXXXX"
    }
}

3. temporary credentialsを利用してPUT Object - Copyを発行する。

項番3,4は併せて説明します。

4. inter-bucket-copy-src上のファイルがinter-bucket-copy-dstにコピーされる。

まず、AssumeRoleの結果、返却された値は下記の通りでした。

Item Name Value
AccessKeyId ASIAXXXXXXXXXXXXXXXX
SecretAccessKey 0hq4**********************************
SesionToken FQoD************************************************************************************************************************************************************************************************************

この値を環境変数にセットすることで、AWS CLIから利用できるようになります。

$ export AWS_ACCESS_KEY_ID="ASIAXXXXXXXXXXXXXXXX"
$ export AWS_SECRET_ACCESS_KEY="0hq4**********************************"
$ export AWS_SESSION_TOKEN="FQoD************************************************************************************************************************************************************************************************************"

GetCallerIdentityを実行することで、現在のユーザを確認することができます。

$ aws sts get-caller-identity
{
    "Account": "999999999999999",
    "UserId": "AROAXXXXXXXXXXXXXXXXX:you-can-put-any-string",
    "Arn": "arn:aws:sts::999999999999999:assumed-role/dst-role/you-can-put-any-string"
}

ここまでくれば、後は簡単です。
単純に、AWS CLIでS3オブジェクトのコピーを行うだけです。

$ aws s3 sync s3://inter-bucket-copy-src/any-dir/ s3://inter-bucket-copy-dst/any-dir/

Type2.1: コピー元S3 BucketにBucket Policyを付与し、IAM Role for EC2 instancesを併用する

上記の構成において、AssumeRoleする主体がIAM Userではなく、EC2 Instance Profileになるケースです。

type2.1.PNG

ファイルコピー元AWSアカウントのAWSリソース設定

S3 Bucket: inter-bucket-copy-src

Bucket Policyは以下のようになります。
dst-roleの権限でコピー操作を行うこととなりますので、当該Roleからの参照権限が必要になります。

BucketPolicy
{
    "Version": "2012-10-17",
    "Statement": {
        "Sid": "SourceBucketPolicy-1",
        "Effect": "Allow",
        "Principal": {
            "AWS": "arn:aws:iam::999999999999999:role/dst-role"
        },
        "Action": [
            "s3:ListBucket",
            "s3:GetObject"
        ],
        "Resource": [
            "arn:aws:s3:::inter-bucket-copy-src",
            "arn:aws:s3:::inter-bucket-copy-src/*"
        ]
    }
}

IAM User: arn:aws:iam::111111111111111:user/src-user

※このケースでは利用しません

IAM Role: arn:aws:iam::111111111111111:role/src-role

PermissionsとTrust Relationshipsは以下のようになります。

  • Permissions

Permissionsは以下のように、AssumeRoleを実行する権限さえあれば十分です。

permissions
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "sts:AssumeRole"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
  • Trust Relationships

EC2 Instance Profileとして利用するための設定が必要です。これは通常、よく利用しているものですね。

TrustRelationships
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

ファイルコピー先AWSアカウントのAWSリソース設定

S3 Bucket: inter-bucket-copy-dst

※Bucket Policyは不要

IAM Role: arn:aws:iam::999999999999999:role/dst-role

PermissionsとTrust Relationshipsは以下のようになります。

  • Permissions

コピー元S3 Bucketへの参照権限、およびコピー先S3 Bucketへの書込権限が全て必要です。

permissions
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:ListBucket",
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::inter-bucket-copy-src",
        "arn:aws:s3:::inter-bucket-copy-src/*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject"
      ],
      "Resource": [
        "arn:aws:s3:::inter-bucket-copy-dst/*"
      ]
    }
  ]
}
  • Trust Relationships

src-roleにAssumeRoleさせるための設定を追加します。

TrustRelationships
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::111111111111111:role/src-role"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

手順

手順を確認していきます。
1. EC2インスタンスにsshログインし、当該EC2インスタンスに紐づくRole(src-role)のTemporary Credentialsを取得する
2. src-roleの権限で、dst-roleをAssumeRoleするようAPIを発行する。
3. dst-roleに紐づくtemporary credentialsを取得する。このtemporary credentialsはdst-roleの権限でAWS APIを発行できる。
4. dst-roleのtemporary credentialsを利用してPUT Object - Copyを発行する。
5. inter-bucket-copy-src上のファイルがinter-bucket-copy-dstにコピーされる。

以下から、具体的なコマンド、AWS APIのレスポンス等を詳しく見ていきます。

1. EC2インスタンスにsshログインし、当該EC2インスタンスに紐づくRole(src-role)のTemporary Credentialsを取得する

対象EC2インスタンスにsshログインします。
下記のドキュメントに従い、Temporary Credentialsを取得します。

IAM Roles for Amazon EC2 - Amazon Elastic Compute Cloud -> Retrieving Security Credentials from Instance Metadata
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html

$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/src-role
{
  "Code" : "Success",
  "LastUpdated" : "2012-04-26T16:39:16Z",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIAIOSFODNN7EXAMPLE",
  "SecretAccessKey" : "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
  "Token" : "nczJ************************************************toKenExaMpLe",
  "Expiration" : "2017-05-17T15:09:54Z"
}

この値を環境変数にセットすることで、AWS CLIから利用できるようになります。

$ export AWS_ACCESS_KEY_ID="ASIAIOSFODNN7EXAMPLE"
$ export AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"
$ export AWS_SESSION_TOKEN="nczJ************************************************toKenExaMpLe"

GetCallerIdentityを実行することで、現在のユーザを確認することができます。

$ aws sts get-caller-identity
{
    "Account": "111111111111111",
    "UserId": "AROA*************QIOE:i-0123456789abcdeff",
    "Arn": "arn:aws:sts::111111111111111:assumed-role/src-role/i-0123456789abcdeff"
}

2. src-roleの権限で、dst-roleをAssumeRoleするようAPIを発行する。

項番2,3は併せて説明します。

3. dst-roleに紐づくtemporary credentialsを取得する。このtemporary credentialsはdst-roleの権限でAWS APIを発行できる。

これ以降の操作は、先ほどのType2のケースと同一です。

AssumeRoleを実行します。オプション「--role-session-name」の値は必須ですが、任意の文字列を指定することができます。
「AccessKeyId」「SecretAccessKey」「SesionToken」という値が返却されたことが分かります。

$ aws sts assume-role --role-arn "arn:aws:iam::999999999999999:role/dst-role" --role-session-name "you-can-put-any-string"
{
    "AssumedRoleUser": {
        "AssumedRoleId": "AROAXXXXXXXXXXXXXXXXX:you-can-put-any-string",
        "Arn": "arn:aws:sts::999999999999999:assumed-role/dst-role/you-can-put-any-string"
    },
    "Credentials": {
        "SecretAccessKey": "0hq4**********************************",
        "SessionToken": "FQoD************************************************************************************************************************************************************************************************************",
        "Expiration": "2018-04-29T12:57:34Z",
        "AccessKeyId": "ASIAXXXXXXXXXXXXXXXX"
    }
}

4. dst-roleのtemporary credentialsを利用してPUT Object - Copyを発行する。

項番4,5は併せて説明します。

5. inter-bucket-copy-src上のファイルがinter-bucket-copy-dstにコピーされる。

AssumeRoleの結果、返却された値は下記の通りでした。

Item Name Value
AccessKeyId ASIAXXXXXXXXXXXXXXXX
SecretAccessKey 0hq4**********************************
SesionToken FQoD************************************************************************************************************************************************************************************************************

この値を環境変数にセットすることで、AWS CLIから利用できるようになります。

$ export AWS_ACCESS_KEY_ID="ASIAXXXXXXXXXXXXXXXX"
$ export AWS_SECRET_ACCESS_KEY="0hq4**********************************"
$ export AWS_SESSION_TOKEN="FQoD************************************************************************************************************************************************************************************************************"

GetCallerIdentityを実行することで、現在のユーザを確認することができます。

$ aws sts get-caller-identity
{
    "Account": "999999999999999",
    "UserId": "AROAXXXXXXXXXXXXXXXXX:you-can-put-any-string",
    "Arn": "arn:aws:sts::999999999999999:assumed-role/dst-role/you-can-put-any-string"
}

ここまでくれば、後は簡単です。
単純に、AWS CLIでS3オブジェクトのコピーを行うだけです。

$ aws s3 sync s3://inter-bucket-copy-src/any-dir/ s3://inter-bucket-copy-dst/any-dir/

おわりに

このケースは一見複雑そうにみえますが、一つ一つの仕組みを確認し、理解していくと、それほど難しいものでないことが分かります。
理解を深めていくにつれ、IAM/STSの仕組みのシンプルさに驚嘆し、またよく考えられていることを実感できると思います。