Pi-hole swap: quadlet + env + records + Caddy vhost (retires dnsmasq)
- dankclaw/pihole/: pihole.container (quadlet, slirp4netns, 53+8083), env.example (ServerIP/TZ/DNS1/DNS2/WEBPASSWORD placeholders), addn-hosts (moved from dankclaw/dnsmasq/, + pihole.franksdank.cloud record) - Caddyfile: pihole.franksdank.cloud vhost (family basicauth -> 8083) - scripts/deploy-pihole-quadlet.sh: idempotent quadlet installer - tests/pihole.bats replaces tests/dnsmasq.bats (same record validation + cross-checks, new quadlet/env seams); retired files in .AGENTTRASH/
This commit is contained in:
@@ -1,43 +1,60 @@
|
||||
#!/usr/bin/env bats
|
||||
# Seam: LAN DNS (dnsmasq) on dankclaw.
|
||||
# Seam: LAN DNS (Pi-hole) on dankclaw.
|
||||
#
|
||||
# The dnsmasq quadlet is the LAN's authoritative DNS for the flat
|
||||
# franksdank.cloud / franksdank.casa name scheme. Every Caddy vhost name
|
||||
# must have an A record pointing at the host that runs it, records must
|
||||
# stay inside the LAN, and the quadlet must be boot-persistent.
|
||||
# The Pi-hole quadlet is the LAN's authoritative DNS for the flat
|
||||
# franksdank.cloud / franksdank.casa name scheme. It retired the dnsmasq
|
||||
# quadlet (2026-08-24; Pi-hole FTL is a dnsmasq fork, so the addn-hosts
|
||||
# record file carries over unchanged). Every Caddy vhost name must have an
|
||||
# A record pointing at the host that runs it, records must stay inside the
|
||||
# LAN, and the quadlet must be boot-persistent.
|
||||
|
||||
load helpers
|
||||
|
||||
DNSMASQ_DIR="$REPO_ROOT/dankclaw/dnsmasq"
|
||||
QUADLET="$DNSMASQ_DIR/dnsmasq.container"
|
||||
CONF="$DNSMASQ_DIR/dnsmasq.conf"
|
||||
HOSTS="$DNSMASQ_DIR/addn-hosts"
|
||||
PIHOLE_DIR="$REPO_ROOT/dankclaw/pihole"
|
||||
QUADLET="$PIHOLE_DIR/quadlet/pihole.container"
|
||||
ENV_EXAMPLE="$PIHOLE_DIR/env.example"
|
||||
HOSTS="$PIHOLE_DIR/addn-hosts"
|
||||
|
||||
@test "the dnsmasq quadlet container file exists" {
|
||||
@test "the pihole quadlet container file exists" {
|
||||
[ -f "$QUADLET" ]
|
||||
}
|
||||
|
||||
@test "the dnsmasq quadlet is installed for boot" {
|
||||
@test "the pihole quadlet is installed for boot" {
|
||||
grep -q 'WantedBy=default.target' "$QUADLET"
|
||||
}
|
||||
|
||||
@test "the dnsmasq quadlet publishes port 53 tcp and udp" {
|
||||
grep -qE '^PublishPort=53:53/tcp$' "$QUADLET"
|
||||
grep -qE '^PublishPort=53:53/udp$' "$QUADLET"
|
||||
@test "the pihole quadlet publishes port 53 tcp and udp" {
|
||||
grep -qE '^PublishPort=0\.0\.0\.0:53:53/tcp$' "$QUADLET"
|
||||
grep -qE '^PublishPort=0\.0\.0\.0:53:53/udp$' "$QUADLET"
|
||||
}
|
||||
|
||||
@test "the dnsmasq quadlet restarts on failure" {
|
||||
@test "the pihole quadlet publishes the web UI on 8083" {
|
||||
grep -qE '^PublishPort=0\.0\.0\.0:8083:80/tcp$' "$QUADLET"
|
||||
}
|
||||
|
||||
@test "the pihole quadlet restarts on failure" {
|
||||
grep -q '^Restart=always' "$QUADLET"
|
||||
}
|
||||
|
||||
@test "dnsmasq.conf exists and pins upstream resolvers" {
|
||||
[ -f "$CONF" ]
|
||||
grep -q '^no-resolv' "$CONF"
|
||||
grep -qE '^server=[0-9.]+$' "$CONF"
|
||||
@test "the pihole quadlet reads its env file" {
|
||||
grep -q '^EnvironmentFile=/home/dank/Containers/pihole/env$' "$QUADLET"
|
||||
}
|
||||
|
||||
@test "dnsmasq.conf loads the addn-hosts record file" {
|
||||
grep -q '^addn-hosts=' "$CONF"
|
||||
@test "the pihole quadlet mounts the addn-hosts record file read-only" {
|
||||
grep -qE '^Volume=/home/dank/Containers/pihole/addn-hosts:/etc/dnsmasq\.d/addn-hosts:ro$' "$QUADLET"
|
||||
}
|
||||
|
||||
@test "the pihole env example has placeholder values only" {
|
||||
[ -f "$ENV_EXAMPLE" ]
|
||||
grep -q '^WEBPASSWORD=changeme$' "$ENV_EXAMPLE"
|
||||
grep -q '^ServerIP=192\.168\.0\.143$' "$ENV_EXAMPLE"
|
||||
grep -q '^DNS1=1\.1\.1\.1$' "$ENV_EXAMPLE"
|
||||
}
|
||||
|
||||
@test "the retired dnsmasq quadlet files are out of the repo" {
|
||||
[ ! -f "$REPO_ROOT/dankclaw/dnsmasq/dnsmasq.container" ]
|
||||
[ ! -f "$REPO_ROOT/dankclaw/dnsmasq/dnsmasq.conf" ]
|
||||
[ ! -f "$REPO_ROOT/dankclaw/dnsmasq/Containerfile" ]
|
||||
}
|
||||
|
||||
@test "every addn-hosts line is a valid IPv4 address plus hostnames" {
|
||||
@@ -98,6 +115,10 @@ record_ip_for() {
|
||||
done <<< "$names"
|
||||
}
|
||||
|
||||
@test "the pihole admin name has an A record on 192.168.0.143" {
|
||||
[ "$(record_ip_for pihole.franksdank.cloud)" = "192.168.0.143" ]
|
||||
}
|
||||
|
||||
@test "no addn-hosts records point at the old per-host split (192.168.0.235)" {
|
||||
! awk '!/^[[:space:]]*#/ && NF {print $1}' "$HOSTS" | grep -q '^192\.168\.0\.235$'
|
||||
}
|
||||
@@ -106,9 +127,10 @@ record_ip_for() {
|
||||
! grep -qE '\billms\.franksdank\.cloud\b' "$HOSTS"
|
||||
}
|
||||
|
||||
@test "no secret material in the dnsmasq files" {
|
||||
@test "no secret material in the pihole files" {
|
||||
local f
|
||||
for f in "$DNSMASQ_DIR"/*; do
|
||||
while IFS= read -r f; do
|
||||
has_no_leaked_secret "$f"
|
||||
done
|
||||
has_no_leaked_sudo_pw "$f"
|
||||
done < <(find "$PIHOLE_DIR" -type f | sort)
|
||||
}
|
||||
Reference in New Issue
Block a user