IAM ロール
概要
IAM Access Analyzer が IAM ロールの外部アクセスをどのように検出するかを検証する。
IAM ロールはグローバルリソースであり、信頼ポリシーにより外部プリンシパルからの AssumeRole を許可できる。Block Public Access のようなデフォルト保護は存在しない。信頼ポリシーに Principal: {"AWS": "*"} を設定するとパブリックアクセスとして検出され、特定の外部アカウントを設定するとクロスアカウントアクセスとして検出される。
IAM ロールの外部アクセス制御レイヤー
| レイヤー | IAM ロールの状況 |
|---|---|
| デフォルト保護 | なし |
| 予防(RCP) | CT.STS.PV.1(組織外プリンシパルによる AssumeRole を拒否) |
| Security Hub CSPM | 外部アクセスの直接チェックなし |
| IAM Access Analyzer | パブリック + クロスアカウントの到達可能性を分析 |
結果
- パブリックアクセスの検出: 信頼ポリシーに
Principal: {"AWS": "*"}を設定すると、isPublic: trueの finding が生成された。IAM ロールでもパブリックアクセスは検出される - クロスアカウントアクセスの検出: 組織外アカウントを信頼ポリシーに設定すると、
isPublic: falseの finding が生成された。principal に組織外アカウント ID が特定された - グローバルリソースの重複検出: IAM ロールはグローバルリソースのため全リージョンで finding が生成されるが、東京以外ではアーカイブルール(ArchiveRule-Iam-Role-Global-Suppress)により自動アーカイブされた
- 予防コントロール(CT.STS.PV.1)との連動: RCP 有効時は組織外からの AssumeRole がパブリック・クロスアカウントの両方でブロックされた。再スキャン後、
resourceControlPolicyRestrictionがAPPLICABLEに変わった - Principal の制約:
create-roleおよびupdate-assume-role-policyでは"Principal": "*"の形式はエラーになり、"Principal": {"AWS": "*"}の形式で設定する必要がある
検証環境
本記事のコマンドは、--profile 指定がない場合は Workload アカウントで実行する。IAM Access Analyzer の finding 確認は Audit アカウントで実行する。
検証の流れ
flowchart LR
A[1. 既存 finding の<br>確認] --> B[2. テスト用ロール<br>作成]
B --> C[3. パブリックアクセス<br>検出の確認]
C --> D[4. クロスアカウント<br>アクセス検出]
D --> E[5. 予防コントロール<br>との連動]
E --> F[6. クリーンアップ]
1. 既存 finding の確認
テスト用ロールを作成する前に、既存の IAM ロール finding を確認する。GitHub Actions OIDC 用ロールが検出されていることを確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resourceType": {"eq": ["AWS::IAM::Role"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit[
{
"id": "<GitHub Actions ロール finding ID>",
"resource": "arn:aws:iam::<Workload アカウント ID>:role/<GitHub Actions OIDC 用ロール名>",
"status": "ACTIVE"
}
]既存 finding の詳細を確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <GitHub Actions ロール finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": ["sts:AssumeRoleWithWebIdentity"],
"condition": {},
"isPublic": false,
"principal": {"Federated": "arn:aws:iam::<Workload アカウント ID>:oidc-provider/token.actions.githubusercontent.com"},
"resourceControlPolicyRestriction": "NOT_APPLICABLE"
}
}
],
"resource": "arn:aws:iam::<Workload アカウント ID>:role/<GitHub Actions OIDC 用ロール名>",
"status": "ACTIVE",
"resourceType": "AWS::IAM::Role",
"findingType": "ExternalAccess",
"resourceOwnerAccount": "<Workload アカウント ID>",
"id": "<GitHub Actions ロール finding ID>"
}以下を確認する。
isPublicがfalse(特定のプリンシパルへの信頼のため)principalに OIDC プロバイダーの ARN が特定されているactionがsts:AssumeRoleWithWebIdentity
2. テスト用ロールの作成
2 つのテスト用ロールを作成する。テスト用ロールにはアクセス許可ポリシーをアタッチしない。信頼ポリシーのみで IAM Access Analyzer の検出対象となる。AssumeRole しても何も操作できない状態であり、検証として安全である。
パブリックアクセス検証用ロール
信頼ポリシーに Principal: {"AWS": "*"} を設定する。まず自アカウントを信頼する形で作成し、その後 update-assume-role-policy で変更する。
create-role および update-assume-role-policy では "Principal": "*" の形式はエラーになる。"Principal": {"AWS": "*"} の形式であれば設定できる。公式ドキュメントでは両者は同等(equivalent)と記載されているが、IAM ロールの信頼ポリシーでは前者が受け付けられない。aws iam create-role \
--role-name iaa-iam-role-public-test \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::<Workload アカウント ID>:root"},
"Action": "sts:AssumeRole"
}
]
}'{
"Role": {
"RoleName": "iaa-iam-role-public-test",
"Arn": "arn:aws:iam::<Workload アカウント ID>:role/iaa-iam-role-public-test"
}
}aws iam update-assume-role-policy \
--role-name iaa-iam-role-public-test \
--policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": "*"},
"Action": "sts:AssumeRole"
}
]
}'
(出力なし)クロスアカウントアクセス検証用ロール
信頼ポリシーに組織外アカウントを設定する。
aws iam create-role \
--role-name iaa-iam-role-crossaccount-test \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::<組織外アカウント ID>:root"},
"Action": "sts:AssumeRole"
}
]
}'{
"Role": {
"RoleName": "iaa-iam-role-crossaccount-test",
"Arn": "arn:aws:iam::<Workload アカウント ID>:role/iaa-iam-role-crossaccount-test"
}
}3. パブリックアクセスの検出
パブリックアクセス検証用ロール(Principal: {"AWS": "*"})の finding を確認する。Principal: {"AWS": "*"} は全 AWS プリンシパルに AssumeRole を許可する設定であり、IAM Access Analyzer がパブリックアクセス(isPublic: true)として検出するかを確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["iaa-iam-role-public-test"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Audit[
{
"id": "<パブリックロール finding ID>",
"resource": "arn:aws:iam::<Workload アカウント ID>:role/iaa-iam-role-public-test",
"resourceType": "AWS::IAM::Role",
"status": "ACTIVE"
}
]finding の詳細を確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <パブリックロール finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": ["sts:AssumeRole"],
"condition": {},
"isPublic": true,
"principal": {"AWS": "*"},
"resourceControlPolicyRestriction": "NOT_APPLICABLE"
}
}
],
"resource": "arn:aws:iam::<Workload アカウント ID>:role/iaa-iam-role-public-test",
"status": "ACTIVE",
"resourceType": "AWS::IAM::Role",
"findingType": "ExternalAccess",
"resourceOwnerAccount": "<Workload アカウント ID>",
"id": "<パブリックロール finding ID>"
}以下を確認する。
isPublicがtrue(Principal: {"AWS": "*"}はパブリックアクセスとして検出される)principalが{"AWS": "*"}actionがsts:AssumeRole
組織外アカウントからの実際のアクセス確認
組織外アカウントからパブリックロールへの AssumeRole が成功することを確認する。
aws sts assume-role \
--role-arn arn:aws:iam::<Workload アカウント ID>:role/iaa-iam-role-public-test \
--role-session-name test-session \
--profile <組織外プロファイル> \
--region ap-northeast-1{
"Credentials": { "...": "..." },
"AssumedRoleUser": {
"Arn": "arn:aws:sts::<Workload アカウント ID>:assumed-role/iaa-iam-role-public-test/test-session"
}
}4. クロスアカウントアクセスの検出
クロスアカウントアクセス検証用ロールの finding を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["iaa-iam-role-crossaccount-test"]}}' \
--query 'findings[].{id:id,resource:resource,resourceType:resourceType,status:status}' \
--region ap-northeast-1 \
--profile Audit[
{
"id": "<クロスアカウントロール finding ID>",
"resource": "arn:aws:iam::<Workload アカウント ID>:role/iaa-iam-role-crossaccount-test",
"resourceType": "AWS::IAM::Role",
"status": "ACTIVE"
}
]finding の詳細を確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <クロスアカウントロール finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": ["sts:AssumeRole"],
"condition": {},
"isPublic": false,
"principal": {"AWS": "<組織外アカウント ID>"},
"resourceControlPolicyRestriction": "NOT_APPLICABLE"
}
}
],
"resource": "arn:aws:iam::<Workload アカウント ID>:role/iaa-iam-role-crossaccount-test",
"status": "ACTIVE",
"resourceType": "AWS::IAM::Role",
"findingType": "ExternalAccess",
"resourceOwnerAccount": "<Workload アカウント ID>",
"id": "<クロスアカウントロール finding ID>"
}以下を確認する。
isPublicがfalseprincipalに組織外アカウント ID が特定されているactionがsts:AssumeRoleresourceControlPolicyRestrictionがNOT_APPLICABLE(CT.STS.PV.1 無効時)
東京以外のリージョンでの確認
IAM ロールはグローバルリソースのため、東京以外のリージョンでも finding が生成される。ただし、アーカイブルール(ArchiveRule-Iam-Role-Global-Suppress)により自動アーカイブされていることを確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-2:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"resource": {"contains": ["iaa-iam-role-crossaccount-test"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-2 \
--profile Audit[
{
"id": "<ソウルリージョン finding ID>",
"resource": "arn:aws:iam::<Workload アカウント ID>:role/iaa-iam-role-crossaccount-test",
"status": "ARCHIVED"
}
]5. 予防コントロール(CT.STS.PV.1)との連動
CT.STS.PV.1 の有効化確認
CT.STS.PV.1 が有効であることを確認する。
aws controltower list-enabled-controls \
--target-identifier <対象 OU ARN> \
--query "enabledControls[?controlIdentifier=='arn:aws:controlcatalog:::control/aqnqv7jjgi2dtl6r1v12xglio'].{controlIdentifier:controlIdentifier,statusSummary:statusSummary}" \
--profile Master[]無効の場合は有効化する。
aws controltower enable-control \
--control-identifier arn:aws:controlcatalog:::control/aqnqv7jjgi2dtl6r1v12xglio \
--target-identifier <対象 OU ARN> \
--profile Master{
"arn": "<有効化コントロール ARN>",
"operationIdentifier": "<オペレーション ID>"
}組織外アカウントからの AssumeRole 確認
CT.STS.PV.1 が有効な状態で、組織外アカウントからクロスアカウントテスト用ロールへの AssumeRole を試み、RCP によりブロックされることを確認する。
aws sts assume-role \
--role-arn arn:aws:iam::<Workload アカウント ID>:role/iaa-iam-role-crossaccount-test \
--role-session-name test-session \
--profile <組織外プロファイル> \
--region ap-northeast-1An error occurred (AccessDenied) when calling the AssumeRole operation: ...パブリックロールへの AssumeRole も同様にブロックされることを確認する。
aws sts assume-role \
--role-arn arn:aws:iam::<Workload アカウント ID>:role/iaa-iam-role-public-test \
--role-session-name test-session \
--profile <組織外プロファイル> \
--region ap-northeast-1An error occurred (AccessDenied) when calling the AssumeRole operation: ...resourceControlPolicyRestriction の確認
リソースの再スキャンを実行して、RCP の反映を確認する。再スキャンにより旧 finding は RESOLVED になり、新しい finding ID で再生成される。
aws accessanalyzer start-resource-scan \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--resource-arn arn:aws:iam::<Workload アカウント ID>:role/iaa-iam-role-crossaccount-test \
--resource-owner-account <Workload アカウント ID> \
--region ap-northeast-1 \
--profile Audit
(出力なし)パブリックロールも再スキャンする。
aws accessanalyzer start-resource-scan \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--resource-arn arn:aws:iam::<Workload アカウント ID>:role/iaa-iam-role-public-test \
--resource-owner-account <Workload アカウント ID> \
--region ap-northeast-1 \
--profile Audit
(出力なし)再スキャン後の finding 一覧を確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}, "resource": {"contains": ["iaa-iam-role-crossaccount-test"]}}' \
--query 'findings[].{id:id,status:status}' \
--region ap-northeast-1 \
--profile Audit[
{
"id": "<新クロスアカウントロール finding ID>",
"status": "ACTIVE"
}
]finding 詳細を確認し、resourceControlPolicyRestriction が APPLICABLE に変わっていることを確認する。
aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <新クロスアカウントロール finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": ["sts:AssumeRole"],
"isPublic": false,
"principal": {"AWS": "<組織外アカウント ID>"},
"resourceControlPolicyRestriction": "APPLICABLE"
}
}
],
"status": "ACTIVE",
"id": "<新クロスアカウントロール finding ID>"
}aws accessanalyzer get-finding-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--id <新パブリックロール finding ID> \
--region ap-northeast-1 \
--profile Audit{
"findingDetails": [
{
"externalAccessDetails": {
"action": ["sts:AssumeRole"],
"isPublic": true,
"principal": {"AWS": "*"},
"resourceControlPolicyRestriction": "APPLICABLE"
}
}
],
"status": "ACTIVE",
"id": "<新パブリックロール finding ID>"
}6. クリーンアップ
CT.STS.PV.1 の無効化
aws controltower disable-control \
--control-identifier arn:aws:controlcatalog:::control/aqnqv7jjgi2dtl6r1v12xglio \
--target-identifier <対象 OU ARN> \
--profile Master{
"operationIdentifier": "<オペレーション ID>"
}テスト用ロールの削除
aws iam delete-role \
--role-name iaa-iam-role-public-test
(出力なし)aws iam delete-role \
--role-name iaa-iam-role-crossaccount-test
(出力なし)finding の確認
リソース削除後、IAM Access Analyzer の finding が自動的に RESOLVED になることを確認する。
aws accessanalyzer list-findings-v2 \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:<Audit アカウント ID>:analyzer/org-access-analyzer \
--filter '{"resource": {"contains": ["iaa-iam-role"]}}' \
--query 'findings[].{id:id,resource:resource,status:status}' \
--region ap-northeast-1 \
--profile Audit[]