Use the Conformity Knowledge Base AI to help improve your Cloud Posture

Sender Policy Framework DNS Lookup Limit

Trend Cloud One™ – Conformity is a continuous assurance tool that provides peace of mind for your cloud infrastructure, delivering over 1000 automated best practice checks.

Risk Level: Medium (should be achieved)

Ensure that the Sender Policy Framework (SPF) implemented for your Amazon Route 53 domains does not exceed more than 10 DNS lookups during SPF evaluation in order to avoid unreasonable load on the DNS infrastructure and to prevent threats such as DDoS attacks.

Security

The Sender Policy Framework implementation for your Amazon Route 53 domains can help you detect and stop email address spoofing in order to reduce spam and increase your domains trustworthiness. To fulfill the Sender Policy Framework (SPF) requirements, your SPF implementation must follow the framework specification that limits the number of DNS lookups to 10 (RFC 7208 section 4.6.4). This limit helps reduce the amount of resources (bandwidth, time, CPU, memory) used by mailbox providers when checking SPF DNS records. If this limit is exceeded, an email message may fail SPF inspection which can cause deliverability issues and may hurt your domain reputation as the message may be flagged as spam or potential fraud. The DNS lookup limit is also imposed to prevent DDoS attacks against the DNS infrastructure.

Note: To follow best practices, you must limit the number of SPF mechanisms and modifiers that do DNS lookups to 10 per SPF evaluation, including any lookups caused by the use of the "include" mechanism or the "redirect" modifier. If this number is exceeded during an SPF evaluation, a "PermError" error is returned. The "include", "a", "mx", "ptr", "exists" mechanisms and the "redirect" modifier do count against this limit. For example, the following SPF policy: v=spf1 a -all requires the receiver to perform 1 additional DNS lookup (e.g. domain.com A) to fully evaluate. The "all", "ip4", and "ip6" mechanisms do not require DNS lookups and therefore do not count against this limit. As an example, this conformity rule demonstrates how to audit and remediate an SPF implementation that uses a combination of "include" and "a" mechanisms.


Audit

To determine if your Amazon Route 53 SPF implementation exceeds the DNS lookup limit, perform the following actions:

Using AWS Console

01 Sign in to the AWS Management Console.

02 Navigate to Amazon Route 53 console at https://console.aws.amazon.com/route53/.

03 In the navigation panel, under Route 53, choose Hosted Zones.

04 Click on the domain name of the Route 53 hosted zone that you want to examine.

05 In the Records section, select TXT from the Type dropdown menu to return all the TXT DNS records created for the selected hosted zone (domain). If the filtering process does not return any TXT entries, the selected domain does not use TXT DNS records to implement the Sender Policy Framework (SPF), therefore the Audit process ends here. If the filtering process returns one or more TXT records, check their value listed in Value/Route traffic to column. If their value contains v=spf1, the selected domain is using TXT DNS records to implement SPF (i.e. SPF records), therefore you can continue the Audit process with the next step.

06 Select the Sender Policy Framework (SPF) record that you want to examine.

07 In the Record details section, check the Value attribute value to determine if the selected SPF record exceeds the DNS lookup limit. The "include", "a", "mx", "ptr", "exists" and "redirect" mechanisms and modifiers count against this limit. If the Value attribute value contains more than 10 mechanisms and/or modifiers, e.g. "v=spf1 a:mail.domain.com a:s1.domain.com a:s2.domain.com a:s4.domain.com a:s7.domain.com include:_spf-a.domain.com include:_spf-b.domain.com include:_spf-c.domain.com include:_spf-ssg-a.domain.com include:_spf-d.domain.com include:_spf1-cc.domain.com -all", the Sender Policy Framework (SPF) implementation for the selected Amazon Route 53 domain exceeds the DNS lookup limit.

08 Repeat steps no. 6 and 7 for each SPF record created for the selected Amazon Route 53 hosted zone.

09 Repeat steps no. 4 – 8 for each DNS hosted zone created with the Amazon Route 53 service.

Using AWS CLI

01 Run list-hosted-zones command (OSX/Linux/UNIX) to retrieve the list with all the DNS hosted zones available in your AWS account:

aws route53 list-hosted-zones 
  --query "HostedZones[*].Id"

02 The command output should return an array with the requested hosted zone IDs:

[
	"/hostedzone/ABCD1234ABCD1234ABCD",
	"/hostedzone/ABCDABCDABCDABCDABCD"
]

03 Run list-resource-record-sets command (OSX/Linux/UNIX) using the ID of the DNS hosted zone that you want to examine as the identifier parameter and custom query filters to describe the TXT DNS records created for the selected hosted zone (domain):

aws route53 list-resource-record-sets
  --hosted-zone-id "/hostedzone/ABCD1234ABCD1234ABCD"
  --query "ResourceRecordSets[?Type == 'TXT']"

04 The command output should return an array with all the TXT DNS record sets created for the specified Amazon Route 53 hosted zone:

  • If the array returned as output is empty, i.e. [], the selected domain name does not use TXT DNS records to implement Sender Policy Framework (SPF), therefore the Audit process ends here:
    []
    
  • If the command output returns an array with one or more TXT DNS records, as shown in the example below, check the value configured for the ResourceRecords.Value property. If this configuration value contains v=spf1, the selected domain is using TXT DNS records to implement SPF (i.e. SPF records). Analyze the ResourceRecords.Value property value to determine if the selected SPF record exceeds the DNS lookup limit. The "include", "a", "mx", "ptr", "exists" and "redirect" mechanisms and modifiers count against this limit. If the Value attribute value contains more than 10 mechanisms and/or modifiers, as shown in the output example below, the Sender Policy Framework (SPF) implementation for the selected Amazon Route 53 domain exceeds the DNS lookup limit:
    [
    	{
    		"ResourceRecords": [
    			{
    				"Value": "\"v=spf1 a:mail.domain.com a:s1.domain.com a:s2.domain.com a:s4.domain.com a:s7.domain.com include:_spf-a.domain.com include:_spf-b.domain.com include:_spf-c.domain.com include:_spf-ssg-a.domain.com include:_spf-d.domain.com include:_spf1-cc.domain.com -all\""
    			}
    		],
    		"Type": "TXT",
    		"Name": "domain.com.",
    		"TTL": 300
    	}
    ]
    

05 Repeat steps no. 3 and 4 for each DNS hosted zone created with Amazon Route 53 service within your AWS cloud account.

Remediation / Resolution

To reduce the number of DNS lookups during Sender Policy Framework (SPF) evaluation, you must check your SPF records and remove any services that you may no longer use and/or use SPF record flattening. You can use SPF record flattening for each mechanism/modifier included in your SPF record by querying the DNS to get the IP addresses and replace the original mechanism/modifier with the IP addresses. Each time an SPF record mechanism/modifier is replaced, the total DNS lookup count is decremented by 1. To reduce the number of DNS lookups for your non-compliant SPF records, perform the following actions:

Using AWS CloudFormation

01 CloudFormation template (JSON):

{
	"AWSTemplateFormatVersion": "2010-09-09",
	"Resources": {
		"EC2Instance": {
			"Type": "AWS::EC2::Instance",
			"Properties": {
				"ImageId": "ami-0123456789abcdef0",
				"InstanceType": "c5.xlarge",
				"KeyName": "cc-ssh-key",
				"SubnetId": "subnet-0123456789abcdefa",
				"SecurityGroupIds": "sg-0123456789abcdef1"
			}
		},
		"Route53HostedZone": {
			"Type": "AWS: : Route53: : HostedZone",
			"Properties": {
				"HostedZoneConfig": {
					"Comment": "Route53 public hosted zone for domain.com"
				},
				"Name": "domain.com",
				"HostedZoneTags": [
					{
						"Key": "Owner",
						"Value": "IT"
					}
				]
			}
		},
		"ARecord": {
			"Type": "AWS::Route53::RecordSet",
			"Properties": {
				"HostedZoneName": {
					"Ref": "Route53HostedZone"
				},
				"Name": "www.domain.com",
				"Type": "A",
				"TTL": "300",
				"ResourceRecords": [
					{
						"Fn::GetAtt": [
							"EC2Instance",
							"PublicIp"
						]
					}
				]
			}
		},
		"SPFRecord": {
			"Type": "AWS::Route53::RecordSet",
			"Properties": {
				"HostedZoneName": {
					"Ref": "Route53HostedZone"
				},
				"Name": "www.domain.com",
				"Type": "TXT",
				"TTL": "3600",
				"ResourceRecords": [
					"v=spf1 a:s1.domain.com a:s2.domain.com a:s4.domain.com ip4:192.168.0.5 ip4:192.168.0.23 ip4:192.168.0.30 ip4:192.168.0.45 ip4:192.168.0.103 ip4:192.168.0.105 -all"
				]
			}
		}
	}
}

