📌

IAM Policyのconditionを使ってS3のパスでGetObjectを制限したいが出来ない話

2023/10/30に公開

ご存知のように、Amazon S3 のオブジェクトへのアクセス権限は policy を書くことで制限できます。
今回、特定のユーザーに ReadOnlyAccess を付与しつつ、特定のオブジェクト読み取れないようにするポリシーを作成しようとしました。
この際、意外な躓きポイントがあったので共有します。

結果だけ知りたい方へ

次のようにallow listを書いても上手くいきません。全て deny になってしまいます。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:*"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Effect": "Deny",
      "Action": [
        "s3:*"
      ],
      "Resource": [
        "*"
      ],
      "Condition": {
        "StringNotEquals": {
          "s3:prefix": [
            "arn:aws:s3:::DOC-EXAMPLE-BUCKET/you-can-read-1.txt",
            "arn:aws:s3:::DOC-EXAMPLE-BUCKET/you-can-read-2.txt"
          ]
        }
      }
    }
  ]
}

なぜなら、GetObject アクションで使用できる条件キーに s3:prefix は入っていないからです。
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/list_amazons3.html

パスで制限したいなら、素直に Resource の中で allow list, deny list を書きましょう。


やりたかったこと

今回ポリシーを変更したい IAM には ReadOnlyAccess が付与されています。
ただ S3 の特定のオブジェクトにはアクセスしてほしくないので、deny のポリシーを追加することになりました。

このアクセスしてほしくないオブジェクトは複数あり、できれば allow list で書きたいという思惑があります。

試したこと

始めに、AWS は公式に IAM Policy Simulator を提供しており、実際の環境で試す前にこのシミュレーターでポリシーが正しく機能するか確かめることができます。
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_testing-policies.html

このシミュレーターを使って、ポリシーの試行錯誤をしました。

まず AWS IAM の評価論理について考えます。
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_evaluation-logic.html

最初は全て暗黙的に拒否されます。明示的な許可が評価されて、次に明示的な拒否が評価されます。

そのため、次のポリシーは上手く動作しません。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:DescribeJob",
        "s3:Get*",
        "s3:List*"
      ],
      "Resource": [
        "arn:aws:s3:::DOC-EXAMPLE-BUCKET/object-1.txt",
        "arn:aws:s3:::DOC-EXAMPLE-BUCKET/object-2.txt"
      ]
    },
    {
      "Effect": "Deny",
      "Action": [
        "s3:*"
      ],
      "Resource": [
        "*"
      ]
    }
  ]
}

また、このポリシーの他に ReadOnlyAccess が付与されているため、Resource を使って allow list で書くことはできないことが分かります。

では Condition を使った allow list はどうでしょうか。
冒頭に書いたポリシーを見てみましょう。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:*"
      ],
      "Resource": [
        "*"
      ]
    },
    {
      "Effect": "Deny",
      "Action": [
        "s3:*"
      ],
      "Resource": [
        "*"
      ],
      "Condition": {
        "StringNotEquals": {
          "s3:prefix": [
            "arn:aws:s3:::DOC-EXAMPLE-BUCKET/you-can-read-1.txt",
            "arn:aws:s3:::DOC-EXAMPLE-BUCKET/you-can-read-2.txt"
          ]
        }
      }
    }
  ]
}

論理評価的に問題は無さそうですし、一見このポリシーは期待した通りに動作するように見えます。
しかし、期待した通りに上手く動作しません。全てのパスで deny となってしまいます。

なぜなのか、色々とドキュメントを漁ってみた結果、GetObject アクションは s3:prefix で条件を指定できないことが分かりました。
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/list_amazons3.html

この Condition は常に真となってしまい、期待した動作をしません。

結論

s3:prefix を使って GetObject を制限できないことが分かりました。
意外と特定のアクションで使える条件キーが制限されていることに気づいてない人いるんじゃないでしょうか...?

また ReadOnlyAccess を付与していると、allow list 形式で書くことはできないことも分かりました。
仕方がないので最終的に次のように deny list でポリシーを書きました。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Action": [
        "s3:*"
      ],
      "Resource": [
        "arn:aws:s3:::DOC-EXAMPLE-BUCKET/dont-read-*",
      ]
    }
  ]
}
サイボウズ 生産性向上チーム 💪

Discussion