暗号化したSNS・SQSにメッセージが届かない ── サービス間連携に必要なKMS権限

目次

概要

S3にファイルが置かれたらSNSトピックに通知し、SNSからSQSキューに配信する。よくある構成です。ここで「保管中のデータはすべて暗号化する」という方針に従ってSNSトピックとSQSキューをSSE-KMSで暗号化した途端、メッセージが届かなくなることがあります。

IAMポリシーは何も変えていない、SNSのサブスクリプションも「確認済み」になっている、S3のイベント通知も設定されている。それでもSQSにメッセージが入りません。

原因はKMSキーポリシーです。暗号化されたトピックやキューに他のAWSサービスがメッセージを入れるには、そのサービスのプリンシパルがKMSキーを使える必要があるからです。そしてAWS管理キー(alias/aws/sns、alias/aws/sqs)はキーポリシーを変更できないため、この構成では最初からカスタマー管理キーを選ぶ必要があります。

この記事では、暗号化したSNS・SQSでメッセージが失われる状態を実際に作り、キーポリシーを直して届くようにするまでを追いかけます。

S3 → SNS → SQS の暗号化メッセージングチェーンと、各区間で必要になるKMS権限の対応図
S3 → SNS → SQS の暗号化メッセージングチェーンと、各区間で必要になるKMS権限の対応図
クラウドおさる

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 → SNSSNSトピックs3.amazonaws.comkms:GenerateDataKey* / kms:Decrypt
SNS → SQSSQSキューsns.amazonaws.comkms:GenerateDataKey* / kms:Decrypt
SQS → コンシューマーSQSキュー受信するIAMロール・ユーザーkms:Decrypt

kms:Decrypt だけでなく kms:GenerateDataKey* が要るのは、エンベロープ暗号化のためです。メッセージ本体はデータキーで暗号化され、そのデータキーの生成にKMSを呼びます。

エンベロープ暗号化におけるGenerateDataKeyとDecryptの役割(送信側がデータキーを生成し、受信側が復号する流れ)
エンベロープ暗号化におけるGenerateDataKeyとDecryptの役割(送信側がデータキーを生成し、受信側が復号する流れ)

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つ目が厄介なのは、設定画面上はどこにもエラーが出ないことです。しかも、どの区間で落ちているかによって見える症状が変わります。

落ちている区間NumberOfMessagesPublishedNumberOfNotificationsFailed見え方
S3 → SNS(トピックの暗号化キーをS3が使えない)0(記録されない)0SNSのメトリクスに何も出ない。発行イベント自体が起きていない
SNS → SQS(キューの暗号化キーをSNSが使えない)11発行は成功し、配信で失敗していることが分かる

「メッセージが来ない」ときは、まず 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 の条件キーで、どのバケット・どのトピックからの利用かを限定します。第三者に自分のキーを使わせない(混乱した代理人問題への対策)ために、本番環境では条件を付けます。

フリーランス案件
クラウドおさる
クラウドキャリアフリーランス

AWS の実務経験があるなら、単価を上げにいく

AWS に特化したフリーランスエージェントです。以下は取り扱い案件の一例です。設計・SRE・セキュリティ・生成 AI の領域で、専門を掛け合わせるほど単価が上がります。

横にスクロールできます。掲載時点の情報のため、募集が終了している場合があります。条件の近い案件をご紹介しますので、お気軽にご相談ください。

実践

暗号化したSNSトピックとSQSキューを作り、キーポリシーが不足した状態ではメッセージが届かないこと、権限を足すと届くようになることを確認します。

前提条件

  • リージョン: ap-northeast-1(東京)
  • KMS・SNS・SQS・S3の操作権限を持つIAMユーザーまたはロールでコンソールにログイン済みであること

このハンズオンで作成するリソースは以下のとおりです。

リソース種別リソース名用途
KMSカスタマー管理キーsns-sqs-lab-keySNSトピックとSQSキューの暗号化用
SQSキューsns-sqs-lab-queueメッセージの受信先
SNSトピックsns-sqs-lab-topicS3イベントの通知先
S3バケットsns-sqs-lab-<アカウントID>イベントの発生元

手順1: KMSカスタマー管理キーを作成する

AWSマネジメントコンソールにログインし、リージョンが ap-northeast-1(東京)であることを確認します。

上部の検索バーに KMS と入力し、表示された「Key Management Service」を選択します。

左側ナビゲーションの「カスタマー管理型のキー」を選択し、「キーの作成」をクリックします。以下を設定して作成します。

  • キーのタイプ: 対称
  • キーの使用法: 暗号化および復号化
  • エイリアス: sns-sqs-lab-key
  • キーの管理者・キーの使用法アクセス許可: 自分のIAMユーザー(またはロール)
作成された sns-sqs-lab-key の詳細画面
作成された sns-sqs-lab-key の詳細画面

この時点では、キーポリシーにS3やSNSのサービスプリンシパルは含まれていません。

手順2: 暗号化したSQSキューを作成する

上部の検索バーに SQS と入力し、表示された「Simple Queue Service」を選択します。

「キューを作成」をクリックし、以下を設定します。

  • タイプ: 標準
  • 名前: sns-sqs-lab-queue
  • 「暗号化」セクション: サーバー側の暗号化 を有効にする
  • キーの種類: AWS Key Management Service キー (SSE-KMS)
  • カスタマーマスターキー: alias/sns-sqs-lab-key を選択
SQSキュー作成画面の暗号化設定(SSE-KMSでカスタマー管理キーを選択した状態)
SQSキュー作成画面の暗号化設定(SSE-KMSでカスタマー管理キーを選択した状態)

「キューを作成」をクリックします。

手順3: 暗号化したSNSトピックを作成してサブスクライブする

上部の検索バーに SNS と入力し、表示された「Simple Notification Service」を選択します。

左側ナビゲーションの「トピック」から「トピックの作成」をクリックし、以下を設定します。

  • タイプ: スタンダード
  • 名前: sns-sqs-lab-topic
  • 「暗号化」セクション: 有効にする
  • カスタマーマスターキー: alias/sns-sqs-lab-key を選択
SNSトピック作成画面の暗号化設定
SNSトピック作成画面の暗号化設定

「トピックの作成」をクリックします。

作成したトピックの画面で「サブスクリプションの作成」をクリックし、以下を設定します。

  • プロトコル: Amazon SQS
  • エンドポイント: sns-sqs-lab-queue のARN
  • 生メッセージ配信: 有効にする
SNSサブスクリプションの作成画面(SQSキューを指定した状態)
SNSサブスクリプションの作成画面(SQSキューを指定した状態)

「サブスクリプションの作成」をクリックします。ステータスが「確認済み」になることを確認します。

[!NOTE] コンソールからこの操作を行うと、SQSキューのアクセスポリシーに「このトピックからの sqs:SendMessage を許可する」ステートメントが自動的に追加されます。KMSキーポリシーは自動では追加されません。

手順4: S3のイベント通知を設定する

上部の検索バーに S3 と入力し、「S3」を選択します。

「バケットを作成」で sns-sqs-lab-<アカウントID> を作成します(設定はすべてデフォルトのままで構いません)。

作成したバケットを開き、「プロパティ」タブの「イベント通知」セクションで「イベント通知を作成」をクリックします。以下を設定します。

  • イベント名: notify-sns
  • イベントタイプ: すべてのオブジェクト作成イベント
  • 送信先: SNS トピック
  • SNS トピック: sns-sqs-lab-topic
S3イベント通知の作成画面(送信先にSNSトピックを指定した状態)
S3イベント通知の作成画面(送信先にSNSトピックを指定した状態)

「変更の保存」をクリックします。SNSトピックのアクセスポリシーに、S3からの sns:Publish を許可するステートメントが必要です。エラーになる場合は、SNSトピックの「アクセスポリシー」を編集して、s3.amazonaws.com からの sns:Publish を許可してください。

手順5: メッセージが届かないことを確認する

作成したバケットに任意のファイルをアップロードします。

SQSの sns-sqs-lab-queue を開き、「メッセージを送受信」→「メッセージをポーリング」をクリックします。

メッセージが1件も受信されないことを確認します。設定はすべて正しく見えるのに、メッセージだけが届いていません。

SQSのメッセージポーリング結果(メッセージが0件の状態)
SQSのメッセージポーリング結果(メッセージが0件の状態)

原因を探るために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 にチェックを入れて、同じグラフに重ねて表示します。

メトリクス値意味
NumberOfMessagesPublished1S3からの発行は成功した(S3 → SNS の区間は通った)
NumberOfNotificationsFailed1SQSへの配信で失敗した(SNS → SQS の区間で落ちた)
NumberOfNotificationsDeliveredデータポイントなし配信が1件も成功していないため、メトリクス自体が現れない

2つの値が同じ時刻に1ずつ立ち、グラフ上で重なります。SQSをポーリングしても、まだメッセージは0件のままです。落ちている区間が S3 → SNS から SNS → SQS へ移ったことが、メトリクスの変化として見えます。

SNSトピックのモニタリング(発行は成功し、配信が失敗している状態)
SNSトピックのモニタリング(発行は成功し、配信が失敗している状態)

[!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": "*"
}
キーポリシー編集画面(S3とSNSのサービスプリンシパルを追加した状態)
キーポリシー編集画面(S3とSNSのサービスプリンシパルを追加した状態)

「変更の保存」をクリックします。

[!NOTE] より厳密にするなら、aws:SourceArn や aws:SourceAccount の条件キーで、どのバケット・どのトピックからの利用かを限定します。本番環境では混乱した代理人問題への対策として条件を付けます。

手順8: メッセージが届くことを確認する

バケットに再度ファイルをアップロードします。

SQSの sns-sqs-lab-queue で「メッセージをポーリング」を実行し、今度はメッセージが受信されることを確認します。本文にS3イベントの内容(バケット名・オブジェクトキー)が含まれています。

SQSで受信したメッセージの本文(S3のイベント内容が確認できる状態)
SQSで受信したメッセージの本文(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 の実務経験があるなら、単価を上げにいく

AWS に特化したフリーランスエージェントです。以下は取り扱い案件の一例です。設計・SRE・セキュリティ・生成 AI の領域で、専門を掛け合わせるほど単価が上がります。

横にスクロールできます。掲載時点の情報のため、募集が終了している場合があります。条件の近い案件をご紹介しますので、お気軽にご相談ください。

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

株式会社ジェニュイン(https://genuine-pt.jp)の代表取締役.

2018年〜インフラエンジニアとしてキャリアをスタートし、オンプレミスのネットワーク・サーバ環境で3年半、クラウド環境で4年半の8年間エンジニアとして従事。
2021年に佐藤氏の創業した株式会社Luxyを引き継ぎ、代表に就任。
その後アガルートグループ内の株式会社ジェニュインと合併。
合併後同社代表に就任。

目次