dns-audit.sh 3.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. #!/usr/bin/env bash
  2. # net-ops :: linux/dns-audit.sh
  3. # Deep DNS forensics for Linux. Use when probe.sh shows rung 4 (dig) PASS
  4. # but rung 5 (getent / resolvectl) FAIL.
  5. set -u
  6. # shellcheck source=../_lib/redact.sh
  7. source "$(dirname "$0")/../_lib/redact.sh"
  8. parse_redact_flag "$@"
  9. maybe_redact_self "$@"
  10. echo "=== /etc/nsswitch.conf (hosts line) ==="
  11. grep "^hosts:" /etc/nsswitch.conf 2>/dev/null || echo " (no hosts entry)"
  12. echo
  13. echo "=== /etc/resolv.conf ==="
  14. if [[ -L /etc/resolv.conf ]]; then
  15. echo " Type: symlink -> $(readlink /etc/resolv.conf)"
  16. else
  17. echo " Type: regular file"
  18. fi
  19. echo " Modified: $(stat -c '%y' /etc/resolv.conf 2>/dev/null || stat -f '%Sm' /etc/resolv.conf 2>/dev/null)"
  20. echo " --- contents ---"
  21. cat /etc/resolv.conf 2>/dev/null | sed 's/^/ /'
  22. echo
  23. echo "=== systemd-resolved ==="
  24. if systemctl is-active systemd-resolved >/dev/null 2>&1; then
  25. echo " Service: active"
  26. echo " --- resolvectl status ---"
  27. resolvectl status 2>/dev/null | sed 's/^/ /'
  28. else
  29. echo " Service: inactive or not installed"
  30. fi
  31. echo
  32. echo "=== NetworkManager DNS config ==="
  33. if command -v nmcli >/dev/null 2>&1; then
  34. echo " --- nmcli dev show (DNS lines) ---"
  35. nmcli dev show 2>/dev/null | grep -E 'DEVICE|IP4.DNS|IP6.DNS|DOMAIN' | sed 's/^/ /'
  36. echo
  37. echo " --- NetworkManager dns mode ---"
  38. awk '/\[main\]/,/\[/{if(/^dns/) print}' /etc/NetworkManager/NetworkManager.conf 2>/dev/null | sed 's/^/ /' || true
  39. ls -la /etc/NetworkManager/conf.d/ 2>/dev/null | sed 's/^/ /' || true
  40. else
  41. echo " nmcli not installed"
  42. fi
  43. echo
  44. echo "=== dnsmasq ==="
  45. if pgrep -x dnsmasq >/dev/null; then
  46. pid=$(pgrep -x dnsmasq | head -1)
  47. echo " Running, PID $pid"
  48. ps -o command -p "$pid" 2>/dev/null | sed 's/^/ /'
  49. else
  50. echo " not running"
  51. fi
  52. for d in /etc/dnsmasq.d /etc/NetworkManager/dnsmasq.d; do
  53. [[ -d "$d" ]] && { echo " $d contents:"; ls "$d" 2>/dev/null | sed 's/^/ /'; }
  54. done
  55. echo
  56. echo "=== Local DNS listeners ==="
  57. ss -tulnp 2>/dev/null | awk 'NR==1 || $5 ~ /:53$/' | sed 's/^/ /'
  58. echo
  59. echo "=== /etc/hosts (non-comment) ==="
  60. grep -vE '^\s*(#|$)' /etc/hosts 2>/dev/null | sed 's/^/ /' || echo " (no custom entries)"
  61. echo
  62. echo "=== VPN / WireGuard interfaces ==="
  63. ip -br link 2>/dev/null | awk '/^(wg|tun|tap|nordlynx|proton|mullvad|nextdns)/' | sed 's/^/ /' || true
  64. if command -v wg >/dev/null 2>&1; then
  65. echo " --- wg show ---"
  66. wg show 2>/dev/null | sed 's/^/ /' | head -30 || true
  67. fi
  68. echo
  69. echo "=== ATTRIBUTION HINTS ==="
  70. # Inspect nameservers visible across the stack for known patterns
  71. ns_list=$( {
  72. awk '/^nameserver/{print $2}' /etc/resolv.conf 2>/dev/null
  73. resolvectl status 2>/dev/null | awk '/Current DNS Server:|DNS Servers:/{for(i=4;i<=NF;i++)print $i}'
  74. nmcli -t -f IP4.DNS,IP6.DNS dev show 2>/dev/null | awk -F: '{print $2}'
  75. } | sort -u | grep -v '^$' )
  76. while read -r n; do
  77. [[ -z "$n" ]] && continue
  78. case "$n" in
  79. 10.2.0.*) echo " $n :: likely Proton VPN gateway" ;;
  80. 10.64.0.*) echo " $n :: likely Mullvad gateway" ;;
  81. 10.211.*|10.212.*) echo " $n :: likely Cisco AnyConnect" ;;
  82. 100.100.100.100) echo " $n :: Tailscale MagicDNS (expected)" ;;
  83. 127.0.0.53) echo " $n :: systemd-resolved stub (expected on most systems)" ;;
  84. 127.0.0.1|127.0.0.2) echo " $n :: local DNS proxy (dnsmasq, NextDNS, AdGuard, etc.)" ;;
  85. esac
  86. done <<< "$ns_list"