アクセス不能になったS3バケットのバケットポリシーを削除する方法

2022.05.25

こんにちは、八木です。

先日、以下のようなバケットポリシーを持ったS3バケットを作成しました。どうなったでしょうか?

{
	"Version": "2012-10-17",
	"Statement": [
		{
            "Action": "s3:*",
            "Effect": "Deny",
            "Resource": [
                "arn:aws:s3:::my-bucket",
                "arn:aws:s3:::my-bucket/*"
            ],
            "Principal": "*"
        }
	]
}

バケットがゾンビ化しました。

バケットの中身を見ることも
オブジェクト一覧画面   ポリシーを変更することも
バケットポリシー画面   バケットを削除することもできません。
バケット削除画面
バケットへの全ての操作が拒否されてしまっているためです。

AWSのIAM権限は、以下4つの中に1つでも明示的なDenyがあれば拒否されます。1

  • AWS OrganizationsのSCP
  • リソースベースポリシー
  • アイデンティティベースポリシー
  • アクセス許可の境界
  • このため、どんな権限を持ったIAMユーザを作ろうが、このバケットを操作することはできません。

    では、私はこのゾンビと化したバケットと生涯を共にするしかないのでしょうか?

    解決方法

    上述の通りIAM認証では打つ手なしです。

    しかし!!我々には!!!!ルートユーザがある!!!!!!(※最終手段)

    AWS公式ページにも記載のある通り、こうなってしまった場合はルートユーザを使用します。

    まずIAMユーザからログアウトし、ルートユーザでログインし直します。

    該当のバケットに行くと、IAMユーザでは表示できなかったバケットポリシーが、編集・削除が可能であることがわかります。今回はポリシーを削除します。
    バケットポリシー編集画面  

    続いてルートユーザからログアウトし、IAMユーザで再度ログインすると、問題なくバケットにアクセスすることができました。
    オブジェクト一覧画面   バケットポリシーも編集可能であるため、再度適切なポリシーを設定しましょう。

    反省

    雑にバケットポリシーを作ってしまったのが問題でした。
    DeleteBucketPolicyPutBucketPolicy をDenyしてしまったために、バケットポリシーの変更ができなくなってしまいました。
    ワイルドカードを含め、上記2つのアクションをDenyするときは、慎重に条件を指定しましょう。

    ご紹介した通り、ルートユーザを使用することでポリシーを削除することができます。しかし、会社管理のアカウントでルートユーザの使用制限などを行なっていた場合、管理者へ依頼し、作業してもらわなければなりません。余計な工数を取らせないためにも十分注意しましょう(自戒)

    予防策

    梶原さんにアドバイスいただきました。
    予防策の1つとして、バケットポリシーを編集するときはCloudFormationの管理下とし、ポリシーの変更はCloudFormation経由でのみ受け付けるようにする、という手法があります。
    グローバル条件コンテキストキーを用いて下記のようなConditionを設定します。

    AWSTemplateFormatVersion: "2010-09-09"
    
    Resources:
      S3Bucket:
        Type: "AWS::S3::Bucket"
        Properties:
          BucketName: "my-s3-bucket"
          PublicAccessBlockConfiguration:
            BlockPublicAcls: true
            BlockPublicPolicy: true
            IgnorePublicAcls: true
            RestrictPublicBuckets: true
      S3BucketPolicy:
        Type: "AWS::S3::BucketPolicy"
        Properties:
          Bucket: !Ref S3Bucket
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Deny"
                Principal: "*"
                Action:
                  - "s3:DeleteBucketPolicy"
                  - "s3:PutBucketPolicy"
                Resource:
                  - !Sub "arn:aws:s3:::${S3Bucket}"
                Condition:
                  StringNotEquals:
                    "aws:CalledVia":
                      - "cloudformation.amazonaws.com"

    また、特定のIPアドレスからも操作を受け付けたいといった場合には、Conditionを以下のように変更します。

                Condition:
                  StringNotEquals:
                    "aws:CalledVia":
                      - "cloudformation.amazonaws.com"
                  NotIpAddress: 
                    "aws:SourceIp": 
                      - "192.0.2.0/24"

    こうすることで、「CloudFormationからの変更でない、かつ特定IPアドレスからの変更でない場合、ポリシーの変更を拒否する」(CloudFormationからの変更、または特定IPアドレスからの変更の場合は、(明示的な)拒否をしない)という設定ができます。
    このようにポリシー変更アクションのDeny条件において、CloudFormationによる変更を除外するように設定しておくことで、今回のような事故は防ぐことができます。

    ただし気をつけていただきたいのですが、CloudFormationによる管理を行なっても、以下のように別のステートメントで明示的にDenyをしてしまうと、CloudFormationによる変更もできなくなってしまいます。

      S3BucketPolicy:
        Type: "AWS::S3::BucketPolicy"
        Properties:
          Bucket: !Ref S3Bucket
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Deny"
                Principal: "*"
                Action:
                  - "s3:DeleteBucketPolicy"
                  - "s3:PutBucketPolicy"
                Resource:
                  - !Sub "arn:aws:s3:::${S3Bucket}"
                Condition:
                  StringNotEquals:
                    "aws:CalledVia":
                      - "cloudformation.amazonaws.com"
                      Statement:
              - Effect: "Deny"
                Principal: "*"
                Action:
                  - "*"
                Resource:
                  - !Sub "arn:aws:s3:::${S3Bucket}"

    複数のステートメントはORで評価されますが、Denyが優先されるため、2つ目のステートメントにより拒否されます。

    CloudFormationで管理したからといって、完全な予防にはなりません。くれぐれもバケットポリシーは慎重に設定しましょう!!

    【余談】どうしてこうなった?

    勉強を兼ねてAmazon S3 アクセスポイントを触っていました。

    公式ドキュメントを参考に、S3バケットがアクセスポイントを介してのみ操作を受け付けるように、バケットポリシーを設定しました。

    {
        "Version": "2012-10-17",
        "Statement" : [
            {
                "Effect": "Allow",
                "Principal" : { "AWS": "*" },
                "Action" : "s3:*",
                "Resource" : [
                    "arn:aws:s3:::my-bucket",
                    "arn:aws:s3:::my-bucket/*"
                ],
                "Condition": {
                    "StringEquals" : { "s3:DataAccessPointAccount" : "123456789012" }
                }
            }
        ]
    }

    しかし、CLIからS3へ直接アクセスしてみると、リクエストが通ってしまいました。

    $ aws s3 ls demo-bucket-151t49hg9
    2022-05-13 11:47:33          0 text1.txt
    2022-05-13 11:48:33          0 text2.txt
    2022-05-13 12:29:02          0 text3.txt

    あれ?と思って調べてみると、IAMユーザにs3:ListBucketの権限がついていました。 バケットポリシーで明示的な拒否が行われていないため、IAMポリシーによって許可されたようです。

    じゃあ IAMユーザの権限を変更するのめんどくさいし、バケットポリシーに明示的にDenyを入れちゃおう!と思いバケットポリシーを修正。

    {
        "Version": "2012-10-17",
        "Statement" : [
            {
                "Effect": "Allow",
                "Principal" : { "AWS": "*" },
                "Action" : "s3:*",
                "Resource" : [
                    "arn:aws:s3:::my-bucket",
                    "arn:aws:s3:::my-bucket/*"
                ],
                "Condition": {
                    "StringEquals" : { "s3:DataAccessPointAccount" : "123456789012" }
                }
            },
            {
                "Effect": "Deny",
                "Principal" : { "AWS": "*" },
                "Action" : "s3:*",
                "Resource" : [
                    "arn:aws:s3:::my-bucket",
                    "arn:aws:s3:::my-bucket/*"
                ],
                "Condition": {
                    "StringNotEquals" : { "s3:DataAccessPointAccount" : "123456789012" }
                }
            }
        ]
    }

    はい。ゾンビバケットの誕生です。

    アクセスポイントを経由した操作以外を全て拒否しました。バケット内のオブジェクトだけでなく、バケット自体の設定も拒否してしまったために、バケットポリシーの編集ができなくなりました。

    くれぐれもバケットポリシーは慎重に設定しましょう!!(大事なので2回目)

    参考

    特定IPアドレス以外を拒否設定したS3バケットをCloudFormationで更新したい時に追加する条件
    複数の否定条件を使ったS3バケットポリシーを正しく理解してますか?