Skip to content

feat(auth): op-level IAM for Route 53 and CloudFront under --enforce-auth (AUTHZ-X1g-1) - #1536

Merged
NitinKumar004 merged 7 commits into
developmentfrom
feat/authz-apigw-eks-r53-cf
Oct 11, 2026
Merged

NitinKumar004 merged 7 commits into
developmentfrom
feat/authz-apigw-eks-r53-cf

Conversation

@NitinKumar004

Copy link
Copy Markdown
Collaborator

Part of #1495 (AUTHZ-X1g, first of four PRs: Route 53 + CloudFront; EKS, API Gateway and execute-api:Invoke follow).

What changed

Under cloudemu serve --enforce-auth, Route 53 and CloudFront are now authorized per operation on real resource ARNs instead of at service level, so fine-grained and resource-scoped policies work for them.

The pattern follows S3 (#1444) and Lambda (#1530):

  • Refactor (behaviour-identical). Each handler gets a pure classify(r) (opID, opArgs). ServeHTTP dispatches on it and IAMChecks / IAMChecksWithContext use the same function, so the operation and resource IAM checks are the ones that run. A request classify cannot name is answered with the handler's existing 4xx and no side effect (fail closed: shortcut principals get that error, policy users get 403). New auth-off golden cases were recorded on the base code and pass unchanged on this branch.
  • Route 53. Actions route53:<Operation> on arn:aws:route53:::hostedzone/<id>, healthcheck/<id> and change/<id> (no region or account); account-level operations on *. Condition keys: route53:ChangeResourceRecordSetsActions, route53:ChangeResourceRecordSetsRecordTypes and route53:ChangeResourceRecordSetsNormalizedRecordNames (from the same XML decode dispatch uses), and route53:VPCs as VPCId=<id>,VPCRegion=<region>. Creating a private zone and AssociateVPCWithHostedZone also need ec2:DescribeVpcs on *.
  • CloudFront. Actions cloudfront:<Operation> on arn:aws:cloudfront::<account>:distribution/<id>; invalidation operations on their distribution; CreateDistribution and ListDistributions on *. CreateDistributionWithTags is authorized as cloudfront:CreateDistribution plus cloudfront:TagResource, the latter on distribution/* since the id does not exist yet. aws:RequestTag/*, aws:TagKeys and aws:ResourceTag/* are set.
  • Fixes found while binding the checked ARN to the acted-on resource:
    • Route 53 tagging ignored {ResourceType}: /tags/healthcheck/<zone-id> tagged the zone, and any id (even one naming nothing) was tagged. It now answers InvalidInput for an unknown type and NoSuchHostedZone / NoSuchHealthCheck when the id is not a resource of that type.
    • CloudFront tagging resolved the distribution by the id at the end of any string, so an ARN of another account acted on this account's distribution. A tagging ARN must now be a distribution ARN of this account, otherwise NoSuchResource.
  • awsauthz.Scope.PartitionARN and awsauthz.XMLBody (XML twin of JSONBody).
  • Docs: flag help, EnforceAuth comment, contrib/server README.

Sources

  • Service Authorization Reference (machine-readable v1.4): servicereference.us-east-1.amazonaws.com/v1/route53/route53.json, .../cloudfront/cloudfront.json, .../ec2/ec2.json (ec2:DescribeVpcs takes no resource).
  • Route 53 Developer Guide, "Using IAM policy conditions for fine-grained access control": record name normalization (lower case, no trailing dot, other characters as \ooo octal, * is \052), uppercase actions and types, route53:VPCs format VPCId=<vpc-id>,VPCRegion=<region> lower case, example pairing the VPC actions with ec2:DescribeVpcs.
  • Route 53 API Reference, CreateHostedZone: "The CreateHostedZone request requires the caller to have an ec2:DescribeVpcs permission." Applied when the request carries a VPC (private zone) and for AssociateVPCWithHostedZone, which reads the same VPC; public zone creation needs only route53:CreateHostedZone.
  • AWS CLI reference, create-distribution-with-tags: "This API operation requires the following IAM permissions: CreateDistribution, TagResource".
  • CloudFront Developer Guide, identity-based policy examples: CreateDistribution uses Resource: "*".

Verification

  • Unit: classify tables, per-op IAMChecksWithContext tables (actions, ARNs, condition keys, body put back), record name normalization, distribution ARN parsing.
  • Gate: TestAuthzMatrixRoute53, TestAuthzMatrixCloudFront (scoped ARNs, RecordTypes condition, single Deny, private zone needs ec2:DescribeVpcs, ResourceTag, WithTags needs TagResource, foreign tagging ARN), TestUnknownRESTOpHasNoSideEffect (shortcut caller gets the 4xx, restricted caller 403, state unchanged), TestOpLevelRESTHandlersAreResolvers, TestAuthOffResponsesUnchanged (13 new cases, golden recorded on the base code).
  • contrib/server: TestEnforceAuthRoute53, TestEnforceAuthCloudFront with the real SDKs.
  • Live, cloudemu serve --enforce-auth, scoped IAM users made with the aws CLI:
    • Route 53 user (TXT only through the RecordTypes condition): create zone, get, TXT change, list records, tags, list zones, delete OK; A change and CreateHealthCheck denied with AccessDenied; no A record created.
    • CloudFront user (cloudfront:* with a Deny on CreateInvalidation): create, get, update (disable), tag/list/untag, delete OK; CreateInvalidation denied with the explicit-deny message; a tagging ARN of another account denied.
    • Terraform (hashicorp/aws 6.66.0) as a scoped user with aws_route53_zone, aws_route53_record (TXT) and aws_cloudfront_distribution: apply, plan -detailed-exitcode = 0, destroy all clean; adding an A record fails with AccessDenied.
    • Auth off: the same Terraform lifecycle plus an A record change all succeed as before.
  • Gates: go build ./..., go vet, go test -race on server/aws/route53, server/aws/cloudfront, server/aws, server/wire/..., persist; go -C contrib/server test -run Enforce; golangci-lint (new issues) 0; go generate no docs diff.

@NitinKumar004
NitinKumar004 marked this pull request as ready for review October 11, 2026 05:59
@NitinKumar004
NitinKumar004 merged commit 62aa720 into development Oct 11, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant