概要
S3にファイルが置かれたらSNSトピックに通知し、SNSからSQSキューに配信する。よくある構成です。ここで「保管中のデータはすべて暗号化する」という方針に従ってSNSトピックとSQSキューをSSE-KMSで暗号化した途端、メッセージが届かなくなることがあります。
IAMポリシーは何も変えていない、SNSのサブスクリプションも「確認済み」になっている、S3のイベント通知も設定されている。それでもSQSにメッセージが入りません。
原因はKMSキーポリシーです。暗号化されたトピックやキューに他のAWSサービスがメッセージを入れるには、そのサービスのプリンシパルがKMSキーを使える必要があるからです。そしてAWS管理キー(alias/aws/sns、alias/aws/sqs)はキーポリシーを変更できないため、この構成では最初からカスタマー管理キーを選ぶ必要があります。
この記事では、暗号化したSNS・SQSでメッセージが失われる状態を実際に作り、キーポリシーを直して届くようにするまでを追いかけます。

クラウドおさるSNS と SQS を暗号化した途端にメッセージが届かなくなる、その正体は KMS キーポリシーでござる。この記事では、わざと届かない状態を作り、区間ごとにキーポリシーを直して届くようになるまで、弊猿と一緒に追いかけるでござるな。
この記事のメリット
- 暗号化されたSNS・SQSにサービス間でメッセージを送るために必要なKMS権限を説明できるようになる
- AWS管理キーではサービス間連携が成立しない理由を理解できる
- メッセージが届かないときに、IAM・リソースポリシー・キーポリシーのどこを見るべきか切り分けられるようになる
- SCS試験で「暗号化を有効にしたら連携が壊れた」型の設問に正確に答えられるようになる
執筆者とキャラクター紹介
執筆者:土肥(とひ)
株式会社ジェニュインの代表であり元エンジニア。これまでオンプレミスと AWS 環境のインフラ業務を担当。AWS 認定資格の学習で得た知識を、ハンズオンとともに解説するのが得意。直近では新人の教育も行っており、これから技術を学ぶ方が納得できる解説を心がけています。基礎編・Cloud Practitioner の記事では、解説役としても登場します。


クラウドおさる



はじめましてでござる。弊猿はクラウドおさる。IT インフラが異常に発達した猿山から、雲に乗って地上の AWS を学びに降りてきたのでござる。



基礎編や Cloud Practitioner の記事では、とひさんに素朴な疑問をぶつける役でござるな。Security - Specialty などの記事では、要所でワンポイントや注意点をひとこと添えるでござる。



猿山にもデータセンターや監視当番があって、地上で起きることはだいたい猿山にも同じ仕組みがあるのでござる。例え話がつい猿山に寄るのは、弊猿の癖でござる。
技術解説
暗号化するのは誰か、鍵を使うのは誰か
SSE-KMSでは、メッセージの暗号化・復号はサービス側(SNSやSQS)が行います。しかしKMSに対してデータキーを要求するとき、そのリクエストの主体は「メッセージを送り込むサービス」になります。
S3 → SNS → SQS の構成では、区間ごとに必要な権限が変わります。
| 区間 | 暗号化されているリソース | KMSキーを使う必要があるプリンシパル | 必要なアクション |
|---|---|---|---|
| S3 → SNS | SNSトピック | s3.amazonaws.com | kms:GenerateDataKey* / kms:Decrypt |
| SNS → SQS | SQSキュー | sns.amazonaws.com | kms:GenerateDataKey* / kms:Decrypt |
| SQS → コンシューマー | SQSキュー | 受信するIAMロール・ユーザー | kms:Decrypt |
kms:Decrypt だけでなく kms:GenerateDataKey* が要るのは、エンベロープ暗号化のためです。メッセージ本体はデータキーで暗号化され、そのデータキーの生成にKMSを呼びます。


AWS管理キーが使えない理由
SNSやSQSの暗号化を有効にすると、既定では alias/aws/sns や alias/aws/sqs というAWS管理キーが選ばれます。しかしAWS管理キーのキーポリシーは変更できません。
上の表のとおり、サービス間連携では「別のサービスのプリンシパルにキーの使用を許可する」ステートメントを足す必要があります。それができない以上、AWS管理キーのままでは連携が成立しません。
この構成では、次のように判断します。
| 状況 | 選ぶキー |
|---|---|
| 自分のアカウントの中で完結し、他サービスからの書き込みが無い | AWS管理キーでもよい |
| 他のAWSサービスがメッセージを送り込む | カスタマー管理キーが必須 |
| クロスアカウントで使う | カスタマー管理キーが必須 |



