Quick and Dirty Dynamic DNS Using GoDaddy

39,574

16

61

Introduction: Quick and Dirty Dynamic DNS Using GoDaddy

Need a quick and dirty answer for a (more or less) free Dynamic DNS provider? Well, if you use GoDaddy to register your domain, you have everything you need to do this.

Why would you need dynamic dns?

Imagine if you have some resources at home that you need to access when you're on vacation, but your ISP (I use Verizon FiOS) changes your IP address from time to time. Some providers, like Cable-based ISPs, change the IPs pretty frequently. Well, your choices are to go in and update your DNS record manually, use one of the free (typically restricted to their domain) or premium Dynamic DNS (DDNS) providers, or set up a simple bash script to detect the change and make the update in your own registered domain.

Plus, its free (free, if you already use GoDaddy).

Prerequisites

  1. GoDaddy-registered and managed domain. These are pretty inexpensive ($14.99/yr) and GoDaddy is the biggest registrar (at least in the US). Chances are if you have a domain name, you're probably using GoDaddy already. Plus, they throw in other features like mail forwarding and DNS zone management for free. They also have a nifty API that allows you to change you DNS records programmatically (more on that later).
  2. Some kind of server at your location that can run a bash script. The script is only a few lines and should be easily portable to php or even PowerShell, so you can make Windows-compatible versions too.

Step 1: Create Your DNS Name

First, you have to create your DNS name in GoDaddy (this assumes you have already registered a domain with GoDaddy).

GoDaddy provides several good resources on how to do this.

Add an A record in GoDaddy

I'm attaching a GIF-video from their web site that shows how easy it is.

For "Type" make sure to use "A" -- CNAME won't work.

For "Host" enter the hostname you'd like to use. I like to use "gateway" because it's how I reach the resources behind the entry point to my home network. FYI, I use ssh and rsa keys to tunnel in. I'm working on moving to a pfSense-based VPN, but that's still a work-in-progress.

For "Points to" you can enter anything you want. I use "1.1.1.1" so that it will be obvious my script is working once I run it. This is the value that will change when your ISP changes you IP address.

For "TTL" choose "Custom" then put in an arbitrarily short timeout. This tells systems that read this DNS record how long they should cache it before getting a new one. Typically, this is long (1 hour) for static DNS entries. It helps reduce DNS traffic on the general internet. For dynamic DNS, I like to use something shorter like 600 seconds (10 minutes). You don't want this to be too long because it governs the "switching" time between your old IP address and your new one. If this is an hour, it could take up to an hour to reach your dynamic resource after an IP change. NOTE: you can always flushdns at the endpoint, but sometimes intermediate DNS servers will cache the reply (again, to save on traffic).

The most important thing is to make sure it's an "A" record.

Step 2: Create a GoDaddy API Key

In order to make programmatic changes to the DNS records, you'll need a GoDaddy API key. Here is some background on the process first:

GoDaddy Developer "Get Started" Guide

The short version is, go to this link GoDaddy API Keys and click "Create New API key"

You'll want to give it a new name (I use "Dynamic DNS") and select an environment. GoDaddy recommends you test with the OTE environment first, then move to Production. You're using my code, so you can probably just create a production key.

Next, you'll see your API key and your key secret. Make sure you copy these down. The API key will still be visible when you come back to this screen, but the secret will never be displayed again and cannot be recovered!! If you lose the secret, you'll have to create a new key. HINT: Don't lose the secret.

Step 3: Create a Script to Update DNS

Here is a quick and dirty script you can use to determine your current IP, compare it to your DNS zone file, and update if needed. It's written for BASH (Un*x):

#!/bin/bash

mydomain="sos-obx.us"
myhostname="gateway"
gdapikey="api_key:key_secret"
logdest="local7.info"

myip=`curl -s "https://api.ipify.org"`
dnsdata=`curl -s -X GET -H "Authorization: sso-key ${gdapikey}" "https://api.godaddy.com/v1/domains/${mydomain}/records/A/${myhostname}"`
gdip=`echo $dnsdata | cut -d ',' -f 1 | tr -d '"' | cut -d ":" -f 2`
echo "`date '+%Y-%m-%d %H:%M:%S'` - Current External IP is $myip, GoDaddy DNS IP is $gdip"

