Building Dynamic DNS with Route53 and PFSense

I use PFSense as my home router, firewall, VPN and much more. I’m sure a lot of my readers do as well. One thing that I have always set up is an entry in Route53 that points to my public IP address on my PFSense box. However, I use Comcast so, my IP address is changing every so often.

Typically this isn’t a big deal because only a few applications utilize the DNS entry I have setup. But, what if I could automate the changes by scheduling a job that automatically checks my IP address on the PFSense side and then updates the Route53 record automatically?

A couple of requirements:
– PFSense with the API package installed
– A subdomain setup in Route53 that points to your PFSense box

Some Python to do some magic:

import requests
import json
import boto3


clientid = "<pfsense clientID here>"
key = "<pfsense api key here>"
route53 = boto3.client('route53')
zoneID = "<route53 hosted zone here>"
# be sure to include a trailing "." as this is how Route53 formats things
# EX: https://google.com.
pfsenseDNS = "<Your subdomain>"

headers = {
    "Authorization": f"{clientid} {key}",
    "Content-type": 'application/json'
    }
#GET Pfsense IP
def getWanIP():
    response = requests.get('https://<your subdomain>/api/v1/system/arp', headers=headers)
    arptable = json.loads(response.content)
    entries = arptable['data']
    wan = []

    for entry in entries:
        # change the interface code if necessary
        if entry['interface'] == 'igb0':
            wan.append(entry)
    for entry in wan:
        if entry['status'] == 'permanent':
            wanIP = entry['ip']
            return wanIP

record_set = route53.list_resource_record_sets(
    HostedZoneId=zoneID
)

for record in record_set['ResourceRecordSets']:
    if record['Name'] == pfsenseDNS:
        #pprint.pprint(record)
        if record['Type'] == 'A':
            for entry in record['ResourceRecords']:
                if entry['Value'] != getWanIP():
                    print("The Records Do Not Match")
                    response = route53.change_resource_record_sets(
                        HostedZoneId=zoneID,
                        ChangeBatch={
                            'Changes': [
                                {
                                'Action': 'UPSERT',
                                'ResourceRecordSet': {
                                    'Name': pfsenseDNS,
                                    'Type': 'A',
                                    'ResourceRecords': [
                                        {
                                            'Value': getWanIP(),
                                        }
                                    ],
                                    'TTL': 300,
                                },
                                }
                            ]
                         }
                    )

What this code does is pretty simple. First we have a function that will get us the WAN IP through the ARP table of the PFSense box. We use this function later when we get and check our record sets against this IP address.

If the addresses do not match, the script will automatically change the entry in Route53 for you!

To test out the function modify your Route53 entry to some bogus IP address and then run the script. If everything goes as planned you should see your DNS entry changed!

If you found this helpful please share it with your friends. If you have questions feel free to comment or reach out to me via any method.