Fixing Route53 CloudWatch Logs Delivery Access Denied: DNS Query Logging Broken
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 10 mins
TL;DR
- What broke: Route53 cannot deliver DNS query logs to CloudWatch Logs because the log group's resource-based policy either doesn't exist, excludes
route53.amazonaws.comas a principal, or has a malformed ARN condition — the delivery silently fails withAccessDeniedException. - How to fix it: Create or update the CloudWatch Logs resource policy to explicitly grant
logs:CreateLogStreamandlogs:PutLogEventsto theroute53.amazonaws.comservice principal, scoped to your target log group ARN. - Fast path: Use the Client-Side Sandbox above to paste your current resource policy and auto-generate the corrected version without sending your ARNs to a third-party server.
▼ 🔍 Root Cause Breakdown & Kernel Diagnostics [Click to Expand Deep Dive]
The Incident (What Does the Error Mean?)
When you enable Route53 query logging via console or CLI, Route53 attempts to write DNS query records to the specified CloudWatch Logs log group. The delivery agent runs under the route53.amazonaws.com service principal — not your IAM role. If the log group's resource policy doesn't explicitly authorize that principal, every DNS query log write is silently dropped.
The error surfaces in the Route53 console as:
Error: CloudWatch Logs delivery access denied.
Route53 does not have permission to write to the log group:
arn:aws:logs:us-east-1:123456789012:log-group:/aws/route53/example.com
AccessDeniedException: User: arn:aws:sts::123456789012:assumed-role/...
is not authorized to perform: logs:PutLogEvents
on resource: arn:aws:logs:us-east-1:123456789012:log-group:/aws/route53/example.comImmediate consequence: Zero DNS query logs are being written. Your security audit trail, anomaly detection pipelines, and compliance evidence for DNS-layer activity are completely blind from the moment this misconfiguration exists.
▼ 🔍 Detailed Impact & Blast Radius Analysis [Click to Expand Deep Dive]
The Attack Vector / Blast Radius
This isn't just a logging gap — it's a detection blind spot. Route53 DNS query logs are the primary data source for:
- DNS exfiltration detection — attackers tunneling data via crafted subdomains (e.g.,
base64payload.evil.attacker.com) are invisible without query logs. - C2 beaconing identification — periodic DNS lookups to DGA (Domain Generation Algorithm) domains go undetected.
- GuardDuty DNS threat findings — GuardDuty's
Backdoor:EC2/DNSDataExfiltrationandTrojan:EC2/DNSDataExfiltrationfinding types depend on Route53 query logs being delivered. A broken delivery pipeline disables these findings entirely. - Compliance failures — PCI-DSS 10.2, SOC 2 CC7.2, and HIPAA audit controls requiring DNS-layer logging are violated silently.
The blast radius: every EC2 instance, Lambda, and ECS task in the VPC making DNS queries is unmonitored. An attacker who compromises one workload can exfiltrate data via DNS for days without triggering a single alert.
How to Fix It
Root Cause
CloudWatch Logs resource policies are not automatically created when you configure Route53 query logging. Route53 requires an explicit resource policy on the log group granting the route53.amazonaws.com service principal write access. Additionally, Route53 query logging only works with log groups in us-east-1, regardless of your hosted zone's region — a second common failure point.
Basic Fix — Apply the Resource Policy via AWS CLI
- # Missing or broken resource policy (no route53 principal)
- {
- "Version": "2012-10-17",
- "Statement": []
- }
+ # Correct resource policy granting Route53 delivery access
+ {
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Sid": "Route53QueryLoggingDelivery",
+ "Effect": "Allow",
+ "Principal": {
+ "Service": "route53.amazonaws.com"
+ },
+ "Action": [
+ "logs:CreateLogStream",
+ "logs:PutLogEvents"
+ ],
+ "Resource": "arn:aws:logs:us-east-1:123456789012:log-group:/aws/route53/*:*"
+ }
+ ]
+ }Apply it:
aws logs put-resource-policy \
--policy-name "AmazonRoute53LogsToCloudWatchLogs" \
--policy-document file://route53-cwl-resource-policy.json \
--region us-east-1⚠️ Critical: This command must be run against
us-east-1regardless of where your hosted zone is. Route53 query log delivery always targetsus-east-1CloudWatch endpoints.
Enterprise Best Practice — Terraform with Least-Privilege ARN Scoping
- # Overly broad or missing resource policy in Terraform
- resource "aws_cloudwatch_log_resource_policy" "route53" {
- policy_name = "route53-logging"
- policy_document = jsonencode({
- Version = "2012-10-17"
- Statement = [{
- Effect = "Allow"
- Principal = { Service = "*" } # NEVER do this
- Action = ["logs:*"] # NEVER do this
- Resource = "*"
- }]
- })
- }
+ resource "aws_cloudwatch_log_group" "route53_query_logs" {
+ name = "/aws/route53/${var.hosted_zone_name}"
+ retention_in_days = 90
+ provider = aws.us_east_1 # MUST be us-east-1
+
+ tags = {
+ ManagedBy = "terraform"
+ Purpose = "route53-query-logging"
+ Compliance = "pci-dns-audit"
+ }
+ }
+
+ resource "aws_cloudwatch_log_resource_policy" "route53_query_logging" {
+ policy_name = "AmazonRoute53LogsToCloudWatchLogs-${var.hosted_zone_name}"
+ provider = aws.us_east_1
+
+ policy_document = jsonencode({
+ Version = "2012-10-17"
+ Statement = [{
+ Sid = "Route53QueryLoggingDelivery"
+ Effect = "Allow"
+ Principal = {
+ Service = "route53.amazonaws.com"
+ }
+ Action = [
+ "logs:CreateLogStream",
+ "logs:PutLogEvents"
+ ]
+ Resource = [
+ "${aws_cloudwatch_log_group.route53_query_logs.arn}:*"
+ ]
+ Condition = {
+ StringEquals = {
+ "aws:SourceAccount" = var.aws_account_id
+ }
+ ArnLike = {
+ "aws:SourceArn" = "arn:aws:route53:::hostedzone/${var.hosted_zone_id}"
+ }
+ }
+ }]
+ })
+ }
+
+ resource "aws_route53_query_log" "main" {
+ depends_on = [aws_cloudwatch_log_resource_policy.route53_query_logging]
+
+ cloudwatch_log_group_arn = aws_cloudwatch_log_group.route53_query_logs.arn
+ zone_id = var.hosted_zone_id
+ }The Condition block with aws:SourceAccount and aws:SourceArn prevents the confused deputy problem — another AWS account's Route53 cannot write to your log group.
💡 Tired of pasting proprietary infrastructure configs into public LLMs? Generic AI interfaces log your company's ARNs, connection strings, and private keys. StackEngine enforces client-side zero-trust boundaries—the sandbox above executes 100% locally in your browser. Drop your broken manifest above to redact secrets locally and synthesize deterministic patch drafts with your own API key.
(For CI/CD pipeline automation, install via
npm i -g stackengine).
▼ 🔍 Continuous Pipeline Governance & CI/CD Guardrails [Click to Expand Deep Dive]
Prevention in CI/CD
Checkov — Catch Missing Resource Policies Pre-Merge
Checkov rule CKV_AWS_84 checks that Route53 query logging is enabled on hosted zones, but doesn't validate the resource policy. Add a custom check:
# .checkov/custom_checks/route53_cwl_resource_policy.py
# Validate that a CloudWatch Logs resource policy exists for route53.amazonaws.com
# Run: checkov -d . --external-checks-dir .checkov/custom_checksOPA / Conftest Policy
# policy/route53_logging.rego
package route53
deny[msg] {
resource := input.resource.aws_route53_query_log[_]
not input.resource.aws_cloudwatch_log_resource_policy
msg := "Route53 query log config exists without a CloudWatch Logs resource policy. DNS audit trail will be dark."
}
deny[msg] {
policy := input.resource.aws_cloudwatch_log_resource_policy[_]
statement := policy.policy_document.Statement[_]
statement.Principal.Service != "route53.amazonaws.com"
msg := "CloudWatch Logs resource policy does not grant access to route53.amazonaws.com service principal."
}GitHub Actions Gate
- name: Validate Route53 Logging Policy
run: |
aws logs describe-resource-policies \
--region us-east-1 \
--query "resourcePolicies[?contains(policyDocument, 'route53.amazonaws.com')] | length(@)" \
--output text | grep -v '^0$' || \
(echo "FATAL: No CloudWatch Logs resource policy found for Route53" && exit 1)Wire this into your pre-deploy stage. A zero return from that query means your DNS logging is broken before you even push to production.
Official Grounding & Normative Specifications
The technical diagnostics, error recovery protocols, and prevention patterns in this specification are grounded in official vendor documentation and standards:
Federal security guidelines for secret exfiltration prevention, automated integrity checks, and peer sign-offs.
Core memory subsystem controls, pressure stall information (PSI), and kernel OOM invocation mechanics.
Industry-standard specification for container execution boundaries and POSIX namespace isolation.