Control Tower 有効化で作成される AggregateSecurityNotifications のSNSサブスクリプションを削除する

2024.03.05

はじめに(モチベーション)

[前提] AggregateSecurityNotifications とは

AWS Control Tower(以降 CT) を新規に有効化すると、 監査アカウント上に aws-controltower-AggregateSecurityNotifications という SNSトピックが自動生成されます。 これはコンプライアンス変更通知(主にConfig)を受け取るために CT側で用意されたものです。

AggregateSecurityNotifications トピック は CT管理のリージョン上にそれぞれ配置され、 デフォルトで『 監査アカウントのメールアドレスへのサブスクリプション 』も作成されます。

AggregateSecurityNotifications は「非常にノイズが多い」

さて、この AggregateSecurityNotifications ですが、 公式ドキュメントにもあるとおり 意図的に非常にノイズが多い ものとなっています。

AWS Control Tower の SNS トピックは、意図的に非常にノイズの多いものとなっています。 例えば、AWS Config は、AWS Config が新しいリソースを検出するたびに通知を送信します。

監査アカウントの SNS によるコンプライアンス通知 - AWS Control Tower

仮に送信されてくるメールの Confirm subscription を押下すると、 大量に通知が来ることになります。

img

メーリングリストなど複数メンバーが見れるような環境下だと、 誤って押下してしまう可能性も高まります。

このサブスクリプションを「完全に」削除したい

そのため、デフォルトで作成されるサブスクリプションは「完全に」削除してしまいたいです。

ただし、SNSトピックにはいくつか制約(↓)があり一筋縄ではいきません。

  • 保留中(PendingConfirmation)の サブスクリプションは強制的に削除できない
  • メールから unsubscribe したとしても、再度サブスクライブできてしまう
    • 完全にサブスクリプションを削除するには API(AWS CLI等) から実行しないといけない

これら制約下でなんとかやりくりした方法を本ブログで紹介します。

手順

前提

本作業は Control Tower 有効化が完了した後に実施します。

メールで届く Confirm subscription を全て押下する

Control Tower 有効化後に送られてくる 「AWS Notification - Subscription Confirmation」 のメールを確認します。 メールボックスにて ${AuditアカウントID}:aws-controltower-AggregateSecurityNotifications といったワードで検索すると見つけやすいです。

「Control Tower 管理のリージョン分」 で Confirm subscription メールが届いているはずです。 それら全ての Confirmリンクを押下します。

img

管理アカウントの CloudShell を起動する

管理アカウントへログインして、CloudShell を起動させます。

img

Auditアカウントへスイッチロールする

前提として、Control Tower には 「管理アカウントから Auditアカウントの AWSControlTowerExecution ロール へスイッチロールできる経路」 が標準で用意されています。

これを用いて Auditアカウントへスイッチロールします。 以下コマンドを実行してください。 (audit_account_id の値部分を置き換えて実行します)

audit_account_id=123456789012
resp=$(aws sts assume-role --output json \
  --role-arn arn:aws:iam::${audit_account_id}:role/AWSControlTowerExecution \
  --role-session-name cm-initial-setup)

export AWS_ACCESS_KEY_ID=$(echo "$resp"     | jq -r ".Credentials.AccessKeyId")
export AWS_SECRET_ACCESS_KEY=$(echo "$resp" | jq -r ".Credentials.SecretAccessKey")
export AWS_SESSION_TOKEN=$(echo "$resp"     | jq -r ".Credentials.SessionToken")

実行後、 sts get-caller-identity 結果を出力します。 Auditアカウントへスイッチロールできていることを確認してください。

aws sts get-caller-identity
# {
#     "UserId": "AROAEXAMPLE:cm-initial-setup",
#     "Account": "${AuditアカウントID}",
#     "Arn": "arn:aws:sts::${AuditアカウントID}:assumed-role/AWSControlTowerExecution/cm-initial-setup"
# }

サブスクリプションを削除する

以下コマンドを実行して、削除対象のサブスクリプションを削除します。

