#!/bin/bash # fips-dns-setup — Configure DNS routing for the .fips domain. # # Detects the system's DNS resolver and configures it to forward .fips # queries to the FIPS DNS responder on 127.0.0.1:5354. # # Backends (tried in order): # 1. systemd dns-delegate (systemd >= 258, declarative drop-in) # 2. systemd-resolved via resolvectl (most systemd distros) # 3. dnsmasq (standalone) # 4. NetworkManager with dnsmasq plugin # 5. Warning with manual instructions set -e FIPS_DNS_ADDR="127.0.0.1" FIPS_DNS_PORT="5354" FIPS_INTERFACE="fips0" DNS_DELEGATE_DIR="/etc/systemd/dns-delegate" DNS_DELEGATE_FILE="${DNS_DELEGATE_DIR}/fips.dns-delegate" DNSMASQ_CONF="/etc/dnsmasq.d/fips.conf" NM_DNSMASQ_CONF="/etc/NetworkManager/dnsmasq.d/fips.conf" # Record which backend was configured for teardown. STATE_DIR="/run/fips" STATE_FILE="${STATE_DIR}/dns-backend" log() { echo "fips-dns: $*"; } save_backend() { mkdir -p "$STATE_DIR" echo "$1" > "$STATE_FILE" } # Wait for fips0 interface (up to 30s). wait_for_interface() { for i in $(seq 1 30); do ip link show "$FIPS_INTERFACE" >/dev/null 2>&1 && return 0 sleep 1 done log "ERROR: $FIPS_INTERFACE interface not found after 30s" return 1 } # Wait for fips0 to have a global IPv6 address assigned (up to 30s). # Returns the address on stdout, or empty string if timeout. wait_for_fips_addr() { for i in $(seq 1 30); do local addr addr=$(ip -6 -o addr show dev "$FIPS_INTERFACE" scope global 2>/dev/null \ | awk '{print $4}' | cut -d/ -f1 | head -1) if [ -n "$addr" ]; then echo "$addr" return 0 fi sleep 1 done return 1 } # Get systemd version number. systemd_version() { systemctl --version 2>/dev/null | head -1 | grep -oE '[0-9]+' | head -1 } # Check if a systemd service is active. is_active() { systemctl is-active --quiet "$1" 2>/dev/null } # Backend 1: systemd dns-delegate (>= 258) try_dns_delegate() { local ver ver=$(systemd_version) [ -z "$ver" ] && return 1 [ "$ver" -lt 258 ] && return 1 is_active systemd-resolved.service || return 1 log "Configuring via dns-delegate (systemd $ver)" mkdir -p "$DNS_DELEGATE_DIR" cat > "$DNS_DELEGATE_FILE" </dev/null 2>&1 || return 1 is_active systemd-resolved.service || return 1 local fips_addr fips_addr=$(wait_for_fips_addr) if [ -z "$fips_addr" ]; then log "ERROR: $FIPS_INTERFACE has no global IPv6 address after 30s" return 1 fi log "Configuring via resolvectl (DNS=[${fips_addr}]:${FIPS_DNS_PORT})" resolvectl dns "$FIPS_INTERFACE" "[${fips_addr}]:${FIPS_DNS_PORT}" resolvectl domain "$FIPS_INTERFACE" "~fips" save_backend "resolvectl" return 0 } # Backend 3: standalone dnsmasq try_dnsmasq() { is_active dnsmasq.service || return 1 [ -d /etc/dnsmasq.d ] || return 1 log "Configuring via dnsmasq" cat > "$DNSMASQ_CONF" <