# pfv-netinfra-01 / pfv-netinfra-02 — Network Services Setup These two nodes replicate the network-infrastructure services of **pfv-netboot** (Pi-hole, Technitium DNS, NTP). They were deployed by `setup-netinfra.sh`, which reads config from pfv-netboot (read-only) and relays it to each target. ## 1. Nodes | | pfv-netinfra-01 | pfv-netinfra-02 | |---|---|---| | OS | Debian 13 (trixie), kernel 6.12.96+deb13 | Debian 13 (trixie) | | LAN | `ens18` `192.168.3.252/24` | `ens18` `192.168.3.253/24` | | Tailscale | `100.70.181.72` | `100.93.194.82` | | RAM / Disk | 1.9 GiB / 30 GB (27 GB free) | 3.7 GiB / 30 GB (27 GB free) | | Resolver | Tailscale MagicDNS (`100.100.100.100`) | same | | Docker | 29.6.2 (pre-installed, enabled) | 29.6.2 | | Access | `ssh localuser@pfv-netinfra-0X`, passwordless sudo; `localuser` **not** in docker group → use `sudo docker` | same | ## 2. Service layout All services live under `/home/localuser/services//` (owned by `localuser` so the compose files are directly editable; data dirs keep container uids): ``` /home/localuser/services/ ├── pihole/ │ ├── docker-compose.yml │ └── etc-pihole/ # copied from netboot /root/pihole/etc-pihole │ ├── pihole.toml # Pi-hole v6 config (upstreams, etc.) │ ├── gravity.db # adlists / domainlists / clients / groups │ ├── adlists.list │ ├── dnsmasq.conf │ ├── tls.{crt,pem,crt_ca} │ └── versions ├── ntp/ │ └── docker-compose.yml # chrony container (see §5 — not used; host ntpsec serves) └── technitium/ ├── docker-compose.yml └── config/ # copied from netboot orphaned volume dns_tsys-dns-config/_data ├── dns.config ├── auth.config ├── scopes/Default.scope ├── self-signed-cert.pfx └── zones/ # knel.net.zone + 12 Tailscale reverse zones ``` ## 3. Pi-hole (container `pihole`) Image `pihole/pihole:latest`; `restart: always`; `cap_add: [SYS_NICE]`. | Host port | Container | Purpose | |---|---|---| | `53/tcp`, `53/udp` | 53 | DNS (the LAN/Tailscale recursive resolver) | | `10002/tcp` | 80 | Web admin (HTTP) | | `10003/tcp` | 443 | Web admin (HTTPS) | `docker-compose.yml`: ```yaml services: pihole: container_name: pihole image: pihole/pihole:latest hostname: pihole ports: - "53:53/tcp" - "53:53/udp" - "10002:80/tcp" - "10003:443/tcp" environment: TZ: 'America/Chicago' FTLCONF_webserver_api_password: 'Gransyan1!' FTLCONF_dns_listeningMode: 'all' volumes: - './etc-pihole:/etc/pihole' cap_add: - SYS_NICE restart: always ``` - Upstream DNS (from copied `pihole.toml`): `192.168.3.16`, `8.8.8.8`, `2001:4860:4860::8888`. - Adlist: `https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts`. - `pihole.toml` `interface` was adapted from netboot's `eth0` to the target's `ens18`. - Web admin: `http://:10002/admin/` — password **`Gransyan1!`** (same as netboot). - Web UI URL per node: `http://100.70.181.72:10002/admin/` (-01), `http://100.93.194.82:10002/admin/` (-02). ## 4. Technitium DNS (container `tsys-dns`) Image `technitium/dns-server`; `restart: always`. Authoritative DNS for `knel.net` (and Tailscale reverse zones), config copied verbatim from netboot's orphaned `dns_tsys-dns-config` volume. | Host port | Container | Purpose | |---|---|---| | `5300/tcp`, `5300/udp` | 53 | DNS (remapped — see note) | | `5380/tcp` | 5380 | Web console (HTTP) | | `53443/tcp` | 53443 | Web console (HTTPS) | `docker-compose.yml`: ```yaml services: technitium: image: technitium/dns-server container_name: tsys-dns ports: - "5300:53/tcp" - "5300:53/udp" - "5380:5380/tcp" - "53443:53443/tcp" volumes: - './config:/etc/dns' restart: always ``` - Zones loaded (verified): `knel.net` SOA → `dns.knel.net. hostadmin.knel.net. 2025062313 900 300 604800 900`, plus 12 Tailscale reverse zones. - Web console: `http://:5380/` → user **`admin`** + the original Technitium password (carried over via `auth.config`). If the password is unknown, reset it from the console or by removing `config/auth.config` and recreating the container. - **Port note:** Technitium's native DNS port (53) is remapped to host **5300** because Pi-hole already owns host :53 (they cannot both bind 0.0.0.0:53). To query the authoritative server: `dig -p 5300 @ knel.net SOA`. To make Pi-hole resolve `knel.net` via Technitium, add a conditional/local upstream in Pi-hole pointing to the container (e.g. `127.0.0.1#5300` is not host-reachable from Pi-hole's netns — use the docker bridge IP of `tsys-dns`, or add `knel.net` A-records directly in Pi-hole's Local DNS). ## 5. NTP (host `ntpsec`, not a container) Both targets **already run a bare-metal `ntpsec` daemon** (active, enabled) that serves NTP on every local address — including the Tailscale IP — and keeps the system clock synced. This is the **same daemon family as netboot's own bare-metal ntpsec**. - **Why no chrony container?** netboot's chrony container (`tsys-ntp`) binds the Tailscale IP `100.103.64.82:123`; on netboot that works only because its ntpsec does **not** pre-bind the specific Tailscale-IP socket. On these targets ntpsec **does** bind the Tailscale IP, so the container cannot claim it (`address already in use`) and would be a non-functional duplicate (verified: the container started but never synced — Stratum 0). It is therefore intentionally **omitted**; host ntpsec provides NTP. `setup-netinfra.sh` detects an active host NTP unit and removes any stale `tsys-ntp` container. - ntpsec config (`/etc/ntpsec/ntp.conf`): Debian NTP pool (`0-3.debian.pool.ntp.org`), `restrict default kod nomodify noquery limited` (serves time, blocks mgmt queries). - Verified sync: -01 stratum 2 (~2 ms offset), -02 stratum 3 (~0.2 ms offset), leap normal. The `ntp/docker-compose.yml` is still written on each node for parity/reference (and in case the host NTP is ever disabled — then `sudo docker compose -f /home/localuser/services/ntp/docker-compose.yml up -d` brings up chrony). ## 6. Verification results (2026-07-28) | Check | pfv-netinfra-01 | pfv-netinfra-02 | |---|---|---| | `pihole` health | healthy | healthy | | `dig @127.0.0.1:53 pi.hole` | `172.18.0.2` | `172.18.0.2` | | Pi-hole web `:10002` | HTTP 302 (→login) | HTTP 302 | | `dig @127.0.0.1:5300 knel.net SOA` | SOA answered | SOA answered | | Technitium web `:5380` | HTTP 200 | HTTP 200 | | NTP daemon | ntpsec, stratum 2, synced | ntpsec, stratum 3, synced | ## 7. Operating the services ```bash # status sudo docker ps # Pi-hole sudo docker compose -f /home/localuser/services/pihole/docker-compose.yml ps sudo docker compose -f /home/localuser/services/pihole/docker-compose.yml logs -f sudo docker exec pihole pihole -v # version sudo docker exec pihole pihole -g # rebuild gravity sudo docker exec pihole pihole -a -p # set/change web password # Technitium sudo docker compose -f /home/localuser/services/technitium/docker-compose.yml logs -f sudo docker exec tsys-dns sh # explore /etc/dns # NTP (host) systemctl status ntpsec ntpq -pn ``` ## 8. Differences from pfv-netboot (intentional) 1. **Layout** under `/home/localuser/services/` instead of `/root` (so `localuser` can manage compose files); Pi-hole data dir still owned by `localuser`, as on netboot. 2. **Pi-hole `interface`** set to `ens18` (targets' NIC) instead of netboot's `eth0`. 3. **NTP:** host `ntpsec` (Debian pool) used instead of netboot's chrony container (the container cannot bind the Tailscale IP here; see §5). 4. **Technitium DNS** host port remapped `53 → 5300` to avoid clashing with Pi-hole on `:53`. The `knel.net` zone and all reverse zones are identical to netboot's. 5. Pi-hole query logs (`pihole-FTL.db*`) and regenerable caches/backups are not copied (transient); gravity DB and all configuration are. ## 9. Re-running / reproducing `setup-netinfra.sh` is **idempotent** — it skips re-copying config if already present and uses `docker compose up -d` (no-ops when unchanged). It reads pfv-netboot read-only and never mutates it. ```bash ./setup-netinfra.sh # deploy to both nodes ./setup-netinfra.sh pfv-netinfra-01 # deploy one node ./setup-netinfra.sh pfv-netinfra-01 verify # verify only ``` Prerequisites: SSH key access to all three hosts as `localuser` with passwordless sudo; the targets reach `192.168.3.16`/`8.8.8.8` for Pi-hole upstream and the internet for image pulls. ## 10. Files in this directory | File | Purpose | |---|---| | `setup-netinfra.sh` | orchestrator: deploys + verifies the clone on -01/-02 | | `audit-netboot.sh` | broad read-only audit of pfv-netboot | | `deep-audit-netboot.sh` | docker inspect / compose / volume deep audit (read-only) | | `gather-configs.sh` | targeted config pull (pihole.toml, technitium) (read-only) | | `baseline.sh` | read-only baseline of a target node | | `netboot-audit.txt`, `netboot-deep-audit.txt`, `netboot-configs.txt` | audit output | | [`pfv-netboot-setup.md`](pfv-netboot-setup.md) | reference-node documentation | | [`pfv-netinfra-setup.md`](pfv-netinfra-setup.md) | this document |