GuardDuty Malware Protection for S3のバケット数25というハードリミットがきつい

こんにちは！
記事をご覧頂きありがとうございます。

突然ですが、皆さんはGuardDuty Malware Protection for S3使ってますか？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236355/9d468b10-152a-48c0-9676-926b898c3521.png)

2024年のre:Inforceで発表されたGuardDuty Malware Protection for S3ですが、利用者が待ち望んでいた機能ということで、結構反響があったと思います。

最近では、これを使おうとするプロジェクトも多くなってきているなと実感しているのですが、この機能を利用する上でよくあがる課題がS3バケットの数が25個までというハードリミット問題です。

今回の記事では、このハードリミット問題に直面した時に考えた、3つの回避策をご紹介したいと思います。

# はじめに
この記事は2025年5月に執筆した記事になります。

AWSサポートにもこのハードリミットはどうにかならんの？と1年程前に問い合わせたところ、どうにもならん！という回答でした。一方で、最近改めて問い合わせを行ったところ、多くの利用者から同様の声が上がっているので改善要望として開発元にフィードバックしているとのことでした。

なので、近いうちに改善されるかもしれないね！ということで、導入する際は最新の情報をご確認下さい。

# GuardDuty Malware Protection for S3の振返り
GuardDuty Malware Protection for S3はS3バケットにアップロードしたファイルに対して、マルウェアスキャンを実行出来る機能です。

この機能がより注目されたポイントとしては、従来GuardDutyは検知までしか出来ませんでしたが、S3のバケットポリシーと組み合わせることで、GuardDutyのスキャンの結果「NG」だったものにアクセスできないようにブロック出来る点です。

文字ばっかり並べても仕方がないということで、機能の振返りがてらやっていきたいと思います。

## 1. GuardDuty Malware Protection for S3の開始
まだ何も設定していない状態のGuardDutyのサービスページにいくと下図のようになっていますので、``GuardDuty Malware Protection for S3のみ``を今回は選択して、``今すぐ始める``をクリックします。
![screenshot.2221.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236355/49f0e027-32ff-4b96-b154-1dd95b4aa910.jpeg)

## 2. GuardDuty Malware Protection for S3を有効にする
GuardDuty Malware Protection for S3のページに遷移するので、``有効にする``をクリックします。
![screenshot.2222.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236355/5fc288d9-1d15-46a8-a751-910bd13ce59f.jpeg)

## 3. GuardDuty Malware Protection for S3に必要な情報を設定
GuardDuty Malware Protection for S3の設定ページに遷移するので、S3バケット部分では``S3を参照``をクリックし、S3一覧の中から有効化したいS3を選択します。

続いてプレフィック部分ですが、今回は``S3バケット内のすべてのオブジェクト``を選択します。

スキャンされたオブジェクトへのタグ付け部分では、「NG」タグのものをブロックしたいので、``オブジェクトにタグを付ける``をクリックします。
![screenshot.2229.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236355/b3950015-6257-4d27-86bf-cfa87989ac98.jpeg)

サービスアクセス部分ではIAM Roleを選択しますが、今回はせっかくなので自動作成のIAM Roleにどんなポリシーが付くのか見るために、``新しいサービスロールを指定して使用する``を選択し、下部の``有効にする``をクリックします。
![screenshot.2230.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236355/298a0c8b-be38-49b7-b909-532ba82a46b5.jpeg)

これでGuardDuty Malware Protection for S3の設定完了です。
![screenshot.2231.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236355/b2ae1898-aa71-41aa-b8db-f32e81945caf.jpeg)

因みに、自動作成されたIAM Roleに紐づくIAM Policyの中身はこんな感じでした。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AllowManagedRuleToSendS3EventsToGuardDuty",
            "Effect": "Allow",
            "Action": [
                "events:PutRule"
            ],
            "Resource": [
                "arn:aws:events:ap-northeast-1:<AccountID>:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*"
            ],
            "Condition": {
                "StringEquals": {
                    "events:ManagedBy": "malware-protection-plan.guardduty.amazonaws.com"
                },
                "ForAllValues:StringEquals": {
                    "events:source": "aws.s3",
                    "events:detail-type": [
                        "Object Created",
                        "AWS API Call via CloudTrail"
                    ]
                },
                "Null": {
                    "events:source": "false",
                    "events:detail-type": "false"
                }
            }
        },
        {
            "Sid": "AllowUpdateTargetAndDeleteManagedRule",
            "Effect": "Allow",
            "Action": [
                "events:DeleteRule",
                "events:PutTargets",
                "events:RemoveTargets"
            ],
            "Resource": [
                "arn:aws:events:ap-northeast-1:<AccountID>:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*"
            ],
            "Condition": {
                "StringEquals": {
                    "events:ManagedBy": "malware-protection-plan.guardduty.amazonaws.com"
                }
            }
        },
        {
            "Sid": "AllowGuardDutyToMonitorEventBridgeManagedRule",
            "Effect": "Allow",
            "Action": [
                "events:DescribeRule",
                "events:ListTargetsByRule"
            ],
            "Resource": [
                "arn:aws:events:ap-northeast-1:<AccountID>:rule/DO-NOT-DELETE-AmazonGuardDutyMalwareProtectionS3*"
            ]
        },
        {
            "Sid": "AllowEnableS3EventBridgeEvents",
            "Effect": "Allow",
            "Action": [
                "s3:PutBucketNotification",
                "s3:GetBucketNotification"
            ],
            "Resource": [
                "arn:aws:s3:::<S3BucketName>"
            ],
            "Condition": {
                "StringEquals": {
                    "aws:ResourceAccount": "<AccountID>"
                }
            }
        },
        {
            "Sid": "AllowPostScanTag",
            "Effect": "Allow",
            "Action": [
                "s3:GetObjectTagging",
                "s3:GetObjectVersionTagging",
                "s3:PutObjectTagging",
                "s3:PutObjectVersionTagging"
            ],
            "Resource": [
                "arn:aws:s3:::<S3BucketName>/*"
            ],
            "Condition": {
                "StringEquals": {
                    "aws:ResourceAccount": "<AccountID>"
                }
            }
        },
        {
            "Sid": "AllowPutValidationObject",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::<S3BucketName>/malware-protection-resource-validation-object"
            ],
            "Condition": {
                "StringEquals": {
                    "aws:ResourceAccount": "<AccountID>"
                }
            }
        },
        {
            "Sid": "AllowCheckBucketOwnership",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::<S3BucketName>"
            ],
            "Condition": {
                "StringEquals": {
                    "aws:ResourceAccount": "<AccountID>"
                }
            }
        },
        {
            "Sid": "AllowMalwareScan",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:GetObjectVersion"
            ],
            "Resource": [
                "arn:aws:s3:::<S3BucketName>/*"
            ],
            "Condition": {
                "StringEquals": {
                    "aws:ResourceAccount": "<AccountID>"
                }
            }
        }
    ]
}
```

## 4. バケットポリシーを設定
GuardDuty Malware Protection for S3側が設定出来たので、次はS3のバケットポリシーを設定して、マルウェアスキャンの結果「NG」だったオブジェクトを利用出来ないようにブロックします。

S3で設定するバケットポリシーは以下の通りです。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "NoReadExceptForClean",
            "Effect": "Deny",
            "Principal": {
                "AWS": "arn:aws:iam::<AccountID>:root"
            },
            "Action": [
                "s3:GetObject",
                "s3:GetObjectVersion"
            ],
            "Resource": [
                "arn:aws:s3:::<S3BucketName>",
                "arn:aws:s3:::<S3BucketName>/*"
            ],
            "Condition": {
                "StringNotEquals": {
                    "s3:ExistingObjectTag/GuardDutyMalwareScanStatus": "NO_THREATS_FOUND",
                    "aws:PrincipalArn": [
                        "arn:aws:iam::<AccountID>:assumed-role/GuardDutyS3MalwareScanRole/GuardDutyMalwareProtection",
                        "arn:aws:iam::<AccountID>:role/service-role/GuardDutyS3MalwareScanRole"
                    ]
                }
            }
        },
        {
            "Sid": "OnlyGuardDutyCanTag",
            "Effect": "Deny",
            "Principal": {
                "AWS": "arn:aws:iam::<AccountID>:root"
            },
            "Action": "s3:PutObjectTagging",
            "Resource": [
                "arn:aws:s3:::<S3BucketName>",
                "arn:aws:s3:::<S3BucketName>/*"
            ],
            "Condition": {
                "StringNotEquals": {
                    "aws:PrincipalArn": [
                        "arn:aws:iam::<AccountID>:assumed-role/GuardDutyS3MalwareScanRole/GuardDutyMalwareProtection",
                        "arn:aws:iam::<AccountID>:role/service-role/GuardDutyS3MalwareScanRole"
                    ]
                }
            }
        }
    ]
}
```

