Merge pull request #2 from Polarolouis/script
Script rewriting of GanDynDNS
This commit is contained in:
commit
ba4035c92c
2 changed files with 103 additions and 68 deletions
32
README.md
32
README.md
|
|
@ -5,29 +5,33 @@ and replacing the IP with the public IP of the machine on which the code is exec
|
||||||
This script was inspired by matt1's gandi-ddns script : [matt1/gandi-ddns](https://github.com/matt1/gandi-ddns)
|
This script was inspired by matt1's gandi-ddns script : [matt1/gandi-ddns](https://github.com/matt1/gandi-ddns)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
1. **Define an object of class GanDynDns at the end of the file** :
|
|
||||||
```
|
|
||||||
main = GanDynDns("example.org", "@", "A", "your-api-key")
|
|
||||||
```
|
|
||||||
Or if you want to define multiple domains to update :
|
|
||||||
|
|
||||||
```
|
```
|
||||||
domain = "example.org"
|
usage: gandyndns.py [-h] [-v] [--type TYPE] DOMAIN SUBDOMAIN APIKEY
|
||||||
apikey = "your-api-key"
|
|
||||||
|
|
||||||
main = GanDynDns(domain, "@", "A", apikey)
|
A script which connect to Gandi.net API to change IP associated to a DNS record
|
||||||
mail = GanDynDns(domain, "mail", "A" apikey)
|
|
||||||
|
positional arguments:
|
||||||
|
DOMAIN The domain for which you want to write a DNS record. Example: example.com
|
||||||
|
SUBDOMAIN The subdomain to point to. Examples: 'sub' or '@'
|
||||||
|
APIKEY Your Gandi.net API key
|
||||||
|
|
||||||
|
options:
|
||||||
|
-h, --help show this help message and exit
|
||||||
|
-v, --verbose Enable verbose mode
|
||||||
|
--type TYPE The type of DNS record to create. Default: A
|
||||||
```
|
```
|
||||||
Each time the script runs and the objects are created they check if *the IPs in the DNS record* match the *current public IP*.
|
|
||||||
|
|
||||||
2. **Run the script using a cron task** :
|
Each time the script runs it check if *the IPs in the DNS record* match the *current public IP* and if they dont use a PUT request to apply the new IP to the DNS.
|
||||||
|
|
||||||
```
|
## Automation : **Run the script using a cron task**
|
||||||
|
|
||||||
|
```bash
|
||||||
sudo crontab -e
|
sudo crontab -e
|
||||||
```
|
```
|
||||||
|
|
||||||
And then to execute *every 15 minutes* write in the crontab :
|
And then to execute *every X minutes* (where X should be an integer between 1 and 59) write in the crontab :
|
||||||
|
|
||||||
```
|
```
|
||||||
*/15 * * * * python /opt/services/scripts/gandyndns.py
|
*/X * * * * python /path/to/script/gandyndns.py example.com @ my-api-key
|
||||||
```
|
```
|
||||||
|
|
|
||||||
109
gandyndns.py
109
gandyndns.py
|
|
@ -3,36 +3,40 @@
|
||||||
# @author: lordof20th
|
# @author: lordof20th
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
import argparse
|
||||||
|
|
||||||
class GanDynDns:
|
parser = argparse.ArgumentParser(
|
||||||
def __init__(self, fqdn, rrset_name, rrset_type, apikey) -> None:
|
description="A script which connect to Gandi.net API to change IP associated to a DNS record")
|
||||||
self.fqdn = fqdn
|
parser.add_argument("-v", "--verbose",
|
||||||
self.rrset_name = rrset_name
|
help="Enable verbose mode", action="store_true")
|
||||||
self.rrset_type = rrset_type
|
parser.add_argument("domain", metavar="DOMAIN",
|
||||||
self.domain_record_string = f"DNS {self.rrset_type} record for {self.rrset_name}.{self.fqdn}"
|
help="The domain for which you want to write a DNS record. Example: example.com")
|
||||||
self.apiUrl = f"https://api.gandi.net/v5/livedns/domains/{fqdn}/records/{rrset_name}/{rrset_type}"
|
parser.add_argument("subdomain", metavar="SUBDOMAIN",
|
||||||
self.headers = {"Authorization": f"Apikey {apikey}", 'User-Agent': 'Mozilla/5.0', "Content-Type": "application/json"}
|
help="The subdomain to point to. Examples: 'sub' or '@'")
|
||||||
self.update_dns_IP()
|
parser.add_argument("apikey", metavar="APIKEY", help="Your Gandi.net API key")
|
||||||
|
parser.add_argument("--type", metavar="TYPE", default='A',
|
||||||
|
help="The type of DNS record to create. Default: A")
|
||||||
|
|
||||||
def retrieve_dns_IP(self):
|
args = parser.parse_args()
|
||||||
"""Retrieves the IP in the DNS record using the fqdn, rrset_name (for instance subdomain like www etc) and rrset_type (the DNS record type A, CNAME ...).
|
print(args)
|
||||||
|
|
||||||
The function uses requests library and connect to Gandi.net API
|
verbose = args.verbose
|
||||||
|
domain = args.domain
|
||||||
|
apikey = args.apikey
|
||||||
|
subdomain = args.subdomain
|
||||||
|
type = args.type
|
||||||
|
|
||||||
Returns:
|
domain_record_string = f"DNS {type} record for {subdomain}.{domain}"
|
||||||
retrievedDnsIp (str): string of the IP address in the DNS record"""
|
|
||||||
|
|
||||||
response = requests.get(self.apiUrl, headers=self.headers)
|
domain_record_string_lenght = len(domain_record_string)
|
||||||
|
|
||||||
responseJson = response.json() # IPs are stored in a list as string
|
apiUrl = f"https://api.gandi.net/v5/livedns/domains/{domain}/records/{subdomain}/{type}"
|
||||||
try:
|
|
||||||
retrievedDnsIp = responseJson['rrset_values'][0]
|
|
||||||
except KeyError as key_error:
|
|
||||||
print(f"{key_error} means the record doesn't exist, we'll return an empty string instead")
|
|
||||||
retrievedDnsIp =""
|
|
||||||
return retrievedDnsIp
|
|
||||||
|
|
||||||
def retrieve_public_IP(self):
|
headers = {"Authorization": f"Apikey {apikey}",
|
||||||
|
'User-Agent': 'Mozilla/5.0', "Content-Type": "application/json"}
|
||||||
|
|
||||||
|
|
||||||
|
def retrieve_public_IP():
|
||||||
"""Retrieves the public IP by connecting to ipinfo.io API
|
"""Retrieves the public IP by connecting to ipinfo.io API
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -43,25 +47,52 @@ class GanDynDns:
|
||||||
data = response.json()
|
data = response.json()
|
||||||
return data['ip']
|
return data['ip']
|
||||||
|
|
||||||
def ips_are_equals(self):
|
|
||||||
"""The method compares both IPs and returns a boolean for the equality test
|
def retrieve_dns_IP(apiUrl, headers):
|
||||||
|
"""Retrieves the IP in the DNS record using the domain, rrset_name (for instance subdomain like www etc) and rrset_type (the DNS record type A, CNAME ...).
|
||||||
|
|
||||||
|
The function uses requests library and connect to Gandi.net API
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(bool) : result of the IP equality test"""
|
retrievedDnsIp (str): string of the IP address in the DNS record"""
|
||||||
self.currentPublicIP = self.retrieve_public_IP()
|
if verbose:
|
||||||
self.dnsIP = self.retrieve_dns_IP()
|
print("Retrieving the DNS IP")
|
||||||
return self.currentPublicIP == self.dnsIP
|
response = requests.get(apiUrl, headers=headers)
|
||||||
|
|
||||||
def update_dns_IP(self):
|
responseJson = response.json() # IPs are stored in a list as string
|
||||||
|
try:
|
||||||
|
retrievedDnsIp = responseJson['rrset_values'][0]
|
||||||
|
except KeyError as key_error:
|
||||||
|
print(
|
||||||
|
f"{key_error} means the record doesn't exist, we'll return an empty string instead")
|
||||||
|
retrievedDnsIp = ""
|
||||||
|
return retrievedDnsIp
|
||||||
|
|
||||||
|
|
||||||
|
def update_dns_IP(apiUrl, headers):
|
||||||
"""Updates the IP in the DNS record using the IP provided (acquired by retrieve_public_IP) if it is different from the DNS IP"""
|
"""Updates the IP in the DNS record using the IP provided (acquired by retrieve_public_IP) if it is different from the DNS IP"""
|
||||||
|
publicIP = retrieve_public_IP()
|
||||||
if not self.ips_are_equals():
|
dnsIP = retrieve_dns_IP(apiUrl, headers)
|
||||||
data = {
|
if verbose:
|
||||||
"rrset_values": [self.currentPublicIP]
|
print(f"Public IP is : {publicIP}")
|
||||||
}
|
if dnsIP:
|
||||||
print(f"{self.domain_record_string : <40} | Old IP : {self.dnsIP} replaced -> by New IP : {self.currentPublicIP}")
|
print(f"DNS IP is : {dnsIP}")
|
||||||
requests.put(url=self.apiUrl, headers=self.headers, json=data)
|
|
||||||
else:
|
else:
|
||||||
print(f"{self.domain_record_string : <40} | {'IPs are the same.':<17}")
|
print("The subdomain doesn't exist, no DNS IP associated")
|
||||||
|
if not publicIP == dnsIP:
|
||||||
|
if verbose:
|
||||||
|
print("IPs do not match, setting DNS IP")
|
||||||
|
data = {
|
||||||
|
"rrset_values": [publicIP]
|
||||||
|
}
|
||||||
|
print(f"{domain_record_string : <{domain_record_string_lenght}} | Old IP : {dnsIP} replaced -> by New IP : {publicIP}")
|
||||||
|
response = requests.put(url=apiUrl, headers=headers, json=data)
|
||||||
|
if verbose:
|
||||||
|
print(f"Request exited with status code : {response.status_code}")
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
f"{domain_record_string : <{domain_record_string_lenght}} | {'IPs are the same.':<17}")
|
||||||
|
|
||||||
main = GanDynDns("example.org", "@", "A", "your-api-key")
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
update_dns_IP(apiUrl, headers)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue