Skip to main content

Route 53 Public Hosted Zone Query Logging

Overview

This check verifies that your Route 53 public hosted zones have DNS query logging enabled. When enabled, Route 53 records information about every DNS query it receives for your domain and sends it to Amazon CloudWatch Logs.

Risk

Without DNS query logging, you lose visibility into how your domain is being used. This creates security blind spots:

  • Data exfiltration - Attackers can use DNS tunneling to steal data, and you would not see it
  • Malware activity - Command-and-control traffic and domain generation algorithms go undetected
  • DNS hijacking - Unauthorized changes to your DNS records may not be noticed
  • Slower incident response - When something goes wrong, you have no logs to investigate

Remediation Steps

Prerequisites

  • Access to the AWS Console with permissions to modify Route 53 and CloudWatch Logs
  • The hosted zone ID for your public domain (found in Route 53 console)
Required IAM permissions

You need permissions for:

  • route53:CreateQueryLoggingConfig
  • route53:GetHostedZone
  • logs:CreateLogGroup
  • logs:PutResourcePolicy
  • logs:DescribeLogGroups

AWS Console Method

  1. Open the Route 53 console
  2. Click Hosted zones in the left navigation
  3. Select your public hosted zone
  4. Click the Query logging tab
  5. Click Configure query logging
  6. For the log group, either:
    • Select an existing log group, or
    • Click Create log group and name it /aws/route53/<your-domain-name>
  7. Click Create
  8. If prompted, approve the CloudWatch Logs resource policy

Important: The CloudWatch Logs log group must be in the us-east-1 region. Route 53 only supports query logging to log groups in us-east-1.

AWS CLI (optional)

Step 1: Create a CloudWatch Logs log group in us-east-1

aws logs create-log-group \
--log-group-name "/aws/route53/example.com" \
--region us-east-1

Step 2: Create a resource policy to allow Route 53 to write logs

aws logs put-resource-policy \
--policy-name "route53-query-logging-policy" \
--policy-document '{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Route53LogsToCloudWatchLogs",
"Effect": "Allow",
"Principal": {
"Service": "route53.amazonaws.com"
},
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:us-east-1:<your-account-id>:log-group:/aws/route53/*"
}
]
}' \
--region us-east-1

Replace <your-account-id> with your AWS account ID.

Step 3: Enable query logging for your hosted zone

aws route53 create-query-logging-config \
--hosted-zone-id <your-hosted-zone-id> \
--cloud-watch-logs-log-group-arn "arn:aws:logs:us-east-1:<your-account-id>:log-group:/aws/route53/example.com"

Replace:

  • <your-hosted-zone-id> with your hosted zone ID (e.g., Z1234567890ABC)
  • <your-account-id> with your AWS account ID
  • example.com with your domain name
CloudFormation (optional)
AWSTemplateFormatVersion: '2010-09-09'
Description: Enable Route 53 DNS query logging to CloudWatch Logs

Parameters:
HostedZoneId:
Type: String
Description: The ID of the Route 53 public hosted zone

LogGroupName:
Type: String
Default: /aws/route53/example.com
Description: Name for the CloudWatch Logs log group

Resources:
Route53QueryLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Ref LogGroupName
RetentionInDays: 90

Route53QueryLoggingConfig:
Type: AWS::Route53::QueryLoggingConfig
Properties:
HostedZoneId: !Ref HostedZoneId
CloudWatchLogsLogGroupArn: !GetAtt Route53QueryLogGroup.Arn

Outputs:
LogGroupArn:
Description: ARN of the CloudWatch Logs log group
Value: !GetAtt Route53QueryLogGroup.Arn

QueryLoggingConfigId:
Description: ID of the query logging configuration
Value: !Ref Route53QueryLoggingConfig

Important: This template must be deployed in the us-east-1 region.

Deploy with:

aws cloudformation deploy \
--template-file template.yaml \
--stack-name route53-query-logging \
--parameter-overrides \
HostedZoneId=<your-hosted-zone-id> \
LogGroupName=/aws/route53/example.com \
--region us-east-1
Terraform (optional)
variable "hosted_zone_id" {
description = "The ID of the Route 53 public hosted zone"
type = string
}

variable "domain_name" {
description = "The domain name for naming the log group"
type = string
default = "example.com"
}

variable "log_retention_days" {
description = "Number of days to retain logs"
type = number
default = 90
}

# CloudWatch Logs log group for DNS query logs
# Must be created in us-east-1 region
resource "aws_cloudwatch_log_group" "route53_query_logs" {
provider = aws.us_east_1
name = "/aws/route53/${var.domain_name}"
retention_in_days = var.log_retention_days
}

# Resource policy to allow Route 53 to write to CloudWatch Logs
resource "aws_cloudwatch_log_resource_policy" "route53_query_logging_policy" {
provider = aws.us_east_1
policy_name = "route53-query-logging-policy"

policy_document = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "Route53LogsToCloudWatchLogs"
Effect = "Allow"
Principal = {
Service = "route53.amazonaws.com"
}
Action = [
"logs:CreateLogStream",
"logs:PutLogEvents"
]
Resource = "${aws_cloudwatch_log_group.route53_query_logs.arn}:*"
}
]
})
}

# Route 53 query logging configuration
resource "aws_route53_query_log" "main" {
depends_on = [aws_cloudwatch_log_resource_policy.route53_query_logging_policy]

zone_id = var.hosted_zone_id
cloudwatch_log_group_arn = aws_cloudwatch_log_group.route53_query_logs.arn
}

output "log_group_arn" {
description = "ARN of the CloudWatch Logs log group"
value = aws_cloudwatch_log_group.route53_query_logs.arn
}

output "query_logging_config_id" {
description = "ID of the query logging configuration"
value = aws_route53_query_log.main.id
}

Note: You need a provider alias for us-east-1:

provider "aws" {
alias = "us_east_1"
region = "us-east-1"
}

Verification

  1. In the Route 53 console, select your hosted zone and click the Query logging tab
  2. Confirm that query logging shows as Enabled with a linked log group
  3. Make a DNS query to your domain (e.g., nslookup example.com)
  4. Check CloudWatch Logs for new log entries (may take a few minutes)
CLI verification commands

Check if query logging is configured:

aws route53 list-query-logging-configs \
--hosted-zone-id <your-hosted-zone-id>

View recent log entries:

aws logs filter-log-events \
--log-group-name "/aws/route53/example.com" \
--region us-east-1 \
--limit 10

Additional Resources

Notes

  • Region requirement: The CloudWatch Logs log group must be in us-east-1, regardless of where your other resources are located. This is an AWS requirement for Route 53 query logging.

  • Log group naming: AWS recommends using the prefix /aws/route53/ followed by your domain name. This makes it easier to manage resource policies across multiple hosted zones.

  • Cost considerations: You are charged for CloudWatch Logs ingestion and storage. Set an appropriate retention period to manage costs.

  • Log delay: DNS query logs may take several minutes to appear in CloudWatch Logs after queries are made.

  • Private hosted zones: This check applies only to public hosted zones. Private hosted zones use a different logging mechanism through Resolver Query Logging.