From b48a143dfca973f5806bf5132dfdb3cdbcba8c35 Mon Sep 17 00:00:00 2001 From: Jonathan Strine Date: Mon, 19 Feb 2018 16:27:36 -0500 Subject: [PATCH 1/8] Change ifconfig.co syntax and modify ifconfig setting to use v4 and v6 specific URLs. --- src/example.config.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/example.config.py b/src/example.config.py index 039c895..97ae83f 100644 --- a/src/example.config.py +++ b/src/example.config.py @@ -31,15 +31,17 @@ ''' IP address lookup service +Leave blank to not update that protocol (v4 or v6) run your own external IP provider: + https://github.com/mpolden/ipd + e.g. -+ https://ifconfig.co ++ https://[v4.|v6.]ifconfig.co/ip + http://ifconfig.me/ip + http://whatismyip.akamai.com/ + http://ipinfo.io/ip + many more ... ''' -ifconfig = 'choose_from_above_or_run_your_own' +ifconfigv4 = 'choose_from_above_or_run_your_own' +ifconfigv6 = 'choose_from_above_or_run_your_own' From 01fc9f7ef58d242da4facb0e51154585e1ac48d8 Mon Sep 17 00:00:00 2001 From: Jonathan Strine Date: Mon, 19 Feb 2018 20:42:02 -0500 Subject: [PATCH 2/8] Added IPv6 update handing --- src/gandi-live-dns.py | 83 ++++++++++++++++++++++++------------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/src/gandi-live-dns.py b/src/gandi-live-dns.py index c5ad256..50bcba0 100755 --- a/src/gandi-live-dns.py +++ b/src/gandi-live-dns.py @@ -8,7 +8,7 @@ https://www.gnu.org/licenses/gpl-3.0.html Created on 13 Aug 2017 -http://doc.livedns.gandi.net/ +http://doc.livedns.gandi.net/ http://doc.livedns.gandi.net/#api-endpoint -> https://dns.beta.gandi.net/api/v5/ ''' @@ -19,18 +19,18 @@ def get_dynip(ifconfig_provider): ''' find out own IPv4 at home <-- this is the dynamic IP which changes more or less frequently - similar to curl ifconfig.me/ip, see example.config.py for details to ifconfig providers - ''' + similar to curl ifconfig.me/ip, see example.config.py for details to ifconfig providers + ''' r = requests.get(ifconfig_provider) print 'Checking dynamic IP: ' , r._content.strip('\n') return r.content.strip('\n') def get_uuid(): - ''' + ''' find out ZONE UUID from domain Info on domain "DOMAIN" GET /domains/: - + ''' url = config.api_endpoint + '/domains/' + config.domain u = requests.get(url, headers={"X-Api-Key":config.api_secret}) @@ -42,16 +42,16 @@ def get_uuid(): print json_object['message'] exit() -def get_dnsip(uuid): +def get_dnsip(uuid, rrtype): ''' find out IP from first Subdomain DNS-Record List all records with name "NAME" and type "TYPE" in the zone UUID GET /zones//records//: - - The first subdomain from config.subdomain will be used to get + + The first subdomain from config.subdomain will be used to get the actual DNS Record IP ''' - url = config.api_endpoint+ '/zones/' + uuid + '/records/' + config.subdomains[0] + '/A' + url = config.api_endpoint+ '/zones/' + uuid + '/records/' + config.subdomains[0] + '/' + rrtype headers = {"X-Api-Key":config.api_secret} u = requests.get(url, headers=headers) if u.status_code == 200: @@ -59,12 +59,12 @@ def get_dnsip(uuid): print 'Checking IP from DNS Record' , config.subdomains[0], ':', json_object['rrset_values'][0].encode('ascii','ignore').strip('\n') return json_object['rrset_values'][0].encode('ascii','ignore').strip('\n') else: - print 'Error: HTTP Status Code ', u.status_code, 'when trying to get IP from subdomain', config.subdomains[0] + print 'Error: HTTP Status Code ', u.status_code, 'when trying to get IP from subdomain', config.subdomains[0] print json_object['message'] exit() -def update_records(uuid, dynIP, subdomain): - ''' update DNS Records for Subdomains +def update_records(uuid, dynIP, subdomain, rrtype): + ''' update DNS Records for Subdomains Change the "NAME"/"TYPE" record from the zone UUID PUT /zones//records//: curl -X PUT -H "Content-Type: application/json" \ @@ -73,7 +73,7 @@ def update_records(uuid, dynIP, subdomain): "rrset_values": [""]}' \ https://dns.beta.gandi.net/api/v5/zones//records// ''' - url = config.api_endpoint+ '/zones/' + uuid + '/records/' + subdomain + '/A' + url = config.api_endpoint+ '/zones/' + uuid + '/records/' + subdomain + '/' + rrtype payload = {"rrset_ttl": config.ttl, "rrset_values": [dynIP]} headers = {"Content-Type": "application/json", "X-Api-Key":config.api_secret} u = requests.put(url, data=json.dumps(payload), headers=headers) @@ -83,7 +83,7 @@ def update_records(uuid, dynIP, subdomain): print 'Status Code:', u.status_code, ',', json_object['message'], ', IP updated for', subdomain return True else: - print 'Error: HTTP Status Code ', u.status_code, 'when trying to update IP from subdomain', subdomain + print 'Error: HTTP Status Code ', u.status_code, 'when trying to update IP from subdomain', subdomain print json_object['message'] exit() @@ -94,37 +94,48 @@ def main(force_update, verbosity): if verbosity: print "verbosity turned on - not implemented by now" - + #get zone ID from Account uuid = get_uuid() - - #compare dynIP and DNS IP - dynIP = get_dynip(config.ifconfig) - dnsIP = get_dnsip(uuid) - - if force_update: - print "Going to update/create the DNS Records for the subdomains" - for sub in config.subdomains: - update_records(uuid, dynIP, sub) - else: - if dynIP == dnsIP: - print "IP Address Match - no further action" + + #compare dynIP and DNS IP + if config.ifconfigv4 != "": + dynIPv4 = get_dynip(config.ifconfigv4) + dnsIPv4 = get_dnsip(uuid, "A") + + if force_update: + print "Going to update/create the DNS Records for the subdomains" + for sub in config.subdomains: + update_records(uuid, dynIPv4, sub, "A") else: - print "IP Address Mismatch - going to update the DNS Records for the subdomains with new IP", dynIP + if dynIPv4 == dnsIPv4: + print "IPv4 Address Match - no further action" + else: + print "IPv4 Address Mismatch - going to update the DNS Records for the subdomains with new IP", dynIPv4 + for sub in config.subdomains: + update_records(uuid, dynIPv4, sub, "A") + + if config.ifconfigv6 != "": + dynIPv6 = get_dynip(config.ifconfigv6) + dnsIPv6 = get_dnsip(uuid, "AAAA") + + if force_update: + print "Going to update/create the DNS Records for the subdomains" for sub in config.subdomains: - update_records(uuid, dynIP, sub) + update_records(uuid, dynIPv6, sub, "AAAA") + else: + if dynIPv6 == dnsIPv6: + print "IPv6 Address Match - no further action" + else: + print "IPv6 Address Mismatch - going to update the DNS Records for the subdomains with new IP", dynIPv6 + for sub in config.subdomains: + update_records(uuid, dynIPv6, sub, "AAAA") if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument('-v', '--verbose', help="increase output verbosity", action="store_true") parser.add_argument('-f', '--force', help="force an update/create", action="store_true") args = parser.parse_args() - - - main(args.force, args.verbose) - - - - \ No newline at end of file + main(args.force, args.verbose) From cee53306e1e8502995709eb0ca597c866db60aab Mon Sep 17 00:00:00 2001 From: Jonathan Strine Date: Tue, 20 Feb 2018 19:15:46 -0500 Subject: [PATCH 3/8] Comment to test commit --- src/gandi-live-dns.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gandi-live-dns.py b/src/gandi-live-dns.py index 50bcba0..c55326d 100755 --- a/src/gandi-live-dns.py +++ b/src/gandi-live-dns.py @@ -98,6 +98,7 @@ def main(force_update, verbosity): #get zone ID from Account uuid = get_uuid() + #need to do some cleanup here #compare dynIP and DNS IP if config.ifconfigv4 != "": dynIPv4 = get_dynip(config.ifconfigv4) From edbe0465c32410c1d8544234bb84e25de61c6caa Mon Sep 17 00:00:00 2001 From: Jonathan Strine Date: Thu, 3 May 2018 11:05:48 -0700 Subject: [PATCH 4/8] Minor formatting --- src/example.config.py | 12 ++++++------ src/gandi-live-dns.py | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/example.config.py b/src/example.config.py index 97ae83f..3c767fd 100644 --- a/src/example.config.py +++ b/src/example.config.py @@ -20,7 +20,7 @@ ''' api_endpoint = 'https://dns.api.gandi.net/api/v5' -#your domain with the subdomains in the zone file/UUID +#your domain with the subdomains in the zone file/UUID domain = 'mydomain.tld' #enter all subdomains to be updated, subdomains must already exist to be updated @@ -29,19 +29,19 @@ #300 seconds = 5 minutes ttl = '300' -''' -IP address lookup service -Leave blank to not update that protocol (v4 or v6) +''' +IP address lookup service run your own external IP provider: + https://github.com/mpolden/ipd + -e.g. -+ https://[v4.|v6.]ifconfig.co/ip +e.g. ++ https://[v4|v6].ifconfig.co/ip + http://ifconfig.me/ip + http://whatismyip.akamai.com/ + http://ipinfo.io/ip + many more ... ''' +#Leave blank to not update that protocol (v4 or v6) ifconfigv4 = 'choose_from_above_or_run_your_own' ifconfigv6 = 'choose_from_above_or_run_your_own' diff --git a/src/gandi-live-dns.py b/src/gandi-live-dns.py index 50bcba0..adb3616 100755 --- a/src/gandi-live-dns.py +++ b/src/gandi-live-dns.py @@ -98,7 +98,7 @@ def main(force_update, verbosity): #get zone ID from Account uuid = get_uuid() - #compare dynIP and DNS IP + #compare dynIP and DNS IP for IPv4 if config.ifconfigv4 != "": dynIPv4 = get_dynip(config.ifconfigv4) dnsIPv4 = get_dnsip(uuid, "A") @@ -115,6 +115,7 @@ def main(force_update, verbosity): for sub in config.subdomains: update_records(uuid, dynIPv4, sub, "A") + #compare dynIP and DNS IP for IPv6 if config.ifconfigv6 != "": dynIPv6 = get_dynip(config.ifconfigv6) dnsIPv6 = get_dnsip(uuid, "AAAA") From 2f435ab400ed9a5bcb29fb7a0c64b9fba7c70455 Mon Sep 17 00:00:00 2001 From: Jonathan Strine Date: Thu, 3 May 2018 11:32:04 -0700 Subject: [PATCH 5/8] Updated with information and examples about IPv6. --- README.md | 63 ++++++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 70fc261..eb7310d 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ gandi-live-dns ---- This is a simple dynamic DNS updater for the -[Gandi](https://www.gandi.net) registrar. It uses their [LiveDNS REST API](http://doc.livedns.gandi.net/) to update the zone file for a subdomain of a domain to point at the external IPv4 address of the computer it has been run from. +[Gandi](https://www.gandi.net) registrar. It uses their [LiveDNS REST API](http://doc.livedns.gandi.net/) to update the zone file for a subdomain of a domain to point at the external IPv4 and/or IPv6 address of the computer it has been run from. It has been developed on Debian 8 Jessie and tested on Debian 9 Stretch GNU/Linux using Python 2.7. @@ -17,25 +17,25 @@ You want your homeserver to be always available at `dynamic_subdomain.mydomain.t `apt-get update && apt-get upgrade && apt-get install unzip python-requests python-args python-simplejson` #### API Key -First, you must apply for an API key with Gandi. Visit -https://account.gandi.net/en/ and apply for (at least) the production API +First, you must apply for an API key with Gandi. Visit +https://account.gandi.net/en/ and apply for (at least) the production API key by following their directions. -#### A DNS Record -Create the DNS A Records in the GANDI Webinterface which you want to update if your IP changes. +#### A or AAAA DNS Record +Create the DNS Records in the GANDI Webinterface which you want to update if your IP changes. Use an A Record for IPv4 and AAAA Record for IPv6. #### Git Clone or Download the Script Download the Script from here as [zip](https://github.com/cavebeat/gandi-live-dns/archive/master.zip)/[tar.gz](https://github.com/cavebeat/gandi-live-dns/archive/master.tar.gz) and extract it. or clone from git -`git clone https://github.com/cavebeat/gandi-live-dns.git` +`git clone https://github.com/cavebeat/gandi-live-dns.git` #### Script Configuration Then you'd need to configure the script in the src directory. Copy `example.config.py` to `config.py`, and put it in the same directory as the script. -Edit the config file to fit your needs. +Edit the config file to fit your needs. ##### api_secret Start by retrieving your API Key from the "Security" section in new [Gandi Account admin panel](https://account.gandi.net/) to be able to make authenticated requests to the API. @@ -50,16 +50,16 @@ api_endpoint = 'https://dns.api.gandi.net/api/v5' ``` ##### domain -Your domain for the subdomains to be updated +Your domain for the subdomains to be updated ##### subdomains All subdomains which should be updated. They get created if they do not yet exist. -``` +``` subdomains = ["subdomain1", "subdomain2", "subdomain3"] ``` -The first subdomain is used to find out the actual IP in the Zone Records. +The first subdomain is used to find out the actual IP in the Zone Records. #### Run the script And run the script: @@ -68,17 +68,26 @@ And run the script: root@dyndns:~/gandi-live-dns-master/src# ./gandi-live-dns.py Checking dynamic IP: 127.0.0.1 Checking IP from DNS Record subdomain1: 127.0.0.1 -IP Address Match - no further action +Checking dynamic IP: fe80::1 +Checking IP from DNS Record subdomain1: fe80::1 +IPv4 Address Match - no further action +IPv6 Address Match - no further action ``` -If your IP has changed, it will be detected and the update will be triggered. +If your IP has changed, it will be detected and the update will be triggered. ``` root@dyndns:~/gandi-live-dns-master/src# ./gandi-live-dns.py Checking dynamic IP: 127.0.0.2 Checking IP from DNS Record subdomain1: 127.0.0.1 -IP Address Mismatch - going to update the DNS Records for the subdomains with new IP 127.0.0.2 +Checking dynamic IP: fe80::2 +Checking IP from DNS Record subdomain1: fe80::1 +IPv4 Address Mismatch - going to update the DNS Records for the subdomains with new IP 127.0.0.2 +Status Code: 201 , DNS Record Created , IP updated for subdomain1 +Status Code: 201 , DNS Record Created , IP updated for subdomain2 +Status Code: 201 , DNS Record Created , IP updated for subdomain3 +IPv6 Address Mismatch - going to update the DNS Records for the subdomains with new IP fe80::2 Status Code: 201 , DNS Record Created , IP updated for subdomain1 Status Code: 201 , DNS Record Created , IP updated for subdomain2 Status Code: 201 , DNS Record Created , IP updated for subdomain3 @@ -96,36 +105,38 @@ optional arguments: ``` -The force option runs the script, even when no IP change has been detected. -It will update all subdomains and even create them if they are missing in the -Zone File/Zone UUID. This can be used if additional/new subdomains get appended to the conig file. +The force option runs the script, even when no IP change has been detected. +It will update all subdomains and even create them if they are missing in the +Zone File/Zone UUID. This can be used if additional/new subdomains get appended to the config file. -### IP address lookup service -There exist several providers for this case, but better is to run your own somewhere. +### IP address lookup service +There exist several providers for this case, but better is to run your own somewhere. #### Poor Mans PHP Solution -On a LAMP Stack, place the file [index.php](https://github.com/cavebeat/gandi-live-dns/blob/master/src/example-index.php) in a directory /ip in your webroot. +On a LAMP Stack, place the file [index.php](https://github.com/cavebeat/gandi-live-dns/blob/master/src/example-index.php) in a directory /ip in your webroot. ``` root@laptop:~# curl https://blog.cavebeat.org/ip/ 127.0.0.1 ``` -This should fit your personal needs and you still selfhost the whole thing. +This should fit your personal needs and you still selfhost the whole thing. #### IP address lookup service https://ifconfig.co https://github.com/mpolden/ipd A simple service for looking up your IP address. This is the code that powers [https://ifconfig.co](https://ifconfig.co) -#### use external services -choose one as described in the config file. +#### Use external services +Choose one as described in the config file. +If you are updating AAAA Records with your IPv6 address, make sure your IP address lookup service supports returning your IPv6 address. +See `example.config.py` for how [https://ifconfig.co]https://ifconfig.co can be used in this way. ### Cron the script -Run the script every five minutes. +Run the script every five minutes. ``` -*/5 * * * * /root/gandi-live-dns-master/src/gandi-live-dns.py >/dev/null 2>&1 +*/5 * * * * /root/gandi-live-dns-master/src/gandi-live-dns.py >/dev/null 2>&1 ``` ### Limitations -The XML-RPC API has a limit of 30 requests per 2 seconds, so i guess it's safe to update 25 subdomains at once with the REST API. +The XML-RPC API has a limit of 30 requests per 2 seconds, so i guess it's safe to update 25 subdomains at once with the REST API. ### Upcoming Features @@ -133,7 +144,7 @@ The XML-RPC API has a limit of 30 requests per 2 seconds, so i guess it's safe t ### Inspiration -This DynDNS updater is inspired by https://github.com/jasontbradshaw/gandi-dyndns which worked very well +This DynDNS updater is inspired by https://github.com/jasontbradshaw/gandi-dyndns which worked very well with the classic DNS from Gandiv4 Website and their XML-RPC API. Gandi has created a new API, i accidently switched to the new DNS Record System, so someone had to start a new updater. From 3e7bf3c8c1e3625ab60cd683baf202cfa74adfc8 Mon Sep 17 00:00:00 2001 From: Jonathan Strine Date: Thu, 3 May 2018 11:34:10 -0700 Subject: [PATCH 6/8] Fix typos --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index eb7310d..486df68 100644 --- a/README.md +++ b/README.md @@ -147,4 +147,4 @@ The XML-RPC API has a limit of 30 requests per 2 seconds, so i guess it's safe t This DynDNS updater is inspired by https://github.com/jasontbradshaw/gandi-dyndns which worked very well with the classic DNS from Gandiv4 Website and their XML-RPC API. -Gandi has created a new API, i accidently switched to the new DNS Record System, so someone had to start a new updater. +Gandi has created a new API, I accidentally switched to the new DNS Record System, so someone had to start a new updater. From 3f73eece6ac7f73d3d5ef8ac4876b709cdcb47b2 Mon Sep 17 00:00:00 2001 From: Jonathan Strine Date: Thu, 3 May 2018 11:37:36 -0700 Subject: [PATCH 7/8] Fix link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 486df68..51c0cf6 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ https://github.com/mpolden/ipd A simple service for looking up your IP address. #### Use external services Choose one as described in the config file. If you are updating AAAA Records with your IPv6 address, make sure your IP address lookup service supports returning your IPv6 address. -See `example.config.py` for how [https://ifconfig.co]https://ifconfig.co can be used in this way. +See `example.config.py` for how https://ifconfig.co can be used in this way. ### Cron the script From 0577829904a9e1b39bcb445235bfeb8571165ccb Mon Sep 17 00:00:00 2001 From: Jonathan Strine Date: Sun, 6 May 2018 12:09:45 -0400 Subject: [PATCH 8/8] Fixed error in order of output for examples. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 51c0cf6..3c621ee 100644 --- a/README.md +++ b/README.md @@ -68,9 +68,9 @@ And run the script: root@dyndns:~/gandi-live-dns-master/src# ./gandi-live-dns.py Checking dynamic IP: 127.0.0.1 Checking IP from DNS Record subdomain1: 127.0.0.1 +IPv4 Address Match - no further action Checking dynamic IP: fe80::1 Checking IP from DNS Record subdomain1: fe80::1 -IPv4 Address Match - no further action IPv6 Address Match - no further action ``` @@ -81,12 +81,12 @@ If your IP has changed, it will be detected and the update will be triggered. root@dyndns:~/gandi-live-dns-master/src# ./gandi-live-dns.py Checking dynamic IP: 127.0.0.2 Checking IP from DNS Record subdomain1: 127.0.0.1 -Checking dynamic IP: fe80::2 -Checking IP from DNS Record subdomain1: fe80::1 IPv4 Address Mismatch - going to update the DNS Records for the subdomains with new IP 127.0.0.2 Status Code: 201 , DNS Record Created , IP updated for subdomain1 Status Code: 201 , DNS Record Created , IP updated for subdomain2 Status Code: 201 , DNS Record Created , IP updated for subdomain3 +Checking dynamic IP: fe80::2 +Checking IP from DNS Record subdomain1: fe80::1 IPv6 Address Mismatch - going to update the DNS Records for the subdomains with new IP fe80::2 Status Code: 201 , DNS Record Created , IP updated for subdomain1 Status Code: 201 , DNS Record Created , IP updated for subdomain2