68 lines
2.5 KiB
Bash
68 lines
2.5 KiB
Bash
#!/bin/sh
|
|
# OpenVPN learn-address hook: registers VPN clients in Technitium DNS.
|
|
# add/update <ip> <cn> -> A <cn>.$VPN_DNS_ZONE + PTR, TTL 60
|
|
# delete <ip> -> removes both records
|
|
#
|
|
# CRITICAL: this script must ALWAYS exit 0 — a non-zero exit makes
|
|
# OpenVPN reject the client's address. DNS registration is best-effort;
|
|
# it must never take the VPN down with it.
|
|
|
|
OP="$1"; ADDR="$2"; CN="${3:-}"
|
|
|
|
API="http://127.0.0.1:5380/api"
|
|
[ -n "${TECHNITIUM_ADMIN_PASSWORD:-}" ] || exit 0
|
|
[ -n "${VPN_DNS_ZONE:-}" ] || exit 0
|
|
|
|
log() { echo "learn-address: $*"; }
|
|
|
|
TOKEN=$(curl -sf -m 5 -G "$API/user/login" \
|
|
--data-urlencode "user=admin" \
|
|
--data-urlencode "pass=$TECHNITIUM_ADMIN_PASSWORD" 2>/dev/null \
|
|
| jq -r '.token // empty')
|
|
[ -n "$TOKEN" ] || { log "Technitium API login failed; skipping $OP $ADDR"; exit 0; }
|
|
AUTH="Authorization: Bearer $TOKEN"
|
|
|
|
# 172.31.8.5 -> 5.8.31.172.in-addr.arpa
|
|
PTR_HOST=$(printf '%s' "$ADDR" | awk -F. '{print $4"."$3"."$2"."$1".in-addr.arpa"}')
|
|
|
|
deregister() {
|
|
# PTR tells us which A record this IP belonged to
|
|
TARGET=$(curl -sf -m 5 -H "$AUTH" -G "$API/zones/records/get" \
|
|
--data-urlencode "domain=$PTR_HOST" 2>/dev/null \
|
|
| jq -r '.response.records[]? | select(.type=="PTR") | .rData.ptrName' | head -n1)
|
|
[ -n "$TARGET" ] || return 0
|
|
curl -sf -m 5 -H "$AUTH" -G "$API/zones/records/delete" \
|
|
--data-urlencode "domain=$PTR_HOST" \
|
|
--data-urlencode "type=PTR" \
|
|
--data-urlencode "ptrName=$TARGET" >/dev/null 2>&1 || log "PTR delete failed for $ADDR"
|
|
curl -sf -m 5 -H "$AUTH" -G "$API/zones/records/delete" \
|
|
--data-urlencode "domain=$TARGET" \
|
|
--data-urlencode "zone=$VPN_DNS_ZONE" \
|
|
--data-urlencode "type=A" \
|
|
--data-urlencode "ipAddress=$ADDR" >/dev/null 2>&1 || log "A delete failed for $TARGET/$ADDR"
|
|
}
|
|
|
|
register() {
|
|
# sanitize CN (= AD username) into a DNS label
|
|
NAME=$(printf '%s' "$CN" | tr '[:upper:]' '[:lower:]' \
|
|
| tr -c 'a-z0-9-' '-' | sed 's/^-*//;s/-*$//' | cut -c1-63)
|
|
[ -n "$NAME" ] || return 0
|
|
curl -sf -m 5 -H "$AUTH" -G "$API/zones/records/add" \
|
|
--data-urlencode "domain=$NAME.$VPN_DNS_ZONE" \
|
|
--data-urlencode "zone=$VPN_DNS_ZONE" \
|
|
--data-urlencode "type=A" \
|
|
--data-urlencode "ttl=60" \
|
|
--data-urlencode "ipAddress=$ADDR" \
|
|
--data-urlencode "ptr=true" \
|
|
--data-urlencode "createPtrZone=true" >/dev/null 2>&1 \
|
|
|| log "A/PTR add failed for $NAME/$ADDR"
|
|
}
|
|
|
|
case "$OP" in
|
|
add) register ;;
|
|
update) deregister; register ;; # IP reassigned to a different client
|
|
delete) deregister ;;
|
|
esac
|
|
|
|
exit 0
|