02 CloudFormation template (YAML):

AWSTemplateFormatVersion: '2010-09-09'
	Resources:
	EC2Instance:
		Type: AWS::EC2::Instance
		Properties:
		ImageId: ami-0123456789abcdef0
		InstanceType: c5.xlarge
		KeyName: cc-ssh-key
		SubnetId: subnet-0123456789abcdefa
		SecurityGroupIds: sg-0123456789abcdef1
	Route53HostedZone:
		Type: 'AWS: : Route53: : HostedZone'
		Properties:
		HostedZoneConfig:
			Comment: Route53 public hosted zone for domain.com
		Name: domain.com
		HostedZoneTags:
			- Key: Owner
			Value: IT
	ARecord:
		Type: AWS::Route53::RecordSet
		Properties:
		HostedZoneName: !Ref 'Route53HostedZone'
		Name: www.domain.com
		Type: A
		TTL: '3600'
		ResourceRecords:
			- !GetAtt 'EC2Instance.PublicIp'
	SPFRecord:
		Type: AWS::Route53::RecordSet
		Properties:
		HostedZoneName: !Ref 'Route53HostedZone'
		Name: www.domain.com
		Type: TXT
		TTL: '3600'
		ResourceRecords:
			- v=spf1 a:s1.domain.com a:s2.domain.com a:s4.domain.com ip4:192.168.0.5 ip4:192.168.0.23 ip4:192.168.0.30 ip4:192.168.0.45 ip4:192.168.0.103 ip4:192.168.0.105 -all

Using Terraform (AWS Provider)

01 Terraform configuration file (.tf):

terraform {
	required_providers {
		aws = {
			source  = "hashicorp/aws"
			version = "~> 4.0"
		}
	}

	required_version = ">= 0.14.9"
}

provider "aws" {
	profile = "default"
	region  = "us-east-1"
}

resource "aws_instance" "ec2-instance" {
	ami                    = "ami-0123456789abcdef0"
	instance_type          = "c5.xlarge"
	key_name               = "cc-ssh-key"
	subnet_id              = "subnet-0123456789abcdefa"
	vpc_security_group_ids = ["sg-0123456789abcdef1"]
}

resource "aws_eip" "elastic-ip" {
	instance = aws_instance.ec2-instance.id
	domain   = "vpc"
}

resource "aws_route53_zone" "route53-hosted-zone" {
	name    = "domain.com"
	comment = "Route53 public hosted zone for domain.com"
	tags    = {
		Owner = "IT"
	}
}

resource "aws_route53_record" "a-record" {
	zone_id = aws_route53_zone.route53-hosted-zone.zone_id
	name    = "www.domain.com"
	type    = "A"
	ttl     = "300"
	records = [aws_eip.elastic-ip.public_ip]
}

resource "aws_route53_record" "spf-record" {
	zone_id = aws_route53_zone.route53-hosted-zone.zone_id
	name    = "www.domain.com"
	type    = "TXT"
	ttl     = "3600"
	records = [
		"v=spf1 a:s1.domain.com a:s2.domain.com a:s4.domain.com ip4:192.168.0.5 ip4:192.168.0.23 ip4:192.168.0.30 ip4:192.168.0.45 ip4:192.168.0.103 ip4:192.168.0.105 -all"
	]
}

Using AWS Console

01 Sign in to the AWS Management Console.

02 Navigate to Route 53 console at https://console.aws.amazon.com/route53/.

03 In the navigation panel, under Route 53, choose Hosted Zones.

04 Click on the domain name of the Route 53 hosted zone that you want to access.

05 In the Records section, select the non-compliant Sender Policy Framework (SPF) record that you want to reconfigure, and choose Edit record.

06 Inside the Value configuration box, remove any mechanism and/or modifier that points to a domain of a service that is no longer in use or use the SPF record flattening technique to replace the specified mechanisms/modifiers with their IP addresses/IP address ranges. For example, you can replace an "include" mechanism such as include:_spf-a.domain.com with the service domain IP address ip4:192.168.0.5.

07 Choose Save to apply the configuration changes.

08 Repeat steps no. 5 – 7 for each non-compliant SPF record created for the selected Route 53 hosted zone.

09 Repeat steps no. 4 – 8 for each DNS hosted zone created with the Amazon Route 53 service.

Using AWS CLI

01 To reconfigure your non-compliant Sender Policy Framework (SPF) record, you must create an Amazon Route 53 change file, declare the modified SPF record set, and save the record definition to a JSON file named update-spf-record-set.json. Modify your non-compliant SPF record by removing any mechanism and/or modifier that points to a domain of a service that is no longer in use or use the SPF record flattening technique to replace the required mechanisms/modifiers with their IP addresses/IP address ranges. For example, you can remove unused mechanisms such as a:mail.domain.com and/or replace "include" mechanisms such as include:_spf-a.domain.com with the domain's IP address ip4:192.168.0.5, as shown in the example below:

{
	"Comment": "SPF TXT-based record set for domain.com hosted zone.",
	"Changes": [
		{
			"Action": "UPSERT",
			"ResourceRecordSet": {
				"Name": "domain.com.",
				"Type": "TXT",
				"TTL": 300,
				"ResourceRecords": [
					{
						"Value": "\"v=spf1 a:s1.domain.com a:s2.domain.com a:s4.domain.com ip4:192.168.0.5 ip4:192.168.0.23 ip4:192.168.0.30 ip4:192.168.0.45 ip4:192.168.0.103 ip4:192.168.0.105 -all\""
					}
				]
			}
		}
	]
}

02 Run change-resource-record-sets command (OSX/Linux/UNIX) using the ID of the Route 53 hosted zone that you want to reconfigure as the identifier parameter and the SPF record change file defined at the previous step (i.e. update-spf-record-set.json) to update the selected, non-compliant Sender Policy Framework (SPF) record available within the specified hosted zone:

aws route53 change-resource-record-sets
  --hosted-zone-id "/hostedzone/ABCD1234ABCD1234ABCD"
  --change-batch file://update-spf-record-set.json

03 The command output should return the new SPF record set metadata. The record set status should be PENDINGat this moment:

{
	"ChangeInfo": {
		"Status": "PENDING",
		"Comment": "SPF TXT-based record set for domain.com hosted zone.",
		"SubmittedAt": "2021-06-20T20:00:30.510Z",
		"Id": "/change/ABCDABCDABCDABCDABCD"
	}
}

04 Run get-change command (OSX/Linux/UNIX) using the ID of the Route 53 change file returned at the previous step as the identifier parameter, to describe the status of the modified SPF record set:

aws route53 get-change 
  --id "/change/ABCDABCDABCDABCDABCD"

05 The command output should return the current status of the DNS record batch request. The current status should be INSYNC, which indicates that the change was fully propagated to all AWS Route 53 DNS server nodes:

{
	"ChangeInfo": {
		"Status": "INSYNC",
		"Comment": "SPF TXT-based record set for domain.com hosted zone.",
		"SubmittedAt": "2021-06-20T20:00:30.510Z",
		"Id": "/change/ABCDABCDABCDABCDABCD"
	}
}

06 Repeat steps no. 1 – 5 for each non-compliant SPF record created within the selected Route 53 hosted zone.

07 Repeat steps no. 1 – 6 for each DNS hosted zone created with the Amazon Route 53 service.

References

Publication date Dec 12, 2023