diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..0e5e922 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding//src/gandi_live_dns.py=utf-8 diff --git a/README.md b/README.md index 71d1796..867bdbb 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,12 @@ This is a simple dynamic DNS updater for the the zone file for a subdomain of a domain to point at the external IPv4 address of the computer it has been run from. -It has been developed and tested on Debian 8 Jessie GNU/Linux using Python 2.7. +It has been developed on Debian 8 Jessie and tested on Debian 9 Sretch GNU/Linux using Python 2.7. With the new v5 Website, Gandi has also launched a new REST API which makes it easier to communicate via bash/curl or python/requests. -### Walkthrough +### Goal You want your homeserver to be always available at `dynamic.mydomain.tld`. @@ -20,32 +20,84 @@ 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 Record Setup +#### A DNS Record Create the DNS A Records in the GANDI Webinterface which you want to update if your IP changes. +### Debian Package Requirements + +`apt-get update && apt-get upgrade && apt-get install unzip python-requests python-args python-simplejson` + #### Git Clone or Download the Script -Download the Script from [GitHub](https://github.com/cavebeat/gandi_live_dns/archive/master.zip) +Download the Script from [GitHub](https://github.com/cavebeat/gandi_live_dns/archive/master.zip) and unzip it. + or + `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. -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. + +##### 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. +api_secret = '---my_secret_API_KEY----' + +##### api_endpoint +Gandiv5 LiveDNS API Location +http://doc.livedns.gandi.net/#api-endpoint +''' +api_endpoint = 'https://dns.beta.gandi.net/api/v5' + +##### domain +Your domain with the subdomains to be updated +domain = 'mydomain.tld' + +##### subdomains +All subdomains which should be updated. They get created if they do not yet exist. +subdomains = ["subdomain1", "subdomain2", "subdomain3"] + +The first domain is used to find out the actual IP in the Zone Records. #### Run the script +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 +` + +If your IP has changed, it will be detected and the update will be triggered. -Make the script executeable. ` -$ cd gandi_live_dns/src -$ chmod +x gandi_live_dns.py +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 +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 ` -And run the script + +#### Command Line Arguments + ` -$ ./gandi_live_dns.py +root@dyndns:~/gandi_live_dns-master/src# ./gandi_live_dns.py -h +usage: gandi_live_dns.py [-h] [-f] + +optional arguments: + -h, --help show this help message and exit + -f, --force force an update/create + ` +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. + + 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. \ No newline at end of file +with the classic DNS from Gandiv4 Website and their XML-RPC API. diff --git a/src/config.pyc b/src/config.pyc index 9a2d324..d7f29c5 100644 Binary files a/src/config.pyc and b/src/config.pyc differ diff --git a/src/example.config.py b/src/example.config.py index f1b30cf..221cd2d 100644 --- a/src/example.config.py +++ b/src/example.config.py @@ -18,7 +18,7 @@ Gandiv5 LiveDNS API Location http://doc.livedns.gandi.net/#api-endpoint https://dns.beta.gandi.net/api/v5/ ''' -api = 'https://dns.beta.gandi.net/api/v5' +api_endpoint = 'https://dns.beta.gandi.net/api/v5' #your domain with the subdomains in the zone file/UUID domain = 'mydomain.tld' diff --git a/src/gandi_live_dns.py b/src/gandi_live_dns.py index bbcd4a8..c5ad256 100755 --- a/src/gandi_live_dns.py +++ b/src/gandi_live_dns.py @@ -32,23 +32,36 @@ def get_uuid(): GET /domains/: ''' - url = config.api + '/domains/' + config.domain + url = config.api_endpoint + '/domains/' + config.domain u = requests.get(url, headers={"X-Api-Key":config.api_secret}) json_object = json.loads(u._content) - return json_object['zone_uuid'] + if u.status_code == 200: + return json_object['zone_uuid'] + else: + print 'Error: HTTP Status Code ', u.status_code, 'when trying to get Zone UUID' + print json_object['message'] + exit() def get_dnsip(uuid): ''' 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 actual DNS Record IP ''' - url = config.api + '/zones/' + uuid + '/records/' + config.subdomains[0] + '/A' + url = config.api_endpoint+ '/zones/' + uuid + '/records/' + config.subdomains[0] + '/A' headers = {"X-Api-Key":config.api_secret} u = requests.get(url, headers=headers) - json_object = json.loads(u._content) - 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') + if u.status_code == 200: + json_object = json.loads(u._content) + 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 json_object['message'] + exit() def update_records(uuid, dynIP, subdomain): ''' update DNS Records for Subdomains @@ -60,21 +73,26 @@ def update_records(uuid, dynIP, subdomain): "rrset_values": [""]}' \ https://dns.beta.gandi.net/api/v5/zones//records// ''' - url = config.api + '/zones/' + uuid + '/records/' + subdomain + '/A' + url = config.api_endpoint+ '/zones/' + uuid + '/records/' + subdomain + '/A' payload = {"rrset_ttl": config.ttl, "rrset_values": [dynIP]} headers = {"Content-Type": "application/json", "X-Api-Key":config.api_secret} - record_update = requests.put(url, data=json.dumps(payload), headers=headers) - json_object = json.loads(record_update._content) - print 'Status Code: ', record_update.status_code, ', ', json_object['message'], ', IP updated for', subdomain - return True + u = requests.put(url, data=json.dumps(payload), headers=headers) + json_object = json.loads(u._content) + + if u.status_code == 201: + 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 json_object['message'] + exit() + def main(force_update, verbosity): - if force_update: - print "force update turned on" if verbosity: - print "verbosity turned on" + print "verbosity turned on - not implemented by now" #get zone ID from Account @@ -85,21 +103,21 @@ def main(force_update, verbosity): dnsIP = get_dnsip(uuid) if force_update: - print "Going to update the DNS Records for the subdomains" + 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" else: - print "IP Address Mismatch - going to update the DNS Records for the subdomains" + print "IP Address Mismatch - going to update the DNS Records for the subdomains with new IP", dynIP for sub in config.subdomains: update_records(uuid, dynIP, sub) 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", action="store_true") + parser.add_argument('-f', '--force', help="force an update/create", action="store_true") args = parser.parse_args()