本業ではないところで題材として上がってきているので、ここにメモっておこうと思います。
環境
今回構築する環境は以下となります。
ユーザを1名作ります。このユーザは特定のタグのついたAWSリソースの閲覧や簡単な操作だけ実行でき、変更や削除などは出来ないユーザというイメージで作ります。
参考
権限回りをJSONで書いていくときは、この辺りの公式リファレンスが役に立ちそうだなと感じております。
AIに要件を食わせて出力されたJSONをベースにして、そこから公式のリファレンスを見て調整をしていくというのが最近は良いのかなと思っています。
用語
ABAC(Attribute-Based Access Control)
属性ベースのアクセスコントロール (ABAC,Attribute-Based Access Control) は、属性に基づいて許可を定義する認可戦略です。AWS はこれらの属性をタグと呼んでいます。タグは、IAM エンティティ (IAM ユーザーまたは IAM ロール) を含めた IAM リソース、および AWS リソースにアタッチできます。IAM プリンシパルに対して、単一の ABAC ポリシー、または少数のポリシーのセットを作成できます。プリンシパルのタグがリソースタグと一致したら操作を許可する ABAC ポリシーを設計できます。
RBAC(Role-Based Access Control)
RBAC(Role-Based Access Control、ロールベースアクセス制御)は、システム内のユーザーの役割(ロール)に応じてアクセスとアクションを割り当てるセキュリティモデルです。同じロールを付与されたすべてのユーザーは同じ権限セットを持ち、異なるロールを付与されたユーザーは異なる権限を持つ。
RBACの課題
-
役割の爆発(Role Explosion)
組織が特定のアクセスニーズに対応するために多くの役割を作成しすぎた場合に起こり、複雑さと混乱につながります。新しい役割が追加されるにつれて、その管理はますます難しくなり、RBACシステムの有効性が損なわれる可能性がある。 -
過剰な権限付与
役割の爆発に伴い、ユーザーが業務に必要な分以上のデータやリソースにアクセスできるようになる可能性(Permission Gap)が高くなる。結果セキュリティの脆弱性につながる。- 余談ですが2021年頃、OWASP Top10でアクセス制御の不備というものがに1位になったようです。
それくらいRBACでの管理には難儀しているということですね。
- 余談ですが2021年頃、OWASP Top10でアクセス制御の不備というものがに1位になったようです。
-
許可のギャップ(Permission Gap)
- 付与された権限(Granted Permissions): 時間とともに増加(右肩上がり)する傾向
- 使用された権限(Used Permissions): 実際に業務で使う権限は限定的
- この2つの差(ギャップ)が攻撃者にとっての「遊び場(Attack Surface)」となり、侵害された際のリスクを高める
ABACで解決できること
1. 「役割の爆発(Role Explosion)」の解消
RBACでは、「営業部」かつ「課長」かつ「東京支社」のような細かい条件が増えるたびに新しいロールを作成する必要がありましたが、ABACでは単一のポリシーでこれらをカバーできる
-
ポリシーの集約:
「ユーザーの部署タグと、リソースの部署タグが一致する場合のみアクセスを許可する」というポリシーを1つ定義するだけで、何百もの部署に対応可能です -
管理コストの削減:
新しいプロジェクトや部署が発足しても、新たなロールを作成する必要はなく、ユーザーやリソースに適切なタグ(属性)を付与するだけで即座にアクセス制御が適用される
2. 「許可のギャップ(Permission Gap)」の最小化とセキュリティ向上
-
動的な権限評価:
ABACは「誰であるか(Who)」だけでなく、「どのような状況か(Context)」を含めたきめ細やかな制御が可能。これにより、静的なロール付与では避けられなかった「使われない過剰な権限」を排除することが出来る。
3. スケーラビリティと柔軟性の確保
-
自動的な保護:
新規作成されたリソース(S3バケットやEC2インスタンス)に適切なタグ(例: env: dev)を付けるだけで、既存のABACポリシーが自動的に適用される。ポリシー自体を修正することなく、保護対象をスケールさせることが可能になる。
端的に言うと
RBACをより細やかに制御するために生まれたのがABAC
環境用意・ハンズオン
EC2,S3,RDS等のAWSリソース準備
大阪リージョンでリソースを準備していきたいと思います。
※リージョンはどこでも大丈夫です。
VPC
dev環境用のVPCやサブネット。
あくまで勉強用なので、subnet等のリソースも一気に作ります。
名前タグの自動生成のところにdevと書きつつ、tagもenv:devと記述しておきます。
vpc endpointはいらないので省いておきます。
作成出来たことを確認します。
同様にprd環境も作ってきます。
env:prdと記載することを忘れないようにしましょう。


