概要
IAM ロールに付けたポリシーでは s3:PutObject を許可しているのに、アプリケーションから S3 に書き込むと AccessDenied が返る。ポリシーを何度見直しても間違いが見つからない。こうしたときに疑うのが、ロールに設定された権限境界(アクセス許可の境界)です。
権限境界は、IAM ユーザーやロールが持てる権限の上限を決めるポリシーです。開発者にロールの作成を任せるとき、作るロールに必ず権限境界を付けさせることで、権限昇格を防げます(IAM Permission Boundary (許可の境界)で開発者のロール作成時に権限昇格を防止する方法)。その一方で、境界の付いたロールを使う側からは、「ポリシーで許可したのに拒否される」という形で見えます。権限境界の付いたエンティティは、アイデンティティベースのポリシーと権限境界の両方で許可された操作しかできないからです。
この記事では、権限境界が付いたときの評価の仕組み、拒否されたときのエラーメッセージの見分け方、境界で解決しないときに次に見るポリシーの順番を整理します。実践では、権限境界の付いた IAM ロールを AWS CloudShell から引き受けて S3 を操作し、「境界で拒否される操作」と「アイデンティティポリシーで拒否される操作」をエラーメッセージで見分けます。そのあと権限境界を直し、拒否されていた操作が通ることを確かめます。

クラウドおさる猿山では、当番の猿が持てる鍵束の上限を長老会が先に決めているのでござる。当番表に「倉庫の鍵を渡す」と書いてあっても、上限に無い鍵は渡されぬ。今日は、その「上限に無いから開かない扉」を見つけて直すでござるよ。
この記事のメリット
- 権限境界が付いた IAM ロール・ユーザーの有効な権限が、どう決まるかを説明できるようになる
AccessDeniedのメッセージから、権限境界で拒否されたのか、アイデンティティポリシーで拒否されたのかを見分けられるようになる- 権限境界で解決しないときに、SCP・リソースベースのポリシーの順に確認する理由が分かる
- コンソールでロールの権限境界を確認し、境界のポリシーを直す操作ができるようになる
- CloudShell から IAM ロールを引き受けて、権限の効き方を試す方法が身につく
執筆者とキャラクター紹介
執筆者:土肥(とひ)
株式会社ジェニュインの代表であり元エンジニア。これまでオンプレミスと AWS 環境のインフラ業務を担当。AWS 認定資格の学習で得た知識を、ハンズオンとともに解説するのが得意。直近では新人の教育も行っており、これから技術を学ぶ方が納得できる解説を心がけています。基礎編・Cloud Practitioner の記事では、解説役としても登場します。


クラウドおさる



はじめましてでござる。弊猿はクラウドおさる。IT インフラが異常に発達した猿山から、雲に乗って地上の AWS を学びに降りてきたのでござる。



基礎編や Cloud Practitioner の記事では、とひさんに素朴な疑問をぶつける役でござるな。Security - Specialty などの記事では、要所でワンポイントや注意点をひとこと添えるでござる。



猿山にもデータセンターや監視当番があって、地上で起きることはだいたい猿山にも同じ仕組みがあるのでござる。例え話がつい猿山に寄るのは、弊猿の癖でござる。
技術解説
権限境界は「上限」で、それ自体は権限を与えない
権限境界は、IAM エンティティ(ユーザーまたはロール)に設定できる管理ポリシーです。AWS 管理ポリシーかカスタマー管理ポリシーを使い、アイデンティティベースのポリシーがそのエンティティに付与できる権限の上限を決めます。
境界のポリシーに Allow を書いても、それだけでは何もできるようになりません。実際に権限を与えるのは、エンティティに付けたアイデンティティベースのポリシー(インラインポリシー・管理ポリシー)です。権限境界が設定されたエンティティの有効な権限は、次の 2 つの共通部分になります。
| 操作 | アイデンティティベースのポリシー | 権限境界 | 結果 |
|---|---|---|---|
| A | 許可 | 許可 | 許可 |
| B | 許可 | 許可していない | 拒否 |
| C | 許可していない | 許可 | 拒否 |
| D | 許可 | 許可(ただし別の文で Deny) | 拒否 |
どちらか一方でも許可していない操作は拒否されます。どちらかのポリシーに明示的な Deny があれば、許可は無効になります。逆に、ユーザーやロールから権限境界を外すと、できる操作が増えることがあります。





