IAM の権限境界で AccessDenied になるときの確認ポイント ── アイデンティティポリシーと境界の「両方で許可」を見る

目次

概要

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 があれば、許可は無効になります。逆に、ユーザーやロールから権限境界を外すと、できる操作が増えることがあります。

有効な権限は 2 つのポリシーの重なり。外れた操作はメッセージで拒否元が分かる
有効な権限は 2 つのポリシーの重なり。外れた操作はメッセージで拒否元が分かる
クラウドおさる

当番表(アイデンティティポリシー)と鍵束の上限(権限境界)、両方に載っている鍵だけが使えるのでござる。当番表に書き足しても、上限に無い鍵はやはり使えぬのでござるな。

エラーメッセージで「どこで拒否されたか」を見る

多くの 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 を満たしているか
3SCPアカウントが AWS Organizations の組織のメンバーなら、組織の SCP でアクションが許可されているか(組織の管理者に確認する)
4リソースベースのポリシーS3 のバケットポリシーなどに、そのプリンシパルへの Deny が無いか

権限境界はアイデンティティベースのポリシーの上限を決めるもので、SCP やリソースベースのポリシーの代わりにはなりません。どれか 1 つのポリシーで明示的に拒否されれば、ほかで許可していてもリクエストは拒否されます。

なお、同じアカウントの中では、リソースベースのポリシーがロールセッションの ARN に直接アクセスを許可している場合、その許可は権限境界の暗黙的な拒否に制限されません。一方、リソースベースのポリシーでロールの ARN に許可を与えた場合は、権限境界の暗黙的な拒否に制限されます。バケットポリシーで許可しているのに拒否される、またはその逆のときは、Principal にどちらの ARN を書いたかも確かめます。

クラウドおさる

エラーメッセージが教えてくれる原因は 1 つだけでござる。境界を直して別の文言に変わったら、次の扉に進んだ合図。最初のメッセージだけで「境界が全部悪い」と決めつけないことでござるぞ。

ポリシーの変更が反映されるまで

IAM は結果整合性のモデルを採用しているため、ポリシーを変更してから、すべてのエンドポイントに反映されるまでに時間がかかることがあります。直した直後にまだ拒否される場合は、少し待ってから試します。一時的な認証情報に割り当てられる権限は、リクエストのたびに評価されます。そのため、ロールを引き受けた既存のセッションにも、ロールのポリシーや権限境界の変更はセッションを取り直さずに反映されます(反映まで数分かかることがあります)。

フリーランス案件
クラウドおさる
クラウドキャリアフリーランス

AWS の実務経験があるなら、単価を上げにいく

AWS に特化したフリーランスエージェントです。以下は取り扱い案件の一例です。設計・SRE・セキュリティ・生成 AI の領域で、専門を掛け合わせるほど単価が上がります。

横にスクロールできます。掲載時点の情報のため、募集が終了している場合があります。条件の近い案件をご紹介しますので、お気軽にご相談ください。

実践

権限境界 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-policypb-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-boundaryAWS 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-boundaryAction に 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-role の「許可」タブ(許可ポリシーに pb-lab-identity-policy、許可の境界に pb-lab-boundary が設定されている)
pb-lab-role の「許可」タブ(許可ポリシーに pb-lab-identity-policy、許可の境界に pb-lab-boundary が設定されている)

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

許可の境界 pb-lab-boundary を展開した状態(Action が s3:ListBucket・s3:GetObject・s3:DeleteObject で、s3:PutObject が無い)
許可の境界 pb-lab-boundary を展開した状態(Action が s3:ListBucket・s3:GetObject・s3:DeleteObject で、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 text

arn: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.txt

1 と 2 は成功し、3 と 4 は AccessDenied になることを確認します。3 と 4 のエラーメッセージの最後の部分が違うことに注目します。

操作結果メッセージの最後の部分拒否したポリシー
3. PutObjectAccessDeniedbecause no permissions boundary allows the s3:PutObject action権限境界(pb-lab-boundary に s3:PutObject が無い)
4. DeleteObjectAccessDeniedbecause no identity-based policy allows the s3:DeleteObject actionアイデンティティポリシー(pb-lab-identity-policy に s3:DeleteObject が無い)
CloudShell で 4 つの操作を実行した結果(ListBucket・GetObject は成功、PutObject は permissions boundary、DeleteObject は identity-based policy で拒否されている)
CloudShell で 4 つの操作を実行した結果(ListBucket・GetObject は成功、PutObject は permissions boundary、DeleteObject は identity-based policy で拒否されている)

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

クラウドおさる

「境界に無い」と「当番表に無い」で、門番の返事の言い回しが違うのでござる。メッセージの最後の一文を読めば、どちらの帳面を書き直せばよいかが分かるのでござるな。

手順 4: 権限境界に s3:PutObject を足す

アプリケーションに書き込みを許可するのが正しい状態だとして、権限境界を直します。

IAM コンソールに戻り、左側ナビゲーションの「ポリシー」を選択します。検索ボックスに pb-lab-boundary と入力し、表示されたポリシー名をクリックします。

「編集」をクリックし、ポリシーエディタで「JSON」を選択します。Action を次のように書き換えます。

"Action": ["s3:ListBucket", "s3:GetObject", "s3:DeleteObject", "s3:PutObject"],
pb-lab-boundary のポリシーエディタ(Action に s3:PutObject を足した状態)
pb-lab-boundary のポリシーエディタ(Action に 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 回目で書き込みが通りました。変更がまだ反映されていないときは、少し待ってから実行し直します。

CloudShell の実行結果(PutObject は成功して ETag が返り、DeleteObject は identity-based policy で拒否されたまま。一覧に data/new.txt と data/sample.txt がある)
CloudShell の実行結果(PutObject は成功して ETag が返り、DeleteObject は identity-based policy で拒否されたまま。一覧に data/new.txt と data/sample.txt がある)

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 → リソースベースのポリシー
  • ロールのポリシーや権限境界の変更は、ロールを引き受け直さなくても既存のセッションに反映される(反映まで時間がかかることがある)

参照先

フリーランス案件
クラウドおさる
クラウドキャリアフリーランス

AWS の実務経験があるなら、単価を上げにいく

AWS に特化したフリーランスエージェントです。以下は取り扱い案件の一例です。設計・SRE・セキュリティ・生成 AI の領域で、専門を掛け合わせるほど単価が上がります。

横にスクロールできます。掲載時点の情報のため、募集が終了している場合があります。条件の近い案件をご紹介しますので、お気軽にご相談ください。

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

株式会社ジェニュイン(https://genuine-pt.jp)の代表取締役.

2018年〜インフラエンジニアとしてキャリアをスタートし、オンプレミスのネットワーク・サーバ環境で3年半、クラウド環境で4年半の8年間エンジニアとして従事。
2021年に佐藤氏の創業した株式会社Luxyを引き継ぎ、代表に就任。
その後アガルートグループ内の株式会社ジェニュインと合併。
合併後同社代表に就任。

目次