if [ "$gdip" != "$myip" -a "$myip" != "" ]; then
  echo "IP has changed!! Updating on GoDaddy"
  curl -s -X PUT "https://api.godaddy.com/v1/domains/${mydomain}/records/A/${myhostname}" -H "Authorization: sso-key ${gdapikey}" -H "Content-Type: application/json" -d "[{\"data\": \"${myip}\"}]"
  logger -p $logdest "Changed IP on ${hostname}.${mydomain} from ${gdip} to ${myip}"
fi

Replace "mydomain" variable with the domain you registered. For example, if you registered "fredshouse.com" then the line should read:

mydomain="fredshouse.com"

Also replace "myhostname" with your hostname, "api_key" with you api key and "key_secret" with your key secret. It's important to note that the actual contents of "gdapikey" will be your api key followed by a colon (":") followed by the secret as one single string. If you get this wrong, the script will not work. Let's say your api key is "abcdefghijklm" and your key secret is "zyx987rst456" then your "gdapikey" will be "abcdefghijklm:zyx987rst456" -- one string with a colon separating the two parts. Again, this is IMPORTANT.

I saved this as /usr/local/sbin/gd-dyndns. Make sure your chmod 700 to both make it executable and to protect it from prying eyes. My script is owned by root (quick and dirty -- I warned you!!).

This script uses ipify.org to determine your current IP address (they return results in a code-friendly way) then compares to the current IP setting in GoDaddy. If they do not match, it uses the API to change the IP address.

I installed this as a cron job (crontab -e) and it runs every 10 minutes:

*/10 * * * *    /usr/local/sbin/gd-dyndns > /dev/null

You should go back to the GoDaddy DNS manager to verify that it updated properly.

That's it!! Quick and dirty but free if you already use GoDaddy as your registrar.

Step 4: Porting to Other Languages

I've given you the bash version. Porting to php should be super simple (use phpcurl). PowerShell might be a bit more of a challenge, but you should look at Invoke-WebRequest.

If the community likes this instructable but needs a php and/or PowerShell version, I might be inclined to write it and come back to update this article.

UPDATE:

Here is a version that works in PowerShell. You can add to the Task Scheduler.

$mydomain = "sos-obx.us"
$myhostname = "gateway" $gdapikey = "api_key:key_secret" $myip = Invoke-RestMethod -Uri "https://api.ipify.org" $dnsdata = Invoke-RestMethod "https://api.godaddy.com/v1/domains/$($mydomain)/records/A/$($myhostname)" -Headers @{ Authorization = "sso-key $($gdapikey)" } $gdip = $dnsdata.data Write-Output "$(Get-Date -Format 'u') - Current External IP is $($myip), GoDaddy DNS IP is $($gdip)" If ( $gpid -ne $myip) { Write-Output "IP has changed!! Updating on GoDaddy" Invoke-RestMethod -Method PUT -Uri "https://api.godaddy.com/v1/domains/$($mydomain)/records/A/$($myhostname)" -Headers @{ Authorization = "sso-key $($gdapikey)" } -ContentType "application/json" -Body "[{`"data`": `"$($myip)`"}]"; }

3 People Made This Project!

Recommendations

  • Digital Fabrication Student Design Challenge

    Digital Fabrication Student Design Challenge
  • Go Big Challenge

    Go Big Challenge
  • Frozen Treats Speed Challenge

    Frozen Treats Speed Challenge

61 Comments

0
bryanl102
bryanl102

3 days ago

Here it is in python!
I named it dynamic_godaddy.py (matters for the Dockerfile - use what you want)

