Files
PFVCluster/netinfra/pfv-netinfra-setup.md
T
mrcharles 37e59ca310 docs: end-to-end gardening — links, stale refs, tailscale.md update
Comprehensive documentation gardening across the merged repo:

- tailscale.md: fully rewritten with current ground truth. The netinfra
  pair now runs production Technitium with all knel.net records
  replicated. Both LAN IPs resolve knel.net device names and recurse
  externally. The old "NXDOMAIN / zone is stale" findings are replaced
  with the resolved state and current recommendations.
- AGENTS.md: rewritten with Gitea-compatible clickable relative links
  to all key scripts and docs. Autonomous commit/push policy
  prominently documented. SSH user corrected to localuser.
- README.md: directory table and docs table now use clickable links.
- All .md cross-references converted to Gitea-renderable relative links.
- Stale path references (ProjectCode/, Project-Tests/, ProjectDocs/)
  updated to current names (provisioning/, tests/) across all docs.
- Stale repo name "FetchApply" / "KNELServerBuild" updated to
  "PFVCluster" in actionable docs; historical AI-review docs tagged
  with an HTML comment notice.
- REFACTORING-EXAMPLES.md: tagged as historical (pre-refactor patterns).
- tests/README.md, dns-cluster-setup/README.md, docs/DEPLOYMENT.md,
  docs/SECURITY.md: path references fixed to current structure.

🤖 Generated with [Crush](https://github.com/charmassociates/crush)

Assisted-by: GLM-5 via Crush <crush@charm.land>
2026-07-28 11:28:52 -05:00

221 lines
9.2 KiB
Markdown

# 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/<svc>/` (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://<node>: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://<node>: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 @<node> 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 |