As I’m running this site itself on my 4TB WDMyCloud home NAS, refer to my post WebHosting on WDMyCloud V4 Firmwares, I will need my DNS to be always pointing to it when my public IP changes.
Although my public IP seldom changes, I’ve been using a Python based script with its ‘pygodaddy’ module for a while to auto update my GoDaddy DDNS pointing to my WDMyCloud NAS. The script is supposed to update my current public IP by parsing GoDaddys DNS update form. But on few occasions when there’s the need to update, the script failed to work because obviously GoDaddy had changed something in their front-end. I’ve also used a similar PHP based script but that too failed to work. And worst still, for as long that I know of, GoDaddy hasn’t provide any means of updating the records other than manually from their site itself.
Like many others with paid GoDaddy domain account, I’ve been looking for permanent solution not until recently and long awaited for, GoDaddy has finally implemented some APIs for developments which now provides the ability to update the DNS records via a REST call. You can see more details at their developer’s site, https://developer.godaddy.com.
Disclaimer: As I’m frequently updating the original guides and installers here on TeaNazaR.com, I will not be responsible for any brick issues if you were to follow my obsolete guides copied elsewhere. Thus subscribe to this post to get latest updates. Modifying any part of a device may void its warranty.
With this API, I’ve written another simple shell script to perform the DDNS update which can be scheduled with a 5mins repeating Cron job. It should work on any Linux based systems. Why shell script? It’s simple, low-resource and does the update faster than any similar Python, PHP, Perl, Java JIT compiler I’ve tested on the NAS. On each run, the script will check for public IP changes against the local cache and GoDaddy records itself using cURL and only attempt to update if necessary. Even though GoDaddy did not specify the minimum update interval, updating too frequently could affect DNS propagation which could cause delay of the DNS being updated across the internet.
First you will need is of course your paid domain name, then a GoDaddy “Production” API key/secret to use with this script. Go to this specific link, log in with your GoDaddy account then generate the API key/secret. Note that the very first time you do this, it will be created as “Test” environment (seen at the time of writing). Go back to the same page again or click on the “Keys” top menu then generate a new “Production” API key/secret instead.
You can place this script anywhere on your server. I’ll explain more for users who’s not familiar with Linux.
Launch the editor i.e. nano /root/scripts/GoDaddy.sh; . Copy & paste the script source from the bottom of this post into the terminal, update the API key/secret , Domain and other desired options, then CTRL+X to save. Test the script if it’s working:
1 |
/bin/bash /root/scripts/GoDaddy.sh; |
Once the script is in place, schedule a Cron job every 5mins, crontab -e; , examples below on a single line:
a. No logging (recommended):
1 |
*/5 * * * * /bin/bash /root/scripts/GoDaddy.sh>/dev/null 2>&1 |
b. With logging for debugging purposes (you will need to do your own log housekeeping, try logrorate?):
1 |
*/5 * * * * /bin/bash /root/scripts/GoDaddy.sh>>/var/log/GoDaddy.sh.log |
To update a subdomain (second-level domain i.e. subdomain.domain.com) instead of primary domain (top-level domain i.e. domain.com), set
Type=A
Type=CNAME (Updated: CNAME by default can’t point to an IP) and
Name=subdomain where subdomain is your subdomain.domain.com as seen in the GoDaddy DNS setup page.
I’ve also added optional advanced variables to include any custom scripts, programs or commands to execute when the IP update occurs or failed. This is so that you can place this script on top of your other applications i.e. those which requires public IP update cached in /tmp/current_ip or those which triggers upon IP changes. Read the script’s comments for more details. For instance, you can use Postfix‘s sendmail to trigger an email notification upon IP update or failures by setting the SuccessExec or FailedExec values. Those who’s using my WebHosting on WDMyCloud V4 Firmwares mods, you can configure its ESMTP to get similar results. For an example to get notified upon successful IP update, set the SuccessExec value to:
1 |
SuccessExec='echo -e "To:myself@mail.teanazar.com\nFrom:alert@mail.teanazar.com\nSubject:GoDaddy.sh IP Updated\n\nGoDaddy.sh IP Updated ${Domain} ${PublicIP} $(date)\n"|sendmail "myself@mail.teanazar.com"' |
This should be the permanent solution for now until GoDaddy decides to make other changes. Need a Windows based DDNS client updater? Not sure why would anyone leave their PC always awaked, unless it’s a Windows based NAS/server. Perhaps I can help to write a simple Windows executable. But you can always run Cygwin to deploy shell scripts also Ubuntu is already runnable in Windows 10 Insider Preview (at the time of writing). For now, only my NAS running 24/7 needs this. Any questions you can post it here, enjoy (-:
GoDaddy.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
#!/bin/bash # GoDaddy.sh v1.3 by Nazar78 @ TeaNazaR.com ########################################### # Simple DDNS script to update GoDaddy's DNS. Just schedule every 5mins in crontab. # With options to run scripts/programs/commands on update failure/success. # # Requirements: # - curl CLI - On Debian, apt-get install curl # # History: # v1.0 - 20160513 - 1st release. # v1.1 - 20170130 - Improved compatibility. # v1.2 - 20180416 - GoDaddy API changes - thanks Timson from Russia for notifying. # v1.3 - 20180419 - GoDaddy API changes - thanks Rene from Mexico for notifying. # # PS: Feel free to distribute but kindly retain the credits (-: ########################################### # Begin settings # Get the Production API key/secret from https://developer.godaddy.com/keys/. # Ensure it's for "Production" as first time it's created for "Test". Key=fsvVddxVsfe_frhN1SAgfdgCdAfyVdWDdv Secret=FwkUfafh8dgrsdydrdfC1e # Domain to update. Domain=teanazar.com # Advanced settings - change only if you know what you're doing :-) # Record type, as seen in the DNS setup page, default A. Type=A # Record name, as seen in the DNS setup page, default @. Name=@ # Time To Live in seconds, minimum default 600 (10mins). # If your public IP seldom changes, set it to 3600 (1hr) or more for DNS servers cache performance. TTL=600 # Writable path to last known Public IP record cached. Best to place in tmpfs. CachedIP=/tmp/current_ip # External URL to check for current Public IP, must contain only a single plain text IP. # Default http://api.ipify.org. CheckURL=http://api.ipify.org # Optional scripts/programs/commands to execute on successful update. Leave blank to disable. # This variable will be evaluated at runtime but will not be parsed for errors nor execution guaranteed. # Take note of the single quotes. If it's a script, ensure it's executable i.e. chmod 755 ./script. # Example: SuccessExec='/bin/echo "$(date): My public IP changed to ${PublicIP}!">>/var/log/GoDaddy.sh.log' SuccessExec='' # Optional scripts/programs/commands to execute on update failure. Leave blank to disable. # This variable will be evaluated at runtime but will not be parsed for errors nor execution guaranteed. # Take note of the single quotes. If it's a script, ensure it's executable i.e. chmod 755 ./script. # Example: FailedExec='/some/path/something-went-wrong.sh ${Update} && /some/path/email-script.sh ${PublicIP}' FailedExec='' # End settings Curl=$(which curl 2>/dev/null) [ "${Curl}" = "" ] && echo "Error: Unable to find 'curl CLI'." && exit 1 [ -z "${Key}" ] || [ -z "${Secret}" ] && echo "Error: Requires API 'Key/Secret' value." && exit 1 [ -z "${Domain}" ] && echo "Error: Requires 'Domain' value." && exit 1 [ -z "${Type}" ] && Type=A [ -z "${Name}" ] && Name=@ [ -z "${TTL}" ] && TTL=600 [ "${TTL}" -lt 600 ] && TTL=600 echo -n>>${CachedIP} 2>/dev/null [ $? -ne 0 ] && echo "Error: Can't write to ${CachedIP}." && exit 1 [ -z "${CheckURL}" ] && CheckURL=http://api.ipify.org echo -n "Checking current 'Public IP' from '${CheckURL}'..." PublicIP=$(${Curl} -kLs ${CheckURL}) if [ $? -eq 0 ] && [[ "${PublicIP}" =~ [0-9]{1,3}\.[0-9]{1,3} ]];then echo "${PublicIP}!" else echo "Fail! ${PublicIP}" eval ${FailedExec} exit 1 fi if [ "$(cat ${CachedIP} 2>/dev/null)" != "${PublicIP}" ];then echo -n "Checking '${Domain}' IP records from 'GoDaddy'..." Check=$(${Curl} -kLsH"Authorization: sso-key ${Key}:${Secret}" \ -H"Content-type: application/json" \ https://api.godaddy.com/v1/domains/${Domain}/records/${Type}/${Name} \ 2>/dev/null|grep -Eo '\b([0-9]{1,3}\.){3}[0-9]{1,3}\b' 2>/dev/null) if [ $? -eq 0 ] && [ "${Check}" = "${PublicIP}" ];then echo -n ${Check}>${CachedIP} echo -e "unchanged!\nCurrent 'Public IP' matches 'GoDaddy' records. No update required!" else echo -en "changed!\nUpdating '${Domain}'..." Update=$(${Curl} -kLsXPUT -H"Authorization: sso-key ${Key}:${Secret}" \ -H"Content-type: application/json" -w"%{http_code}" -o/dev/null \ https://api.godaddy.com/v1/domains/${Domain}/records/${Type}/${Name} \ -d"[{\"data\":\"${PublicIP}\",\"ttl\":${TTL}}]" 2>/dev/null) if [ $? -eq 0 ] && [ "${Update}" -eq 200 ];then echo -n ${PublicIP}>${CachedIP} echo "Success!" eval ${SuccessExec} else echo "Fail! HTTP_ERROR:${Update}" eval ${FailedExec} exit 1 fi fi else echo "Current 'Public IP' matches 'Cached IP' recorded. No update required!" fi exit $? |
Only one thing i added. I put the “date” command prior to the run. I am logging and the log looks like nothing is happening. I also setup a log rotation with Webmin.
Hi Jayden,
If you’re logging you should see outputs in the log file similar to the first image top of this post. Yes there’s no time-stamps (which you can easily add) because for me I’m only interested in successful updates or failures which will be emailed to me together with the time-stamp when it occurs using the
SuccessExec
andFailedExec
variables.Hi Nazar,
Any reason why you didn’t just use noip.com’s free ddns service?
You configure a host for yourself and configure your godaddy’s hosts as cnames that point at the noip.com free host. That’s what I did. It works great. No script necessary… no cross platform issues (I use a windows server) if you configure noip updating in your router either. For the root domain, just configure a redirect in godaddy dns that points yourdomain.com to http://www.yourdomain.com, and your done. I even have a mail server set using this method. Works great…
Pete
Hello Pete,
I have it in my router but no longer using it. Noip.com’s free service needs confirmation (re-login) every 30 days. We could have it automated but that’ll just add another process on top of the IP updates.
But the main reason I prefer “A” records is due to this:
https://tools.ietf.org/html/rfc1912#section-2.4
Nazar,
That article is an interesting read, but it doesn’t appear to be the case with godaddy’s dns server. I have several cnames set up for my domains, an mx record, an spf record, and autodiscover, and a redirect for the root host and everything is working as expected. The cnames (maybe 4 of them) all work, and coexist with one another without issue. I have one set aside for my exchange server, and it works in harmony with the ssl cert I purchased for it as expected when I setup outlook for someone outside of my network.
If you think about that, you’ll realize that by autodiscover cname, my exchange server cname, my mx record and my spf record (which refrence the cnames) are all being accessed and accurately read by Outlook 2016 while the email account is being set up.
So it appears that the cname coexisting issue doesn’t appear to be an issue with godaddy’s dns servers.
I wasn’t aware of noip’s 30 day login requirement. I’ll look into that… if that turns out to be an issue I’ll give dyn.com their 3 bucks a month for their dynamic updating service, and configure it the same way.
If I run into an issue with the cnames, I’ll come back and post it here and let you know you were right. But at the moment, I think you should explore my alternative. You may be pleasantly surprised.
Pete
Hello Pete,
The RFC 1912 Section 2.4 article still applies to GoDaddy’s DNS server. If you already have an existing CNAME “mail” setup, you won’t be able to add any other records like MX or TXT with the same “mail” name. You will get this error “The specified CNAME already exists in the zone”. If you noticed this also applies if you already have any named data including a named “A” record. But you will still be able to add multiple data records to the root “@” which is the “A” record.
I agree that the method you use is a great simple alternative. I guess it depends on our requirements and how we want the setup to be. As mentioned I have it in my router (noip.com’s updater) and used this canonical name IP updates method before sometime ago but not till I decided to fully rely on the root domain as an “A” record not just forwarding (cloaked or 301 redirected). I have a customized Perl FCGI daemon running on my server linked to my PHP APIs that needs the root domain to be SSL (no redirects, HTTP referrers are dropped when redirected from SSL to non-SSL and you can’t have SSL on GoDaddy’s forwarded/cloaked URL). Notwithstanding that, I also have setup several CNAMEs with their SSL certs (SANs), some pointing to the root “@” while others to external DNS (CDN stuffs) and a wildcard “*” pointing back to the root “@”.
Nazar,
Yes, I see that noip requires you to log in and “confirm” your free dynamic dns account every 30 days… like they can’t tell who’s using their dynamic hosts and who’s not. Lol.
On the other hand, to avoid this issue they only want 25 bucks a year (or around $2.08 / month). In my opinion, that’s well worth it.
Pete
Hello.
I download and run the ddns updater, but this message appear:
Fail! {“code”:” UNABLE_TO_AUTHENTICATE”,”message”:”Unable to authenticate”,”name”:”ApiError”}
I have created a production key and secret at godaddy.com and put its into script
name is set as: @
What fail?
I need help, please.
Sorry for my previous message: the key was bad writed! Solved!
Hi cesar,
No problem, glad it worked out for you (-:
How can I use this to update multiple A domain records at once?
Right now my config looks like this:
Name=”@”,”www”,”cloud”
But I get an invalid records error, “One or more of the given records is invalid”
Hi Alex,
This script was made simple and only can update one record. This is mainly because for a machine that has only one IP, you can just simply use one “A” record e.g. “@” then point the rest of the records as a “CNAME” to this is particular “A” record.
However if you wish, you may still run multiple instances of this script on the same machine to update the same IP, note that the
CachedIP
variable needs to be unique for each script e.g.CachedIP=/tmp/current_ip-domain-a
. Best scenario is to use theSuccessExec
variable. I.e. Only schedule one script every 5mins to check for “@” records, then upon update success, it will run the rest of the scripts stated in theSuccessExec
variable e.g.SuccessExec='/script/GoDaddy-www.sh;/script/GoDaddy-cloud.sh;'
.Thank you for the Script it worked great
except the /tmp/current _ip file would not be deleted at the end of it. which caused the updater to not update the dns on godaddy on my ubuntu server when ran a second time.
I adding rm -f /tmp/current_ip at the end of the GoDaddy.sh fixed my issue.
I hope this helps someone else.
Hi Adamt2,
No problem glad it works great for you (-:
As for your deal with the
/tmp/current_ip
, this file wasn’t supposed to be deleted at the end of the run. It contains your current cached IP updated from theCheckURL
variable so that when the script runs again, it will only check for IP changes and will not trigger a check with GoDaddy unless of course your IP has actually changed. If this file is deleted after the run, the script is then forced to check with GoDaddy every 5mins hence defeating the purpose of having a cache. I don’t think GoDaddy’s API is suit to do this frequent checks, thus the reason of having a cache from a faster way of retrieving IP from REMOTE_ADDR.I’m assuming you encountered this because you’re testing by changing the “A” records manually to a dummy IP at GoDaddy DNS settings page and expect the script to update it automatically. If this is the case then yes the script won’t be updating your IP because the cache
/tmp/current_ip
still contains your current IP unless this cache file doesn’t exists (on the first run or tmpfs cleared at reboot).Nazar,
Wonderful script. I am currently using a pfSense router to update the @ A record of 3 domains to point to a ESXi based VM wordpress host.
It works well. It’s automated and it’s free.
But, I’m limited to updating 3 domains. And I need about 3 times that. So, while looking for some method of improving my current process without incurring cost and without ramping up the complexity (multiple DYNDNS accounts to keep track of) I came across this jewel you’ve created.
It would be PERFECT if one could specify multiple domain names to update.
In my scenario the following would be true:
– Update would be to a single A record for each of the domain names.
– {PublicIP} would be the same for each domain name
-Each Domain is covered by the same API key.
Any ideas on how to create the array of Domains to update, without simply looping the script or running multiple instances?
Thanks again for a very nice script.
After working on this a couple of hours this morning, I came up with this.
Notable changes:
Multiple Domain support along with tracking for each domain.
Option to always refresh cache incase domain record was updated from a different source.
Email notifications
Some minor console output changes suited more to my taste.
!/bin/bash
GoDaddy.sh v1.1 by Nazar78 @ TeaNazaR.com
v1.A – 20170607 – First Fork by me@kaacee.com to add support for multiple domains
#
Simple DDNS script to update GoDaddy’s DNS. Just schedule every 5mins in crontab.
With options to run scripts/programs/commands on update failure/success.
#
Requirements:
– curl CLI – On Debian, apt-get install curl
#
History:
v1.0 – 20160513 – 1st release.
v1.1 – 20170130 – Improved compatibility.
#
v1.A – 20170607 – First Fork by me@kaacee.com to add support for multiple domains and email notifications
PS: Feel free to distribute but kindly retain the credits (-:
#
Begin settings
Get the Production API key/secret from https://developer.godaddy.com/keys/.
Ensure it’s for “Production” as first time it’s created for “Test”.
Key=xxxxxxx
Secret=xxxxx
Domain to update.
Domain=lupa.com
6/7/2017 Addition to use array.
DomainArray=(abc1.com abc2.com abc3.com)
Advanced settings – change only if you know what you’re doing 🙂
Record type, as seen in the DNS setup page, default A.
Type=A
Record name, as seen in the DNS setup page, default @.
# 6/7/2017 This record will be created if it doesn’t exist
Name=@
6/7/2017 Value to count Total Records
intTotalRecords=0
6/7/2017 Value to count Successful updates
intSuccess=0
6/7/2017 Value to count faile updates
intFailure=0
6/7/2017 Value to count no changes
intNoChange=0
# 6/7/2017 Forced cached ip refresh
Forces the cached ip file to be refresh from the NS’s zone
MandatoryRefresh=Yes #Valid values are Yes or No
# 6/7/2017 Send results email
Please ensure sendEmail is installed in /usr/bin/sendEmail – Or change the path
SendEmail=Yes #Valid values are Yes or No
6/7/2017 Send email to
stTo=”whoareyou@gmail.com”
6/7/2017 Email From
stFrom=”whoamI@gmail.com”
6/7/2017 Email Server
stServer=”smtp.server.com”
Time To Live in seconds, minimum default 600 (10mins).
If your public IP seldom changes, set it to 3600 (1hr) or more for DNS servers cache performance.
TTL=600
Writable path to last known Public IP record cached. Best to place in tmpfs.
CachedIP=/tmp/current_ip
6/7/2017 Moved inside the Array to create a domain specific IP address
External URL to check for current Public IP, must contain only a single plain text IP.
Default http://api.ipify.org.
CheckURL=http://api.ipify.org
Optional scripts/programs/commands to execute on successful update. Leave blank to disable.
This variable will be evaluated at runtime but will not be parsed for errors nor execution guaranteed.
Take note of the single quotes. If it’s a script, ensure it’s executable i.e. chmod 755 ./script.
Example: SuccessExec=’/bin/echo “$(date): My public IP changed to ${PublicIP}!”>>/var/log/GoDaddy.sh.log’
SuccessExec=”
Optional scripts/programs/commands to execute on update failure. Leave blank to disable.
This variable will be evaluated at runtime but will not be parsed for errors nor execution guaranteed.
Take note of the single quotes. If it’s a script, ensure it’s executable i.e. chmod 755 ./script.
Example: FailedExec=’/some/path/something-went-wrong.sh ${Update} && /some/path/email-script.sh ${PublicIP}’
FailedExec=”
End settings
for Domain in “${DomainArray[@]}”
do
#Increase total record count
intTotalRecords=$((intTotalRecords+1))
done
6/7/2017 Following summary added
echo “”
echo “Of ${intTotalRecords} total records, ${intSuccess} were successful, ${intFailure} failed, and ${intNoChange} needed no changes.”
echo “”
if [ $SendEmail=”Yes” ]; then
echo “Sending email . . . ”
# Please ensure sendEmail is installed in /usr/bin/sendEmail – Or change the path
/usr/bin/sendEmail -s $stServer -f $stFrom -t $stTo -u “Dynamic IP Update Results” -m “Of ${intTotalRecords} total records, ${intSuccess} were successful, ${intFailure} failed, and ${intNoChange} needed no changes.” $(date) on $(hostname)
echo “” # 6/7/2017 Added for console readability
fi
exit $?
Hi KC,
Glad the script helped you out and thanks for sharing. It might help those who needs it.
KC,
I’m trying to run use this script with pfsense but it keeps failing. Was there anything you changed to run this under pfsense?
I’m not run running it on my pfSense box, I’m running it on a Ubuntu 17.04 server (VM, actually) behind the pfSense.
Since pfSense doesn’t give you a way to trigger a script upon IP change (silly, IMHO) I didn’t see any gains to running it there.
What is failing on your end?
i think i’m stupid, but launching the script gives me this error:
:\
Hi Rob,
The line 88 you posted doesn’t translate to the actual line in the script. Ensure you’d copy & paste the text with Linux text editor (i.e. nano, vi or gedit) else there might be CRLF issue. You could test running it without first editing the options which will of course resulted in GoDaddy error but at least you’re sure the script is properly saved. Also try running it with
/bin/bash ./godaddy.sh
.I’m guessing that, with some modification, this could be used to add TXT records as well. I’m going to use it as reference for LetsEncrypt and DNS challenges.
I’ve found out that re-using an authorization doesn’t extend its lifetime, and find out that GoDaddy now support an API, I’ll need to dig into it
Thanks for the reference material,
Doug