White Label / Vanity Name Servers with Amazon Route 53

White Label or Vanity name servers are DNS servers that are referenced by the name of the domain they host. So for example the name servers for the cloudjunkie.io domain are:

  • ns1.cloudjunkie.io
  • ns2.cloudjunkie.io
  • ns3.cloudjunkie.io
  • ns4.cloudjunkie.io

This can seem counter-intuitive when you think about it, as how can a lookup for the cloudjunkie.io domain reference a record set that is on the cloudjunkie.io domain? (Glue records hold the key to the answer, read-on to find out more)

If you'd just like a script that does this for you, jump down to the PowerShell Script.

Process Overview

The high level steps for setting up white labelled name servers are:

  1. Purchase the domain
     
  2. Register a reusable delegation set
    This allocates name servers before creating the hosted zone.
     
  3. Create the hosted zone referencing the reusable delegation set
    This tells Route 53 to use the name servers that were allocated to us via the reusable delegation set (instead of going through the normal process to assign name servers).
     
  4. Resolve the IP addresses of the name servers in the reusable delegation set
    We need the IP addresses to create the A / AAAA records in the next step.
     
  5. Create A / AAAA resource records on the zone using the names for the name servers
    This creates the linkage between the name NSx.CloudJunkie.io and the IP address of the name server that will respond to queries for this zone.
     
  6. Update the SOA record
    We've changed the name servers, a name server is mentioned in the SOA, we should update the SOA to include the updated name server.
     
  7. Update the NS records
    The NS records also reference the names of the name servers. As we've updated the names of the name servers, we should also update the names in the NS records.
     
  8. Update the name servers in the registered domain to use the new names and include glue records
    For a normal domain we would just provide the names of the name servers that will respond to queries for our domains. For white labelled domains, we also need to provide a glue record which contains both the name and the IP addresses of the name servers. This allows queries to be 

Delegation Sets

A delegation set can be thought of as a way to allocate name servers before creating a hosted zone in Route 53.

Having a delegation set means you are able to use the same name servers to host multiple zones. This allows you to have multiple domains hosted on and pointing to your vanity name servers. The following diagram shows the concept.

Note: Only the CloudJunkie.io domain has name server records defined. All other domains use these name servers. This works because all domains are hosted on the same Reusable delegation set, so when a query for a different domain is sent, it will still be answered by those servers.

Note: Only the CloudJunkie.io domain has name server records defined. All other domains use these name servers. This works because all domains are hosted on the same Reusable delegation set, so when a query for a different domain is sent, it will still be answered by those servers.

Route 53 Hosted Zone Configuration

A normal domain looks like the following within the Route 53 console.

Normal-Domain.png

A white labelled domain looks like the following.

White-Labelled-Domain.png

For the white labelled domain:

  • NS records point to the vanity name servers
  • Each vanity name server has A / AAAA resource records resolving to the appropriate IP address
  • The Start Of Authority (SOA) record references the first white labelled name server.

Glue Records

A DNS Glue record associates a DNS name with an IP address at the DNS registry. This allows clients to be provided with the name of a name server (e.g. ns1.cloudjunkie.io) along with the corresponding IP address for it. This, in turn, allows clients to then query the correct name servers for records on the domain.

Within AWS this is configured within Route 53 -> Registered Domains.

RegisteredDomains.png

Then once you have selected the domain name, click the link to Add or edit name servers (see image below).

AddEditNameServers.png

If you enter a name server that is on the domain that you're editing, you will be prompted to provide the Glue records.

GlueRecords.png

PowerShell Script

The following PowerShell script will create a white labelled name server and use it on one or more domains.

For this to execute, you'll need to have the AWS Tools for Windows PowerShell installed, and to have setup a named profile via Set-AWSCredential -AccessKey <AccessKeyGoesHere> -SecretKey <SecretKeyGoesHere> -StoreAs <ProfileNameGoesHere>.

A sample of the script execution is included further down. It is also worth noting that this script has not been prepared for production usage, but just as a quick example (so it doesn't have things like robust error handling, logging, and it uses cheap hacks such as a 15 second delay to allow time for Route 53 to create the hosted zones).

[CmdletBinding()]
Param (
    [Parameter(Mandatory=$true)]
    [string[]] $DomainNames,

    [Parameter(Mandatory=$true)]
    [string] $ProfileName
)

ipmo AWSPowerShell

$NameServerCount = 0
$NameServerRecords = @()
$NameServerTranslation = @()

# The first domain name provided, is the one which will house 
# the resource record sets for the white labelled name servers
$NameServerDomainName = $DomainNames[0]

# Create an Amazon Route 53 reusable delegation set
Write-Host "Creating Reusable Delegation Set"
$OperationReference = [System.Guid]::NewGuid().ToString()
$ReusableDelegationSet = New-R53ReusableDelegationSet -CallerReference $OperationReference -ProfileName $ProfileName


$ReusableDelegationSet.DelegationSet.NameServers | % {

    # Get the IP Adddresses for the Name Servers in the Reusable Delegation Set

    Write-Host "Resolving DNS Entries for $_"

    $DnsEntries = Resolve-DnsName $_
    $ResourceName = "ns{0}.{1}." -f ++$NameServerCount, $NameServerDomainName

    $NameServerTranslation += @{ $("{0}." -f $_) = $ResourceName }

    # Get the IPv4 record
    $NameServerRecords += @{
        Type = "A";
        IP = ($DnsEntries | ? { $_.Type -eq "A" })[0].IPAddress;
        ResourceName = $ResourceName
    }

    # Get the IPv6 record
    $NameServerRecords += @{
        Type = "AAAA";
        IP = ($DnsEntries | ? { $_.Type -eq "AAAA" })[0].IPAddress;
        ResourceName = $ResourceName
    }
}

$DomainNames | % {

    # Create the hosted zones
    Write-Host ""
    Write-Host "Creating Hosted Zone for $_"

    $OperationReference = [System.Guid]::NewGuid().ToString()
    $CreateHostedZoneResult = New-R53HostedZone -CallerReference $OperationReference -DelegationSetId $ReusableDelegationSet.DelegationSet.Id -Name $_ -ProfileName $ProfileName

    Write-Host "Waiting for Hosted Zone to be Created (15 second delay)"

    Start-Sleep -Seconds 15

    Write-Host "Getting Initial Resource Record Sets for Hosted Zone"

    $DefaultResourceRecordsResponse = Get-R53ResourceRecordSet -HostedZoneId $CreateHostedZoneResult.HostedZone.Id -ProfileName $ProfileName

    if ($_ -eq $NameServerDomainName) {

        # Create Resource Record Sets for White Label Name Servers

        Write-Host "Creating Resource Record Sets for White Label Name Servers"

        $ChangeSet = @()

        $NameServerRecords | % {
            $Change = New-Object Amazon.Route53.Model.Change
            $Change.Action = "CREATE"
            
            $RRSet = New-Object Amazon.Route53.Model.ResourceRecordSet
            $RRSet.Name = $_.ResourceName
            $RRSet.Type = $_.Type
            $RRSet.TTL = 60
            $RRSet.ResourceRecords.Add(@{Value=$_.IP})
            
            $Change.ResourceRecordSet = $RRSet

            $ChangeSet += $Change
        }

        $EditWhiteLabelResourceRecordSetResponse = Edit-R53ResourceRecordSet -HostedZoneId $CreateHostedZoneResult.HostedZone.Id -ProfileName $ProfileName -ChangeBatch_Change $ChangeSet -ChangeBatch_Comment "Create resource record sets for while label name servers"

    }

    # Update NS and SOA records

    Write-Host "Preparing SOA Record for $_"

    $ChangeSet = @()

    $DefaultSOA = ($DefaultResourceRecordsResponse.ResourceRecordSets | ? { $_.Type -eq "SOA" })[0]
    $SOAValues = $DefaultSOA.ResourceRecords[0].Value.Split(' ')
    $SOAValues[0] = $NameServerTranslation.$($SOAValues[0])

    $RemoveSOA = New-Object Amazon.Route53.Model.Change
    $RemoveSOA.Action = "DELETE"
    $RemoveSOA.ResourceRecordSet = $DefaultSOA

    $CreateSOA = New-Object Amazon.Route53.Model.Change
    $CreateSOA.Action = "CREATE"

    $RRSet = New-Object Amazon.Route53.Model.ResourceRecordSet
    $RRSet.Name = $DefaultSOA.Name
    $RRSet.Type = $DefaultSOA.Type
    $RRSet.TTL = $DefaultSOA.TTL
    $RRSet.ResourceRecords.Add(@{ Value = [System.String]::Join(' ', $SOAValues) })

    $CreateSOA.ResourceRecordSet = $RRSet

    $ChangeSet += $RemoveSOA, $CreateSOA

    Write-Host "Updating SOA Record to use White Labelled Name Servers for $_"

    $EditSoaResourceRecordSetResponse = Edit-R53ResourceRecordSet -HostedZoneId $CreateHostedZoneResult.HostedZone.Id -ProfileName $ProfileName -ChangeBatch_Change $ChangeSet -ChangeBatch_Comment "Update SOA to use white label name servers"

    Write-Host "Preparing NS Records for $_"

    $ChangeSet = @()

    $DefaultNS = ($DefaultResourceRecordsResponse.ResourceRecordSets | ? { $_.Type -eq "NS" })[0]

    $RemoveNS = New-Object Amazon.Route53.Model.Change
    $RemoveNS.Action = "DELETE"
    $RemoveNS.ResourceRecordSet = $DefaultNS

    $CreateNS = New-Object Amazon.Route53.Model.Change
    $CreateNS.Action = "CREATE"

    $RRSet = New-Object Amazon.Route53.Model.ResourceRecordSet
    $RRSet.Name = $DefaultNS.Name
    $RRSet.Type = $DefaultNS.Type
    $RRSet.TTL = $DefaultNS.TTL
    $DefaultNS.ResourceRecords | % {
        $RRSet.ResourceRecords.Add(@{ Value = $NameServerTranslation.$($_.Value) })
    }

    $CreateNS.ResourceRecordSet = $RRSet

    $ChangeSet += $RemoveNS, $CreateNS

    $EditNsResourceRecordSetResponse = Edit-R53ResourceRecordSet -HostedZoneId $CreateHostedZoneResult.HostedZone.Id -ProfileName $ProfileName -ChangeBatch_Change $ChangeSet -ChangeBatch_Comment "Update NS to use white label name servers"
}

Write-Host ""

$NameServerRecords | select @{ Label="ResourceName";Expression={$_["ResourceName"] } } -Unique | % {
    $ResourceName = $_.ResourceName

    Write-Output @{
        NameServer = $ResourceName;
        GlueIPv4 = ($NameServerRecords | ? { $_.ResourceName -eq $ResourceName -and $_.Type -eq 'A' }).IP;
        GlueIPv6 = ($NameServerRecords | ? { $_.ResourceName -eq $ResourceName -and $_.Type -eq 'AAAA' }).IP;
    }
} | Select-Object @{ Label="NameServer"; Expression={$_["NameServer"]} }, @{ Label="GlueIPv4"; Expression={$_["GlueIPv4"]} }, @{ Label="GlueIPv6"; Expression={$_["GlueIPv6"]} }

Write-Host "Done"
Write-Host ""

 

PowerShell Script Sample Output

The following script shows how to create a white labelled name server for cloudjunkie.io and have the same name servers used for cloudjunkiehq.com and cloudjunkyhq.com.

The first domain name in the list will have the white labelled name servers created, and any additional ones will also use those name servers. The script will output the glue records required.

.\New-WhiteLabelNameServers.ps1 -DomainNames @("cloudjunkie.io", "cloudjunkiehq.com", "cloudjunkyhq.com") -ProfileName Alex.CJ.DNS

Creating Reusable Delegation Set
Resolving DNS Entries for ns-422.awsdns-52.com
Resolving DNS Entries for ns-1783.awsdns-30.co.uk
Resolving DNS Entries for ns-601.awsdns-11.net
Resolving DNS Entries for ns-1315.awsdns-36.org

Creating Hosted Zone for cloudjunkie.io
Waiting for Hosted Zone to be Created (15 second delay)
Getting Initial Resource Record Sets for Hosted Zone
Creating Resource Record Sets for White Label Name Servers
Preparing SOA Record for cloudjunkie.io
Updating SOA Record to use White Labelled Name Servers for cloudjunkie.io
Preparing NS Records for cloudjunkie.io

Creating Hosted Zone for cloudjunkiehq.com
Waiting for Hosted Zone to be Created (15 second delay)
Getting Initial Resource Record Sets for Hosted Zone
Preparing SOA Record for cloudjunkiehq.com
Updating SOA Record to use White Labelled Name Servers for cloudjunkiehq.com
Preparing NS Records for cloudjunkiehq.com

Creating Hosted Zone for cloudjunkyhq.com
Waiting for Hosted Zone to be Created (15 second delay)
Getting Initial Resource Record Sets for Hosted Zone
Preparing SOA Record for cloudjunkyhq.com
Updating SOA Record to use White Labelled Name Servers for cloudjunkyhq.com
Preparing NS Records for cloudjunkyhq.com

Done

NameServer          GlueIPv4        GlueIPv6              
----------          --------        --------              
ns1.cloudjunkie.io. 205.251.193.166 2600:9000:5301:a600::1
ns2.cloudjunkie.io. 205.251.198.247 2600:9000:5306:f700::1
ns3.cloudjunkie.io. 205.251.194.89  2600:9000:5302:5900::1
ns4.cloudjunkie.io. 205.251.197.35  2600:9000:5305:2300::1