set setting reset

インフラ関連の小ネタと備忘録

S3のクロスアカウント問題をSTS AssumeRole で解決する

背景

例えばアカウントAにある my-bucket という S3 バケットに対して、
アカウントBのリソースからオブジェクトをアップロードした時に、
my-bucket に付与されているバケットポリシーが効かずにハマることがあります。

バケットポリシーでIPアドレス制限だけして他のアカウントのクレデンシャルからアップロードするようなケースです。

アカウントBからオブジェクトをアップロードした場合、オブジェクトの所有者はアカウントB(バケット所有者のアカウントAとは別のアカウント)になるためバケットポリシーがオブジェクトに適用されません。
例えば、静的ウェブホスティングを有効にしているバケットに、アカウントBからオブジェクトをアップロードしても、バケットポリシーが適用されないため、アップロードしただけではそのコンテンツは公開できません (公開するためには、アップロード後にオブジェクトの権限を変更する必要があります)。

blog.serverworks.co.jp

この問題を STS Assume Role を使って解決します。
以降、アクセスされる側を アカウントA、アクセスする側を アカウントB とします。

アカウントAにIAM Roleを作成する

IAM → Roles → Create Role → Another AWS account を選択して必要事項を入力。
作成する IAM Role は AllowPutToS3 という名前にします。

f:id:rriifftt:20181102122453p:plain

external id は事前共有鍵のようなもの?。
一致しなければ認証エラーとなるものなので、取扱には注意が必要です。

なお、人間がAssumeRoleをして他AWSアカウントのリソースを触るのならば、セキュリティの観点から MFA を利用した方がよいでしょう。
今回はシステム的にアクセスをしたいので、MFAではなく external id のみで進めてみます。

また、このウィザードからは1つのアカウントしか登録できないので、複数登録したい場合はウィザード完了後にポリシーを編集する必要があります。

IAM Policy

作成した AllowPutToS3 Role に Policy をアタッチします。
今回は S3 への Put のみを許可するポリシーとします。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowPutToS3",
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::my-bucket/*"
        }
    ]
}

Trust relationships

IAM Role を触ることができるアカウントを指定します。
Trust relationships タブ から Edit trust relationships を選択し、以下の様に設定します。
複数のアカウントから許可したい場合は、ここでアカウントIDを指定することで増やすことができます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::アカウントBのアカウントID:root",
        ]
      },
      "Action": "sts:AssumeRole",
      "Condition": {
        "StringEquals": {
          "sts:ExternalId": "hoge"
        }
      }
    }
  ]
}

アカウントBからAssume Roleする

IAM Policy の設定

my-bucket をさわりたいアカウントBのリソースに以下の様なPolicyを設定します。
IAM UserでもGroupでもIAM Roleでもなんでもいいです。

{
    "Action": "sts:AssumeRole",
    "Effect": "Allow",
    "Resource": "arn:aws:iam::アカウントAのアカウントID:role/AllowPutToS3"
}

Credential の取得

上記の IAM Policy が設定されたリソースから Credential を取得します。
以下は AWS CLI での例です。

$ aws sts assume-role \
    --role-arn arn:aws:iam::アカウントAのアカウントID:role/AllowPutToS3 \ 
    --role-session-name hoge \ # セッションに付与する任意の名前
    --external-id hoge # IAM Role に設定した内容と一致しなければエラー

以下の様なレスポンスが返ってきます。

{
    "AssumedRoleUser": {
        "AssumedRoleId": "**************************",
        "Arn": "arn:aws:sts::アカウントAのアカウントID:assumed-role/******************"
    },
    "Credentials": {
        "SecretAccessKey": "*************",
        "SessionToken": "******************************************************************************************",
        "Expiration": "2018-03-05T04:45:01Z",
        "AccessKeyId": "******************"
    }
}

この認証情報をつかってオブジェクトを Put すると、他アカウントからのアクセスでもバケットポリシーが効くようになります。
ややこしいけど便利ですね。

AWS CLI では .aws/config などに必要な設定をすることで自動的に AssumeRole をすることも可能のようです。
とても便利そうですね。

dev.classmethod.jp

boto3 で AssumeRole したクライアントを作成する実装例

を晒してみます。
微妙に client と resource を選択することができて便利な気がします。

gist.github.com

ご査収くださいませ。