これで全ての設定が完了です。

試しに正常なファイルをアップロードすると、マルウェアスキャンが走り、スキャンの結果問題なければ```「NO_THREATS_FOUND」```というタグがオブジェクトに付きます。このタグが付いたオブジェクトであれば取得が可能です。

![screenshot.2232.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236355/afda4428-8468-4e0a-a1e8-c7ceda6d1a65.jpeg)

一方で、このタグ以外のタグが付いたり、そもそもタグが付いていないものはAccess Deniedで取得が出来なくなります。
![screenshot.2233.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236355/23ff69b2-866e-4f3f-906b-2898331f6d19.jpeg)

因みに、GuardDuty Malware Protection for S3が付けるタグは以下の通りです。

| タグの値          | 意味                                                              |
| :--------------- | :---------------------------------------------------------------- |
| NO_THREATS_FOUND | スキャンされたオブジェクトに脅威は見つかりませんでした。               |
| THREATS_FOUND    | スキャン中に潜在的な脅威が検出されました。                           |
| UNSUPPORTED      | このオブジェクトのサイズが原因で GuardDuty はスキャンできません。     |
| ACCESS_DENIED    | GuardDuty はオブジェクトにアクセスできません。許可を確認してください。 |
| FAILED           | GuardDuty はオブジェクトをスキャンできませんでした。                 |

[参考：Amazon GuardDuty Malware Protection for Amazon S3 のご紹介](https://aws.amazon.com/jp/blogs/news/introducing-amazon-guardduty-malware-protection-for-amazon-s3/)

# バケット数のハードリミット
機能の振返りをしたところで今回の本題に入りますが、冒頭でも記載した通りGuardDuty Malware Protection for S3には対象に出来るS3バケットの数に制限があり、リージョンあたり25個までとなっています。また、この制限はハードリミットのため、緩和申請等はなく変更出来ない値となります。
[参考：Malware Protection for S3 のクォータ](https://docs.aws.amazon.com/ja_jp/guardduty/latest/ug/malware-protection-s3-quotas-guardduty.html)

そのため、26個以上のバケットを対象にしたい場合は何らかの回避策を考える必要があります。

今回ご紹介する3つの回避策は、私もこの問題に直面し、プロジェクト内メンバーと検討したり、AWSサポートから意見を頂いたりして、考えたものになります。

# ハードリミット回避策

## 1. マルチアカウント
1つ目の回避策はマルチアカウントで対応する方法です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236355/678f47b3-656b-4f53-b2bf-2ab0bdfc144f.png)

アカウント毎に1リージョンあたり25個のS3バケットまでという制限なので、26個目が必要になった段階でアカウントを増やして対応する回避策です。

注意点としては、Amazon GuardDuty Malware Protection for Amazon S3は別アカウントのS3がスキャン対象外のため、S3も別アカウントに作る必要があります。

また、S3とGuardDutyしか主に利用はしないとはいえ、CloudTrailの設定等のアカウントに対するセキュリティ対策等も実施する必要があります。

## 2. マルチリージョン
2つ目の回避策はマルチリージョンで対応する方法です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236355/49d89225-eed8-4559-ac8d-5486cbd4bf72.png)

アカウント毎に1リージョンあたり25個のS3バケットまでという制限なので、26個目が必要になった段階で別リージョンを利用して対応する回避策です。

