Add a shared Docker network (dnsnet, 10.53.0.0/24) connecting Pi-hole and
Technitium containers so Pi-hole can conditionally forward knel.net and
Tailscale-reverse queries to the local authoritative Technitium instance
(10.53.0.53) instead of netboot's upstream 192.168.3.16. Also adds
end-to-end documentation for both the reference node (pfv-netboot) and
the replicated nodes (pfv-netinfra-01/02).
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
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: 'REDACTED_PASSWORD'
|
|
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 **`REDACTED_PASSWORD`** (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` | reference-node documentation |
|
|
| `pfv-netinfra-setup.md` | this document |
|