#!/usr/bin/env bash # net-ops :: linux/dns-audit.sh # Deep DNS forensics for Linux. Use when probe.sh shows rung 4 (dig) PASS # but rung 5 (getent / resolvectl) FAIL. set -u # shellcheck source=../_lib/redact.sh source "$(dirname "$0")/../_lib/redact.sh" parse_redact_flag "$@" maybe_redact_self "$@" echo "=== /etc/nsswitch.conf (hosts line) ===" grep "^hosts:" /etc/nsswitch.conf 2>/dev/null || echo " (no hosts entry)" echo echo "=== /etc/resolv.conf ===" if [[ -L /etc/resolv.conf ]]; then echo " Type: symlink -> $(readlink /etc/resolv.conf)" else echo " Type: regular file" fi echo " Modified: $(stat -c '%y' /etc/resolv.conf 2>/dev/null || stat -f '%Sm' /etc/resolv.conf 2>/dev/null)" echo " --- contents ---" cat /etc/resolv.conf 2>/dev/null | sed 's/^/ /' echo echo "=== systemd-resolved ===" if systemctl is-active systemd-resolved >/dev/null 2>&1; then echo " Service: active" echo " --- resolvectl status ---" resolvectl status 2>/dev/null | sed 's/^/ /' else echo " Service: inactive or not installed" fi echo echo "=== NetworkManager DNS config ===" if command -v nmcli >/dev/null 2>&1; then echo " --- nmcli dev show (DNS lines) ---" nmcli dev show 2>/dev/null | grep -E 'DEVICE|IP4.DNS|IP6.DNS|DOMAIN' | sed 's/^/ /' echo echo " --- NetworkManager dns mode ---" awk '/\[main\]/,/\[/{if(/^dns/) print}' /etc/NetworkManager/NetworkManager.conf 2>/dev/null | sed 's/^/ /' || true ls -la /etc/NetworkManager/conf.d/ 2>/dev/null | sed 's/^/ /' || true else echo " nmcli not installed" fi echo echo "=== dnsmasq ===" if pgrep -x dnsmasq >/dev/null; then pid=$(pgrep -x dnsmasq | head -1) echo " Running, PID $pid" ps -o command -p "$pid" 2>/dev/null | sed 's/^/ /' else echo " not running" fi for d in /etc/dnsmasq.d /etc/NetworkManager/dnsmasq.d; do [[ -d "$d" ]] && { echo " $d contents:"; ls "$d" 2>/dev/null | sed 's/^/ /'; } done echo echo "=== Local DNS listeners ===" ss -tulnp 2>/dev/null | awk 'NR==1 || $5 ~ /:53$/' | sed 's/^/ /' echo echo "=== /etc/hosts (non-comment) ===" grep -vE '^\s*(#|$)' /etc/hosts 2>/dev/null | sed 's/^/ /' || echo " (no custom entries)" echo echo "=== VPN / WireGuard interfaces ===" ip -br link 2>/dev/null | awk '/^(wg|tun|tap|nordlynx|proton|mullvad|nextdns)/' | sed 's/^/ /' || true if command -v wg >/dev/null 2>&1; then echo " --- wg show ---" wg show 2>/dev/null | sed 's/^/ /' | head -30 || true fi echo echo "=== ATTRIBUTION HINTS ===" # Inspect nameservers visible across the stack for known patterns ns_list=$( { awk '/^nameserver/{print $2}' /etc/resolv.conf 2>/dev/null resolvectl status 2>/dev/null | awk '/Current DNS Server:|DNS Servers:/{for(i=4;i<=NF;i++)print $i}' nmcli -t -f IP4.DNS,IP6.DNS dev show 2>/dev/null | awk -F: '{print $2}' } | sort -u | grep -v '^$' ) while read -r n; do [[ -z "$n" ]] && continue case "$n" in 10.2.0.*) echo " $n :: likely Proton VPN gateway" ;; 10.64.0.*) echo " $n :: likely Mullvad gateway" ;; 10.211.*|10.212.*) echo " $n :: likely Cisco AnyConnect" ;; 100.100.100.100) echo " $n :: Tailscale MagicDNS (expected)" ;; 127.0.0.53) echo " $n :: systemd-resolved stub (expected on most systems)" ;; 127.0.0.1|127.0.0.2) echo " $n :: local DNS proxy (dnsmasq, NextDNS, AdGuard, etc.)" ;; esac done <<< "$ns_list"