docs: README, changelog, contributing, security, deployment, operations, CI
Some checks are pending
build / build (push) Waiting to run
Some checks are pending
build / build (push) Waiting to run
This commit is contained in:
commit
473855e59a
12 changed files with 551 additions and 0 deletions
119
dns_sync.py
Normal file
119
dns_sync.py
Normal 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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue