Amazon SNSにデッドレターキューとしてSQSを設定できるのですが、SQSに対して謎の(?)権限エラーが出たので調査してみました。
現象
Amazon SNSのデッドレターキューとしてSQSを指定し、SQSにはアクセスポリシーを適切に設定したにも関わらず、SNSコンソール上で「デッドレターキュー(Redrive ポリシー)アクセス許可」という権限エラーが出る。
Amazon SNS デッドレターキュー (DLQ)とは
Lambda, SQSなどにもデッドレターキューは設定できますが、要は正常に動作しないときのメッセージを送信し貯めておくキュー といったものです。
SNSでは、トピックからサブスクリプション(サブスクライバ)に対して、(指定した回数のリトライを通して)正常にメッセージが送れなかった場合の送信先として指定できます。
SNS デッドレターキューの設定手順
- SNSトピックを作成する
- LambdaやSQSをSNSのサブスクリプションとして登録する
- その際に指定できるサブスクリプションのデッドレターキューにSQSを指定する
- SNSのRedrive ポリシー (デッドレターキュー)にSQSのARNを指定する
- そのSQSには、SNSからアクセスできるように適切なアクセスポリシーを設定する
※SNSのデッドレターキューは、サブスクリプションごとの設定になります。
SQS アクセスポリシー
SQSであったりLambdaやCloudWatch LogsにS3、様々なリソースに対してアクセス権限を設定することができます。
これらのように、リソースに対して設定する権限(ポリシー)のことを、「リソースベースポリシー」といいます。
このリソースに対して、「誰(アクター)が」「何ができる」といった権限設定になります。
※「誰が」に関して、プリンシパルという呼び方が正しいかもしれませんが、ここではわかりやすくアクターと呼んでいます。
一方で、リソースに対してではなく、アクターに対して設定する権限(ポリシー)を「アイデンティティベースポリシー」といいます。
こちらは恐らくAWSを触っているとほとんどの方が触れているであろうIAMポリシーなどが該当します。
このアクターは、「何(リソース)に」「何ができる」といった権限設定になります。
SQSに対して設定するポリシー、すなわちSQSにおける「リソースベースポリシー」は、「アクセスポリシー」と呼ばれます。
「アクセスポリシー」は、IAMポリシードキュメントなどと同じように以下のようなjsonで表します。
(※CloudFormationなどで作成する場合はyamlでも記入できますが、コンソール上ではjsonで出力されます。)
{ "Version": "2012-10-17", "Id": "TestGotoTopicToDLQPolicy", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "sns.amazonaws.com" }, "Action": "SQS:SendMessage", "Resource": "arn:aws:sqs:ap-northeast-1:123456789012:test-goto-dlq", "Condition": { "ArnEquals": { "aws:SourceArn": "arn:aws:sns:ap-northeast-1:123456789012:test-goto-topic" } } } ] }
AWSによるSNSのデッドレターキューの公式ドキュメントにも、上記の記入のような例が挙げられています。
要は以下のような許可ポリシーになります。
- SNS(sns.amazonaws.com)が(=Principal)
- このSQSに対して(=Resource)
- SQS:SendMessageが行える(=Action)
- このような条件下で
- アクセスされる際のソースのARNが指定したSNSトピックのARNのとき
しかし・・・
上記のように、公式ドキュメントに沿ったアクセスポリシーを付与したSQSを、SNSサブスクリプションのデッドレターキューに指定したにも関わらず、SNSのコンソール上のサブスクリプションページにて以下のようなエラーが出力されます。
デッドレターキュー (Redrive ポリシー) アクセス許可
Amazon SQS キューのアクセス許可を確認できませんでした。キューが存在し、アカウントにキューの属性を読み取るアクセス許可があることを確認してください。Amazon SNS トピックが Amazon SQS キューにメッセージを送信できるようにするには、Amazon SQS キューポリシーを作成する必要があります。
何度もポリシーを確認してミスがないことを確認したり、少しずつポリシーを変えたりして試しても、一向にエラーメッセージは消えません。
動作させてみると・・・
「もう全然わからん!」となって、試しにデッドレターキューのSQSにメッセージがいくかどうかのテストをしてみました。
具体的には、SNSサブスクリプションとして登録した Lambdaのリソースベースポリシーを空にし(今まではSNSからアクセスできるようにしていた)、わざとSNSからLambdaへのアクセスを失敗させて、デッドレターキューのSQSにメッセージがいくようにしました。
その状態でSNSトピックにメッセージを発行すると、デッドレターキューに指定したSQSまでメッセージが伝達されます(アクセスポリシーが正しければ)。
上記のような設定をしてSNSにメッセージを発行しました。
すると、、、
デッドレターキューのSQSにメッセージが到達しました!!!
どうやら
上記の通り、SQSのアクセスポリシーに関するエラーが表示されていても動作はするようです。
つまり、気にしなくていい?特に間違った設定はしていない?ということになるのでしょうか。
まあそこで色々と調べてみたら、案の定同じ現象に陥った方がいらっしゃって、さらにはAWSのサポートにまで問い合わせて下さっている方がいました。
I've reached out to AWS Support, and it's a known UI issue. And as of now, there is still no ETA confirmed for the fix.
どうやら、既知のUIの問題(修正予定のない)だそうです。
amazon web services - How to add a Redrive policy to an SNS with an encrypted DLQ - Stack Overflow
また、エラーが出なくなるための方法も載っておりました。
"Principal": { "Service": "sns.amazonaws.com" },
こちらを
"Principal": "*",
こうか
"Principal": { "AWS": "*" },
こうすると良いそうです。
エラーが出なくなった!!
上記の通りにアクセスポリシーを書き換えてみると、確かにエラーメッセージが出なくなりました!!
どちらのパターンでもエラーが出なくなり、そこから元のポリシーに戻すと再度エラーが出るようになることを確認しました。
最後に
こういった些細なエラーで、かつ修正されないようなものも、割とAWSのような大規模なサービスでも(むしろだからこそ?)あるようです。
確かに公式ドキュメントの些細なミスとかも、指摘をしても中々直してもらえなかったりとかはありますね・・・
とりあえず今回の件は動作はできるので、そのままでいってみることにします。
(実は今回これらのリソースはserverless frameworkで生成していてSQSのアクセスポリシーが自動で生成されていて、手を加えて余計なポリシーリソースを増やしたくなかったため。。。)