AWS 管理キーはキーポリシーを変更できぬので、他のサービスのプリンシパルに使用を許可するステートメントを足せないのでござる。猿山でいえば、長老会が作った錠前は合鍵を渡す相手を当番が書き足せぬ、というのと同じでござる。他のサービスがメッセージを送り込むなら、最初からカスタマー管理キーを選ぶでござるぞ。
リソースポリシーとキーポリシーは別物
「届かない」トラブルの切り分けで混同しやすいのが、リソースポリシー(アクセスポリシー)とキーポリシーの役割の違いです。
| ポリシー | 何を許可するか | 足りないと何が起きるか |
|---|---|---|
| SNSトピックのアクセスポリシー | S3から sns:Publish できるか | S3のイベント通知設定時にエラーになる |
| SQSキューのアクセスポリシー | SNSから sqs:SendMessage できるか | SNSの配信が失敗する |
| KMSキーポリシー | サービスがキーを使えるか | 設定は通るのにメッセージだけが届かない。落ちる区間によって症状が変わる(下記) |
3つ目が厄介なのは、設定画面上はどこにもエラーが出ないことです。しかも、どの区間で落ちているかによって見える症状が変わります。
| 落ちている区間 | NumberOfMessagesPublished | NumberOfNotificationsFailed | 見え方 |
|---|---|---|---|
| S3 → SNS(トピックの暗号化キーをS3が使えない) | 0(記録されない) | 0 | SNSのメトリクスに何も出ない。発行イベント自体が起きていない |
| SNS → SQS(キューの暗号化キーをSNSが使えない) | 1 | 1 | 発行は成功し、配信で失敗していることが分かる |
「メッセージが来ない」ときは、まず NumberOfMessagesPublished が立っているかを見ます。立っていなければ発行側(送り込むサービスとトピックのキー)、立っていれば配信側(SNSとキューのキー)を疑う、という順序になります。



キーポリシーが足りないと、設定画面にエラーは出ずにメッセージだけが消えるのでござる。まず NumberOfMessagesPublished が立っているかを見て、立っていなければ発行側、立っていれば配信側を疑う。この順番を覚えておくと切り分けが速いでござるな。
キーポリシーに足すステートメント
S3とSNSの両方から使われるキーには、次の2つを追加します。
{
"Sid": "AllowS3ToUseKey",
"Effect": "Allow",
"Principal": { "Service": "s3.amazonaws.com" },
"Action": ["kms:GenerateDataKey*", "kms:Decrypt"],
"Resource": "*"
},
{
"Sid": "AllowSNSToUseKey",
"Effect": "Allow",
"Principal": { "Service": "sns.amazonaws.com" },
"Action": ["kms:GenerateDataKey*", "kms:Decrypt"],
"Resource": "*"
}Resource: "*" はキーポリシーの中では「このキー自身」を指します。他のキーへの権限を与えているわけではありません。
より厳密にするなら、aws:SourceArn や aws:SourceAccount の条件キーで、どのバケット・どのトピックからの利用かを限定します。第三者に自分のキーを使わせない(混乱した代理人問題への対策)ために、本番環境では条件を付けます。
実践
暗号化したSNSトピックとSQSキューを作り、キーポリシーが不足した状態ではメッセージが届かないこと、権限を足すと届くようになることを確認します。
前提条件
- リージョン:
ap-northeast-1(東京) - KMS・SNS・SQS・S3の操作権限を持つIAMユーザーまたはロールでコンソールにログイン済みであること
このハンズオンで作成するリソースは以下のとおりです。
| リソース種別 | リソース名 | 用途 |
|---|---|---|
| KMSカスタマー管理キー | sns-sqs-lab-key | SNSトピックとSQSキューの暗号化用 |
| SQSキュー | sns-sqs-lab-queue | メッセージの受信先 |
| SNSトピック | sns-sqs-lab-topic | S3イベントの通知先 |
| S3バケット | sns-sqs-lab-<アカウントID> | イベントの発生元 |
手順1: KMSカスタマー管理キーを作成する
AWSマネジメントコンソールにログインし、リージョンが ap-northeast-1(東京)であることを確認します。
上部の検索バーに KMS と入力し、表示された「Key Management Service」を選択します。
左側ナビゲーションの「カスタマー管理型のキー」を選択し、「キーの作成」をクリックします。以下を設定して作成します。
- キーのタイプ:
対称 - キーの使用法:
暗号化および復号化 - エイリアス:
sns-sqs-lab-key - キーの管理者・キーの使用法アクセス許可: 自分のIAMユーザー(またはロール)