当番表(アイデンティティポリシー)と鍵束の上限(権限境界)、両方に載っている鍵だけが使えるのでござる。当番表に書き足しても、上限に無い鍵はやはり使えぬのでござるな。
エラーメッセージで「どこで拒否されたか」を見る
多くの AWS サービスは、アクセス拒否のエラーメッセージに、拒否したポリシーの種類を含めます。メッセージはおおむね次の形です。
User: <拒否されたプリンシパルの ARN> is not authorized to perform: <アクション> on resource: <リソース> <理由>最後の「理由」の部分で、どのポリシーで止まったかが分かります。
| 理由の部分 | 意味 | 直す場所 |
|---|---|---|
because no permissions boundary allows the <アクション> action | 権限境界に、そのアクションを許可する Allow が無い(暗黙的な拒否) | 権限境界のポリシーに Allow を足す |
with an explicit deny in a permissions boundary | 権限境界の Deny に当たった(明示的な拒否)。ポリシーの ARN が付くこともある | 権限境界の Deny を見直す |
because no identity-based policy allows the <アクション> action | アイデンティティベースのポリシーに Allow が無い | ロール・ユーザーのポリシーに Allow を足す |
with an explicit deny in an identity-based policy | アイデンティティベースのポリシーの Deny に当たった | そのポリシーの Deny を見直す |
ロールを引き受けて操作した場合、メッセージの User: には、ロールそのものではなくロールセッションの ARN(arn:aws:sts::<アカウント ID>:assumed-role/<ロール名>/<セッション名>)が入ります。
注意したいのは、メッセージが原因を 1 つしか示さないことです。複数の種類のポリシーが同時に拒否している場合でも、メッセージに含まれるのはそのうち 1 種類だけです。メッセージに出た場所を直してもまだ拒否されるときは、ほかのポリシーも順に確かめます。また、サービスによってはこの形式のメッセージに対応していません。
境界で解決しないときに見る順番
AWS re:Post のナレッジセンターでは、権限境界を確かめてもエラーが解決しない場合、次に SCP(サービスコントロールポリシー)、その次にリソースベースのポリシーにそのエンティティへの拒否が含まれていないかを確かめる、という順番を示しています。
| 順番 | 確認するもの | 見るところ |
|---|---|---|
| 1 | 権限境界 | ロールの「許可」タブの権限境界。必要なアクションが Allow にあるか、Deny に当たっていないか |
| 2 | アイデンティティベースのポリシー | 同じアクションを Allow しているか。Condition を満たしているか |
| 3 | SCP | アカウントが AWS Organizations の組織のメンバーなら、組織の SCP でアクションが許可されているか(組織の管理者に確認する) |
| 4 | リソースベースのポリシー | S3 のバケットポリシーなどに、そのプリンシパルへの Deny が無いか |
権限境界はアイデンティティベースのポリシーの上限を決めるもので、SCP やリソースベースのポリシーの代わりにはなりません。どれか 1 つのポリシーで明示的に拒否されれば、ほかで許可していてもリクエストは拒否されます。
なお、同じアカウントの中では、リソースベースのポリシーがロールセッションの ARN に直接アクセスを許可している場合、その許可は権限境界の暗黙的な拒否に制限されません。一方、リソースベースのポリシーでロールの ARN に許可を与えた場合は、権限境界の暗黙的な拒否に制限されます。バケットポリシーで許可しているのに拒否される、またはその逆のときは、Principal にどちらの ARN を書いたかも確かめます。



エラーメッセージが教えてくれる原因は 1 つだけでござる。境界を直して別の文言に変わったら、次の扉に進んだ合図。最初のメッセージだけで「境界が全部悪い」と決めつけないことでござるぞ。
ポリシーの変更が反映されるまで
IAM は結果整合性のモデルを採用しているため、ポリシーを変更してから、すべてのエンドポイントに反映されるまでに時間がかかることがあります。直した直後にまだ拒否される場合は、少し待ってから試します。一時的な認証情報に割り当てられる権限は、リクエストのたびに評価されます。そのため、ロールを引き受けた既存のセッションにも、ロールのポリシーや権限境界の変更はセッションを取り直さずに反映されます(反映まで数分かかることがあります)。
実践
権限境界 pb-lab-boundary が付いた IAM ロール pb-lab-role を CloudShell から引き受け、S3 バケットの読み書きと削除を試します。権限境界で拒否される PutObject と、アイデンティティポリシーで拒否される DeleteObject を、エラーメッセージで見分けます。そのあと権限境界に s3:PutObject を足し、書き込みが通るようになることを確かめます。
前提条件
- リージョン:
ap-northeast-1(東京) - IAM・S3・CloudShell の操作権限(
sts:AssumeRoleを含む)を持つ IAM ユーザーまたはロールでコンソールにログイン済みであること
以下のリソースが作成済みであることを前提とします。
まだ用意していない場合は、「作り方の参考」の記事の手順で作れます。リソース名は、この記事の名前に読み替えてください。
| リソース種別 | リソース名 | 内容 | 作り方の参考 |
|---|---|---|---|
| S3 バケット | pb-lab-<アカウント ID> | 東京リージョン。オブジェクト data/sample.txt を 1 つ置いておく | Amazon S3 でバケットを作成しオブジェクトを管理する |
| IAM ポリシー(カスタマー管理) | pb-lab-identity-policy | pb-lab-<アカウント ID> に対する s3:ListBucket・s3:GetObject・s3:PutObject を許可 | AWS IAM でユーザー・ロール・ポリシーを作成しアクセス制御する |
| IAM ポリシー(カスタマー管理) | pb-lab-boundary | 権限境界に使う。pb-lab-<アカウント ID> に対する s3:ListBucket・s3:GetObject・s3:DeleteObject を許可 | 同上 |
| IAM ロール | pb-lab-role | 信頼されたエンティティはこのアカウント(同じアカウントのプリンシパルが引き受けられる)。許可ポリシーは pb-lab-identity-policy、権限境界は pb-lab-boundary | AWS STS で一時的な認証情報を発行しクロスアカウントアクセスする ※信頼するアカウントは自分のアカウント。権限境界を設定する |
2 つのポリシーの中身は次のとおりです(<アカウント ID> は自分のアカウント ID)。
pb-lab-identity-policy(アイデンティティベースのポリシー):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AppReadWrite",
"Effect": "Allow",
"Action": ["s3:ListBucket", "s3:GetObject", "s3:PutObject"],
"Resource": [
"arn:aws:s3:::pb-lab-<アカウント ID>",
"arn:aws:s3:::pb-lab-<アカウント ID>/*"
]
}
]
}pb-lab-boundary(権限境界):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "BoundaryS3ReadAndDelete",
"Effect": "Allow",
"Action": ["s3:ListBucket", "s3:GetObject", "s3:DeleteObject"],
"Resource": [
"arn:aws:s3:::pb-lab-<アカウント ID>",
"arn:aws:s3:::pb-lab-<アカウント ID>/*"
]
}
]
}アイデンティティポリシーは「読み書き」、権限境界は「読み取りと削除」を許可しています。両方で許可されているのは ListBucket と GetObject だけです。
このハンズオンで変更するリソースは以下のとおりです。
| リソース種別 | リソース名 | 内容 |
|---|---|---|
| IAM ポリシー | pb-lab-boundary | Action に s3:PutObject を足す |
| S3 オブジェクト | data/new.txt | 書き込めるようになったことの確認で作る |
S3 バケットに保存したオブジェクトには、ストレージ料金とリクエスト料金がかかります(このハンズオンの量ではごくわずかです)。IAM のロール・ポリシーには料金はかかりません。
手順 1: ロールの権限境界を確かめる
AWS マネジメントコンソールにログインし、リージョンが ap-northeast-1(東京)であることを確認します。
上部の検索バーに IAM と入力し、「IAM」を選択します。左側ナビゲーションの「ロール」を選択し、検索ボックスに pb-lab-role と入力して、表示されたロール名をクリックします。
「許可」タブを選択します。「許可ポリシー」に pb-lab-identity-policy が、その下の「許可の境界」(画面によっては「アクセス許可の境界」)に pb-lab-boundary が設定されていることを確認します。


