1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

S3 の暗号化を SSE-S3 から SSE-KMS に変えた途端、署名付き URL がエラーを返すようになった。
そんな経験はありませんか?
私はあります。

署名付き URL や SSE-KMS は、単体ではよく使う機能ですが、 組み合わせると、権限設計に一癖あります

本記事では

  • なぜ SSE-KMS にすると署名付き URL がエラーになるのか
  • 見落としがちな権限設定の落とし穴(IAM ポリシー・KMS キーポリシー)
  • ダウンロード(GET)とアップロード(PUT)で異なる注意点

をまとめます。

前提知識

署名付き URL(Presigned URL)とは

S3 のオブジェクトに対して、一時的なアクセスを許可する URL です。
IAM 認証情報を持たないユーザーでも、この URL を使って S3 オブジェクトのダウンロードやアップロードが可能になります。

主な用途はこんな感じです。

  • ユーザーにファイルを一時的にダウンロードさせたい
  • ユーザーからファイルをアップロードさせたい(フォーム送信など)
# 署名付き URL の生成例(ダウンロード用・有効期限 3600 秒)
aws s3 presign s3://your-bucket/your-file.pdf --expires-in 3600

気を付けないといけないのは、署名付き URL は生成者の権限を借りてアクセスする ということです。
生成者に権限がなければ、URL を発行できても実際のアクセスは失敗します。

SSE-KMS とは

S3 のサーバーサイド暗号化の一つで、AWS KMS のカスタマーマネージドキーを使ってオブジェクトを暗号化する方式です。

S3 の暗号化方式を比較すると以下の通りです。

