Skip to content

Instantly share code, notes, and snippets.

@nicolasdao
Last active March 23, 2024 13:52
Show Gist options
  • Save nicolasdao/e8face1a5483111a2a29d17aaed79199 to your computer and use it in GitHub Desktop.
Save nicolasdao/e8face1a5483111a2a29d17aaed79199 to your computer and use it in GitHub Desktop.
AWS Route 53. Keywords: route53 route 53 dns apex cname record aaaa custom domain

AWS ROUTE 53

Table of contents

The basics

  • The root domain, aka apex domain, is the naked domain (e.g., the root domain of https://www.example.com is example.com).
  • A CNAME cannot be placed at the root domain level.
  • CNAME records must point to another domain name, not an IP address.
  • CNAME records can point to other CNAME records, but this is not considered a good practice as it is inefficient.
  • An alias is like a CNAME where the target is an AWS service (e.g., CloudFront, ELB, API Gateway).
  • When an alias is defined on the root domain, this is called an zone apex (more about this topic in the Alias records section).
  • hosted zone is a container for DNS records. There are two types of hosted zones:
    • public: Stores DNS records to route traffic on the internet.
    • private: Stores DNS records to route traffic in your VPC.
  • Can log DNS queries to CloudWatch (via a Log Group only in US East). Usefull for traffic analysis.

Alias records

Why do we need them?

The DNS specification is quite old (1987) and its limitation created needs to innovate outside of the official standard. The main challenge of the standard specification is the inability to define a CNAME at the root domain (e.g., example.com pointing to anotherexample.com). The most popular solutions are proprietary:

  1. AWS Route 53's Alias: This is typically used to create a CNAME record with a root domain to an AWS service (e.g., CloudFront, ELB).
  2. Cloudflare's CNAME Flattening: https://blog.cloudflare.com/introducing-cname-flattening-rfc-compliant-cnames-at-a-domains-root/

Alias on the apex domain does not cover all scenarios

The AWS documentation mentions that alias record can overcome the DNS specification limiting apex domain to A records only. When the alias is toggled on the apex domain, this A redord is not limited to Ipv4 or IPv6 and support the following URL types (making it worked as a CNAME):

  • CloudFront URL
  • S3 URL
  • ELB URL
  • Another record in the same hosted zone

Unfortunatelly, there is a big limitation on the last point (record in the same hosted zone). It only works if that record is NOT a CNAME. This means that it is not possible to configure a Route 53 apex domain to resolve to its www CNAME record which points to another domain. The alternative options are:

  • Instead of configuring the Apex to resolve to www., configure www. to resolve to the Apex. This means that the Apex is configured using an Alias and that wwww. is configured as a standard CNAME record resolving to the Apex.
  • If that other domain is a CloudFront distribution (even if that's one from another AWS account), you can simply point the apex domain to the CloudFront URL (e.g., 12345567.cloudfront.net) using the alias option as exlained in the next section Configuring custom domain on any CloudFront distribution.
  • Create a new S3 bucket and configure it to redirect traffic. Then, configure the Alias using that S3 bucket.
  • If the domain is not an AWS service, then you have no choice but configuring the APEX domain to point to a server that can perform a 301 redirect to the www CNAME record (e.g., CloudFront on an empty S3 bucket with forward rules).

Domain migration

Before any migration

  • Backup the current DNS records:
dig +noall +answer +multiline YOURDOMAIN any > dns.backup.dns

Migrating domains from one AWS account to another

Two components can be migrated:

Both of them can be migrated separately without causing an interuption of service. If the domain and the hosted zone are hosted in separate accounts, they will also have to be maintained in those separate accounts, which can lead to confusion. That's why I would recommend to migrate both in the same account.

Migrating a domain to another AWS account

  1. Setup your default AWS CLI profile to the account containing the domain.
  2. Get the account ID of the destinatin AWS Account.
  3. Run this command
aws route53domains transfer-domain-to-another-aws-account --region us-east-1 --domain-name <YOUR-DOMAIN> --account-id <YOUR-DESTINATION-ACCOUNT-ID>
  1. Save the response, which is similar to:
{
		"OperationId": "9e75aaad-ef21-4b49-bd24-49eb3b15441a",
		"Password": "1234567"
}
  1. When this is done, your new domain will appear in the destination account in the Domains section under the Pending requests link. The domain must be approved using the CLI.
  2. Switch to the destination account in the AWS CLI profile.
  3. Test you are logged to the correct account: aws sts get-caller-identity
  4. Run this command:
aws route53domains accept-domain-transfer-from-another-aws-account --region us-east-1 --domain-name <YOUR-DOMAIN> --password "<YOUR-PASSWORD>"

WARNING: The password may contain characters that must be escaped(i.e., add a \) to prevent your terminal to get confused. Those characters are: !, `.

NOTICE: --region us-east-1 is required and hardcoded because Route 53 is a global service. Without this option, you'll receive a Could not connect to the endpoint URL: "https://route53domains.ap-southeast-2.amazonaws.com/" error message.

Migrating a hosted zone to another AWS account

WARNING: Do not delete the old hosted zone until you've confirmed the traffic is served by the new hosted zone. This means keeping the old hosted zone for at least 48 hours.

The workflow consists in steps:

  1. Connect to your AWS source account profile using the AWS CLI (tips: use npx switch-profile).
  2. Import the records in a JSON file:
aws route53 list-resource-record-sets --hosted-zone-id <HOSTED ZONE ID> > YOUR-RECORDS.json
  1. Login to the AWS console for the account that hosts the new hosted zones and browse to Route 53.
  2. Click on Hosted zones and then click on the Create hosted zone button.
  3. Wrangle the YOUR-RECORDS.json obtained in step 2 and don't include the NS and SOA records as the new hosted zone uses it own. Be careful with alias(more details [here]https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/hosted-zones-migrating.html#hosted-zones-migrating-edit-records). The output should be similar to this sample:
{
	"Changes": [
		{
			"Action": "CREATE",
			"ResourceRecordSet": {
				"Name": "cloudandbytes.com.",
				"Type": "MX",
				"TTL": 300,
				"ResourceRecords": [
					{
						"Value": "1 ASPMX.L.GOOGLE.COM"
					},
					{
						"Value": "5 ALT1.ASPMX.L.GOOGLE.COM"
					},
					{
						"Value": "5 ALT2.ASPMX.L.GOOGLE.COM"
					},
					{
						"Value": "10 ALT3.ASPMX.L.GOOGLE.COM"
					},
					{
						"Value": "10 ALT4.ASPMX.L.GOOGLE.COM"
					}
				]
			}
		},
		{
			"Action": "CREATE",
			"ResourceRecordSet": {
				"Name": "cloudandbytes.com.",
				"Type": "TXT",
				"TTL": 300,
				"ResourceRecords": [
					{
						"Value": "\"v=spf1 include:_spf.google.com ~all\""
					}
				]
			}
		}
	]
}

Trick, use this command to easily achieve the above outcome:

func() { node -e "console.log(JSON.stringify({ Changes:require('$1').ResourceRecordSets.filter(r => r.Type != 'NS' && r.Type != 'SOA').map(r => ({Action:'CREATE',ResourceRecordSet:r}))},null,'  '))" > $2; };func <INPUT_PATH.json> <OUTPUT_PATH.json>
  1. Connect to your AWS target account profile using the AWS CLI (tips: use npx switch-profile).
  2. Export the DNS records JSON file created in step 5:
aws route53 change-resource-record-sets --hosted-zone-id <NEW HOSTED ZONE ID> --change-batch file://<OUTPUT_PATH.json>
  1. Update the domain's name servers with the new name servers from the new hosted zone:
    1. Click on the Registered domains link under the Domains section.
    2. Click on your domain.
    3. Click on the Add or edit name servers button under the Name servers section.
  2. Check until those changes have fully propagated. Use dig <YOUR DOMAIN> NS to monitor the name servers.
  3. When the name servers appear to be associated with the new hosted zone, wait another 48 hours minimum before deleting the old hosted zone.

Migrating a GoDaddy domain to AWS Route 53

  • Migrate the DNS first to avoid traffic interruption:
    1. Backup your current DNS records.
    2. Create a new hosted zone using the domain name in your AWS account.
      • Open AWS Route 53.
      • In the left menu, select Hosted zones, then click on the Create hosted zone button.
    3. Copy your new NS records (excl. the DS record).
    4. (Optional but highly recommended) Decrease the NS records TTL in both your existing DNS and the newly created hosted zone to 1 minute (1).
    5. Configure the hosted zone with the same records than your existing DNS using the records backed up in step 1.
    6. (Optional) Copy the Delegation Signer (DS) record and remove it from the parent zone before you migrate your domain to Route 53 (2).
    7. Browse to your domain provider and replace the NS records with the new Route 53 NS records from step 3 (ignore the dot trailing dot on each NS record). Updating the NS records may take a while. To check whether that change is live, use this command:
     dig YOURDOMAIN NS
    
    1. (Optional) When the NS records are live and traffic is managed by Route 53, re-add the DS record to Route 53.
    2. (Optional) Set the NS records TTL in Route 53 to a higher value if it was lowered in step 4 (recommended value: 172,800 seconds, i.e., 2 days).

(1) The typical TTL for NS records is around 2 days (172,800 seconds). If something goes wrong during your migration, your website could be down for 2 days. (2) If you've configured DNSSEC for your domain, remove the Delegation Signer (DS) record from the parent zone before you migrate your domain to Route 53.

Because it isn't currently possible to have DNSSEC signing enabled across two providers, you must remove any DS or DNSKEYs to deactivate DNSSEC. This temporarily signals to DNS resolvers to disable DNSSEC validation. In step 11, you can re-enable DNSSEC validation if desired, after the transition to Route 53 is completed.

HTTP redirect

Advanced redirect scenarios with S3

Redirecting to a path

In this scenario, you wish you can redirect all request to https://hello.com to https://example.com/test. This will not mask https://example.com/test behind https://hello.com. Instead, it is a real redirect in the browser.

  1. Create a new bucket (WARNING: Its name must match the domain name. For example, example.com or www.example.com (1)).
  2. Select the Properties tab, scroll at the bottom to the Static website hosting section, then click the Edit button.
  3. Under the Static website hosting section, tick the Enable option.
  4. Under the Hosting type select the Redirect requests for an object option.
  5. Under the Host name input, enter the path to the redirect without the protocol (e.g., example.com/test).
  6. Under the Protocol, select the protocol you need.

This above configure a 301 (permanent redirect) to https://example.com/test (assuming that the configured protocol is https).

You can verify this by executing this command:

curl -I -L YOUR_S3_BUCKET_URL

(1) Hosting a static website on S3 does not support wildcard subdomains. Therefore, you must choose the exact subdomain you wish to configure as the bucket name. To support wildcard subdomain, you must configure CloudFront on top of the S3 bucket.

If the requirements must include redirect codes other than 301 (e.g., 302 for found or 307 for temporary redirect), then use an S3 bucket configured with redirect. If requirements also include support for wildcard subdomains, then add a CloudFront distribution on top of the S3 redirection.

Configuring the redirect status code (301, 302 or 307)

In the example above, the HTTP code used was 301 (permanent redirect). To configure the status code to 302 or 307, step 4 in the previous section must be updated to use Host a static website rather than Redirect requests for an object. Once that's done, a new Redirection rules section allows to configure the redirection. For example, to redirect to https://example.com/test with 307, use this:

[
	{
		"Condition": {
			"HttpErrorCodeReturnedEquals": "404"
		},
		"Redirect": {
			"HostName": "example.com",
			"HttpRedirectCode": "307",
			"Protocol": "https",
			"ReplaceKeyPrefixWith": "test"
		}
	},
	{
		"Condition": {
			"HttpErrorCodeReturnedEquals": "403"
		},
		"Redirect": {
			"HostName": "example.com",
			"HttpRedirectCode": "307",
			"Protocol": "https",
			"ReplaceKeyPrefixWith": "test"
		}
	}
]

Configuring wildcard subdomain redirect

  • Create an S3 bucket configured with the correct redirection rules as described in the previous sections.
  • Create a new CloudFront distribution. WARNING: When setting up the Origin domain do not use the auto-completed S3 bucket. This would create an AccessDenied error when browsing the distribution. Instead, get the HTTP public link of the S3 static website (e.g., http://BUCKETNAME.s3-website-REGION.amazonaws.com) and paste it.
  • Select the General tab and click on the Edit button.
  • Under the Alternate domain name (CNAME) section, click on the Add item button and Add your wildcard domain (e.g., *.example.com). WARNING: Add 2 alternate domains, one for the root domain and one for the wildcard (e.g., example.com and *.example.com).
  • Under the Custom SSL certificate section, click on the Request certificate link to create a new SSL certificate for that widlcard subdomain. Once the SSL has been provisioned, refresh the input and select that SSL. WARNING: Request an SSL certificate for the 2 domains (e.g., example.com and *.example.com). This can be done by using a comma seperated list of domains under the Fully qualified domain name input by using the Add another name to this certitifcate button.
  • Save.
  • In Route 53, create 2 new Alias records, one A record for example.com and another for *.example.com to point to the CloudFront distribution.

Troubleshooting

Wildcart CNAME to root domain for S3 bucket yields a 404 Not Found - NoSuchBucket error

That's because hosting a static website on S3 does not support wildcard subdomains. Therefore, you must choose the exact subdomain you wish to configure as the bucket name. To support wildcard subdomain, you must configure CloudFront on top of the S3 bucket.

FAQ

How to configure a custom domain on any CloudFront distribution?

  1. In CloudFront:
    1. Add as many custom domains under the alias (e.g., example.com, app.example.com). The special case of www. resolving to the Apex domain is covered later.
    2. Add an ACM SSL that was created to include all the domains defined in the previous step.
  2. In Route 53, add a new CNAME record (or an A record configured as Alias(1) if the domain is the Apex domain). The record's value is the CloudFront URL (e.g., 12345567.cloudfront.net).

If the requirement is that www. must resolve to the Apex domain, then update step 2 above as follow:

In Route 53:

  1. Add an A record configured as Alias(1). Use the Apex domain for the the record's name and the CloudFront URL (e.g., 12345567.cloudfront.net) for the record's value.
  2. Add a standard CNAME record. Use the www. subdomain for the record's name and the Apex domain for the record's value.

(1) In Route 53, an A record configured with the Alias option is equivalent to a CNAME

How to configure a CNAME on an RDS database?

Opening connections to an Amazon RDS database instance using your domain name

How to migrate a domain to another AWS Account?

Please refer to the Migrating domains from one AWS account to another section.

How to update a domain's name servers?

  • Login to Route 53
  • Click on the Registered domains link under the Domains section.
  • Click on your domain.
  • Click on the Add or edit name servers button under the Name servers section.

How to restore a hosted zone's nameservers in Route 53?

This happens when you forgot to backup the original AWS nameservers. To figure out what it once was, we'll consult the CloudTrail history:

  • Open CloudTrail.
  • Select the Event History.
  • Search for ChangeResourceRecordSets.

How to backup hosted zone's records?

aws route53 list-resource-record-sets --hosted-zone-id YOUR_HOSTED_ZONE_ID --output json > route53_records.json

Annexes

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment