Last active
March 8, 2024 23:48
-
-
Save lumapu/f3901dcb0a5dc5a4a45b0a8f17009318 to your computer and use it in GitHub Desktop.
Hetzner DNS records updater using DNS API (dyndns)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import requests | |
import json | |
import re | |
import sys | |
import os | |
from datetime import datetime | |
# --- CONFIGURATION -------------------------------- | |
auth_token = "YOUR-TOKEN" | |
zone_name = "YOUR_TLD" | |
record_names = ["@"] | |
# path to temporarily file which holds the last ip | |
# note: /dev/shm is a virtual filesystem in RAM | |
tmp_file = "/dev/shm/public_ip" | |
# sample crontab which checks public IP each minute | |
# * * * * * python3 /home/user/hetzner.dyndns.py >> /home/user/dyndns.log 2>&1 | |
# --- END CONFIGURATION ---------------------------- | |
def getPublicIp(): | |
try: | |
response = requests.get(url="http://checkip.dyndns.com/") | |
if response.status_code == 200: | |
return re.findall(r"[0-9.]+", response.content.decode('utf-8'))[0] | |
except requests.exceptions.RequestException: | |
print('HTTP Request failed') | |
def hetznerApi(param, data = None): | |
try: | |
response = None | |
body = { | |
"url": "https://dns.hetzner.com/api/v1/"+param, | |
"headers": { | |
"Auth-API-Token": auth_token, | |
"Content-Type": "application/json; charset=utf-8" | |
}, | |
"data": data | |
} | |
if data == None: | |
response = requests.get(**body) | |
else: | |
response = requests.put(**body) | |
if response.status_code == 200: | |
return response.content | |
else: | |
sys.exit(str(response.status_code) | |
+ ": " + response.content.decode('utf-8')) | |
except requests.exceptions.RequestException: | |
print('HTTP Request failed') | |
def getZoneByName(data, name): | |
for zone in data["zones"]: | |
if zone["name"] == name: | |
return zone["id"] | |
sys.exit("zone '" + name + "' not found") | |
def getARecordZoneIdByName(data, name): | |
for record in data["records"]: | |
if (record["name"] == name) and (record["type"] == "A"): | |
return record["id"] | |
sys.exit("record '" + name + "' not found") | |
def updateRecord(zoneId, recordId, name, ip, type="A"): | |
data = json.loads(hetznerApi("records/"+recordId, json.dumps({ | |
"type": type, | |
"name": name, | |
"value": ip, | |
"zone_id": zoneId | |
})))["record"] | |
if data["value"] == str(ip): | |
print( | |
name + "." + zone_name + " successfully updated to " + str(ip) | |
+ "\n -> last modified: " + data["modified"] | |
) | |
# check if public IP has changed | |
lastIp = None | |
pubIp = getPublicIp() | |
if os.path.exists(tmp_file): | |
with open(tmp_file, "r") as f: | |
lastIp = f.read() | |
if lastIp != pubIp: | |
with open(tmp_file, "w") as f: | |
f.write(pubIp) | |
print("-----------------------------------------------") | |
print("update: " + datetime.now().strftime("%Y-%m-%d %H:%M:%S")) | |
# get zones and find requested zone | |
data = json.loads(hetznerApi("zones")) | |
zoneId = getZoneByName(data, zone_name) | |
# get records of zone | |
data = json.loads(hetznerApi("records?zone_id="+zoneId)) | |
# loop through records and update them | |
for record in record_names: | |
recordId = getARecordZoneIdByName(data, record) | |
updateRecord(zoneId, recordId, record, pubIp) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment