Fixed purgeUnknownRecords behavior

This commit is contained in:
Timothy Miller 2022-07-30 20:07:36 -04:00
parent a9d25c743a
commit 464d2792b1
2 changed files with 64 additions and 48 deletions

38
.vscode/settings.json vendored
View File

@ -1,19 +1,21 @@
{ {
"files.exclude": { "files.exclude": {
"**/.git": true, "**/.git": true,
"**/.svn": true, "**/.svn": true,
"**/.hg": true, "**/.hg": true,
"**/CVS": true, "**/CVS": true,
"**/.DS_Store": true, "**/.DS_Store": true,
".github": true, "**/Thumbs.db": true,
".gitignore": true, ".github": true,
".vscode": true, ".gitignore": true,
"Dockerfile": true, ".vscode": true,
"LICENSE": true, "Dockerfile": true,
"requirements.txt": true, "LICENSE": true,
"venv": true "requirements.txt": true,
}, "venv": true
"explorerExclude.backup": null, },
"python.linting.pylintEnabled": true, "explorerExclude.backup": null,
"python.linting.enabled": true "python.linting.pylintEnabled": true,
} "python.linting.enabled": true,
"python.formatting.provider": "autopep8"
}

View File

@ -3,7 +3,7 @@
# Summary: Access your home network remotely via a custom domain name without a static IP! # Summary: Access your home network remotely via a custom domain name without a static IP!
# Description: Access your home network remotely via a custom domain # Description: Access your home network remotely via a custom domain
# Access your home network remotely via a custom domain # Access your home network remotely via a custom domain
# A small, 🕵️ privacy centric, and ⚡ # A small, 🕵️ privacy centric, and ⚡
# lightning fast multi-architecture Docker image for self hosting projects. # lightning fast multi-architecture Docker image for self hosting projects.
__version__ = "1.0.1" __version__ = "1.0.1"
@ -18,15 +18,17 @@ import requests
CONFIG_PATH = os.environ.get('CONFIG_PATH', os.getcwd() + "/") CONFIG_PATH = os.environ.get('CONFIG_PATH', os.getcwd() + "/")
class GracefulExit:
def __init__(self):
self.kill_now = threading.Event()
signal.signal(signal.SIGINT, self.exit_gracefully)
signal.signal(signal.SIGTERM, self.exit_gracefully)
def exit_gracefully(self, signum, frame): class GracefulExit:
print("🛑 Stopping main thread...") def __init__(self):
self.kill_now.set() self.kill_now = threading.Event()
signal.signal(signal.SIGINT, self.exit_gracefully)
signal.signal(signal.SIGTERM, self.exit_gracefully)
def exit_gracefully(self, signum, frame):
print("🛑 Stopping main thread...")
self.kill_now.set()
def deleteEntries(type): def deleteEntries(type):
# Helper function for deleting A or AAAA records # Helper function for deleting A or AAAA records
@ -34,17 +36,19 @@ def deleteEntries(type):
# existing A or AAAA records are found. # existing A or AAAA records are found.
for option in config["cloudflare"]: for option in config["cloudflare"]:
answer = cf_api( answer = cf_api(
"zones/" + option['zone_id'] + "/dns_records?per_page=100&type=" + type, "zones/" + option['zone_id'] +
"/dns_records?per_page=100&type=" + type,
"GET", option) "GET", option)
if answer is None or answer["result"] is None: if answer is None or answer["result"] is None:
time.sleep(5) time.sleep(5)
return return
for record in answer["result"]: for record in answer["result"]:
identifier = str(record["id"]) identifier = str(record["id"])
cf_api( cf_api(
"zones/" + option['zone_id'] + "/dns_records/" + identifier, "zones/" + option['zone_id'] + "/dns_records/" + identifier,
"DELETE", option) "DELETE", option)
print("🗑️ Deleted stale record " + identifier) print("🗑️ Deleted stale record " + identifier)
def getIPs(): def getIPs():
a = None a = None
@ -66,7 +70,8 @@ def getIPs():
deleteEntries("A") deleteEntries("A")
if ipv6_enabled: if ipv6_enabled:
try: try:
aaaa = requests.get("https://[2606:4700:4700::1111]/cdn-cgi/trace").text.split("\n") aaaa = requests.get(
"https://[2606:4700:4700::1111]/cdn-cgi/trace").text.split("\n")
aaaa.pop() aaaa.pop()
aaaa = dict(s.split("=") for s in aaaa)["ip"] aaaa = dict(s.split("=") for s in aaaa)["ip"]
except Exception: except Exception:
@ -89,6 +94,7 @@ def getIPs():
} }
return ips return ips
def commitRecord(ip): def commitRecord(ip):
for option in config["cloudflare"]: for option in config["cloudflare"]:
subdomains = option["subdomains"] subdomains = option["subdomains"]
@ -97,7 +103,7 @@ def commitRecord(ip):
time.sleep(5) time.sleep(5)
return return
base_domain_name = response["result"]["name"] base_domain_name = response["result"]["name"]
ttl = 300 # default Cloudflare TTL ttl = 300 # default Cloudflare TTL
for subdomain in subdomains: for subdomain in subdomains:
subdomain = subdomain.lower().strip() subdomain = subdomain.lower().strip()
record = { record = {
@ -108,7 +114,8 @@ def commitRecord(ip):
"ttl": ttl "ttl": ttl
} }
dns_records = cf_api( dns_records = cf_api(
"zones/" + option['zone_id'] + "/dns_records?per_page=100&type=" + ip["type"], "zones/" + option['zone_id'] +
"/dns_records?per_page=100&type=" + ip["type"],
"GET", option) "GET", option)
fqdn = base_domain_name fqdn = base_domain_name
if subdomain: if subdomain:
@ -133,7 +140,8 @@ def commitRecord(ip):
if modified: if modified:
print("📡 Updating record " + str(record)) print("📡 Updating record " + str(record))
response = cf_api( response = cf_api(
"zones/" + option['zone_id'] + "/dns_records/" + identifier, "zones/" + option['zone_id'] +
"/dns_records/" + identifier,
"PUT", option, {}, record) "PUT", option, {}, record)
else: else:
print(" Adding new record " + str(record)) print(" Adding new record " + str(record))
@ -144,16 +152,17 @@ def commitRecord(ip):
identifier = str(identifier) identifier = str(identifier)
print("🗑️ Deleting stale record " + identifier) print("🗑️ Deleting stale record " + identifier)
response = cf_api( response = cf_api(
"zones/" + option['zone_id'] + "/dns_records/" + identifier, "zones/" + option['zone_id'] +
"/dns_records/" + identifier,
"DELETE", option) "DELETE", option)
return True return True
def cf_api(endpoint, method, config, headers={}, data=False): def cf_api(endpoint, method, config, headers={}, data=False):
api_token = config['authentication']['api_token'] api_token = config['authentication']['api_token']
if api_token != '' and api_token != 'api_token_here': if api_token != '' and api_token != 'api_token_here':
headers = { headers = {
"Authorization": "Bearer " + api_token, "Authorization": "Bearer " + api_token, **headers
**headers
} }
else: else:
headers = { headers = {
@ -172,14 +181,17 @@ def cf_api(endpoint, method, config, headers={}, data=False):
if response.ok: if response.ok:
return response.json() return response.json()
else: else:
print("📈 Error sending '" + method + "' request to '" + response.url + "':") print("📈 Error sending '" + method +
"' request to '" + response.url + "':")
print(response.text) print(response.text)
return None return None
def updateIPs(ips): def updateIPs(ips):
for ip in ips.values(): for ip in ips.values():
commitRecord(ip) commitRecord(ip)
if __name__ == '__main__': if __name__ == '__main__':
shown_ipv4_warning = False shown_ipv4_warning = False
shown_ipv6_warning = False shown_ipv6_warning = False
@ -196,7 +208,8 @@ if __name__ == '__main__':
config = json.loads(config_file.read()) config = json.loads(config_file.read())
except: except:
print("😡 Error reading config.json") print("😡 Error reading config.json")
time.sleep(60) # wait 60 seconds to prevent excessive logging on docker auto restart # wait 60 seconds to prevent excessive logging on docker auto restart
time.sleep(60)
if config is not None: if config is not None:
try: try:
@ -223,11 +236,12 @@ if __name__ == '__main__':
next_time = time.time() next_time = time.time()
killer = GracefulExit() killer = GracefulExit()
prev_ips = None prev_ips = None
while True: while True:
updateIPs(getIPs()) updateIPs(getIPs())
if killer.kill_now.wait(delay): if killer.kill_now.wait(delay):
break break
else: else:
print("❓ Unrecognized parameter '" + sys.argv[1] + "'. Stopping now.") print("❓ Unrecognized parameter '" +
sys.argv[1] + "'. Stopping now.")
else: else:
updateIPs(getIPs()) updateIPs(getIPs())