Initial implementation: dns-updater daemon
RFC2136 dynamic-DNS updater. Watches a records file (inotify) and new interface addresses and pushes TSIG-signed updates to BIND per zone, sending only the delta. Native miekg/dns (structured per-zone RCODEs), local status API + facter fact, systemd unit, nfpm RPM, Woodpecker CI. Replaces the puppet dns-update shell script; keeps the same records-file and TSIG-key contract.
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
[Unit]
|
||||
Description=DNS record updater (RFC2136 dynamic DNS from a records file)
|
||||
Documentation=https://git.unkin.net/unkin/dns-updater
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
EnvironmentFile=-/etc/dns-updater/env
|
||||
ExecStart=/usr/bin/dns-updater
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
WatchdogSec=0
|
||||
|
||||
# Hardening. Runs as root to read the root-owned TSIG key; lock the rest down.
|
||||
NoNewPrivileges=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
PrivateTmp=true
|
||||
ProtectKernelTunables=true
|
||||
ProtectControlGroups=true
|
||||
ProtectKernelModules=true
|
||||
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
|
||||
RestrictNamespaces=true
|
||||
ReadWritePaths=/var/lib/dns-updater /run/dns-updater
|
||||
RuntimeDirectory=dns-updater
|
||||
StateDirectory=dns-updater
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,24 @@
|
||||
# dns-updater configuration (systemd EnvironmentFile).
|
||||
# The authoritative write endpoint (BIND primary). Required.
|
||||
DNS_UPDATER_SERVER=198.18.200.9
|
||||
|
||||
# BIND-style TSIG key file (algorithm + secret). Puppet manages this.
|
||||
DNS_UPDATER_KEY_FILE=/etc/dns-updater/key
|
||||
|
||||
# Desired records, one per line: zone|name|type|ttl|value. Puppet writes this.
|
||||
DNS_UPDATER_RECORDS_FILE=/var/lib/dns-updater/records
|
||||
|
||||
# Last-applied state (managed by the daemon).
|
||||
DNS_UPDATER_STATE_FILE=/var/lib/dns-updater/applied
|
||||
|
||||
# Reconcile on interface address changes (DHCP renew, link reconfig).
|
||||
DNS_UPDATER_WATCH_INTERFACES=true
|
||||
|
||||
# Periodic safety-net resync (0 disables).
|
||||
DNS_UPDATER_RESYNC=10m
|
||||
|
||||
# Local status API (unix socket path or host:port; empty disables).
|
||||
DNS_UPDATER_API=/run/dns-updater/api.sock
|
||||
|
||||
# debug|info|warn|error
|
||||
DNS_UPDATER_LOG_LEVEL=info
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env bash
|
||||
# Facter external fact: report dns-updater's status by querying its local API
|
||||
# socket. Emits flat key=value facts (compatible across facter versions).
|
||||
# Puppet already knows the DESIRED records (it writes the records file); these
|
||||
# facts report what the daemon actually achieved on the server.
|
||||
set -u
|
||||
|
||||
SOCK="${DNS_UPDATER_API_SOCK:-/run/dns-updater/api.sock}"
|
||||
|
||||
emit() { printf 'dns_updater_%s=%s\n' "$1" "$2"; }
|
||||
|
||||
if [ ! -S "$SOCK" ]; then
|
||||
emit running false
|
||||
emit healthy false
|
||||
exit 0
|
||||
fi
|
||||
|
||||
json="$(curl -s --max-time 3 --unix-socket "$SOCK" http://local/status 2>/dev/null)"
|
||||
if [ -z "$json" ]; then
|
||||
emit running true
|
||||
emit healthy false
|
||||
emit last_error "api_unreachable"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
emit running true
|
||||
|
||||
python3 - "$json" <<'PY'
|
||||
import json, sys
|
||||
try:
|
||||
d = json.loads(sys.argv[1])
|
||||
except Exception:
|
||||
print("dns_updater_healthy=false")
|
||||
print("dns_updater_last_error=bad_json")
|
||||
sys.exit(0)
|
||||
|
||||
def emit(k, v):
|
||||
print(f"dns_updater_{k}={v}")
|
||||
|
||||
emit("healthy", str(d.get("healthy", False)).lower())
|
||||
emit("managed_records", d.get("managed_records", 0))
|
||||
if d.get("last_reconcile"):
|
||||
emit("last_reconcile", d["last_reconcile"])
|
||||
if d.get("last_change"):
|
||||
emit("last_change", d["last_change"])
|
||||
if d.get("last_error"):
|
||||
emit("last_error", d["last_error"].replace("\n", " ")[:200])
|
||||
|
||||
zones = d.get("zones") or []
|
||||
failed = [z for z in zones if (z.get("error") or z.get("rcode", 0) != 0)]
|
||||
emit("zones_total", len(zones))
|
||||
emit("zones_failed", len(failed))
|
||||
if failed:
|
||||
emit("failed_zones", ",".join(f"{z['zone']}:{z.get('rcode_text','?')}" for z in failed))
|
||||
PY
|
||||
@@ -0,0 +1,45 @@
|
||||
# nfpm packaging for dns-updater. Rendered by scripts/build-rpm.sh (envsubst)
|
||||
# after `make build` produces dist/dns-updater.
|
||||
name: ${PACKAGE_NAME}
|
||||
arch: ${PACKAGE_ARCH}
|
||||
platform: ${PACKAGE_PLATFORM}
|
||||
version: ${PACKAGE_VERSION}
|
||||
release: ${PACKAGE_RELEASE}
|
||||
section: net
|
||||
maintainer: ${PACKAGE_MAINTAINER}
|
||||
homepage: ${PACKAGE_HOMEPAGE}
|
||||
license: ${PACKAGE_LICENSE}
|
||||
description: "${PACKAGE_DESCRIPTION}"
|
||||
provides:
|
||||
- dns-updater
|
||||
|
||||
contents:
|
||||
- src: ./dist/dns-updater
|
||||
dst: /usr/bin/dns-updater
|
||||
file_info:
|
||||
mode: 0755
|
||||
|
||||
- src: ./packaging/dns-updater.service
|
||||
dst: /usr/lib/systemd/system/dns-updater.service
|
||||
file_info:
|
||||
mode: 0644
|
||||
|
||||
- src: ./packaging/facts.d/dns_updater.sh
|
||||
dst: /opt/puppetlabs/facter/facts.d/dns_updater.sh
|
||||
file_info:
|
||||
mode: 0755
|
||||
|
||||
- src: ./packaging/env.sample
|
||||
dst: /etc/dns-updater/env
|
||||
type: config|noreplace
|
||||
file_info:
|
||||
mode: 0644
|
||||
|
||||
scripts:
|
||||
postinstall: ./packaging/scripts/postinstall.sh
|
||||
preremove: ./packaging/scripts/preremove.sh
|
||||
|
||||
overrides:
|
||||
rpm:
|
||||
depends:
|
||||
- systemd
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
systemctl daemon-reload >/dev/null 2>&1 || true
|
||||
# Restart if already enabled/running; otherwise leave it for puppet to enable
|
||||
# once the TSIG key and records file are in place.
|
||||
if systemctl is-enabled dns-updater.service >/dev/null 2>&1; then
|
||||
systemctl try-restart dns-updater.service >/dev/null 2>&1 || true
|
||||
fi
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
# $1 is 0 on final removal (rpm), "remove" on purge (deb).
|
||||
if [ "$1" = "0" ] || [ "$1" = "remove" ] || [ "$1" = "purge" ]; then
|
||||
systemctl disable --now dns-updater.service >/dev/null 2>&1 || true
|
||||
fi
|
||||
Reference in New Issue
Block a user