EC2
VPCで作成したsubnetに適当に作ります。
private-subnetに作ります。作成したらすぐに停止します。起動せずで問題ないので。


S3
tagにenv:devとenv:prdを付けているバケットを2つ作っておきます。
併せてABACを有効化しておきます。
これを有効化しておかないと、S3でABACが有効化されずにエラーを吐かれて頭を抱えることになります(1敗)

IAM policyの準備
dev環境で限られたことしかできないユーザ向けのpolicyを作成していこうと思います。
policy内の以下の記載がABACで制御することを意味しています。
ResourceTag/env": "${aws:PrincipalTag/env}"
dev-ec2-general-employee
あまりABACで制御できる部分が無かったのですが、ec2のstartをABACで許可するようにしています。
restartやstop、terminateはABAC以前にそもそも許可をしていません。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowDescribeEC2",
"Effect": "Allow",
"Action": "ec2:Describe*",
"Resource": "*"
},
{
"Sid": "AllowEC2ActionsBasedOnUserTags",
"Effect": "Allow",
"Action": [
"ec2:StartInstances"
],
"Resource": "arn:aws:ec2:*:535002847634:instance/*",
"Condition": {
"StringEquals": {
"ec2:ResourceTag/env": "${aws:PrincipalTag/env}"
}
}
},
{
"Sid": "AllowCloudWatchReadMetrics",
"Effect": "Allow",
"Action": [
"cloudwatch:GetMetricData",
"cloudwatch:GetMetricStatistics",
"cloudwatch:ListMetrics"
],
"Resource": "*"
},
{
"Sid": "AllowCloudWatchDescribeAlarms",
"Effect": "Allow",
"Action": [
"cloudwatch:DescribeAlarms",
"cloudwatch:DescribeAlarmsForMetric"
],
"Resource": "*"
},
{
"Sid": "AllowHealthDescribeEvents",
"Effect": "Allow",
"Action": [
"health:DescribeEvents",
"health:DescribeEventDetails",
"health:DescribeAffectedEntities"
],
"Resource": "*"
},
{
"Sid": "AllowComputeOptimizerReadOnly",
"Effect": "Allow",
"Action": [
"compute-optimizer:GetEnrollmentStatus",
"compute-optimizer:GetEnrollmentStatusesForOrganization",
"compute-optimizer:GetRecommendationSummaries"
],
"Resource": "*"
}
]
}
dev-vpc-general-employee
こちらも下手なことが出来ないように、SGの作成だけABACで制御を掛けてます。ユーザが持っているタグが、環境の持っているタグと一致した時に作成が可能です。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowDescribeAllVPCResources",
"Effect": "Allow",
"Action": [
"ec2:DescribeVpcs",
"ec2:DescribeSubnets",
"ec2:DescribeRouteTables",
"ec2:DescribeSecurityGroups",
"ec2:DescribeNetworkAcls"
],
"Resource": "*"
},
{
"Sid": "AllowCreateSGWithPrincipalTag",
"Effect": "Allow",
"Action": [
"ec2:CreateSecurityGroup"
],
"Resource": [
"arn:aws:ec2:*:*:security-group/*"
],
"Condition": {
"StringEquals": {
"aws:RequestTag/env": "${aws:PrincipalTag/env}"
}
}
},
{
"Sid": "EnableTaggingDuringSGCreation",
"Effect": "Allow",
"Action": [
"ec2:CreateTags"
],
"Resource": [
"arn:aws:ec2:*:*:security-group/*"
],
"Condition": {
"StringEquals": {
"ec2:CreateAction": [
"CreateSecurityGroup"
]
}
}
},
{
"Sid": "RestrictSGCreationToMatchingVPC",
"Effect": "Allow",
"Action": [
"ec2:CreateSecurityGroup"
],
"Resource": "arn:aws:ec2:*:*:vpc/*",
"Condition": {
"StringEquals": {
"ec2:ResourceTag/env": "${aws:PrincipalTag/env}"
}
}
},
{
"Sid": "AllowInternetMonitorReadOnly",
"Effect": "Allow",
"Action": [
"internetmonitor:ListMonitors",
"internetmonitor:GetMonitor",
"internetmonitor:GetHealthEvent",
"internetmonitor:ListHealthEvents"
],
"Resource": "*"
}
]
}
dev-s3-general-employee
S3バケットの中に入っているデータ一覧を表示させたり、バケット内のデータのダウンロード可否をABACで制御しています。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowListAllBuckets",
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets",
"s3:ListAccessPoints",
"s3:GetAccountPublicAccessBlock"
],
"Resource": "*"
},
{
"Sid": "AllowReadBucketConfigurationWithMatchingTag",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:GetBucketLocation",
"s3:GetBucketVersioning",
"s3:GetBucketAbac",
"s3:GetBucketTagging",
"s3:ListBucketVersions",
"s3:GetBucketMetadataTableConfiguration",
"s3:GetEncryptionConfiguration",
"s3:GetIntelligentTieringConfiguration",
"s3:GetBucketLogging",
"s3:GetBucketNotification",
"s3:GetAccelerateConfiguration",
"s3:GetBucketObjectLockConfiguration",
"s3:GetBucketRequestPayment",
"s3:GetBucketWebsite",
"s3:GetBucketCORS",
"s3:GetBucketPolicy",
"s3:GetBucketPolicyStatus",
"s3:GetBucketPublicAccessBlock",
"s3:GetBucketAcl",
"s3:GetBucketOwnershipControls",
"s3:GetReplicationConfiguration",
"s3:GetLifecycleConfiguration",
"s3:GetInventoryConfiguration",
"s3:GetAnalyticsConfiguration",
"s3:GetMetricsConfiguration"
],
"Resource": "arn:aws:s3:::*",
"Condition": {
"StringEquals": {
"aws:ResourceTag/env": "${aws:PrincipalTag/env}"
}
}
},
{
"Sid": "AllowReadObjectsWithMatchingTag",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:GetObjectTagging",
"s3:GetObjectVersionTagging",
"s3:GetObjectAttributes",
"s3:GetObjectRetention",
"s3:GetObjectLegalHold"
],
"Resource": "arn:aws:s3:::*/*",
"Condition": {
"StringEquals": {
"s3:ExistingObjectTag/env": "${aws:PrincipalTag/env}"
}
}
},
{
"Sid": "AllowReadAccessPointWithMatchingTag",
"Effect": "Allow",
"Action": [
"s3:GetAccessPoint",
"s3:GetAccessPointPolicy",
"s3:GetAccessPointPolicyStatus",
"s3:GetAccessPointConfigurationForObjectLambda",
"s3:GetAccessPointForObjectLambda",
"s3:GetAccessPointPolicyForObjectLambda",
"s3:GetAccessPointPolicyStatusForObjectLambda"
],
"Resource": [
"arn:aws:s3:ap-northeast-3:535002847634:accesspoint/*",
"arn:aws:s3-object-lambda:ap-northeast-3:535002847634:accesspoint/*"
],
"Condition": {
"StringEquals": {
"aws:ResourceTag/env": "${aws:PrincipalTag/env}"
}
}
},
{
"Sid": "AllowReadS3TablesMetadata",
"Effect": "Allow",
"Action": [
"s3tables:GetTable",
"s3tables:GetTableBucket",
"s3tables:GetNamespace",
"s3tables:ListTables",
"s3tables:ListNamespaces"
],
"Resource": "*"
},
{
"Sid": "DenyWriteAndDeleteOperations",
"Effect": "Deny",
"Action": [
"s3:PutObject",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"s3:PutObjectAcl",
"s3:PutObjectTagging",
"s3:DeleteObjectTagging",
"s3:RestoreObject",
"s3:AbortMultipartUpload",
"s3:PutBucketAbac",
"s3:PutBucketTagging",
"s3:PutBucketVersioning",
"s3:CreateBucketMetadataTableConfiguration",
"s3:DeleteBucketMetadataTableConfiguration",
"s3:UpdateBucketMetadataJournalTableConfiguration",
"s3:UpdateBucketMetadataInventoryTableConfiguration",
"s3:PutEncryptionConfiguration",
"s3:PutIntelligentTieringConfiguration",
"s3:PutBucketLogging",
"s3:PutBucketNotification",
"s3:PutAccelerateConfiguration",
"s3:PutBucketObjectLockConfiguration",
"s3:PutBucketRequestPayment",
"s3:PutBucketWebsite",
"s3:PutBucketCORS",
"s3:PutBucketPolicy",
"s3:PutBucketPublicAccessBlock",
"s3:PutBucketAcl",
"s3:PutBucketOwnershipControls",
"s3:PutReplicationConfiguration",
"s3:PutLifecycleConfiguration",
"s3:PutInventoryConfiguration",
"s3:PutAnalyticsConfiguration",
"s3:PutMetricsConfiguration",
"s3:DeleteBucketPolicy",
"s3:DeleteBucketWebsite",
"s3:CreateAccessPoint",
"s3:DeleteAccessPoint",
"s3:PutAccessPointPolicy",
"s3:DeleteAccessPointPolicy",
"s3:CreateAccessPointForObjectLambda",
"s3:DeleteAccessPointForObjectLambda",
"s3:PutAccessPointPolicyForObjectLambda",
"s3:DeleteAccessPointPolicyForObjectLambda"
],
"Resource": "*"
}
]
}
IAM Userの準備
generalemployeeというユーザを作成して、そこに上記3つのpolicyをアタッチします。
タグにenv:devという設定も併せて入れておきます。
この設定と、各種policyを照らし合わせてAWSリソースと合致していれば指定の操作が出来るという事になりますね。
動作確認
作成したgeneralemployeeでログインします。
EC2周り
一覧でEC2が表示されていることがわかります。
詳細を見ていくとdevだろうがprdだろうが表示されていると思います。
この辺りはpolicyの以下の部分がAllowとなっていることが要因になります。Resouceを"*"にしている為です。
※cloudwatchやalarmはここのjsonではないですが。
{
"Sid": "AllowDescribeEC2",
"Effect": "Allow",
"Action": "ec2:Describe*",
"Resource": "*"
},
ABACのこの記載が正しく機能するかを確認していきましょう。
{
"Sid": "AllowEC2ActionsBasedOnUserTags",
"Effect": "Allow",
"Action": [
"ec2:StartInstances"
],
"Resource": "arn:aws:ec2:*:535002847634:instance/*",
"Condition": {
"StringEquals": {
"ec2:ResourceTag/env": "${aws:PrincipalTag/env}"
}
}
},
まずenv:devというタグが付いているEC2インスタンスであるdev-ec2に対して起動を試みます。

起動できましたね。これはユーザが持つタグとEC2が持つタグ"env"が一致していることによります。

一方でprd-ec2を起動してみようとします。

エラーが出力されました。これはユーザが持つタグとEC2が持つタグが一致しない為ですね。
本番のEC2のタグがenv:prdとなれば、起動できてしまいますが、そもそもアクセスしているこのユーザにはその権限がありませんので、起動する手立てはありません。

env:devというタグが付いているEC2であってもstart以外のstop,restart等は実行できません。

S3周り
まず、tagが異なっているS3の場合はABACが機能して中身を見ることが出来ません。
次にS3自体にenv:devというタグをつけている場合、バケットの中身を確認することが出来ます。
試しにRootアカウントでデータをアップロードしてみます。
tagを付ける方のデータについてはenv:devというものを付与したうえでアップロードをします。

元のユーザに戻ります。
tagを付けずにアップロードしたデータに対してダウンロード操作をしてみます。

タグをつけているデータに対してダウンロード操作をしてみます。

エクスプローラが開きダウンロードが出来ることがわかります。
S3の場合、バケットそのものに対してとその中にあるデータに対してABAC制御を掛けることが出来るようですね。















