はじめに
GitHub Actions から Terraform を apply する構成を作っていて、apply 用の IAM ロールを「最小権限」で組んでいました。S3 の権限も「使うものだけ」を意識して s3:GetBucket* のように列挙していたのですが、フル apply の途中でこんなエラーで止まりました。
AccessDenied: ... is not authorized to perform:
s3:GetAccelerateConfiguration on resource:
"arn:aws:s3:::aws-study-alb-logs-xxxx"
「GetBucket* で読み取り系はカバーしたつもりなのに、なんで GetAccelerateConfiguration で弾かれるんだ…?」としばらく悩みました。
先に結論:s3:GetBucket* は「GetBucket で始まるアクション」しか拾いません。s3:GetAccelerateConfiguration は名前が GetBucket で始まっていないので、ワイルドカードに当たっていませんでした。
環境
- Terraform v1.10 / AWS プロバイダ v6 系
- GitHub Actions から OIDC で apply ロールを引き受けて実行
- 対象に S3 バケット(ALB アクセスログ用など)を含む
- リージョン: ap-northeast-1(東京)
(S3 バケット周りの読み取り動作は provider の世代で変わることがあるので、バージョンを添えておきます。)
起きたこと
apply ロールの S3 権限を、こんな感じで「読み取りは GetBucket 系」と思って書いていました(抜粋・一部)。
actions = [
"s3:CreateBucket", "s3:DeleteBucket", "s3:ListBucket",
"s3:GetBucket*", "s3:PutBucket*",
"s3:GetObject", "s3:PutObject", "s3:DeleteObject",
# ...
]
resources = [
"arn:aws:s3:::aws-study-*",
"arn:aws:s3:::aws-study-*/*",
]
ところがフル apply の途中、provider が S3 バケットの状態を読みにいくところで s3:GetAccelerateConfiguration が拒否され、apply が失敗しました。
原因:動詞のワイルドカードは「前方一致」
IAM のアクション名のワイルドカードは、指定した文字列で始まるアクションにマッチします。s3:GetBucket* がマッチするのは、
s3:GetBucketPolicys3:GetBucketAcl-
s3:GetBucketPublicAccessBlock… など
といった GetBucket で始まるアクションだけです。
一方、今回弾かれたのは s3:GetAccelerateConfiguration。これは S3 の「転送高速化(Transfer Acceleration)」の設定を読むアクションですが、アクション名が GetBucket… ではなく GetAccelerate… から始まります。なので GetBucket* の前方一致には引っかかりません。
Terraform の AWS provider は、S3 バケットを作成したり refresh したりするときに、バケットの各種サブ設定(公開ブロック、暗号化、そして accelerate など)を読みにいきます。そのうちの1つが、自分のワイルドカードの“すき間”に落ちていた、というわけでした。「GetBucket* で読み取りは網羅できている」という思い込みが原因です。
対処:アクションは広げ、リソースで絞る
最小権限を「アクションを細かく列挙して絞る」方向で頑張ると、こういう取りこぼしがどうしても起きます。そこで方針を変えて、
- アクションは
s3:Get*/s3:Put*/s3:List*に広げる - そのかわり リソースは
aws-study-*に限定したままにする
という形にしました(変更点の抜粋)。
actions = [
"s3:CreateBucket", "s3:DeleteBucket", "s3:DeleteObject",
"s3:Get*", "s3:Put*", "s3:List*",
]
resources = [
"arn:aws:s3:::aws-study-*",
"arn:aws:s3:::aws-study-*/*",
]
これで apply は通りました。ポイントは、「最小権限=アクションを絞ること」だけではないところです。アクションを Get*/Put*/List* と広めにしても、リソースを自分のプロジェクトのバケット(aws-study-*)に限定していれば、「他人のバケットには触れない」という肝心の絞りは効きます。
ただし、これは「リソースで絞れば常に安全」という話ではありません。注意点が2つあります。
-
すべての S3 アクションがバケット ARN で絞れるわけではない。AWS のサービス認可リファレンスを見ると、アクションごとに指定できるリソースタイプが違い、中には
Resource: "*"を要求するものもあります。Get*/Put*/List*を使うときは、各アクションが自分の ARN で絞れるかを確認し、絞れないものは条件キーや別ステートメントで分離する、という検討が要ります。 -
s3:Put*は「書き込み」だけでなく設定変更系も含む(バケットポリシーや公開設定の変更など)。広げる前に「何が含まれるか」を一度見ておくと安心です。
今回は学習用途で範囲も aws-study-* に閉じているので広めにしましたが、本番では apply が通ったあとに CloudTrail や拒否ログで“実際に使われたアクション”を確認し、不要な権限を削っていくのが本筋だと思います。「広げて通す」で終わらせない、というのを次の宿題にしました。
学び
- IAM のアクション・ワイルドカードは 前方一致。
s3:GetBucket*はGetAccelerate…を拾わない。 - S3 のサブ設定系アクションは、名前が
GetBucket…で始まるとは限らない(GetAccelerateConfigurationなど)。 - Terraform provider は作成・refresh 時にバケットのサブ設定をまとめて読むので、読み取り権限の取りこぼしが apply で表面化する。
- 最小権限は「アクションを列挙して絞る」だけでなく、リソースで絞るのも有効。ただし 全アクションが ARN で絞れるわけではない(
Resource:"*"必須のものもある)ので、サービス認可リファレンスで確認する。 - 広げたら広げっぱなしにせず、使われた権限を確認して削るのが最小権限の本筋。
おわりに
「読み取りはカバーしたつもり」が一番あぶないな、と反省した一件でした😅 最小権限を頑張るほどワイルドカードの“すき間”を踏みやすいので、アクションの粒度・リソースの限定・あとからの削り込みをセットで考えるようになりました🙌
参考
- AWS公式: Amazon S3 のアクション・リソース・条件キー(アクションごとの指定可能リソースタイプ)
https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazons3.html - AWS公式: IAM ポリシーの Action 要素(ワイルドカード
*の扱い)
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_action.html - AWS公式: 最小権限の原則(必要な権限だけを付与し、見直す)
https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html