#!/usr/bin/python3.10
import sys
import logging
import logging.handlers
import requests
# create logger
logger = logging.getLogger('dynamic_gd_dns')
logger.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, CRITICAL
# create console handler and set level to debug
# comment out the StreamHandler() if running as a cron job not a container
ch = logging.StreamHandler()
# comment out the SysLogHandler() if running as a container not a cronjob
#ch = logging.handlers.SysLogHandler(address = '/dev/log')
ch.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, CRITICAL
# create formatter
formatter = logging.Formatter('%(name)s - %(levelname)s - %(message)s')
# add formatter to ch
ch.setFormatter(formatter)
# add ch to logger
logger.addHandler(ch)
my_domain = ' ' # name of GoDaddy domain
gd_hostname = ' ' # name of DNS record in GoDaddy to look for
gd_api_key = ' ' # GoDaddy API key
gd_secret = ' ' # GoDaddy secret
ipify_url = 'https://api.ipify.org' # ipify - to get current external IP address
gd_url = f'https://api.godaddy.com/v1/domains/{my_domain}/records/A/{gd_hostname}'# GoDaddy API URL
headers = {'Authorization': f'sso-key {gd_api_key}:{gd_secret}'}
external_ip = requests.get(ipify_url).text
logger.debug(f'External IP: {external_ip}')
# get the IP address from the GoDaddy DNS
gd_dns_request = requests.get(gd_url, headers=headers)
if gd_dns_request.status_code == 200:
gd_dns = gd_dns_request.json()[0]['data']
logger.debug(f'GoDaddy IP: {gd_dns}')
logger.debug(f'GoDaddy response: {gd_dns_request.text}')
else:
logger.error(f'Received {gd_dns_request.status_code} from GoDaddy with info:\n\t\t'
f'{gd_dns_request.text} -- CANNOT CONTINUE')
sys.exit(1)
# check if the addresses match, change GoDaddy if they don't
if external_ip == gd_dns:
logger.info(f'current External IP is {external_ip}, GoDaddy DNS IP is {gd_dns}. All is good')
elif external_ip != gd_dns:
logger.info(f'IP has changed from {gd_dns} to {external_ip}!! Updating GoDaddy')
headers['Content-Type'] = 'application/json'
data = f'[{{"data": "{external_ip}"}}]'
new_gd_dns = requests.put(gd_url, data=data, headers=headers)
if new_gd_dns.status_code == 200:
logger.info('Successfully changed GoDaddy IP')
logger.debug(f'GoDaddy response: {gd_dns_request.text}')
else:
logger.error(f'Received {gd_dns_request.status_code} from GoDaddy with info:\n\t\t'
f'GoDaddy IP address probably NOT CHANGED.')

Use the same cron job if you want to run this directly, or for more layers of abstraction you can make this a container by making a requirements.txt file:
certifi==2022.5.18.1
charset-normalizer==2.0.12
idna==3.3
python-dotenv==0.20.0
requests==2.27.1
urllib3==1.26.9

and a Dockerfile:
FROM python:alpine3.16
WORKDIR /usr/app/src
COPY dynamic_godaddy.py ./
COPY requirements.txt ./
RUN pip3 install -r requirements.txt
CMD ["python3", "./dynamic_godaddy.py"]

then build the image:
docker image build -t gd_dyndns:0.0.1 .

and add this line as your cronjob:
*/10 * * * * docker run --log-driver syslog --log-opt syslog-facility=daemon --rm gd_dyndns:0.0.1 > /dev/null 2>&1

0
douglascblasdell
douglascblasdell

5 weeks ago

Does anyone have a script to run on windows 10 using IPv6?

0
letshin
letshin

3 months ago

Can someone give an explain like I'm 5 version of this?
1. I updated godaddy dns records
2. Ran the bash script - ip was updated on the dns records so all good so far
3. Port forwarded port 22 on my router to my server internal IP address
4. Can't seem to ssh in to gateway.domainname.xxx:22

Do I need to do anything else? Are the nameservers fine on the domaincontrol one? Am I missing anything else out?

0
Tod-SoS
Tod-SoS

Reply 3 months ago

Let's verify a few things...

1) go to https://toolbox.googleapps.com/apps/dig/ and enter your dns name (gateway.domainname.xxx). Pay attention to the TTL (should be low -- 10 mins is good)
2) verify that the ip returned from step 1 matches the ip you see when you browse to https://whatmyip.org
3) make sure you are trying to ssh from an external network -- some commodity routers won't allow you to ssh to the external address from inside your network
4) try to ssh to the ip address as well -- if that doesn't work, the name won't work either

Good luck!!

0
letshin
letshin

Reply 3 months ago

Thanks Tod. I did some research and it seems I may not be able to port forward because my ISP uses CGNAT. Port scanning shows that my forwarded ports are not open and my WAN's IP is also different from what IP scanners deem my IP to be, sadly.

0
Tod-SoS
Tod-SoS

Reply 3 months ago

That sucks. You could work around it with a tiny virtual machine (I use linode.com) and a reverse ssh tunnel. The short version is:

- ssh from your home server to the linode (or other vm)
- allocate a port for a reverse tunnel back to your house (like 8022)
- ssh to the vm on port 8022 and that will open a session to your home server

I use something like this to get to my daughter's emby server behind CGNAT. ssh is an amazing tool for moving traffic to where you want it. also saves you the trouble of this dynamic dns workaround because the vm address will be static.

Good luck!!

0
Web Sharp Studios
Web Sharp Studios

Tip 1 year ago on Step 3

DO NOT set your CronTab job to run every ten minutes. GoDaddy will shut you down faster than you know what happened. DHCP expirations are much longer than that typically

0
mutukrp
mutukrp

Reply 8 months ago

The script updates GoDaddy only when an IP change is detected. So, I don't think this contacts GoDaddy that often.

0
Klaus_K
Klaus_K

9 months ago on Step 3

Brilliant work - many thanks! :-) There is a minor bug in your bash script - the logger statement should reference 'myhostname' rather than 'hostname'. I made a minor addition to log the case when no change was made. I also wrote a 1-liner shell script, to display the log, since I couldn't remember the command syntax:

journalctl --facility local7 -g MyDynDNS

This required me to add the string "MyDynDNS" to the two log statements, to allow me to filter out any other local7 logs.

0
Tod-SoS
Tod-SoS

Reply 9 months ago

Nice updates. I'm probably going to move the code to github so I can make updates like what you suggest and put all the versions (I have bash, powershell, and python) in one location along with tips and tricks like your journalctl above.

0
sumo1rev
sumo1rev

1 year ago

here my fix for powershell: https://pastebin.com/BrU36q1z
added a logfile.txt and fixed the match
also if you want to add to task (hidden and at startup) check my task (xml) or import it and change it
https://pastebin.com/TExt4nwu

run powershell with this arguments and with start in directory (the same of the script)
<Command>C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe</Command>
<Arguments>-noprofile -executionpolicy unrestricted -noninteractive -File C:\myScriptRUN\my_dns\mydns.ps1</Arguments>
<WorkingDirectory>C:\myScriptRUN\my_dns</WorkingDirectory>

and also choose any users to run it, so the script will be hidden (no blue screen for 3-4 sec)


0
paragonuk
paragonuk

Reply 1 year ago

Log file is a great idea, but I personally thing working locally is a better approach, so that you can compare the WAN IP with the last IP in the log file only reaching GD DNS when is necessary, that is, for an update. Then, it doesnt matter how often you check, there wont be any risk of problems with GD due to too many request queries.

0
1_21gigawatts
1_21gigawatts

1 year ago

Thank you. Just what I needed.

0
roadrunner1024
roadrunner1024

1 year ago

just a quick one, powershell uses tls1.0 by default,
so you get this error:
Invoke-RestMethod : The request was aborted: Could not create SSL/TLS secure channel.

put this this line in at the beginning:
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

0
Web Sharp Studios
Web Sharp Studios

Tip 1 year ago on Step 4

Great article! I love GoDaddy I recommend them to all my clients. Here is an updated script that works with IPv6. Make sure your CronTab job runs at a lesser than or equal to interval for your DHCP expiration. If you use Comcast that expiration interval is every 6000 minutes. Here's the CronTab script as well...

