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>
221 lines
9.2 KiB
Markdown
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 |
|