docs: lock in storage philosophy and PDM migration capability
Storage philosophy (user directive):
- NVMe/SSD: k8s worker scratch + ultix-streaming (dev workstation
running "cluster of 1" pre-prod jobs before full k8s deployment)
- Spinning rust: all infrastructure VMs (UCS, netinfra, LibreNMS, SIEM)
Clarified that hosts are standalone but managed via Proxmox Datacenter
Manager (PDM), which supports VM migration between nodes through the
UI -- eliminating the need for manual disk copies in the migration plan.
Updated all migration steps to reference PDM storage migrate instead
of manual cp commands.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
Executable
+297
@@ -0,0 +1,297 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# audit-netboot.sh
|
||||
# -----------------------------------------------------------------------------
|
||||
# READ-ONLY audit of the reference node "pfv-netboot".
|
||||
#
|
||||
# Purpose: gather enough information to replicate its Pi-hole, Technitium DNS,
|
||||
# and NTP services onto pfv-netinfra-01 and pfv-netinfra-02.
|
||||
#
|
||||
# Guarantees:
|
||||
# * NO writes, NO installs, NO service restarts, NO network changes.
|
||||
# * Every command below is read-only (status, cat, ls, ss, ps, sqlite3 SELECT).
|
||||
#
|
||||
# Intended to be run as the "localuser" account on pfv-netboot:
|
||||
#
|
||||
# ssh localuser@pfv-netboot 'bash -s' < audit-netboot.sh > netboot-audit.txt
|
||||
#
|
||||
# or, if sudo is needed for a few reads, the script will try `sudo -n` for
|
||||
# specific files that are normally root-readable only. It will NEVER use sudo
|
||||
# to write or modify anything.
|
||||
# =============================================================================
|
||||
|
||||
set -u
|
||||
AUDIT_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
|
||||
# Helper: print a clearly delimited section header.
|
||||
section() {
|
||||
printf '\n========================================================================\n'
|
||||
printf 'SECTION: %s\n' "$1"
|
||||
printf '========================================================================\n'
|
||||
}
|
||||
|
||||
# Helper: read a file with sudo if needed, silently skip if missing.
|
||||
read_file() {
|
||||
local path="$1"
|
||||
if [ -r "$path" ]; then
|
||||
printf '--- %s (uid-readable) ---\n' "$path"
|
||||
cat "$path" 2>/dev/null
|
||||
elif sudo -n true 2>/dev/null; then
|
||||
if sudo -n test -e "$path"; then
|
||||
printf '--- %s (via sudo -n) ---\n' "$path"
|
||||
sudo -n cat "$path" 2>/dev/null
|
||||
else
|
||||
printf '--- %s : NOT FOUND ---\n' "$path"
|
||||
fi
|
||||
else
|
||||
printf '--- %s : NOT READABLE (no passwordless sudo) ---\n' "$path"
|
||||
fi
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
list_dir() {
|
||||
local path="$1"
|
||||
printf '--- ls -la %s ---\n' "$path"
|
||||
ls -la "$path" 2>/dev/null || sudo -n ls -la "$path" 2>/dev/null || printf '(cannot list %s)\n' "$path"
|
||||
printf '\n'
|
||||
}
|
||||
|
||||
printf '##### AUDIT START %s #####\n' "$AUDIT_DATE"
|
||||
printf 'Audit host: %s\n' "$(hostname -f 2>/dev/null || hostname)"
|
||||
printf 'Audit user: %s\n' "$(id -un 2>/dev/null)"
|
||||
printf 'Script: audit-netboot.sh (READ-ONLY)\n'
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 1. System basics
|
||||
# -----------------------------------------------------------------------------
|
||||
section "1. SYSTEM BASICS"
|
||||
echo "-- uname --"; uname -a
|
||||
echo "-- /etc/os-release --"; cat /etc/os-release 2>/dev/null
|
||||
echo "-- uptime --"; uptime
|
||||
echo "-- timezone --"; timedatectl 2>/dev/null || cat /etc/timezone 2>/dev/null || date
|
||||
echo "-- arch --"; dpkg --print-architecture 2>/dev/null || uname -m
|
||||
echo "-- memory --"; free -h 2>/dev/null
|
||||
echo "-- disk --"; df -h / 2>/dev/null
|
||||
echo "-- cpu count --"; nproc 2>/dev/null
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 2. Network configuration
|
||||
# -----------------------------------------------------------------------------
|
||||
section "2. NETWORK"
|
||||
echo "-- hostname --"; hostname; hostname -f 2>/dev/null; hostname -I 2>/dev/null
|
||||
echo "-- ip addr --"; ip -br addr 2>/dev/null; echo; ip addr 2>/dev/null
|
||||
echo "-- default route --"; ip route 2>/dev/null
|
||||
echo "-- /etc/resolv.conf --"; cat /etc/resolv.conf 2>/dev/null
|
||||
echo "-- /etc/hosts --"; cat /etc/hosts 2>/dev/null
|
||||
echo "-- listening TCP/UDP sockets --"
|
||||
ss -tlnup 2>/dev/null || sudo -n ss -tlnup 2>/dev/null
|
||||
echo "-- /etc/network/interfaces --"; cat /etc/network/interfaces 2>/dev/null
|
||||
echo "-- netplan --"; ls -la /etc/netplan/ 2>/dev/null; for f in /etc/netplan/*.yaml; do [ -e "$f" ] && { echo "--- $f ---"; cat "$f"; }; done 2>/dev/null
|
||||
echo "-- systemd-networkd --"; ls -la /etc/systemd/network/ 2>/dev/null; networkctl status 2>/dev/null | head -40
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 3. DNS / DHCP / NTP related systemd units
|
||||
# -----------------------------------------------------------------------------
|
||||
section "3. RELEVANT SYSTEMD UNITS"
|
||||
echo "-- all units matching dns|pihole|dnsmasq|technitium|ftl|ntp|chrony|timesync --"
|
||||
systemctl list-units --type=service --all --no-pager 2>/dev/null \
|
||||
| grep -Ei 'pihole|dnsmasq|ftl|technitium|dns|ntp|chrony|timesync|resolv|resolved' || true
|
||||
echo "-- unit files (enabled state) --"
|
||||
systemctl list-unit-files --no-pager 2>/dev/null \
|
||||
| grep -Ei 'pihole|dnsmasq|ftl|technitium|dns|ntp|chrony|timesync|resolv|resolved' || true
|
||||
echo "-- installed packages of interest --"
|
||||
dpkg -l 2>/dev/null | grep -Ei 'pihole|dnsmasq|ftl|technitium|ntp|chrony|timesync|unbound|resolved|resolvconf' || true
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 4. NTP service details
|
||||
# -----------------------------------------------------------------------------
|
||||
section "4. NTP"
|
||||
echo "-- chrony --"
|
||||
systemctl status chrony --no-pager 2>/dev/null | head -15 || echo "(no chrony unit)"
|
||||
read_file /etc/chrony/chrony.conf
|
||||
echo "-- chronyc sources/stats (if available) --"
|
||||
chronyc -n sources 2>/dev/null || sudo -n chronyc -n sources 2>/dev/null || true
|
||||
chronyc -n tracking 2>/dev/null || sudo -n chronyc -n tracking 2>/dev/null || true
|
||||
|
||||
echo "-- ntpsec / ntp classic --"
|
||||
systemctl status ntp --no-pager 2>/dev/null | head -15 || echo "(no ntp unit)"
|
||||
systemctl status ntpsec --no-pager 2>/dev/null | head -15 || true
|
||||
read_file /etc/ntp.conf
|
||||
read_file /etc/ntpsec/ntp.conf
|
||||
|
||||
echo "-- systemd-timesyncd --"
|
||||
systemctl status systemd-timesyncd --no-pager 2>/dev/null | head -15 || echo "(no timesyncd)"
|
||||
read_file /etc/systemd/timesyncd.conf
|
||||
|
||||
echo "-- openntpd --"
|
||||
systemctl status openntpd --no-pager 2>/dev/null | head -15 || true
|
||||
read_file /etc/openntpd/ntpd.conf
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 5. Docker (Pi-hole and/or Technitium may be containerized)
|
||||
# -----------------------------------------------------------------------------
|
||||
section "5. DOCKER"
|
||||
if command -v docker >/dev/null 2>&1; then
|
||||
echo "-- docker version --"; docker version 2>/dev/null || sudo -n docker version 2>/dev/null || true
|
||||
echo "-- containers (running) --"; docker ps 2>/dev/null || sudo -n docker ps 2>/dev/null || true
|
||||
echo "-- containers (all) --"; docker ps -a 2>/dev/null || sudo -n docker ps -a 2>/dev/null || true
|
||||
echo "-- images --"; docker images 2>/dev/null || sudo -n docker images 2>/dev/null || true
|
||||
echo "-- volumes --"; docker volume ls 2>/dev/null || sudo -n docker volume ls 2>/dev/null || true
|
||||
echo "-- networks --"; docker network ls 2>/dev/null || sudo -n docker network ls 2>/dev/null || true
|
||||
else
|
||||
echo "(docker not installed / not on PATH)"
|
||||
fi
|
||||
echo "-- compose files in common locations --"
|
||||
for d in /etc/docker-compose /opt/docker-compose /root/docker-compose /home/localuser/docker-compose /srv/docker-compose /opt/pihole /opt/technitium /opt; do
|
||||
if sudo -n test -d "$d" 2>/dev/null || [ -d "$d" ]; then
|
||||
echo "### $d ###"
|
||||
sudo -n ls -la "$d" 2>/dev/null || ls -la "$d" 2>/dev/null || true
|
||||
for f in docker-compose.yml docker-compose.yaml compose.yml compose.yaml; do
|
||||
if sudo -n test -f "$d/$f" 2>/dev/null; then
|
||||
read_file "$d/$f"
|
||||
elif [ -f "$d/$f" ]; then
|
||||
read_file "$d/$f"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 6. Pi-hole
|
||||
# -----------------------------------------------------------------------------
|
||||
section "6. PI-HOLE"
|
||||
if command -v pihole >/dev/null 2>&1; then
|
||||
echo "-- pihole version --"; pihole -v 2>/dev/null || sudo -n pihole -v 2>/dev/null || true
|
||||
echo "-- pihole status --"; pihole status 2>/dev/null || sudo -n pihole status 2>/dev/null || true
|
||||
else
|
||||
echo "(pihole command not on PATH)"
|
||||
fi
|
||||
|
||||
echo "-- /etc/pihole listing --"
|
||||
list_dir /etc/pihole
|
||||
|
||||
# Key Pi-hole config files
|
||||
for f in \
|
||||
/etc/pihole/setupVars.conf \
|
||||
/etc/pihole/pihole-FTL.conf \
|
||||
/etc/pihole/adlists.list \
|
||||
/etc/pihole/whitelist.txt \
|
||||
/etc/pihole/blacklist.txt \
|
||||
/etc/pihole/regex.list \
|
||||
/etc/pihole/custom.list \
|
||||
/etc/pihole/local.list \
|
||||
/etc/pihole/hostnameMappings.txt \
|
||||
/etc/pihole/dhcp.leases \
|
||||
/etc/pihole/static_ip.conf \
|
||||
/etc/pihole/GitHubVersions \
|
||||
/etc/pihole/macvendor.db ; do
|
||||
read_file "$f"
|
||||
done
|
||||
|
||||
echo "-- /etc/pihole/*.conf (all) --"
|
||||
for f in /etc/pihole/*.conf; do [ -e "$f" ] && read_file "$f"; done 2>/dev/null
|
||||
|
||||
echo "-- /etc/dnsmasq.d listing --"
|
||||
list_dir /etc/dnsmasq.d
|
||||
for f in /etc/dnsmasq.d/*; do [ -e "$f" ] && read_file "$f"; done 2>/dev/null
|
||||
|
||||
echo "-- /etc/dnsmasq.conf (if present) --"
|
||||
read_file /etc/dnsmasq.conf
|
||||
|
||||
echo "-- gravity.db schema + row counts (Pi-hole v5+) --"
|
||||
GRAVITY=""
|
||||
for g in /etc/pihole/gravity.db /etc/pihole/gravity.db.*; do
|
||||
if [ -e "$g" ]; then GRAVITY="$g"; break; fi
|
||||
done
|
||||
if [ -n "${GRAVITY:-}" ]; then
|
||||
echo "gravity.db = $GRAVITY"
|
||||
if command -v sqlite3 >/dev/null 2>&1; then
|
||||
sqlite3 -readonly "$GRAVITY" ".tables" 2>/dev/null || sudo -n sqlite3 -readonly "$GRAVITY" ".tables" 2>/dev/null || true
|
||||
for tbl in adlist adlist_by_group domainlist domainlist_by_group client client_by_group group info; do
|
||||
cnt=$(sqlite3 -readonly "$GRAVITY" "SELECT COUNT(*) FROM $tbl;" 2>/dev/null || sudo -n sqlite3 -readonly "$GRAVITY" "SELECT COUNT(*) FROM $tbl;" 2>/dev/null || echo "n/a")
|
||||
printf 'gravity.%s count = %s\n' "$tbl" "$cnt"
|
||||
done
|
||||
echo "-- adlist entries (url, enabled, comment) --"
|
||||
sqlite3 -readonly "$GRAVITY" "SELECT id,address,enabled,comment FROM adlist;" 2>/dev/null \
|
||||
|| sudo -n sqlite3 -readonly "$GRAVITY" "SELECT id,address,enabled,comment FROM adlist;" 2>/dev/null || true
|
||||
echo "-- domainlist sample (first 50) --"
|
||||
sqlite3 -readonly "$GRAVITY" "SELECT id,type,domain,enabled,comment FROM domainlist LIMIT 50;" 2>/dev/null \
|
||||
|| sudo -n sqlite3 -readonly "$GRAVITY" "SELECT id,type,domain,enabled,comment FROM domainlist LIMIT 50;" 2>/dev/null || true
|
||||
echo "-- client list --"
|
||||
sqlite3 -readonly "$GRAVITY" "SELECT id,ip,comment FROM client;" 2>/dev/null \
|
||||
|| sudo -n sqlite3 -readonly "$GRAVITY" "SELECT id,ip,comment FROM client;" 2>/dev/null || true
|
||||
echo "-- group list --"
|
||||
sqlite3 -readonly "$GRAVITY" "SELECT id,name,enabled,comment FROM 'group';" 2>/dev/null \
|
||||
|| sudo -n sqlite3 -readonly "$GRAVITY" "SELECT id,name,enabled,comment FROM 'group';" 2>/dev/null || true
|
||||
echo "-- info table --"
|
||||
sqlite3 -readonly "$GRAVITY" "SELECT * FROM info;" 2>/dev/null \
|
||||
|| sudo -n sqlite3 -readonly "$GRAVITY" "SELECT * FROM info;" 2>/dev/null || true
|
||||
else
|
||||
echo "(sqlite3 not installed; gravity.db present at $GRAVITY)"
|
||||
fi
|
||||
else
|
||||
echo "(no gravity.db found)"
|
||||
fi
|
||||
|
||||
echo "-- lighttpd / pihole web admin --"
|
||||
systemctl status lighttpd --no-pager 2>/dev/null | head -15 || true
|
||||
read_file /etc/lighttpd/lighttpd.conf
|
||||
for f in /etc/lighttpd/conf-enabled/*; do [ -e "$f" ] && read_file "$f"; done 2>/dev/null
|
||||
|
||||
echo "-- pihole-FTL service --"
|
||||
systemctl status pihole-FTL --no-pager 2>/dev/null | head -20 || true
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 7. Technitium DNS
|
||||
# -----------------------------------------------------------------------------
|
||||
section "7. TECHNITIUM DNS"
|
||||
echo "-- technitium systemd unit --"
|
||||
systemctl status technitium-dns --no-pager 2>/dev/null | head -20 || echo "(no technitium-dns unit)"
|
||||
echo "-- any unit containing technitium --"
|
||||
systemctl list-units --all --no-pager 2>/dev/null | grep -i technitium || true
|
||||
echo "-- unit file path --"
|
||||
sudo -n systemctl cat technitium-dns 2>/dev/null || systemctl cat technitium-dns 2>/dev/null || true
|
||||
|
||||
# Technitium common install locations
|
||||
for d in /etc/technitium /etc/technitium/dns /opt/technitium /opt/technitium/dns /var/lib/technitium /usr/local/technitium; do
|
||||
echo "### checking $d ###"
|
||||
if sudo -n test -d "$d" 2>/dev/null || [ -d "$d" ]; then
|
||||
list_dir "$d"
|
||||
# recurse one level for config files
|
||||
for sub in "$d" "$d"/*; do
|
||||
[ -e "$sub" ] || continue
|
||||
if [ -f "$sub" ] && echo "$sub" | grep -Eq '\.(xml|json|conf|config|txt)$'; then
|
||||
read_file "$sub"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
echo "-- technitium config.xml / dnsServer.conf (search) --"
|
||||
sudo -n find /etc/technitium /opt/technitium /var/lib/technitium -maxdepth 4 \
|
||||
\( -name 'config.xml' -o -name '*.config' -o -name 'dnsServer.conf' -o -name 'blockList.txt' \) \
|
||||
-print 2>/dev/null || true
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 8. Firewall / SELinux / misc
|
||||
# -----------------------------------------------------------------------------
|
||||
section "8. FIREWALL & MISC"
|
||||
echo "-- nftables --"; sudo -n nft list ruleset 2>/dev/null | head -80 || true
|
||||
echo "-- iptables --"; sudo -n iptables -S 2>/dev/null | head -40 || true
|
||||
echo "-- ufw --"; sudo -n ufw status verbose 2>/dev/null || true
|
||||
echo "-- firewalld --"; systemctl status firewalld --no-pager 2>/dev/null | head -8 || true
|
||||
echo "-- selinux/apparmor --"; getenforce 2>/dev/null || echo "(SELinux not present)"; aa-status 2>/dev/null | head -5 || true
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# 9. Cron / timers that maintain these services
|
||||
# -----------------------------------------------------------------------------
|
||||
section "9. CRON & TIMERS"
|
||||
echo "-- root crontab --"; sudo -n crontab -l 2>/dev/null || echo "(n/a)"
|
||||
echo "-- localuser crontab --"; crontab -l 2>/dev/null || echo "(none)"
|
||||
echo "-- /etc/cron.d --"; ls -la /etc/cron.d 2>/dev/null
|
||||
for f in /etc/cron.d/*pihole* /etc/cron.d/*technitium* /etc/cron.d/*gravity*; do
|
||||
[ -e "$f" ] && read_file "$f"
|
||||
done 2>/dev/null
|
||||
echo "-- pihole timer --"; systemctl list-timers --all --no-pager 2>/dev/null | grep -Ei 'pihole|gravity|technitium' || true
|
||||
|
||||
printf '\n##### AUDIT END %s #####\n' "$AUDIT_DATE"
|
||||
Reference in New Issue
Block a user