***************

#!/bin/bash

mydomain="websharpstudios.com"
myhostname="@"
gdapikey=""

myip=`curl -s "https://api.ipify.org"`
dnsdata=`curl -s -X GET -H "Authorization: sso-key ${gdapikey}" "https://api.godaddy.com/v1/domains/${mydomain}/records/A/${myhostname}"`
gdip=`echo $dnsdata | cut -d ',' -f 1 | tr -d '"' | cut -d ":" -f 2`
echo "`date '+%Y-%m-%d %H:%M:%S'` - Current External IP is $myip, GoDaddy DNS IP is $gdip"

if [ "$gdip" != "$myip" -a "$myip" != "" ]; then
echo "IP has changed!! Updating on GoDaddy"
curl -s -X PUT "https://api.godaddy.com/v1/domains/${mydomain}/records/A/${myhostname}" -H "Authorization: sso-key ${gdapikey}" -H "Content-Type: application/json" -d "[{\"data\": \"${myip}\"}]"
fi

## IP 6 AAAA Records

myip=`wget -6 "https://ifconfig.co"`
dnsdata=`curl -s -X GET -H "Authorization: sso-key ${gdapikey}" "https://api.godaddy.com/v1/domains/${mydomain}/records/AAAA/${myhostname}"`
gdip=`echo $dnsdata | cut -d ',' -f 1 | tr -d '"' | cut -d ":" -f 2`
echo "`date '+%Y-%m-%d %H:%M:%S'` - Current External IP6 is $myip, GoDaddy DNS IP6 is $gdip"

if [ "$gdip" != "$myip" -a "$myip" != "" ]; then
echo "IP6 has changed!! Updating on GoDaddy"
curl -s -X PUT "https://api.godaddy.com/v1/domains/${mydomain}/records/AAAA/${myhostname}" -H "Authorization: sso-key ${gdapikey}" -H "Content-Type: application/json" -d "[{\"data\": \"${myip}\"}]"
fi

0
bootcamp20
bootcamp20

1 year ago

This was a big help. Thank you very much!

0
daveritchie
daveritchie

1 year ago

Great work Tod, I have been looking for something like this for a long time.

I am using the powershell script and noticed it runs fine on Windows 10, however when I move it on Windows Server 2016 Datacenter, I get the following error when connecting to https://api.godaddy.com.

"Invoke-RestMethod : The request was aborted: Could not create SSL/TLS secure channel"

To fix this included the following line before the "$myip = ....."

[Net.ServicePointManager]::SecurityProtocol = "tls12, tls11, tls"

And now it works as it should.

Dave

0
MikeysLab
MikeysLab

1 year ago

Here is a cleaner shell script:

#!/bin/bash
domain=$1
host=$2
APIKey=$3
APISecret=$4

WanIP=`curl -s "https://api.ipify.org"`

GDIP=`curl -s -X GET -H "Authorization: sso-key ${APIKey}:${APISecret}" "https://api.godaddy.com/v1/domains/${domain}/records/A/${host}" | cut -d'[' -f 2 | cut -d']' -f 1 | jq -r '.data'`

if [ "$WanIP" != "$GDIP" -a "$WanIP" != "" ]; then
curl -s -X PUT "https://api.godaddy.com/v1/domains/${domain}/records/A/${host}" -H "Authorization: sso-key ${APIKey}:${APISecret}" -H "Content-Type: application/json" -d " [{\"data\": \"${WanIP}\"}]"
fi

0
FedderSkovgaard
FedderSkovgaard

1 year ago on Step 4

There's a typo in the Powershell script at line 10:
"If ( $gpid -ne $myip) {"
should read:
If ( $gdip -ne $myip) {

Otherwise you are comparing $myip with an empty (non-initialized) variable
0
CristianC22
CristianC22

1 year ago

A good formulated query on Google pointed me to this brilliant article.
KUDOS to the writer, this is exactly what I was after for one of my clients using an unreliable ISP.
Respect and many thanks!