「許可の境界」の pb-lab-boundary を展開すると、境界のポリシーの JSON を確認できます。s3:PutObject が含まれていないことを確認します。


手順 2: CloudShell からロールを引き受ける
コンソール上部のツールバーにある CloudShell のアイコンをクリックし、CloudShell を開きます。
自分のアカウント ID とバケット名を変数に入れ、pb-lab-role を引き受けて一時的な認証情報を取得します。
ACCOUNT=$(aws sts get-caller-identity --query Account --output text)
BUCKET=pb-lab-$ACCOUNT
read AK SK ST < <(aws sts assume-role \
--role-arn arn:aws:iam::$ACCOUNT:role/pb-lab-role \
--role-session-name pb-lab-session \
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text)ロールの認証情報で AWS CLI を実行する関数 as_role を用意します。as_role を付けたコマンドだけがロールで実行され、付けないコマンドはコンソールにログインしている自分の権限で実行されます。
as_role() {
( export AWS_ACCESS_KEY_ID=$AK AWS_SECRET_ACCESS_KEY=$SK AWS_SESSION_TOKEN=$ST
aws "$@" )
}
as_role sts get-caller-identity --query Arn --output textarn:aws:sts::<アカウント ID>:assumed-role/pb-lab-role/pb-lab-session と表示されれば、ロールを引き受けられています。一時的な認証情報の有効期限は既定で 1 時間です。途中で ExpiredToken が出たら、read AK SK ST ... からやり直します。
手順 3: 読み取り・書き込み・削除を試す
ロールで、バケットの一覧表示、読み取り、書き込み、削除を順に試します。
echo hello > note.txt
echo "== 1. ListBucket"; as_role s3api list-objects-v2 --bucket $BUCKET --query 'Contents[].Key'
echo "== 2. GetObject"; as_role s3api get-object --bucket $BUCKET --key data/sample.txt /tmp/sample.txt --query ContentLength
echo "== 3. PutObject"; as_role s3api put-object --bucket $BUCKET --key data/new.txt --body note.txt
echo "== 4. DeleteObject"; as_role s3api delete-object --bucket $BUCKET --key data/sample.txt1 と 2 は成功し、3 と 4 は AccessDenied になることを確認します。3 と 4 のエラーメッセージの最後の部分が違うことに注目します。
| 操作 | 結果 | メッセージの最後の部分 | 拒否したポリシー |
|---|---|---|---|
3. PutObject | AccessDenied | because no permissions boundary allows the s3:PutObject action | 権限境界(pb-lab-boundary に s3:PutObject が無い) |
4. DeleteObject | AccessDenied | because no identity-based policy allows the s3:DeleteObject action | アイデンティティポリシー(pb-lab-identity-policy に s3:DeleteObject が無い) |


PutObject はアイデンティティポリシーで許可しているのに拒否されました。境界に無いためです。DeleteObject は境界で許可しているのに拒否されました。アイデンティティポリシーに無いためです。どちらも「片方でしか許可されていない」操作です。



