この記事でわかること
- IAMポリシーの最小権限設計で「使いやすさ」と「セキュリティ」を両立する方法
- 条件キー(Condition)を使った細粒度のアクセス制御パターン
- Permission Boundaryを使って開発者に安全にIAM権限を委任する方法
- IAM Access Analyzerでポリシーの問題を自動検出する方法
実務での背景
IAMポリシーの設計でよくある失敗パターンがあります。
よくある失敗パターン:
-
"Action": "*"や"Resource": "*"を安易に使ってしまう - 「とりあえずAdministratorAccess」を付けてしまう
- 開発者にIAMフル権限を与えてしまい、意図しないリソース作成が起きる
- 手作業でポリシーを作るたびに過剰な権限になる
最小権限の原則は「理想論」ではなく、実際にインシデントを防ぐための実践的な設計です。
解決方法
設計の基本3原則
- 最小権限(Least Privilege): 必要最低限のアクションのみ許可
- タグベース制御: 自分が作ったリソースだけ操作可能
- Permission Boundary: 開発者が作れるIAMロールに上限を設ける
具体的な手順
Step 1: よく使うポリシーパターン集
パターン1: 特定のS3バケットにのみアクセス
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "S3BucketListAccess",
"Effect": "Allow",
"Action": "s3:ListBucket",
"Resource": "arn:aws:s3:::my-app-prod-data"
},
{
"Sid": "S3ObjectAccess",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "arn:aws:s3:::my-app-prod-data/*"
}
]
}
パターン2: 自分が作ったリソースのみ操作可能(タグ制御)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowEC2DescribeAll",
"Effect": "Allow",
"Action": [
"ec2:Describe*"
],
"Resource": "*"
},
{
"Sid": "AllowEC2ManageOwnedResources",
"Effect": "Allow",
"Action": [
"ec2:StartInstances",
"ec2:StopInstances",
"ec2:TerminateInstances"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/Owner": "${aws:username}"
}
}
},
{
"Sid": "RequireOwnerTagOnCreate",
"Effect": "Allow",
"Action": "ec2:RunInstances",
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:RequestTag/Owner": "${aws:username}"
}
}
}
]
}
⚠️
${aws:username}は IAMユーザーでのみ展開される変数です。AssumeRoleで操作する運用(推奨構成)では空文字列になり機能しません。ロールで同等の制御をしたい場合は後述の「ハマりポイント」を参照してください。
パターン3: MFA認証済みの場合のみ許可
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyWithoutMFA",
"Effect": "Deny",
"NotAction": [
"iam:CreateVirtualMFADevice",
"iam:EnableMFADevice",
"iam:GetUser",
"iam:ListMFADevices",
"iam:ListVirtualMFADevices",
"iam:ResyncMFADevice",
"sts:GetSessionToken"
],
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
]
}
Step 2: Permission Boundary の実装
開発者にIAMロールの作成権限を与えつつ、過剰な権限を付与できないように制限します。
// permission-boundary.json(開発者が作成するロールの上限)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*",
"dynamodb:*",
"lambda:*",
"cloudwatch:*",
"logs:*"
],
"Resource": "*"
},
{
"Effect": "Deny",
"Action": [
"iam:*",
"organizations:*",
"account:*"
],
"Resource": "*"
}
]
}
// developer-policy.json(開発者に与えるポリシー:Boundary強制部分の抜粋)
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"iam:CreateRole",
"iam:PutRolePolicy",
"iam:AttachRolePolicy"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"iam:PermissionsBoundary": "arn:aws:iam::ACCOUNT_ID:policy/DeveloperBoundary"
}
}
},
{
"Effect": "Allow",
"Action": [
"iam:GetRole",
"iam:ListRoles",
"iam:DeleteRole",
"iam:DetachRolePolicy",
"iam:DeleteRolePolicy"
],
"Resource": "*"
}
]
}
💡 ポイント:
iam:PutRolePolicy/iam:AttachRolePolicy側にもiam:PermissionsBoundary条件を付けることで、開発者が「Boundary無しのロールを後から作り替える」抜け道を塞げます。iam:DeleteRoleなどのライフサイクル系は Boundary 条件を付けると失敗するので別Statementに分けています。
Step 3: IAM Access Analyzer で問題を自動検出
# アナライザーの作成
# ※ --type ORGANIZATION は AWS Organizations の管理アカウント(または委任管理アカウント)でのみ実行可能。
# 単一アカウントで使う場合は --type ACCOUNT に変更してください。
aws accessanalyzer create-analyzer \
--analyzer-name "my-org-analyzer" \
--type ORGANIZATION
# 検出結果の確認
aws accessanalyzer list-findings \
--analyzer-arn arn:aws:access-analyzer:ap-northeast-1:123456789:analyzer/my-org-analyzer \
--filter '{"status": {"eq": ["ACTIVE"]}}' \
--query 'findings[*].[id,resourceType,resource,isPublic]' \
--output table
# ポリシーの検証(作成前にチェック)
aws accessanalyzer validate-policy \
--policy-type IDENTITY_POLICY \
--policy-document file://my-policy.json \
--query 'findings[*].[findingType,issueCode,learnMoreLink]' \
--output table
構成図
[組織管理者]
|
v
+------------------------+
| SCP (Service Control |
| Policy) |
| - 全アカウント共通制限 |
| - root操作の禁止など |
+------------------------+
|
v
[開発者アカウント]
|
+---> Permission Boundary(上限制限)
| |
v v
+------------------------+
| 開発者IAMユーザー/ロール|
| - developer-policy |
| - PermBoundary強制付与 |
+------------------------+
|
v
+------------------------+
| 開発者が作成するロール |
| - Permission Boundary |
| の範囲内に自動制限 |
+------------------------+
[IAM Access Analyzer]
定期スキャン → 外部公開リソース・過剰権限を検出 → SNS通知
ハマりポイント
❌ Denyポリシーの順序を誤解する
IAMポリシーの評価順序は「Explicit Deny > Allow > Default Deny」です。どこかで Deny があれば、どんなに Allow があっても拒否されます。
評価順序:
1. 明示的Deny (Explicit Deny) → 即座に拒否(他のポリシーは無視)
2. 明示的Allow → 許可
3. 暗黙のDeny (Default Deny) → デフォルト拒否
❌ aws:username はIAMロールでは使えない
"${aws:username}" の変数はIAMユーザーにのみ有効です。IAMロール(AssumeRole)では aws:userid を使います。IAMロールの aws:userid は AROAXXXXXXXXXX:session-name の形式になるため、セッション名を規則化(例:ユーザー名をセッション名にする)した上で照合します。
// IAMロール向け(roleセッション名で照合する例)
// ※ AssumeRole時に --role-session-name にユーザー名を指定する運用が前提
"StringLike": {
"aws:userid": "*:targeted-session-name"
}
❌ iam:PassRole を忘れる
EC2インスタンスにIAMロールをアタッチするには、実行者に iam:PassRole が必要です。「EC2の起動権限があるのにロールがアタッチできない」というときはこれが原因です。
{
"Effect": "Allow",
"Action": "iam:PassRole",
"Resource": "arn:aws:iam::*:role/ec2-*"
}
まとめ
| 手法 | 効果 | 対象 |
|---|---|---|
| リソースARN指定 | 対象リソースを明示的に限定 | 全ユーザー |
| タグベース制御 | 自分のリソースのみ操作可能 | 開発者 |
| MFA Condition | 重要操作にMFA必須化 | 管理者 |
| Permission Boundary | 開発者のIAM権限に上限を設定 | 組織 |
| Access Analyzer | 過剰権限の自動検出 | 運用チーム |
IAMは「とりあえず動けばいい」ではなく、設計の良し悪しがセキュリティインシデントに直結します。Permission Boundary + Access Analyzer の組み合わせが最も効果的な防御策です。