この時点では、キーポリシーにS3やSNSのサービスプリンシパルは含まれていません。
手順2: 暗号化したSQSキューを作成する
上部の検索バーに SQS と入力し、表示された「Simple Queue Service」を選択します。
「キューを作成」をクリックし、以下を設定します。
- タイプ:
標準 - 名前:
sns-sqs-lab-queue - 「暗号化」セクション:
サーバー側の暗号化を有効にする - キーの種類:
AWS Key Management Service キー (SSE-KMS) - カスタマーマスターキー:
alias/sns-sqs-lab-keyを選択


「キューを作成」をクリックします。
手順3: 暗号化したSNSトピックを作成してサブスクライブする
上部の検索バーに SNS と入力し、表示された「Simple Notification Service」を選択します。
左側ナビゲーションの「トピック」から「トピックの作成」をクリックし、以下を設定します。
- タイプ:
スタンダード - 名前:
sns-sqs-lab-topic - 「暗号化」セクション: 有効にする
- カスタマーマスターキー:
alias/sns-sqs-lab-keyを選択


「トピックの作成」をクリックします。
作成したトピックの画面で「サブスクリプションの作成」をクリックし、以下を設定します。
- プロトコル:
Amazon SQS - エンドポイント:
sns-sqs-lab-queueのARN - 生メッセージ配信: 有効にする


「サブスクリプションの作成」をクリックします。ステータスが「確認済み」になることを確認します。
[!NOTE] コンソールからこの操作を行うと、SQSキューのアクセスポリシーに「このトピックからの
sqs:SendMessageを許可する」ステートメントが自動的に追加されます。KMSキーポリシーは自動では追加されません。
手順4: S3のイベント通知を設定する
上部の検索バーに S3 と入力し、「S3」を選択します。
「バケットを作成」で sns-sqs-lab-<アカウントID> を作成します(設定はすべてデフォルトのままで構いません)。
作成したバケットを開き、「プロパティ」タブの「イベント通知」セクションで「イベント通知を作成」をクリックします。以下を設定します。
- イベント名:
notify-sns - イベントタイプ:
すべてのオブジェクト作成イベント - 送信先:
SNS トピック - SNS トピック:
sns-sqs-lab-topic


「変更の保存」をクリックします。SNSトピックのアクセスポリシーに、S3からの sns:Publish を許可するステートメントが必要です。エラーになる場合は、SNSトピックの「アクセスポリシー」を編集して、s3.amazonaws.com からの sns:Publish を許可してください。
手順5: メッセージが届かないことを確認する
作成したバケットに任意のファイルをアップロードします。
SQSの sns-sqs-lab-queue を開き、「メッセージを送受信」→「メッセージをポーリング」をクリックします。
メッセージが1件も受信されないことを確認します。設定はすべて正しく見えるのに、メッセージだけが届いていません。


原因を探るためにCloudWatchのメトリクスを見ます。上部の検索バーに CloudWatch と入力して「CloudWatch」を開き、左側ナビゲーションの「メトリクス」→「すべてのメトリクス」から「SNS」→「トピックメトリクス」を選び、検索欄に sns-sqs-lab-topic を入力します。
ここで重要なのは、メトリクスがほとんど存在しないことです。NumberOfNotificationsFailed はもちろん、NumberOfMessagesPublished にも値が入りません。
[!NOTE] SNSトピックの詳細画面に「モニタリング」タブは無くなりました。メトリクスはCloudWatchのメトリクス画面から見ます。
つまり、SNSから見るとそもそも発行イベントが起きていません。トピックがカスタマー管理キーで暗号化されているのに、S3がそのキーを使えないため、S3側の発行そのものが失敗しているからです。S3のイベント通知には配信失敗のメトリクスが無いため、この段階では「どこで落ちているか」がAWS側の画面からは見えません。
手順6: S3にだけ権限を与えて、症状が変わることを確認する
区間ごとに症状が違うことを確かめます。KMSの sns-sqs-lab-key を開き、「キーポリシー」タブで「編集」をクリックして、S3のステートメントだけを追加します。
{
"Sid": "AllowS3ToUseKey",
"Effect": "Allow",
"Principal": { "Service": "s3.amazonaws.com" },
"Action": ["kms:GenerateDataKey*", "kms:Decrypt"],
"Resource": "*"
}保存したら、バケットに再度ファイルをアップロードします。数分待ってから、先ほどと同じCloudWatchのメトリクス画面(SNS →「トピックメトリクス」→ sns-sqs-lab-topic)を開きます。
今度はメトリクスが現れます。NumberOfMessagesPublished と NumberOfNotificationsFailed にチェックを入れて、同じグラフに重ねて表示します。
| メトリクス | 値 | 意味 |
|---|---|---|
NumberOfMessagesPublished | 1 | S3からの発行は成功した(S3 → SNS の区間は通った) |
NumberOfNotificationsFailed | 1 | SQSへの配信で失敗した(SNS → SQS の区間で落ちた) |
NumberOfNotificationsDelivered | データポイントなし | 配信が1件も成功していないため、メトリクス自体が現れない |
2つの値が同じ時刻に1ずつ立ち、グラフ上で重なります。SQSをポーリングしても、まだメッセージは0件のままです。落ちている区間が S3 → SNS から SNS → SQS へ移ったことが、メトリクスの変化として見えます。


[!NOTE] 「メッセージが届かない」ときにCloudWatchメトリクスで気づけるのは、発行までは成功している場合だけです。発行そのものが失敗しているときはメトリクスに何も出ません。切り分けの順序としては、まず
NumberOfMessagesPublishedが立っているかを見て、立っていなければ発行側(この例ではS3とトピックの暗号化キー)を疑います。



S3 にだけ権限を足したら、NumberOfMessagesPublished と NumberOfNotificationsFailed が 1 ずつ立ったでござるな。落ちている区間が S3 → SNS から SNS → SQS へ移ったことが、メトリクスの変化で見えたのでござる。
手順7: キーポリシーにSNSを追加する
同じ「キーポリシー」の編集画面で、SNSのステートメントも追加します。
{
"Sid": "AllowSNSToUseKey",
"Effect": "Allow",
"Principal": { "Service": "sns.amazonaws.com" },
"Action": ["kms:GenerateDataKey*", "kms:Decrypt"],
"Resource": "*"
}

「変更の保存」をクリックします。
[!NOTE] より厳密にするなら、
aws:SourceArnやaws:SourceAccountの条件キーで、どのバケット・どのトピックからの利用かを限定します。本番環境では混乱した代理人問題への対策として条件を付けます。
手順8: メッセージが届くことを確認する
バケットに再度ファイルをアップロードします。
SQSの sns-sqs-lab-queue で「メッセージをポーリング」を実行し、今度はメッセージが受信されることを確認します。本文にS3イベントの内容(バケット名・オブジェクトキー)が含まれています。





暗号化を有効にしたらサービス間の連携が壊れた、というとき、IAM・リソースポリシー・キーポリシーのどこに原因があるかを見分けるのが問われる観点でござる。AWS 管理キーとカスタマー管理キーの違いや、kms:Decrypt だけでは足りない点も、見落としやすいところでござるな。
まとめ
| 症状 | 見るべき場所 |
|---|---|
| イベント通知の設定時にエラーになる | SNSトピックのアクセスポリシー(sns:Publish) |
| サブスクリプションが「確認済み」にならない | SQSキューのアクセスポリシー(sqs:SendMessage) |
| 設定は通るのにメッセージだけ届かない | KMSキーポリシー(kms:GenerateDataKey* / kms:Decrypt) |
- 暗号化されたSNS・SQSに他のAWSサービスがメッセージを送るには、そのサービスのプリンシパルにKMSキーの使用を許可する必要がある
kms:Decryptだけでは足りない。データキーの生成にkms:GenerateDataKey*が要る- AWS管理キーはキーポリシーを変更できないため、サービス間連携ではカスタマー管理キーを使う
- 配信の失敗はSNSの
NumberOfNotificationsFailedメトリクスで検知できる - 本番では
aws:SourceArn/aws:SourceAccount条件でキーの利用元を限定する
参照先
- AWS KMS を使用した保管時の暗号化 – Amazon Simple Notification Service
- キー管理 – Amazon Simple Notification Service
- キー管理 – Amazon Simple Queue Service
- Amazon SQS の保管時の暗号化 – Amazon Simple Queue Service
- Amazon S3 イベント通知 – Amazon Simple Storage Service