「境界に無い」と「当番表に無い」で、門番の返事の言い回しが違うのでござる。メッセージの最後の一文を読めば、どちらの帳面を書き直せばよいかが分かるのでござるな。
手順 4: 権限境界に s3:PutObject を足す
アプリケーションに書き込みを許可するのが正しい状態だとして、権限境界を直します。
IAM コンソールに戻り、左側ナビゲーションの「ポリシー」を選択します。検索ボックスに pb-lab-boundary と入力し、表示されたポリシー名をクリックします。
「編集」をクリックし、ポリシーエディタで「JSON」を選択します。Action を次のように書き換えます。
"Action": ["s3:ListBucket", "s3:GetObject", "s3:DeleteObject", "s3:PutObject"],

「次へ」をクリックし、「確認して保存」画面で「変更を保存」をクリックします。ポリシーの詳細画面に戻り、「許可」タブの JSON に s3:PutObject が入っていることを確認します。
手順 5: 書き込みが通ることを確かめる
CloudShell に戻り、同じセッションのまま書き込みと削除をもう一度試し、最後にバケットの中身を一覧にします。ロールを引き受け直す必要はありません。
echo "== 3. PutObject"; as_role s3api put-object --bucket $BUCKET --key data/new.txt --body note.txt
echo "== 4. DeleteObject"; as_role s3api delete-object --bucket $BUCKET --key data/sample.txt
echo "== 1. ListBucket"; as_role s3api list-objects-v2 --bucket $BUCKET --query 'Contents[].Key'3 の PutObject は成功して ETag が返り、4 の DeleteObject は引き続き because no identity-based policy allows the s3:DeleteObject action で拒否されることを確認します。一覧には data/new.txt と data/sample.txt の 2 つが表示されます。撮影時は、ポリシーを保存してから約 40 秒後に実行し、1 回目で書き込みが通りました。変更がまだ反映されていないときは、少し待ってから実行し直します。


DeleteObject は境界で許可していても、アイデンティティポリシーに無いので拒否されたままです。境界を広げても、ロールの権限そのものが増えるわけではありません。



試験では、「ポリシーで許可しているのに拒否される」状況で、境界・SCP・リソースベースのポリシーのどれが原因かを選ばせる観点が問われやすいでござる。境界は上限を決めるだけで権限は与えない、と言い切れるかが分かれ目でござるな。
まとめ
| 状況 | 確認するところ |
|---|---|
メッセージに because no permissions boundary allows | 権限境界のポリシーに、そのアクションの Allow を足す |
メッセージに because no identity-based policy allows | ロール・ユーザーのアイデンティティポリシーに Allow を足す(境界を広げても解決しない) |
メッセージに with an explicit deny in ... | メッセージに出たポリシーの Deny を見直す。ARN が付いていればそのポリシー |
| 境界を直しても拒否される | SCP、リソースベースのポリシーの順に確認する |
- 権限境界は、アイデンティティベースのポリシーが与えられる権限の上限を決める管理ポリシーで、それ自体は権限を与えない
- 権限境界が付いたエンティティは、アイデンティティベースのポリシーと権限境界の両方で許可された操作だけができる
- アクセス拒否のメッセージには、拒否したポリシーの種類が入る。ただし示されるのは 1 種類だけで、対応していないサービスもある
- ナレッジセンターが示す確認の順番は、権限境界 → SCP → リソースベースのポリシー
- ロールのポリシーや権限境界の変更は、ロールを引き受け直さなくても既存のセッションに反映される(反映まで時間がかかることがある)
参照先
- アクセス許可の境界が原因でアクセスが拒否される問題を解決する方法を教えてください。
- IAM エンティティのアクセス許可境界 – AWS Identity and Access Management
- ポリシーの評価論理 – AWS Identity and Access Management
- AWS エンフォースメントコードロジックがリクエストを評価してアクセスを許可または拒否する方法 – AWS Identity and Access Management
- アクセス拒否エラーメッセージをトラブルシューティングする – AWS Identity and Access Management
- IAM をトラブルシューティングする – AWS Identity and Access Management
- 一時的なセキュリティ認証情報のアクセス権限を無効にする – AWS Identity and Access Management
- Amazon S3 でのアクセス拒否 (403 Forbidden) エラーのトラブルシューティング – Amazon Simple Storage Service













