docs: README, changelog, contributing, security, deployment, operations, CI
Some checks are pending
build / build (push) Waiting to run

This commit is contained in:
root 2026-05-24 18:21:04 +01:00
commit 473855e59a
12 changed files with 551 additions and 0 deletions

119
dns_sync.py Normal file
View file

@ -0,0 +1,119 @@
#!/usr/bin/env python3
import time
import re
import subprocess
import logging
import os
import json
from pathlib import Path
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s'
)
log = logging.getLogger(__name__)
TRAEFIK_API = os.environ.get('TRAEFIK_API', 'http://192.168.88.2:8080')
DNS_SERVER = os.environ.get('DNS_SERVER', '192.168.88.1')
DNS_ZONE = os.environ.get('DNS_ZONE', 'ankh-morpork.discworld.network')
TARGET_IP = os.environ.get('TARGET_IP', '192.168.88.2')
TTL = int(os.environ.get('TTL', '300'))
POLL_INTERVAL = int(os.environ.get('POLL_INTERVAL', '60'))
KEYTAB = os.environ.get('KEYTAB', '/run/secrets/dns-updater-keytab')
KRB_PRINCIPAL = os.environ.get('KRB_PRINCIPAL', 'dns-updater@DISCWORLD.NETWORK')
STATE_FILE = os.environ.get('STATE_FILE', '/tmp/dns_state.json')
def kinit():
result = subprocess.run(
['kinit', '-kt', KEYTAB, KRB_PRINCIPAL],
capture_output=True, text=True
)
if result.returncode != 0:
raise RuntimeError(f"kinit failed: {result.stderr}")
log.info("Kerberos ticket obtained")
def get_traefik_hostnames():
import urllib.request
import urllib.error
try:
with urllib.request.urlopen(f"{TRAEFIK_API}/api/http/routers", timeout=10) as r:
routers = json.loads(r.read())
except urllib.error.URLError as e:
log.error(f"Failed to reach Traefik API: {e}")
return set()
hostnames = set()
for router in routers:
if router.get('provider') != 'docker':
continue
rule = router.get('rule', '')
for match in re.finditer(r'Host\(`([^`]+)`\)', rule):
host = match.group(1)
if host.endswith(DNS_ZONE):
hostnames.add(host)
return hostnames
def nsupdate(commands):
input_data = f"gss-tsig\n{commands}\n"
result = subprocess.run(
['nsupdate', '-g'],
input=input_data, capture_output=True, text=True
)
if result.returncode != 0:
raise RuntimeError(f"nsupdate failed: {result.stderr}")
def add_record(hostname):
log.info(f"Adding {hostname} -> {TARGET_IP}")
nsupdate(f"""server {DNS_SERVER}
zone {DNS_ZONE}
update add {hostname}. {TTL} A {TARGET_IP}
send""")
def remove_record(hostname):
log.info(f"Removing {hostname}")
nsupdate(f"""server {DNS_SERVER}
zone {DNS_ZONE}
update delete {hostname}. A
send""")
def load_state():
try:
return set(json.loads(Path(STATE_FILE).read_text()))
except Exception:
return set()
def save_state(hostnames):
Path(STATE_FILE).write_text(json.dumps(list(hostnames)))
def sync():
kinit()
current = get_traefik_hostnames()
previous = load_state()
to_add = current - previous
to_remove = previous - current
for h in to_add:
try:
add_record(h)
except Exception as e:
log.error(f"Failed to add {h}: {e}")
current.discard(h)
for h in to_remove:
try:
remove_record(h)
except Exception as e:
log.error(f"Failed to remove {h}: {e}")
save_state(current)
if __name__ == '__main__':
log.info("dns-traefik-sync starting")
while True:
try:
sync()
except Exception as e:
log.error(f"Sync failed: {e}")
time.sleep(POLL_INTERVAL)