Using Python for Dynamic DNS with optional Email Notification
If you host services at home, an ISP can ruin your day by suddenly changing your home IP address. Luckily, many DNS providers allow you to automatically update your records with them via an offering known as Dynamic DNS. My DNS provider, Google Domains, supports either a client or API option and offers instructions here. I chose to use the API method so that I could customize the process and get some practice with Google’s API. The script is scheduled to run every hour and will update my DNS information in the following way:
Lets start at the top.
import requests
import base64
import relayMessage
I rely on two mandatory imports: requests and base64. The requests module is how I query another API to determine my current IP address as well as let Google know of any updates. The Base64 module is used when creating an authorization header I have to provide Google when updates are required. The final module, relayMessage, is an optional module I created that will send me an email notification if my IP has changed.
ip = requests.get('https://api.ipify.org').text
with open('address.txt') as a:
last = a.read()
My first task is to determine what my current IP address is. I found a good API, https://api.ipify.org, that will return your IP address back in a very simple format. Using the requests module I assign what the API returns to the variable ip. To determine if my address has changed, I reference a text file that stored my last known IP address. I assign my last know IP to the variable last. In the next step I will compare the two variables. If they match the script ends and nothing happens. If they don’t the following actions take place:
if last != ip:
with open('address.txt', 'w') as a:
a.write(ip)
relayMessage.send_email('Your IP address has changed!', 'Your IP address was changed to ' + ip)
The first thing I decided to do was to update the text file where I was storing my previous IP address. This prevents me from having to manually update my script or the file if my address does change. Next the script sends an email to me notifying me of the IP change.
In order to use Google Domains’ Dynamic DNS API, you are required to generate a username and password.
username = 'thisIsAFakeuser'
password = 'thisIsAFakePass'
auth = base64.b64encode((username + ':' + password).encode('ascii')).decode()
headers = {'Authorization': 'Basic ' + auth}
Google requires these credentials are submitted via in an Authorization header using Basic Authentication. This is a common header used by many other APIs and applications and thus has a format that must be followed. Specifically it requires that the credentials are submitted in base64. I assign the credentials in Basic auth format using the base64 module to the variable auth. I then reference that when creating a the headers dictionary. Now that our headers are complete, Google requires our updates are submitted as parameters. Here are the parameters we care about updating:
hostname = 'example.com'
params = {'hostname': hostname, 'myip': ip}
I statically set the site I would like to update the record for to the hostname variable. I then create a dictionary called params that assigns the hostname variable and the ip variable to the appropriate parameter names required by Google. We now have all the information required to submit our update to Google’s API.
url = 'https://domains.google.com/nic/update'
updateIp = requests.post(url, headers=headers, params=params)
I assign Google’s API address to the url variable to keep my final update short and sweet. Using the HTTP POST method I send the required information to Google to update my record. For debugging or logging purposes you can reference the updateIp variable if needed. Here is the complete code:
import requests
import base64
import relayMessage
# Check for current and previous IP
ip = requests.get('https://api.ipify.org').text
with open('address.txt') as a:
last = a.read()
# Compare current and previous IP with if statement
if last != ip:
# Update previous IP with new IP, send an email notification
with open('address.txt', 'w') as a:
a.write(ip)
relayMessage.send_email('Your IP address has changed!', 'Your IP address was changed to ' + ip)
# Create an authorization header using credentials provided by Google
username = 'thisIsAFakeuser'
password = 'thisIsAFakePass'
auth = base64.b64encode((username + ':' + password).encode('ascii')).decode()
headers = {'Authorization': 'Basic ' + auth}
# Create a parameters dictionary that includes new ip and hostname
hostname = 'example.com'
params = {'hostname': hostname, 'myip': ip}
# Send the update to Google's URL with the above created headers/parameters
url = 'https://domains.google.com/nic/update'
updateIp = requests.post(url, headers=headers, params=params)
This script keeps my DNS information up to date and prevents me from losing access to my self hosted services while I am away from home.