注意点としては1の方法と似ていますが、Amazon GuardDuty Malware Protection for Amazon S3は別リージョンのS3がスキャン対象外のため、S3も別リージョンに作る必要があります。

また、呼び出し元サービスがある場合、リージョン跨ぎとなるので同じリージョンよりは多少のレイテンシーが発生する可能性もあります。

## 3. スキャン専用バケット
3つ目の回避策はスキャン専用バケットを用意する方法です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/236355/dd77684a-5348-4209-b0de-11c6fa2d0e56.png)

アカウント毎に1リージョンあたり25個のS3バケットまでという制限なので、間に1個スキャン用のS3バケットをはさんで、スキャン対象を1つのS3バケットにする回避策です。

この方法は何パターンか実装パターンがあるかもしれませんが、今回記載するのはEventBridgeとLambdaのパターンです。GuardDuty Malware Protection for S3のスキャンが完了したことをEventBridgeで検知し、Lambdaを起動します。Lambdaで対象のオブジェクトのスキャン結果を確認し、結果が問題なければ本来配置するべきS3バケットにコピーし、最後に元のバケットからオブジェクトを削除するといった方法です。
[参考：Amazon EventBridge による S3 オブジェクトスキャンのモニタリング](https://docs.aws.amazon.com/ja_jp/guardduty/latest/ug/monitor-with-eventbridge-s3-malware-protection.html)

注意点としては、上記で記載しているLambdaのようなものを開発する必要があったり、開発の手間があります。

また、同じS3バケット内に一時的とはいえ、全てのファイルが集約されるため、セキュリティ要件の確認が必要です。

## メリット・デメリット
上記で記載した3つの回避策のメリデメを簡単にまとめると以下の通りです。

| 回避策               | メリット                                                                                                                  | デメリット |
| :------------------ | :----------------------------------------------------------------------------------------------------------------------- | :-------- |
| マルチアカウント     | ・1つのリージョンで実現出来る <br>・独自ツールの開発なく実現可能                                                               | ・複数アカウントの管理が面倒 <br>・呼び出し元サービスとS3バケットでアカウントが分かれてしまう <br>・リソースが少ないとはいえ、CloudTrail等のアカウントセキュリティ対策を施す必要がある |
| マルチリージョン     | ・1つのアカウントで実現できる <br>・独自ツールの開発なく実現可能                                                               | ・複数リージョンの管理が面倒 <br>・呼び出し元サービスとS3バケットでリージョンが分かれてしまう |
| スキャン専用バケット | ・1つのリージョンで実現できる <br>・1つのアカウントで実現できる <br>・呼び出し元サービスと同じリージョン/同じアカウントで構成出来る  | ・独自ツールの開発が発生する <br>・一時的に全てのファイルが同じバケット内に入ってしまう |

## まとめ
今回はGuardDuty Malware Protection for S3のアカウント毎に1リージョンあたり25個のS3バケットまでというハードリミットを回避する方法を3つご紹介しました。

メリデメ表を見るとスキャン専用バケットパターンが良さそう！という印象を持たれるかもしれませんが、実際私が参画したプロジェクトではスキャン専用バケットパターンが最初に検討から除外されました。

理由としましては、このプロジェクトではセキュリティ要件が高く、例え細かく権限等を制御したとしても1つのS3バケット内に様々なファイルがアップロードされるというのが許されなかったからです。

その上で、アップロードするファイルのサイズが大きいものがあったため、マルチリージョンパターンのレイテンシーを気にしてマルチアカウントパターンを選択しました。

このように、利用する状況や要件によって選択するパターンが分かれるため、その状況・要件に応じて皆様の方で最適な選択肢を選ばれることと思いますが、その検討に今回の記事が少しでもお役に立てば幸いです。

ただ、冒頭でも述べましたが、このハードリミットについては数多くの改善要望が上がっているようです。そのため、改善される可能性もあると思いますので、こう記事が不要になることを祈っております！

# We Are Hiring!

https://www.biprogy.com/recruit/recruiting/