カスタマーマネージドキーで暗号化されたS3オブジェクトにクロスアカウントアクセスする方法

2022.06.12

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

サービスや環境ごとにアカウントを分けたものの、別アカウントのS3に保存されたオブジェクトにアクセスしたいといった場合はあるのではないでしょうか。
今回はカスタマーマネージドキーで暗号化されたS3オブジェクトへ、別のアカウントからアクセスする方法(クロスアカウントアクセス)を紹介します。

カスタマーマネージドキーで暗号化されたS3オブジェクトにクロスアカウントアクセスする場合は、以下の3つの設定をする必要があります。

  1. 【アクセス先アカウント】S3バケットポリシーで、アクセス元アカウントからのアクセスを許可
  2. 【アクセス先アカウント】S3バケットの暗号化に使用しているカスタマーマネージドキーのキーポリシーで、アクセス元アカウントからのアクセスを許可
  3. 【アクセス元アカウント】IAMポリシーでアクセス先アカウントのS3バケットとカスタマーマネージドキーへアクセスを許可

なお今回の手法は、AWSマネージドキーで暗号化されたオブジェクトへのクロスアカウントアクセスでは使用することができません。これは AWSマネージドキーのキーポリシーを変更することができないためです。
この場合、下記記事のようにAssumeRoleする必要があります。

やってみる

アクセス先アカウントをアカウントA、アクセス元アカウントをアカウントBとします。

【アカウントA】 事前準備

  1. KMSの作成
    KMS → カスタマー管理型のキー → キーの作成 からカスタマーマネージドキーを作成します。
    エイリアスには任意の名前を設定し、それ以外はデフォルトの設定のままにします。

  2. S3 バケットの作成
    S3 → バケット → バケットを作成 から新たなバケットを作成します。
    バケットのリージョンは、先に作ったカスタマーマネージドキーと同じにします。
    デフォルトの暗号化で、先に作成したカスタマーマネージドキーを指定します。

  3. 暗号化されたオブジェクトの作成
    作成したバケットに適当なファイルをアップロードします。アップロード時に暗号化キーを指定することができますが、「暗号化キーを指定しない」を選択したままにしてください。デフォルトの暗号化設定が使用されます。
    これでアップロードしたファイルは、カスタマーマネージドキーで暗号化されました。

【アカウントB】 事前準備

  1. EC2用IAMロールの作成. IAM → ロール → ロールを作成 からIAMロールを作成します。
    信頼されたエンティティは AWSのサービス → EC2 を選択。
    許可ポリシーは何も追加せず、IAMロールを作成します。
  2. EC2インスタンスの作成. EC2 → インスタンス → インスタンスの作成 からEC2インスタンスを起動します。
    高度な詳細のIAMインスタンスプロフィールの欄で、作成したIAMロールを選択します。

【アカウントA】 S3のバケットポリシーでアカウントBのIAMロールからのアクセスを許可

事前準備が終わったので、クロスアカウントアクセスを許可する設定を行なっていきます。

バケットポリシーを以下のように変更し、アカウントBのIAMロールからのアクセスを許可します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DownloadAndUpload",
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:PutObject",
        "s3:PutObjectAcl"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::<bucket-name>/*",
      "Principal": {
        "AWS": [
          "arn:aws:iam::<accountB ID>:role/<role-name>"
        ]
      }
    },
    {
      "Sid": "ListBucket",
      "Action": [
        "s3:ListBucket"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::<bucket-name>",
      "Principal": {
        "AWS": [
          "arn:aws:iam::<accountB ID>:role/<role-name>"
        ]
      }
    }
  ]
}

【アカウントA】 KMSのキーポリシーでアカウントBのIAMロールからのアクセスを許可

S3バケットの暗号化に使用しているKMSキーのキーポリシーを変更します。
キーの詳細画面 → キーポリシー からポリシービューへと切り替え、ポリシーを編集します。
Statementに以下を加え、保存します。

{
  "Action": [
    "kms:Decrypt",
    "kms:GenerateDataKey"
  ],
  "Effect": "Allow",
  "Principal": {
    "AWS": "arn:aws:iam::<accountB ID>:role/<role-name>"
  },
  "Resource": "*"
}

【アカウントB】 IAMポリシーでアカウントAのS3バケット、KMSへのアクセスを許可

以下のIAMポリシーを作成し、EC2に設定しているIAMロールにアタッチします。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DownloadAndUpload",
      "Action": [
        "s3:GetObject",
        "s3:GetObjectVersion",
        "s3:PutObject",
        "s3:PutObjectAcl"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::<bucket-name>/*"
    },
    {
      "Sid": "ListBucket",
      "Action": [
        "s3:ListBucket"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:s3:::<bucket-name>"
    },
    {
      "Sid": "KMSAccess",
      "Action": [
        "kms:Decrypt",
        "kms:GenerateDataKey"
      ],
      "Effect": "Allow",
      "Resource": "arn:aws:kms:<region>:<AccountA ID>:key/<key-id>"
    }
  ]
}

S3へクロスアカウントアクセスしてみる

設定が完了したので、アカウントBのEC2からアカウントAのS3バケットへ、CLIを使ってアクセスしてみます。

まず事前準備でアップロードしたS3オブジェクトを確認します。

$ aws s3 ls s3://<bucket-name>
2022-06-12 04:13:51          6 test1.txt

続いてカスタマーマネージドキーによって暗号化されたオブジェクト(test1.txt)をダウンロードしてみます。

$ aws s3api get-object --bucket <bucket-name> --key test1.txt test1.txt
{
    "AcceptRanges": "bytes",
    "LastModified": "2022-06-12T04:13:51+00:00",
    "ContentLength": 6,
    "ETag": "\"99db59e8ad6e557b25ce74c11e13abf8\"",
    "ContentType": "text/plain",
    "ServerSideEncryption": "aws:kms",
    "Metadata": {},
    "SSEKMSKeyId": "arn:aws:kms:<region>:<AccountA ID>:key/<key-id>",
    "BucketKeyEnabled": true
}
$ ls
test1.txt

暗号化されたオブジェクトをクロスアカウントでダウンロードすることができました。

続いてアップロードをしてみます。

$ echo 'Hello accountA' > object_from_accountB.txt
$ aws s3api put-object --bucket <bucket-name> --key object_from_accountB.txt --body object_from_accountB.txt
{
    "ETag": "\"dd13b1e3eb26982069d023f01444ee01\"",
    "ServerSideEncryption": "aws:kms",
    "SSEKMSKeyId": "arn:aws:kms:<region>:<AccountA ID>:key/<key-id>",
    "BucketKeyEnabled": true
}

アカウントAのカスタマーマネージドキーを使って暗号化したオブジェクトを、アップロードすることができました。
アカウントAのマネジメントコンソールでも、アカウントBからアップロードしたオブジェクトを確認することができました。

最後に

今回の記事では、カスタマーマネージドキーで暗号化したS3オブジェクトのクロスアカウントダウンロード、アップロードをする方法をご紹介しました。
このためには、バケットポリシーとIAMポリシーに加えて、キーポリシーでの許可が必要になります。

また、今回はクロスアカウントのアップロード、ダウンロードを両方とも許可しましたが、ケースによってはダウンロードだけ必要といった場面もあるかと思います。ユースケースによって最小権限を付与するようにしましょう。

参考記事

https://aws.amazon.com/premiumsupport/knowledge-center/s3-bucket-access-default-encryption/
https://aws.amazon.com/jp/premiumsupport/knowledge-center/cross-account-access-denied-error-s3/