ROBOT PAYMENT TECH-BLOG

株式会社ROBOT PAYMENTのテックブログです

Amazon SNS トピックを無効化するツールを作ってみた

こんにちは、SREチームの西浜です。

今回はAmazon SNSトピックを一時的に無効化するツールを作ったのでその作り方を紹介をしたいと思います。

SNSって?トピックって?となっている人へザックリ言うと、AWSの各種サービスからメッセージ配信を提供するサービスです。 詳しくは公式サイトをご確認ください。

docs.aws.amazon.com

 

導入

そもそも、なぜSNSトピックを無効にしたいと思ったのか。

構成

ロボペイの決済システムではシステムの状態監視を行い、異常が起きた時にメールやSlackへ通知しています。

通常はこれでいいのですが、例えばメンテナンス作業でサーバを再起動するといった時、死活監視がNGになって通知(アラート)が飛んじゃいます。 問題が無いのに通知が飛ぶのは避けたいですよね。 つまり静観がしたくて、SNSトピックを一時的に無効にしたかったんです。

ただ、AWSの仕組で一時的に静観状態にするような機能が見当たらなかったので、ツールを作ることにしました。  

検討したこと

ツールを作るにあたってどこで無効化するのがいいか考えました。検討段階では以下の3つが候補に挙がりました(構成図の番号と紐付けています)。

  1. CloudWatch Alarmの設定を一時的に変更して通知を飛ばなくする
  2. SNSトピックを一時的に無効な状態にして通知を飛ばなくする
  3. サブスクリプションのフィルタを一時的に設定して通知を飛ばなくする

結論としては冒頭でも触れていますが「2」にしました。

その理由は消去法ですが、「3」のサブスクリプションのフィルタは設定時は即時適用されるが、フィルタ設定を解除(削除)した時は反映までに15分程度のタイムラグがありました。

これは私の設定が悪いのかも?。。。

また、「1」、「3」は比較的設定箇所が多いため、必然的に環境に変更を加える箇所が多くなるので避けました。 更に「2」のトピックに比べると今後も追加・削除が発生しやすいため、その変化をツールで考慮にいれるのは難易度が高くなりそうだったので候補から外しました。

SNSトピックを無効化する方法

前述した通り、AWSの設定でSNSトピックを無効化するような機能はありません。 そこで目をつけたのがSNSトピックのアクセスポリシーです。

SNSトピックを作成するとデフォルトで下記のようなアクセスポリシーが作成されます。

{
  "Version": "2008-10-17",
  "Id": "__default_policy_ID",
  "Statement": [
    {
      "Sid": "__default_statement_ID",
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": [
        "SNS:GetTopicAttributes",
        "SNS:SetTopicAttributes",
        "SNS:AddPermission",
        "SNS:RemovePermission",
        "SNS:DeleteTopic",
        "SNS:Subscribe",
        "SNS:ListSubscriptionsByTopic",
        "SNS:Publish",
        "SNS:Receive"
      ],
      "Resource": "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:sample-topic",
      "Condition": {
        "StringEquals": {
          "AWS:SourceOwner": "xxxxxxxxxxxx"
        }
      }
    }
  ]
}

このアクセスポリシーのResourceの箇所を以下のように存在しないarnに書き換えることで、このSNSトピックはどこからもアクセスを許可しなくなります。 つまり無効化状態になるということです。

"arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:sample-topic", "arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:sample-topic-dummy",

※ sample-topic-dummyというSNSトピックが存在しないことが前提となります

実装

無効化の方法がわかったのでツールを作って行きましょう。 ツールを作るにあたって以下のことを考慮しました。

  • 作業者がワンクリックでSNSトピックを無効・有効の切り替えができること
  • 複数のSNSトピックを指定して、まとめて無効・有効にできること
  • リージョン跨ぎでもSNSトピックを無効・有効にできること

ツールを簡単に説明するとCodeBuildで作ります。 CodeBuildからAWS CLI を使用してSNS トピックのアクセスポリシーを編集するという感じにしていきます。

CodeBuildのビルドプロジェクトを作りますが、大事なところだけ触れていきます。

プロジェクトの設定

プロジェクト名は何でもいいのですが、無効・有効それぞれ2つのビルドプロジェクトを作るので見分けがつきやすい名前にしてください。 私は 無効にする方はdisable-sns-topic、有効にする方は enable-sns-topic としました。

ソース

ソースはなし(「ソースがありません」)にしてください。

環境

イメージのOSは Amazon Linux 2 を選択してください。後はお好みでどうぞ。

続いて環境変数は

IS_ENABLED SNSトピックを無効化する場合はfalse 、有効化の場合は true を設定してください。

TOPIC_ARNS 無効化または有効したいSNSトピックのarnを記入します。複数指定するときはカンマ区切りで指定してください。

(例:arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:sample-topic,arn:aws:sns:us-east-1:xxxxxxxxxxxx:sample-topic

Buildspec

Buildspecはこちらを貼り付けてください。

解説は後述します。

version: 0.2
phases:
  pre_build:
    commands:
      - |
        # 有効化無効化の判定
        if "${IS_ENABLED}"; then
          replace_before_word="-dummy"
          replace_after_word=""
        else
          replace_before_word=""
          replace_after_word="-dummy"
        fi
  build:
    commands:
      - |
        for topic_arn in ${TOPIC_ARNS//,/ }
        do
          # リージョン情報の取得
          region="$(echo ${topic_arn} | cut -d: -f 4)"
          # 指定したSNSトピックのpolicyを取得&policyのResource情報を編集する
          edit_policy=$(aws sns get-topic-attributes --region ${region} --output json --topic-arn ${topic_arn} | jq .Attributes.Policy | sed -e s/${topic_arn}${replace_before_word}\\\\\"/${topic_arn}${replace_after_word}\\\\\"/g)
          echo "{\"TopicArn\": \"${topic_arn}\"," > input.json
          echo "\"AttributeName\": \"Policy\"," >> input.json
          echo "\"AttributeValue\": ${edit_policy}}" >> input.json
          # 上記で加工したpolicyを適用
          aws sns set-topic-attributes --region ${region} --cli-input-json file://input.json
        done

以上でビルドプロジェクトの作成は終了です。

IAMロール

ただ、このままでは、ビルドプロジェクトを実行してもSNSトピックを操作する権限がないため、エラーになってしまいます。 なので、CodeBuildが使用しているサービスロールに権限を追加してあげましょう。

「あれ、サービスロールどれだっけ?」ってなった人は作成したビルドプロジェクト→ビルド詳細の環境のセクションにリンクがあるので、そちらからIAMの設定画面へ飛びましょう。

では対象のロールにインラインポリシーを追加します。 追加するものは以下です。

サービス:SNS

アクション:GetTopicAttributes, SetTopicAttributes

これで、SNSトピックを無効化するビルドプロジェクトは完成です。

次は有効化するビルドプロジェクト(enable-sns-topic)を作る必要がありますが、手順はほぼ同じなので割愛します。

違いはビルドプロジェクトの環境変数を IS_ENABLEDfalse として設定するだけです。

動作確認

では動かしてみましょう。

ビルドプロジェクトのdisable-sns-topicを実行してSNSトピックのアクセスポリシーの設定が変わっていることを確認します。

ちゃんとSNSトピックのアクセスポリシー変わりましたね。 リソースのarnの末尾に「-dummy」が付与されました。

では、ビルドプロジェクトのenable-sns-topicを実行して設定が元に戻ることも確認します。

今度は、SNSトピックのアクセスポリシーが元に戻りました。 リソースのarnの末尾の「-dummy」が消えましたね。

Buildspecの解説

最後にBuildspecの内容を簡単に解説して終わります。 Buildspecでやっていることは、SNSトピックのアクセスポリシーの情報を取得して、リソース情報のお尻にdummyを付与して適用をということをしています。

まず pre_build のブロックから。

ここは、有効化・無効化の判定に応じてarnの置換前後の文字を設定しています。 これをすることで有効化と無効化のロジックを共通で使えるようにしました。

次にbuildブロックです。

これは、リージョン跨ぎでもアクセスポリシーが設定できるように、指定されたarnからリージョン情報を抽出してCLIのコマンドで利用します。

キャプチャが切れていますが get-topic-attributes コマンドと jq コマンドを使ってSNSトピックのアクセスポリシーを抽出しています。 抽出すると同時にアクセスポリシーのリソースのarnは適用後の情報に置換してます。

(無効化の場合にはARNの最後に-dummyと置換するし、有効化の場合にはARNの最後の-dummyを消すという動きになります。)

その後「input.json」に適用予定のアクセスポリシーの設定を出力して、 set-topic-attributes コマンドで設定を反映しています。 ここで地味にハマったのが、set-topic-attributes コマンドはコマンドラインに直接適用したいポリシーを書けばファイルから読み取らずに実行できます。 でも、codebuilから実行するとシンタックスエラーが発生して何をやっても解消できませんでした。 なので、ファイルから読み取る形式で実行することにしました。

以上です。

かなりマニアックな使い方をしているので、需要はなさそうですが参考にしていただけると幸いです(^^)

参考

get-topic-attributes — AWS CLI 2.5.8 Command Reference

set-topic-attributes — AWS CLI 2.5.8 Command Reference

We are hiring!! 絶賛仲間募集中です。

hrmos.co