方式 鍵の管理者 署名付き URL との相性
SSE-S3 AWS が完全管理 〇 問題なし
SSE-KMS(AWS マネージドキー) AWS が管理(aws/s3 △ 制約あり
SSE-KMS(カスタマーマネージドキー) ユーザーが管理 △ 制約あり

SSE-S3 なら動くのに SSE-KMS だとエラーになる理由

SSE-S3 は S3 が内部で鍵を管理するため、追加の権限設定は不要です。
一方で SSE-KMS は KMS キーへのアクセス権限が別途必要 になります。ここが気を付けないといけない部分です。

注意したいのは、SSE-KMS で AWS マネージドキー(aws/s3)を選んだ場合も例外ではないということです。
「AWS が管理しているキーだから、SSE-S3 と同じように権限不要では?」と思いがちですが、SSE-KMS である以上、キーの管理者に関係なく KMS への権限が必要 です。

署名付き URL でオブジェクトにアクセスするとき、内部では以下が起きています。

SSE-S3 の場合

  1. 署名付き URL でリクエスト
  2. S3 が内部の鍵でデータを復号
  3. データを返す

SSE-KMS の場合

  1. 署名付き URL でリクエスト
  2. S3 が KMS にデータキーの復号を依頼 ← ここで KMS キーへの権限がないとエラー
  3. KMS がデータキーを復号して返す
  4. S3 がデータキーでデータを復号
  5. データを返す

SSE-KMS では S3 へのアクセス権限に加えて、KMS キーへの権限も必要 です。
署名付き URL の生成者が KMS キーの権限を持っていなければ、URL は発行できても実際のリクエストでエラーが返ります。

ハマりどころ

1. IAM ポリシーに KMS 権限が必要

署名付き URL を生成する IAM ユーザーやロールのポリシーに、KMS 関連の権限を追加する必要があります。

ダウンロード(GET)の場合

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowS3GetObject",
            "Effect": "Allow",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::my-bucket/*"
        },
        {
            "Sid": "AllowKMSDecrypt",
            "Effect": "Allow",
            "Action": "kms:Decrypt",
            "Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/your-key-id"
        }
    ]
}

s3:GetObject だけでは不十分で、kms:Decrypt がないとエラーが発生します

アップロード(PUT)の場合

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowS3PutObject",
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::my-bucket/*"
        },
        {
            "Sid": "AllowKMSForUpload",
            "Effect": "Allow",
            "Action": "kms:GenerateDataKey",
            "Resource": "arn:aws:kms:ap-northeast-1:123456789012:key/your-key-id"
        }
    ]
}

アップロード時は kms:GenerateDataKey が必要です。
S3 がオブジェクトを暗号化するために、KMS にデータキーの生成を依頼するからです。

注意
マルチパートアップロードの場合は kms:Decrypt も必要です。
各パートの暗号化で同じデータキーを再利用するため、暗号化データキーの復号が発生します。

2. Boto3 で Signature Version 4 を明示する

SSE-KMS を使う場合、Signature Version 4(SigV4)が必須 です。
AWS 公式ドキュメントにも明記されています。

最近のバージョンの Boto3 はデフォルトで SigV4 を使いますが、環境や設定によっては SigV2 になるケースがあります。
その場合、以下のような InvalidArgument エラーが返ります。

<Error>
  <Code>InvalidArgument</Code>
  <Message>
    Requests specifying Server Side Encryption with AWS KMS managed keys
    require AWS Signature Version 4.
  </Message>
  <ArgumentName>Authorization</ArgumentName>
</Error>

確実に動かすには、クライアント生成時に明示的に SigV4 を指定します。

from botocore.config import Config

s3_client = boto3.client("s3", config=Config(signature_version="s3v4"))

私も実際にこれでハマりました。
SSE-S3 のときは問題なかったのに、SSE-KMS に変えた途端に署名付き URL が通らなくなり、原因がこの SigV4 の指定漏れでした。

注意
以下のような環境では SigV4 がデフォルトにならないことがあります。

  • 古いバージョンの Boto3 / Botocore
  • ~/.aws/configsignature_version が明示的に設定されている
  • レガシーなリージョン設定

SSE-KMS を使うなら、常に Config(signature_version="s3v4") を指定する のが安全です。

3. KMS キーポリシーの許可

デフォルト設定で KMS キーを作成した場合は、root の委任ポリシーがあるため IAM ポリシーだけで通ります。
キーポリシーを変更している場合は、KMS キーポリシー側でも許可されていないとエラーが発生します

KMS キーにはキーポリシーがあり、「誰がこの鍵を使えるか」を制御しています。
IAM ポリシーとキーポリシーの 両方 で許可されている必要があります。

キーポリシーの設定例

{
    "Sid": "AllowPresignedUrlRole",
    "Effect": "Allow",
    "Principal": {
        "AWS": "arn:aws:iam::123456789012:role/PresignedUrlGeneratorRole"
    },
    "Action": [
        "kms:Decrypt",
        "kms:GenerateDataKey"
    ],
    "Resource": "*"
}

補足
KMS キーポリシーの Resource* で OK です。
キーポリシーはキー自身に紐づくため、* はそのキー自体を指します。

デフォルト設定で作成した KMS キーには、Principalroot が指定されたポリシーが含まれています。
root を指定すると、そのアカウントの IAM ポリシーに判断を委任する意味になるため、この場合は IAM ポリシーでの許可だけで KMS キーを使えます
ただし、セキュリティ要件でこの記述を削除・変更している場合は、キーポリシーで明示的に署名付き URL 生成者のプリンシパルを許可する必要があります。

4. PUT 時の署名付き URL 生成に SSE 指定が必要

バケットのデフォルト暗号化を使わず、リクエストで明示的に KMS キーを指定する場合の話です。
デフォルト暗号化が SSE-KMS に設定されている場合は、S3 がサーバー側で自動的に暗号化するため、この手順は不要です。

アップロード用の署名付き URL を生成する際、SSE-KMS のパラメータを署名に含める 必要があります。

AWS CLI の場合

AWS CLI の presign コマンドは GET(ダウンロード)用途を想定したコマンドです。
PUT 用途で SSE-KMS パラメータを含めたい場合は、SDK を使う方が確実です。

AWS SDK(Python / Boto3)の場合

import boto3
from botocore.config import Config

s3_client = boto3.client("s3", config=Config(signature_version="s3v4"))

# ダウンロード用(GET)
download_url = s3_client.generate_presigned_url(
    "get_object",
    Params={
        "Bucket": "my-bucket",
        "Key": "my-file.pdf",
    },
    ExpiresIn=3600,
)

# アップロード用(PUT)
upload_url = s3_client.generate_presigned_url(
    "put_object",
    Params={
        "Bucket": "my-bucket",
        "Key": "upload/new-file.pdf",
        "ServerSideEncryption": "aws:kms",
        "SSEKMSKeyId": "arn:aws:kms:ap-northeast-1:123456789012:key/your-key-id",
    },
    ExpiresIn=3600,
)

PUT 用の署名付き URL では、ServerSideEncryptionSSEKMSKeyIdParams に含めます。

注意
PUT リクエスト時のヘッダーも署名に含めたパラメータと一致させる必要があります。
クライアント側(curl や fetch)でリクエストする際に、以下のヘッダーを付与してください。

curl -X PUT \
  -H "x-amz-server-side-encryption: aws:kms" \
  -H "x-amz-server-side-encryption-aws-kms-key-id: arn:aws:kms:ap-northeast-1:123456789012:key/your-key-id" \
  --upload-file ./new-file.pdf \
  "<presigned-url>"

ヘッダーが不足していると SignatureDoesNotMatch エラーになります。

補足
マルチパートアップロード(upload_part)の場合、SSE パラメータは create_multipart_upload の時点で指定します。
各パートの署名付き URL には ServerSideEncryptionSSEKMSKeyId は不要です。

トラブルシューティング

署名付き URL × SSE-KMS でエラーが出た場合のチェックリストです。

# 確認項目 対象
1 IAM ポリシーに kms:Decrypt があるか 署名付き URL 生成者
2 IAM ポリシーに kms:GenerateDataKey があるか(PUT の場合) 署名付き URL 生成者
3 Boto3 で Config(signature_version="s3v4") を指定しているか アプリケーション
4 KMS キーポリシーで生成者のプリンシパルが許可されているか KMS キー
5 PUT 時に SSE パラメータを署名に含めているか(デフォルト暗号化を使わない場合) アプリケーション
6 PUT リクエストのヘッダーが署名と一致しているか(デフォルト暗号化を使わない場合) クライアント
7 署名付き URL の有効期限内か URL
8 KMS キーが有効な状態か(無効化・削除スケジュール中でないか) KMS キー

まとめ

署名付き URL と SSE-KMS の組み合わせでハマるのは、SSE-KMS にすると KMS キーへの権限が別途必要になる ことが最大の原因です。
加えて、SigV4 の指定や SSE パラメータの扱いにも注意が必要です。

SSE方式 追加で必要な権限設定
SSE-S3 なし
SSE-KMS IAM ポリシー + KMS キーポリシー

覚えておきたいことは以下の通りです。

  1. SSE-KMS にしたら KMS 権限を追加するkms:Decrypt、PUT なら kms:GenerateDataKey
  2. Boto3 では Config(signature_version="s3v4") を明示する — SigV4 でないと InvalidArgument になる
  3. KMS キーポリシーも見る — IAM ポリシーだけでは不十分な場合がある
  4. PUT の署名付き URL には SSE パラメータを含める — ヘッダーの不一致は SignatureDoesNotMatch

SSE-S3 で動いていたものを SSE-KMS に切り替えるタイミングでエラーに遭遇しがちなので、移行時はこの記事のチェックリストを参考にしてみてください!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?