-
Notifications
You must be signed in to change notification settings - Fork 5
fix(security): narrow IAM Resource="*" on ECS/ECR/CloudFront/KMS/SES #103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1957279
eccfdf7
57fefbc
18138b0
2a67339
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -69,7 +69,11 @@ resource "aws_iam_policy" "compute" { | |
| Resource = "arn:aws:events:*:*:rule/cudly-*" | ||
| }, | ||
| { | ||
| Sid = "ECR" | ||
| # ECR repository-scoped actions: every action that takes a | ||
| # repository ARN is restricted to cudly-* repositories. This | ||
| # prevents the deploy SA from poisoning, deleting, or exfiltrating | ||
| # images in unrelated ECR repositories sharing the same account. | ||
| Sid = "ECRRepositoryScoped" | ||
| Effect = "Allow" | ||
| Action = [ | ||
| "ecr:BatchCheckLayerAvailability", | ||
|
|
@@ -81,7 +85,6 @@ resource "aws_iam_policy" "compute" { | |
| "ecr:DeleteRepository", | ||
| "ecr:DeleteRepositoryPolicy", | ||
| "ecr:DescribeRepositories", | ||
| "ecr:GetAuthorizationToken", | ||
| "ecr:GetDownloadUrlForLayer", | ||
| "ecr:GetLifecyclePolicy", | ||
| "ecr:GetRepositoryPolicy", | ||
|
|
@@ -96,28 +99,73 @@ resource "aws_iam_policy" "compute" { | |
| "ecr:UntagResource", | ||
| "ecr:UploadLayerPart", | ||
| ] | ||
| Resource = "arn:aws:ecr:*:*:repository/cudly-*" | ||
| }, | ||
| { | ||
| # ECR token endpoint — does not take a resource ARN and is | ||
| # account-wide by design. Required for `docker login` against ECR. | ||
| Sid = "ECRGetAuthorizationToken" | ||
| Effect = "Allow" | ||
| Action = ["ecr:GetAuthorizationToken"] | ||
| Resource = "*" | ||
| }, | ||
| { | ||
| Sid = "CloudFrontFrontend" | ||
| # CloudFront create + distribution-read actions don't take a | ||
| # resource ARN at the API level (a distribution's ARN is only | ||
| # known after create, and CreateFunction has no resource type | ||
| # per the AWS Service Authorization Reference). Read actions | ||
| # against distributions are also broad because terraform plan | ||
| # needs to enumerate resources. Mutating actions are gated | ||
| # below; function reads are scoped in CloudFrontFnRead. | ||
| Sid = "CloudFrontCreateAndRead" | ||
| Effect = "Allow" | ||
| Action = [ | ||
| "cloudfront:CreateDistribution", | ||
| "cloudfront:CreateFunction", | ||
| "cloudfront:DeleteDistribution", | ||
| "cloudfront:DeleteFunction", | ||
| "cloudfront:DescribeFunction", | ||
| "cloudfront:GetDistribution", | ||
| "cloudfront:GetDistributionConfig", | ||
| "cloudfront:GetFunction", | ||
| "cloudfront:ListTagsForResource", | ||
| "cloudfront:PublishFunction", | ||
| ] | ||
| Resource = "*" | ||
| }, | ||
| { | ||
| # CloudFront DescribeFunction/GetFunction support the | ||
| # function resource type per the AWS Service Authorization | ||
| # Reference, so scope them to CUDly-owned functions to match | ||
| # the ARN scoping used by CloudFrontFnMutate in | ||
| # policy_compute_b.tf. | ||
| Sid = "CloudFrontFnRead" | ||
| Effect = "Allow" | ||
| Action = [ | ||
| "cloudfront:DescribeFunction", | ||
| "cloudfront:GetFunction", | ||
| ] | ||
| Resource = "arn:aws:cloudfront::*:function/cudly-*" | ||
| }, | ||
| { | ||
| # CloudFront distribution mutations are restricted to distributions | ||
| # tagged Project=CUDly. The Terraform aws_cloudfront_distribution | ||
| # resource sets this tag at creation time (see modules/frontend/aws), | ||
| # so subsequent Update/Delete/Tag/Untag operations against | ||
| # CUDly-owned distributions succeed while attempts to mutate any | ||
| # third-party distribution sharing the account are denied. | ||
| # Function mutations live in policy_compute_b.tf — the function | ||
| # resource type does not support aws:ResourceTag per the AWS | ||
| # Service Authorization Reference, so it needs ARN scoping. | ||
| Sid = "CloudFrontMutateTaggedOnly" | ||
| Effect = "Allow" | ||
| Action = [ | ||
| "cloudfront:DeleteDistribution", | ||
| "cloudfront:TagResource", | ||
| "cloudfront:UntagResource", | ||
| "cloudfront:UpdateDistribution", | ||
| "cloudfront:UpdateFunction", | ||
| ] | ||
| Resource = "*" | ||
| Condition = { | ||
| StringEquals = { | ||
| "aws:ResourceTag/Project" = "CUDly" | ||
| } | ||
| } | ||
| }, | ||
| { | ||
| Sid = "CloudWatchAlarms" | ||
|
|
@@ -133,29 +181,58 @@ resource "aws_iam_policy" "compute" { | |
| Resource = "arn:aws:cloudwatch:*:*:alarm:cudly-*" | ||
| }, | ||
| { | ||
| Sid = "ECSFargate" | ||
| # ECS resource-scoped actions: cluster, service, and task ARNs | ||
| # all match cudly-* prefix. RegisterTaskDefinition cannot take a | ||
| # specific ARN at registration time and is split below. | ||
| # ecs:ListTasks does NOT support resource-level permissions per | ||
| # the AWS Service Authorization Reference; it lives in | ||
| # policy_compute_b.tf gated by an ecs:cluster condition. | ||
| Sid = "ECSFargateResourceScoped" | ||
| Effect = "Allow" | ||
| Action = [ | ||
| "ecs:CreateCluster", | ||
| "ecs:CreateService", | ||
| "ecs:DeleteCluster", | ||
| "ecs:DeleteService", | ||
| "ecs:DeregisterTaskDefinition", | ||
| "ecs:DescribeClusters", | ||
| "ecs:DescribeServices", | ||
| "ecs:DescribeTaskDefinition", | ||
| "ecs:ListTagsForResource", | ||
| "ecs:ListTasks", | ||
| "ecs:PutClusterCapacityProviders", | ||
| "ecs:StopTask", | ||
| "ecs:RegisterTaskDefinition", | ||
| "ecs:TagResource", | ||
| "ecs:UntagResource", | ||
| "ecs:UpdateCluster", | ||
| "ecs:UpdateService", | ||
| ] | ||
| Resource = [ | ||
| "arn:aws:ecs:*:*:cluster/cudly-*", | ||
| "arn:aws:ecs:*:*:service/cudly-*/*", | ||
| "arn:aws:ecs:*:*:task/cudly-*/*", | ||
| "arn:aws:ecs:*:*:task-definition/cudly-*:*", | ||
| ] | ||
|
Comment on lines
+190
to
+211
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: Yes, the AWS ECS ListTasks API supports filtering by container-instance. The 'containerInstance' parameter accepts the container instance ID or full ARN and limits results to tasks belonging to that container instance. This is documented in the official AWS ECS API reference. Citations:
🌐 Web query:
💡 Result: The AWS ecs:ListTasks action does not support resource-level permissions. In IAM policies, you must specify "Resource": "" (all resources) to grant permission for this action. This is indicated in the official AWS Service Authorization Reference for Amazon ECS, where List actions like ecs:ListTasks have no specific resource types listed in the "Resource types" column of the Actions table. Per AWS documentation: "If there is no value for this column [Resource types], you must specify all resources ("") to which the policy applies in the Resource element of your policy statement." Examples in AWS ECS IAM policy documentation consistently use "Resource": "" for ecs:ListTasks and ecs:List actions, often combined with conditions like "ArnEquals": {"ecs:cluster": "arn:..."} to restrict to specific clusters. ECS defines resource types such as cluster, service, task, container-instance, task-definition, etc., but ecs:ListTasks (a List access level action) is not scoped to any of them. Citations:
According to AWS service authorization documentation, List actions like 🤖 Prompt for AI Agents |
||
| }, | ||
| { | ||
| # ECS task-definition + tag-listing actions don't take a specific | ||
| # ARN at API level. RegisterTaskDefinition has no resource; | ||
| # DeregisterTaskDefinition + DescribeTaskDefinition take a | ||
| # task-definition ARN that we constrain to cudly-*. | ||
| # ListTagsForResource takes any ARN type but is read-only. | ||
| Sid = "ECSFargateAccountWide" | ||
| Effect = "Allow" | ||
| Action = [ | ||
| "ecs:RegisterTaskDefinition", | ||
| "ecs:ListTagsForResource", | ||
| ] | ||
| Resource = "*" | ||
| }, | ||
| { | ||
| Sid = "ECSFargateTaskDefinition" | ||
| Effect = "Allow" | ||
| Action = [ | ||
| "ecs:DeregisterTaskDefinition", | ||
| "ecs:DescribeTaskDefinition", | ||
| ] | ||
| Resource = "arn:aws:ecs:*:*:task-definition/cudly-*:*" | ||
| }, | ||
| { | ||
| Sid = "ELBFargate" | ||
| Effect = "Allow" | ||
|
|
@@ -199,38 +276,65 @@ resource "aws_iam_policy" "compute" { | |
| Resource = "*" | ||
| }, | ||
| { | ||
| # CI deploy SA configures email identities (verify domain, | ||
| # rotate DKIM, tag) but does NOT send mail — that path lives | ||
| # in the Lambda runtime role (modules/compute/aws/lambda). | ||
| # ses:SendEmail intentionally excluded here per CR pass-3 | ||
| # nitpick — adding it would let a leaked deploy SA exfiltrate | ||
| # via mail without unlocking any deploy capability. | ||
| Sid = "SES" | ||
| Effect = "Allow" | ||
| Action = [ | ||
| "ses:CreateEmailIdentity", | ||
| "ses:DeleteEmailIdentity", | ||
| "ses:GetEmailIdentity", | ||
| "ses:PutEmailIdentityDkimSigningAttributes", | ||
| "ses:SendEmail", | ||
| "ses:TagResource", | ||
| "ses:UntagResource", | ||
| ] | ||
| Resource = "*" | ||
| }, | ||
| { | ||
| # KMS asymmetric signing key for the CUDly OIDC issuer. | ||
| # Broad KMS management perms scoped to Describe/Tag/Alias | ||
| # actions required by terraform-aws-provider on key create | ||
| # and update. Resource is "*" because KMS key ARNs are only | ||
| # known after creation. | ||
| Sid = "KMSSigningKey" | ||
| # KMS create + read-only actions don't accept a key ARN at API | ||
| # level (key ARNs are only known after CreateKey). Read-only | ||
| # actions are also broad because terraform plan needs to | ||
| # enumerate key state. | ||
| # kms:CreateAlias is NOT here — it moved to KMSMutateTaggedOnly | ||
| # below (key-side check, tag-gated) and policy_compute_b.tf | ||
| # KMSAliasMutate (alias-side check, ARN-scoped). KMS evaluates | ||
| # alias actions against both the alias and the target key. | ||
| Sid = "KMSCreateAndRead" | ||
| Effect = "Allow" | ||
| Action = [ | ||
| "kms:CreateAlias", | ||
| "kms:CreateKey", | ||
| "kms:DeleteAlias", | ||
| "kms:DescribeKey", | ||
| "kms:DisableKey", | ||
| "kms:EnableKey", | ||
| "kms:GetKeyPolicy", | ||
| "kms:GetKeyRotationStatus", | ||
| "kms:ListAliases", | ||
| "kms:ListResourceTags", | ||
| ] | ||
| Resource = "*" | ||
| }, | ||
| { | ||
| # KMS mutating + destructive actions are gated on the key being | ||
| # tagged Project=CUDly. The CUDly OIDC signing key is tagged at | ||
| # creation by Terraform (see modules/security/aws/kms_signing.tf). | ||
| # Without this gate the deploy SA could schedule deletion or | ||
| # disable any KMS key in the account, causing denial of service | ||
| # for unrelated workloads. | ||
| # Alias mutations (CreateAlias/DeleteAlias/UpdateAlias) appear | ||
| # here because AWS KMS evaluates these actions against BOTH the | ||
| # alias and the target key resource — this statement covers the | ||
| # key-side check (tag-gated). The alias-side check lives in | ||
| # policy_compute_b.tf KMSAliasMutate (ARN-scoped because aliases | ||
| # have no IAM-visible tags). | ||
| Sid = "KMSMutateTaggedOnly" | ||
| Effect = "Allow" | ||
| Action = [ | ||
| "kms:CreateAlias", | ||
| "kms:DeleteAlias", | ||
| "kms:DisableKey", | ||
| "kms:EnableKey", | ||
| "kms:PutKeyPolicy", | ||
| "kms:ScheduleKeyDeletion", | ||
| "kms:TagResource", | ||
|
|
@@ -239,6 +343,11 @@ resource "aws_iam_policy" "compute" { | |
| "kms:UpdateKeyDescription", | ||
| ] | ||
| Resource = "*" | ||
| Condition = { | ||
| StringEquals = { | ||
| "aws:ResourceTag/Project" = "CUDly" | ||
| } | ||
| } | ||
| }, | ||
| ] | ||
| }) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,72 @@ | ||||||||||||||||||||||||||||||||||||||||
| # Second compute-permissions policy. This file exists because the main | ||||||||||||||||||||||||||||||||||||||||
| # `aws_iam_policy.compute` is at AWS's 6144-char managed-policy limit; | ||||||||||||||||||||||||||||||||||||||||
| # additional ARN-scoped statements (added in response to a CodeRabbit | ||||||||||||||||||||||||||||||||||||||||
| # review of fix/iam-resource-wildcards) wouldn't fit there without | ||||||||||||||||||||||||||||||||||||||||
| # evicting unrelated existing grants. Each statement here corrects a | ||||||||||||||||||||||||||||||||||||||||
| # scoping bug that the AWS service authorization reference forced us to | ||||||||||||||||||||||||||||||||||||||||
| # split out of `policy_compute.tf`: | ||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||
| # - CloudFrontFnMutate: cloudfront:DeleteFunction/PublishFunction/ | ||||||||||||||||||||||||||||||||||||||||
| # UpdateFunction operate on the `function` resource type which does | ||||||||||||||||||||||||||||||||||||||||
| # not support aws:ResourceTag, so tag-gating them is a no-op. ARN | ||||||||||||||||||||||||||||||||||||||||
| # scope used instead. | ||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||
| # - ECSListTasks: ecs:ListTasks does not support resource-level | ||||||||||||||||||||||||||||||||||||||||
| # permissions, so Resource must be "*". The ecs:cluster condition | ||||||||||||||||||||||||||||||||||||||||
| # key restricts it to CUDly clusters. | ||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||
| # - KMSAliasMutate: kms:CreateAlias/DeleteAlias/UpdateAlias act on | ||||||||||||||||||||||||||||||||||||||||
| # alias resources, but aws:ResourceTag/Project gates only key | ||||||||||||||||||||||||||||||||||||||||
| # resources (aliases inherit nothing). KMS evaluates these actions | ||||||||||||||||||||||||||||||||||||||||
| # against BOTH the alias and the target key, so the alias-side | ||||||||||||||||||||||||||||||||||||||||
| # check lives here (ARN-scoped) and the key-side check lives in | ||||||||||||||||||||||||||||||||||||||||
| # policy_compute.tf KMSMutateTaggedOnly (tag-gated to CUDly keys). | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| resource "aws_iam_policy" "compute_b" { | ||||||||||||||||||||||||||||||||||||||||
| name = "cudly-deploy-compute-b" | ||||||||||||||||||||||||||||||||||||||||
| description = "CUDly Terraform deploy: ARN-scoped statements split out of compute policy due to size" | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| policy = jsonencode({ | ||||||||||||||||||||||||||||||||||||||||
| Version = "2012-10-17" | ||||||||||||||||||||||||||||||||||||||||
| Statement = [ | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| Sid = "CloudFrontFnMutate" | ||||||||||||||||||||||||||||||||||||||||
| Effect = "Allow" | ||||||||||||||||||||||||||||||||||||||||
| Action = [ | ||||||||||||||||||||||||||||||||||||||||
| "cloudfront:DeleteFunction", | ||||||||||||||||||||||||||||||||||||||||
| "cloudfront:PublishFunction", | ||||||||||||||||||||||||||||||||||||||||
| "cloudfront:UpdateFunction", | ||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||
| Resource = "arn:aws:cloudfront::*:function/cudly-*" | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| Sid = "ECSListTasks" | ||||||||||||||||||||||||||||||||||||||||
| Effect = "Allow" | ||||||||||||||||||||||||||||||||||||||||
| Action = [ | ||||||||||||||||||||||||||||||||||||||||
| "ecs:ListTasks", | ||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||
| Resource = "*" | ||||||||||||||||||||||||||||||||||||||||
| Condition = { | ||||||||||||||||||||||||||||||||||||||||
| ArnLike = { | ||||||||||||||||||||||||||||||||||||||||
| "ecs:cluster" = "arn:aws:ecs:*:*:cluster/cudly-*" | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||
| Sid = "KMSAliasMutate" | ||||||||||||||||||||||||||||||||||||||||
| Effect = "Allow" | ||||||||||||||||||||||||||||||||||||||||
| Action = [ | ||||||||||||||||||||||||||||||||||||||||
| "kms:CreateAlias", | ||||||||||||||||||||||||||||||||||||||||
| "kms:DeleteAlias", | ||||||||||||||||||||||||||||||||||||||||
| "kms:UpdateAlias", | ||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||
| Resource = "arn:aws:kms:*:*:alias/cudly-*" | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+56
to
+63
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🌐 Web query:
💡 Result: In the AWS IAM Service Authorization Reference for KMS, the resource types for kms:CreateAlias, kms:UpdateAlias, and kms:DeleteAlias are both "alias" and "key". KMS defines two resource types: alias (arn:aws:kms:::alias/) and key (arn:aws:kms:::key/). These actions require permissions on both resource types: - kms:CreateAlias requires kms:CreateAlias permission on the alias (IAM policy) and on the target key (key policy). - kms:DeleteAlias requires kms:DeleteAlias permission on the alias (IAM policy) and on the associated key (key policy). - kms:UpdateAlias requires kms:UpdateAlias permission on the alias (IAM policy), on the current key (key policy), and on the new target key (key policy). Yes, CreateAlias and UpdateAlias require both alias and key resources (permissions on both types). DeleteAlias also requires both. Citations:
🏁 Script executed: # First, find the file and check if it exists
fd -t f "policy_compute_b.tf" -x wc -l {}
# Then read the specific lines mentioned
cat -n terraform/environments/aws/ci-cd-permissions/policy_compute_b.tf | sed -n '50,65p'Repository: LeanerCloud/CUDly Length of output: 528 🏁 Script executed: # Search for other KMS policies in the same directory
grep -r "kms:CreateAlias\|kms:UpdateAlias\|kms:DeleteAlias" terraform/environments/aws/ci-cd-permissions/ -A 5 -B 5
# Also check if there are other policy files that might have similar issues
fd -t f "policy_.*\.tf" terraform/environments/aws/ci-cd-permissions/Repository: LeanerCloud/CUDly Length of output: 3871 KMS alias mutations are under-authorized for Create/Update flows. Line 60 scopes only alias ARNs. For 🔧 Proposed fix {
Sid = "KMSAliasMutate"
Effect = "Allow"
Action = [
"kms:CreateAlias",
"kms:DeleteAlias",
"kms:UpdateAlias",
]
- Resource = "arn:aws:kms:*:*:alias/cudly-*"
+ Resource = [
+ "arn:aws:kms:*:*:alias/cudly-*",
+ "arn:aws:kms:*:*:key/*",
+ ]
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| tags = { | ||||||||||||||||||||||||||||||||||||||||
| Project = "CUDly" | ||||||||||||||||||||||||||||||||||||||||
| ManagedBy = "terraform" | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
AWS CloudFront service authorization reference function resource type ResourceTag💡 Result:
In AWS CloudFront's Service Authorization Reference, ResourceTag refers to the resource condition key
#amazoncloudfront:aws:ResourceTag/${TagKey}, available for all taggable CloudFront resource types. This key allows attribute-based access control (ABAC) in IAM policies by evaluating tags on CloudFront resources like distributions, streaming-distributions, anycast-ip-list, vpcorigin, distribution-tenant, connection-group, trust-store, and connection-function. Example ARN format for distribution: arn:${Partition}:cloudfront::${Account}:distribution/${DistributionId} Condition keys column lists#amazoncloudfront:aws:ResourceTag/${TagKey} for each, enabling policies like: { "Condition": { "StringEquals": { "cloudfront:aws:ResourceTag/environment": "production" } } } This supports tag-based permissions on supported resources. CloudFront partially supports ABAC for taggable resources. Tagging is managed via console, API, CLI for distributions and other listed types (not origin access identities or invalidations).Citations:
🌐 Web query:
CloudFront DeleteFunction PublishFunction UpdateFunction IAM resource types💡 Result:
The IAM actions cloudfront:DeleteFunction, cloudfront:PublishFunction, and cloudfront:UpdateFunction all use the CloudFront function resource type. Resource type: function ARN format: arn:${Partition}:cloudfront::${Account}:function/${Name} These actions support resource-level permissions, so specify the function ARN in the IAM policy Resource element. No condition keys are defined for the function resource. Example IAM policy statement: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "cloudfront:DeleteFunction", "cloudfront:PublishFunction", "cloudfront:UpdateFunction" ], "Resource": "arn:aws:cloudfront::123456789012:function/my-function" } ] }
Citations:
🌐 Web query:
CloudFront function resource type tagging support aws:ResourceTag condition💡 Result:
CloudFront Functions (resource type AWS::CloudFront::Function, ARN arn:aws:cloudfront::account:function/name) do not support aws:ResourceTag condition keys in IAM policies. The official AWS Service Authorization Reference lists no condition keys (empty column) for this resource type, unlike distributions and other CloudFront resources which explicitly support
#aws:ResourceTag/${TagKey}. CloudFront provides general TagResource/TagFunction APIs that accept function ARNs, indicating functions can be tagged. However, the IAM service authorization documentation confirms no support for tag-based conditions (aws:ResourceTag) specifically for CloudFront Functions. This means you cannot use conditions like "StringEquals": {"aws:ResourceTag/MyTag": "MyValue"} to restrict CloudFront function actions (e.g., cloudfront:CreateFunction, cloudfront:PublishFunction, cloudfront:UpdateFunction) based on resource tags.Citations:
Split function permissions from distribution mutations—CloudFront Functions don't support the
aws:ResourceTagcondition.This statement mixes distribution mutations with
cloudfront:DeleteFunction,cloudfront:PublishFunction, andcloudfront:UpdateFunction. The CloudFront function resource type does not supportaws:ResourceTagas a condition key in IAM policies. This means theProject=CUDlytag condition will not apply to or restrict function actions—leaving function permissions unscoped by tags. Move function actions into a separate statement with explicit ARN-based resource scoping (e.g.,arn:aws:cloudfront::ACCOUNT:function/function-name), and keep tag-based conditions only for resource types that support them (distributions, streaming distributions, etc.).🤖 Prompt for AI Agents