## 全リージョンで AggregateSecurityNotifications トピックをチェックする
aws ec2 describe-regions --query "Regions[].[RegionName]" --output text \
| while read region; do
    echo "### ${region}"
    ## 対象トピックが存在しない場合は continue
    ct_topic_arn=$(aws sns list-topics --region "${region}" --output text \
      --query 'Topics[?ends_with(TopicArn, `:aws-controltower-AggregateSecurityNotifications`)].TopicArn')
    if [[ "$ct_topic_arn" = "" ]]; then
      echo "- The topic is not found in ${region}"
      continue
    fi

    ## 対象トピックのサブスクリプションが存在しない場合は continue
    echo "- ${ct_topic_arn} found"
    ct_subscriptions=$(aws sns list-subscriptions-by-topic --region "${region}" --output text \
      --topic-arn ${ct_topic_arn} --query "Subscriptions[].[Endpoint, SubscriptionArn]")
    if [[ "$ct_subscriptions" = "" ]]; then
      echo "    - No subscriptions"
      continue
    fi

    ## サブスクリプションを削除する
    echo "$ct_subscriptions" \
    | while read sub_endpoint sub_arn; do
        ## サブスクライブ済みであれば、サブスクライブを削除する
        if [[ "$sub_arn" == arn:aws:sns:* ]]; then
          echo "    - deleting subscription: ${sub_arn} (${sub_endpoint})"
          aws sns unsubscribe --region "${region}" --subscription-arn "${sub_arn}"
        ## サブスクライブ済みでない場合は何もしない
        else
          echo "    - skipped, because subscription status is ${sub_arn} (${sub_endpoint})"
        fi
      done
  done

以降使っているAWSコマンドの説明です。

ec2 describe-region

aws ec2 describe-regions --query "Regions[].[RegionName]" --output text
# ap-south-1
# eu-north-1
# eu-west-3
# ...略

全リージョンのSNSトピック(サブスクリプション)を確認するために使用しています。

sns list-topics

aws sns list-topics --region "${region}" --output text \
   --query 'Topics[?ends_with(TopicArn, `:aws-controltower-AggregateSecurityNotifications`)].TopicArn'
# arn:aws:sns:${region}:123456789012:aws-controltower-AggregateSecurityNotifications

aws-controltower-AggregateSecurityNotifications SNSトピックが無いか確認します。 queryオプションを使ってフィルターしています。

sns list-subscriptions-by-topic

aws sns list-subscriptions-by-topic --region "${region}" --output text \
  --topic-arn ${ct_topic_arn} --query "Subscriptions[].[Endpoint, SubscriptionArn]"
# (subscribe した場合) → audit@example.com  arn:aws:sns:${region}:123456789012:aws-controltower-AggregateSecurityNotifications:b75116c0-47a4-4df6-819a-517example
# (保留中の場合) → audit@example.com  PendingConfirmation
# (メールから unsubscribe した場合) → audit@example.com  Deleted

aws-controltower-AggregateSecurityNotifications SNSトピックのサブスクリプションをリストします。

sns unsubscribe

## サブスクライブ済みであれば、サブスクライブを削除する
if [[ "$sub_arn" == arn:aws:sns:* ]]; then
  echo "    - deleting subscription: ${sub_arn} (${sub_endpoint})"
  aws sns unsubscribe --region "${region}" --subscription-arn "${sub_arn}"
## サブスクライブ済みでない場合は何もしない
else
  echo "    - skipped, because subscription status is ${sub_arn} (${sub_endpoint})"
fi

サブスクリプションを AWS CLIから削除します。 これにより再度 subscribe されることは無くなります。

注意ですが、サブスクリプション削除( aws sns unsubscribe ) は PendingConfirmation および Deleted 状態のものは消せません 。 そのため if-else を使って条件分岐させています。

おわりに

以上、AggregateSecurityNotifications のSNSサブスクリプションを「完全に」削除する手順の紹介でした。

ちなみに 保留中のSNSトピックは、 48時間後に自動削除されます。

「48時間待って、完全削除とする」でももちろん良いのですが、 その間に誰かが Confirm subscription を押してしまうといけないので難しいところです。

本ブログが参考になれば幸いです。

参考