Amazon GuardDuty を組織単位で管理したいなら知っておきたいこと [AWS Organization / Control Tower]

## Amazon GuardDuty とは

Amazon GuardDuty は、私が個人的に AWS の好きなサービスの1つで、 AWS のリソースに対する不審なアクティビティを検知するサービスです。

https://aws.amazon.com/jp/guardduty/

組織単位で管理することもでき、単一のチャネルに全てのアカウントの上記の通知を受け取るようにすることで、通知の設定漏れや取りこぼしを減らすことができます。

## 把握しておきたいこと

GuardDuty を組織で導入する際に、既存のアカウントや新しく組織に入れたアカウントにどのような影響が及ぶかを把握しておかなくてはいけません。
そこで、私が確認しておきたかった挙動を以下にまとめ共有いたします。

1. 「GuardDuty自動有効化」 が 「新しいアカウントについて有効化」 になっている場合について
    - 既存のアカウントを新しく組織に入れた場合、それは "新しいアカウント" として扱われるのか
        - "新しいアカウント" として扱われるので、有効化される
2. 新しく管理対象になるアカウントで既にGuardDutyの設定がある場合、設定はGuardDuty管理アカウントの設定が優先されるか
    - GuardDuty管理アカウントで設定されたGuardDutyの設定に上書きされる
    - 例えば既存のアカウントのS3プロテクションが有効であり、GuardDuty管理アカウントでは無効だった場合、S3プロテクションは無効になる
3. 組織からアカウントが離れた場合、 GuardDutyの設定はどうなるか
    - 組織から離れたアカウントは組織に属していた時のGuardDutyの設定はそのまま維持される
4. GuardDuty管理アカウントにて、管理対象の全てのアカウントのGuardDutyのイベントを受信できるのか
    - できます
5. 被管理アカウントで GuardDutyのイベントを受信することができるか
    - できます。管理アカウントでイベントを受信し、個別アカウントでもイベントを受信して、別個の通知先に通知を飛ばすこともできます

## 有効化するリージョンは全リージョンが良いのか

- 全リージョンが良いと思います
    - 普段使っていないリージョンにて不審なアクティビティが発生する可能性は0ではないため
- 全リージョン有効化すると料金はどれくらいかかるか
    - 普段使っていないリージョンに関しては、本当に全く使っていない場合は0USDになることもある
    - あるアカウントで確認したところ、全リージョンが有効である場合に増加するコストは `0.2 USD/月`だった

## GuardDuty自動有効化 について

- 組織に自社で管理していない、他社のアカウントを入れることがあり、その際にGuardDutyを有効化できない、もしくは、独自の設定基準がある場合には、組織に招待する際 GuardDuty自動有効化 を一時的にオフにする必要がある
- Organization や Control Tower を使って新しくアカウントを作成する頻度が高い場合は、GuardDuty自動有効化 を 新しいアカウントについて有効化 に

## GuardDuty管理アカウントを指定する

- 管理アカウントより、GuardDutyへ移動し、委任された管理アカウントに、GuardDuty管理アカウントのIDを設定する


![スクリーンショット 2024-05-08 19.19.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/632997/bd66cb77-2046-2fb7-7c8d-422b1e11fe50.png)


## GuardDuty管理アカウントにて通知基盤を作成する(ChatbotのSlackを利用する場合)

↓こちらを参考に、 GuardDuty管理アカウントにて StackSets の実行に必要な IAM ロールを作成する

https://dev.classmethod.jp/articles/create-stacksets-iam-role/

↓こちらの GuardDuty の Detector の設定を省いたものを利用して、全リージョンにデプロイする(ClassMethodさん、いつも有用な記事をありがとうございます)

https://dev.classmethod.jp/articles/set-guardduty-all-region/

全部のリージョンにデプロイできたことを確認したら、新しくできた GuardDuty用の Topic を Chatbot の通知先として指定する
Chatbot の使い方がわからない場合、設定の仕方などは↓こちらが参考になります
(サーバーワークスさんも、いつも有用な記事をありがとうございます)

https://blog.serverworks.co.jp/2022/02/23/182459

## (必要があれば) 既存の設定を維持したいAWSアカウントについて、現状のGuardDutyの設定を取得する

個別のアカウントにログインし、CloudShell が使える場合は、 CloudShellを立ち上げ、以下を貼り付けて実行する

``` 
cat <<'EOL' > guardduty_check.sh
#!/bin/bash
AWS_PAGER=
# 利用可能なすべてのリージョンを取得
regions=$(aws ec2 describe-regions --query "Regions[].RegionName" --output text)

# 各リージョンでGuardDutyが有効になっているかチェック
for region in $regions; do
  echo "Checking GuardDuty status in region: $region"
  
  # GuardDutyのDetectorIdを取得
  detectorId=$(aws guardduty list-detectors --region $region --query "DetectorIds[0]" --output text 2>/dev/null)
  
  if [ "$detectorId" == "None" ]; then
    echo "GuardDuty is not enabled in $region"
  else
    echo "GuardDuty is ENABLED in $region"
    aws guardduty get-detector --detector-id $detectorId --region $region > guardduty_result
  fi
done
EOL

chmod +x guardduty_check.sh

./guardduty_check.sh

```

全てのアカウントの設定を確認したい && Control Tower を有効にしている場合は、
各アカウントが持つ共通の Control Tower で自動生成される管理用のロールを使うことで一斉に情報を取得できます

``` .sh
#!/bin/bash
AWS_PROFILE=<任意のプロファイル名>
ROLE_NAME=<各アカウントが持つ共通のロール>
AWS_PAGER=""

# 利用可能なすべてのリージョンを取得
regions=$(aws --profile $AWS_PROFILE ec2 describe-regions --query "Regions[].RegionName" --output text)

# Organizationのメンバーアカウントを取得
accounts=$(aws --profile $AWS_PROFILE organizations list-accounts --query 'Accounts[?Status==`ACTIVE`].Id' --output text)

# 各メンバーアカウントに対して実行
for account in $accounts; do
  echo "Checking GuardDuty status for Account ID: $account"

  # IAM ロールをアサーム
  role_arn="arn:aws:iam::$account:role/$ROLE_NAME"
  assumed_role=$(aws --profile $AWS_PROFILE sts assume-role --role-arn $role_arn --role-session-name GuardDutyCheck --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text)
  export AWS_ACCESS_KEY_ID=$(echo $assumed_role | awk '{print $1}')
  export AWS_SECRET_ACCESS_KEY=$(echo $assumed_role | awk '{print $2}')
  export AWS_SESSION_TOKEN=$(echo $assumed_role | awk '{print $3}')

  # 各リージョンでGuardDutyが有効になっているかチェック
  for region in $regions; do
    echo "Checking GuardDuty status in region: $region"
    
    # GuardDutyのDetectorIdを取得
    detectorId=$(aws guardduty list-detectors --region $region --query "DetectorIds[0]" --output text 2>/dev/null)
    
    if [ "$detectorId" == "None" ]; then
      echo "GuardDuty is not enabled in $region for Account ID: $account" >> guardduty_result
    else
      echo "GuardDuty is ENABLED in $region for Account ID: $account" >> guardduty_result
      aws guardduty get-detector --detector-id $detectorId --region $region >> guardduty_result
    fi
  done

  # IAM ロールのクレデンシャルをアンセット
  unset AWS_ACCESS_KEY_ID
  unset AWS_SECRET_ACCESS_KEY
  unset AWS_SESSION_TOKEN
done
```

## GuardDuty の設定を反映していく

あとは、GuardDuty 管理アカウントにて、各リージョンで管理しているアカウントごとに任意の設定を行うだけです

## 個別アカウントでイベントを受信して別の通知先に通知を飛ばしたい場合

GuardDuty管理アカウントにて通知基盤を作成する(ChatbotのSlackを利用する場合) と同じ作業を個別アカウントで行います


## 参考になれば幸いです

Organization に属しているアカウントが少ない場合には、簡単に導入できますが、いろんな事情のアカウントが Organization に混在する場合にはいろいろ考えることがでてきますね。
以上のメモが、参考になれば幸いです。