docs(public): populate phase 2 content

Full public/ directory — services, network, decisions, security,
inventory, rebuild sequence, and per-LXC setup guides. Sourced from
wiki. No secrets or WAN IPs included.
This commit is contained in:
lerko96
2026-04-17 21:23:59 -04:00
parent b3104df54d
commit cd454b2926
17 changed files with 2053 additions and 20 deletions
+86 -2
View File
@@ -1,3 +1,87 @@
# DECISIONS
# Decisions
_stub_
Architecture Decision Records (ADR-lite). Key choices and rationale.
---
## D001 — Public/private split: git subtree + push script + pre-push hook
**Decision:** Public content lives under `public/`. Push script uses `git subtree push --prefix=public` to publish it as root to the public remote. Pre-push hook blocks direct pushes that bypass the script.
**Why:** git-filter-repo is designed for one-time rewrites, not recurring pushes. Separate branches require manual discipline. git subtree is pure git, produces clean history on the public remote, and the script stays two lines.
**Status:** decided
---
## D002 — Public remote: self-hosted Gitea → GitHub mirror
**Decision:** Public remote is a self-hosted Gitea instance. Gitea mirrors to GitHub automatically.
**Why:** Matches existing portfolio site setup. Local workflow only pushes to Gitea; GitHub propagation is transparent. No extra tooling needed.
**Constraint:** Filtering must be airtight before push — whatever reaches Gitea lands on GitHub within seconds.
**Status:** decided
---
## D003 — Private remote: private repo on same Gitea instance
**Decision:** Private remote is a separate private repository on the same self-hosted Gitea instance.
**Why:** Easiest path — infrastructure already exists, one tool to manage.
**Risk:** Single point of failure. If Gitea host goes down, both remotes are inaccessible. Accepted for now.
**Status:** decided
---
## D004 — Shared Postgres + Redis in apps LXC
**Decision:** Single Postgres instance with multiple databases + single Redis instance, both in the `apps` LXC. All productivity apps share this infrastructure.
**Why:** Avoids 15 separate DB containers. A single init script provisions all schemas on first run.
**Risk:** If Postgres goes down, all productivity apps go down simultaneously.
**Status:** decided
---
## D005 — AT&T gateway kept in-line (IP Passthrough, not EAP bypass)
**Decision:** BGW320 stays in-line with IP Passthrough mode (DHCPS-fixed to pfSense WAN MAC). pfSense gets the public IP directly. Gateway WiFi disabled.
**Why:** AT&T locks 802.1X certificate auth to their gateway hardware. EAP proxy bypass breaks on AT&T firmware updates and only saves 12ms latency. True bridge mode not supported.
**Status:** decided
---
## D006 — Caddy over NGINX Proxy Manager, with Cloudflare DNS-01
**Decision:** Caddy with `caddy-dns/cloudflare` plugin. DNS-01 challenge via Cloudflare API. All `*.lerkolabs.com` subdomains → 10.2.0.20 in Pi-hole. Caddy terminates SSL, proxies to backends.
**Why:** Single Caddyfile, auto-cert, no UI overhead. No port 80/443 needed on WAN.
**Alternatives:** NGINX Proxy Manager (more UI overhead), Traefik (more complex config, same result), self-signed certs (browser warnings).
**Status:** decided
---
## D007 — WireGuard over OpenVPN
**Decision:** WireGuard on pfSense, UDP 51820, VPN subnet 10.200.0.0/24. VPN clients get same access as LAN.
**Why:** Lower latency, better mobile battery life, ~600Mbps on the N100. OpenVPN adds complexity with no advantage here.
**Status:** decided
---
## D008 — Authentik over Authelia
**Decision:** Authentik as SSO provider for all services.
**Why:** Full OIDC provider + forward auth in one. Lets services like Outline, Gitea, and Vikunja use real SSO rather than just a login gate. Authelia only does forward auth.
**Status:** decided
---
## D009 — Pi-hole in Homelab VLAN (1020), not MGMT
**Decision:** Pi-hole at 10.2.0.11 in VLAN 1020. Firewall allows port 53 inbound from all VLANs. MGMT VLAN uses pfSense as primary DNS.
**Why:** Placing Pi-hole in MGMT would require allowing all VLANs to reach MGMT — larger attack surface than filtering DNS traffic from Homelab VLAN.
**Status:** decided
---
## D010 — Intel N100 for pfSense
**Decision:** Intel N100 mini PC. 4-core 3.4GHz, ~6W idle. Handles 23Gbps routing, 600900Mbps WireGuard.
**Why:** Right-sized for 1Gbps fiber with headroom. Raspberry Pi insufficient for 1Gbps + VPN. Full rack server overkill power draw.
**Status:** decided
+56 -2
View File
@@ -1,3 +1,57 @@
# INVENTORY
# Inventory
_stub_
Hardware inventory — make/model, role, specs. See [README](../README.md) for how everything fits together.
## Active Hardware
| Device | Role | Model | Notes |
|--------|------|-------|-------|
| Proxmox host | Hypervisor | [make/model TBD] | Hosts all LXCs + VMs |
| pfSense router | Firewall / VPN / DHCP / routing | Intel N100 mini PC | ~6W idle, handles 23Gbps routing + 600Mbps WireGuard |
| Managed switch | VLAN switching | TP-Link Omada [model TBD] | All port VLANs managed via Omada Controller |
| Access point (Guest) | Guest + IoT WiFi | TP-Link Omada [model TBD] | Auto-adopted by Omada Controller |
| AT&T Gateway | ISP ONT + IP Passthrough | BGW320-500 | ISP-owned; WiFi disabled; IP Passthrough → pfSense WAN |
## pfSense Box Detail
| Property | Value |
|----------|-------|
| CPU | Intel N100 (4-core, 3.4GHz) |
| Idle power | ~6W |
| Routing throughput | 23Gbps |
| WireGuard throughput | ~600Mbps |
| pfSense version | [TBD] |
## Proxmox Host Detail
| Property | Value |
|----------|-------|
| CPU | [TBD] |
| RAM | [TBD] |
| Boot drive | [TBD] |
| Storage | [TBD] |
| Proxmox version | [TBD] |
| PBS | [TBD — confirm if PBS runs on same host or separate] |
## Licensing / Subscriptions
| Service | Type | Notes |
|---------|------|-------|
| Cloudflare | Free | lerkolabs.com DNS + DNS-01 challenge |
| Let's Encrypt | Free | Via Caddy — auto-renewal |
| AT&T Fiber | Monthly | 1Gbps symmetric |
## Backup (PBS)
All LXCs and VMs are backed up via Proxmox Backup Server. Schedules and retention TBD — fill in when confirmed.
| Container | Schedule | Retention |
|-----------|----------|-----------|
| pihole | TBD | TBD |
| auth | TBD | TBD |
| infra | TBD | TBD |
| monitor | TBD | TBD |
| apps | TBD | TBD |
| vault | TBD | TBD |
| servarr VM | TBD | TBD |
| haos VM | TBD | TBD |
+129 -2
View File
@@ -1,3 +1,130 @@
# NETWORK
# Network
_stub_
VLAN map, firewall policy, DNS architecture, and physical topology. See [README](../README.md) for the big picture and [Services](SERVICES.md) for what lives where.
## VLAN Map
| VLAN ID | Name | Subnet | Gateway | DHCP Range | DNS |
|---------|------|--------|---------|------------|-----|
| 1000 | MGMT | 10.0.0.0/24 | 10.0.0.1 | 10.0.0.100150 | pfSense only |
| 1010 | LAN | 10.1.0.0/24 | 10.1.0.1 | 10.1.0.100200 | Pi-hole → pfSense |
| 1020 | Homelab | 10.2.0.0/24 | 10.2.0.1 | 10.2.0.100200 | Pi-hole → pfSense |
| 1030 | Guests | 10.3.0.0/24 | 10.3.0.1 | 10.3.0.100250 | Pi-hole → pfSense |
| 1040 | IoT | 10.4.0.0/24 | 10.4.0.1 | 10.4.0.100250 | Pi-hole → pfSense |
| 1050 | WFH | 10.5.0.0/24 | 10.5.0.1 | 10.5.0.100200 | pfSense only |
| 1 | DMZ | 10.99.0.0/24 | 10.99.0.1 | static only | pfSense only |
| — | VPN | 10.200.0.0/24 | pfSense | assigned by WG | Pi-hole → pfSense |
## Firewall Policy
Default: **deny all inter-VLAN unless explicitly allowed.**
| VLAN | Policy Summary |
|------|---------------|
| LAN (1010) | Full internet; can reach Homelab + MGMT; blocked from Guest/IoT/WFH |
| Homelab (1020) | Internet for updates (HTTP/S, SSH, NTP); cannot initiate to other VLANs |
| Guests (1030) | Internet only — hard block on all RFC1918 |
| IoT (1040) | Internet + Home Assistant (explicit rule); blocked from LAN |
| WFH (1050) | Internet only; pfSense DNS only; no personal network access |
| MGMT (1000) | Updates + NTP outbound; inbound from LAN + VPN only |
| DMZ (1) | HTTP/S + NTP outbound; hard-blocked from all internal VLANs |
| VPN (10.200.0.0/24) | Same as LAN: Homelab + MGMT web GUI + Pi-hole DNS |
## Static IP Reservations
### VLAN 1000 — MGMT
| IP | Device |
|----|--------|
| 10.0.0.1 | pfSense MGMT |
| 10.0.0.2 | Omada Switch |
| 10.0.0.3 | Guest AP |
| 10.0.0.4 | IoT AP |
### VLAN 1010 — LAN
| IP | Device |
|----|--------|
| 10.1.0.1 | pfSense LAN gateway |
### VLAN 1020 — Homelab
| IP | Device |
|----|--------|
| 10.2.0.1 | pfSense Homelab gateway |
| 10.2.0.10 | Proxmox |
| 10.2.0.11 | Pi-hole |
| 10.2.0.20 | Caddy (infra LXC) |
| 10.2.0.25 | Authentik (auth LXC) |
| 10.2.0.51 | Monitor LXC |
| 10.2.0.60 | Apps LXC |
| 10.2.0.X | Vaultwarden (vault LXC) |
### VLAN 1 — DMZ
| IP | Device |
|----|--------|
| 10.99.0.1 | pfSense DMZ gateway |
| 10.99.0.20 | Caddy (DMZ) |
| 10.99.0.22 | Gitea (public) |
| 10.99.0.23 | Portfolio site |
## IP Block Allocation (VLAN 1020)
| Block | Purpose |
|-------|---------|
| 10.2.0.19 | Infrastructure (gateway, pfSense interfaces) |
| 10.2.0.1019 | Network critical (Proxmox, Pi-hole) |
| 10.2.0.2029 | Auth / Proxy (Caddy, Authentik, Vaultwarden) |
| 10.2.0.3039 | Observability |
| 10.2.0.4049 | Dev tools |
| 10.2.0.5059 | Data |
| 10.2.0.6069 | Apps |
| 10.2.0.7079 | Files |
| 10.2.0.8099 | Media |
| 10.2.0.100+ | DHCP pool (dynamic) |
## DNS Architecture
```
Device → Pi-hole (10.2.0.11)
pfSense Unbound (10.x.0.1) — local records + DHCP hostnames
Cloudflare 1.1.1.1 (upstream)
```
- Pi-hole: ad/tracker blocking, local DNS records (all `*.lerkolabs.com` → 10.2.0.20 Caddy), query logging
- pfSense Unbound: DHCP hostname registration, backup resolver if Pi-hole is down
- WFH VLAN: pfSense DNS only — Pi-hole unreachable by design
## Physical Topology
```
AT&T Fiber ONT
|
AT&T BGW320 (IP Passthrough)
|
pfSense N100 (WAN/LAN)
|
Omada Managed Switch
├── Trunk port → pfSense (all VLANs tagged)
├── VLAN 1000 — MGMT devices
├── VLAN 1010 — Desktop / LAN
├── VLAN 1020 — Proxmox / Homelab servers
├── VLAN 1030 — Guest WiFi AP
├── VLAN 1040 — IoT WiFi AP
├── VLAN 1050 — Work laptop
└── VLAN 1 — DMZ
```
## WireGuard VPN
| Property | Value |
|----------|-------|
| Listen Port | 51820 UDP |
| VPN Subnet | 10.200.0.0/24 |
| Access granted | Homelab + MGMT web GUI + Pi-hole DNS |
| Access blocked | Guest, IoT, WFH |
No management ports (22, 8006, 443) exposed to the internet. WireGuard is the only inbound port on the WAN interface (aside from Cloudflare DNS-01 challenge traffic, which uses no inbound ports).
+59 -2
View File
@@ -1,3 +1,60 @@
# SECURITY
# Security
_stub_
Security posture — what's exposed, how auth works, update cadence, known debt. See [Network](NETWORK.md) for VLAN isolation details.
## Internet-Exposed Ports
| Port | Protocol | Destination | Purpose |
|------|----------|-------------|---------|
| 51820 | UDP | pfSense WAN | WireGuard VPN |
No management ports (22, 8006, 443) exposed to the internet. All admin access requires an active WireGuard connection first. Cloudflare DNS-01 challenge handles TLS — no port 80/443 needed on WAN.
## Authentication Layers
| Layer | Mechanism | Coverage |
|-------|-----------|----------|
| All web services | Authentik SSO (OIDC or forward auth) | 100% of `*.lerkolabs.com` |
| VPN | WireGuard pre-shared keys | Required for all remote access |
| pfSense | Web GUI + SSH key | VPN-only access |
| Proxmox | Web GUI + SSH key | VPN-only access |
| Secrets | Vaultwarden (isolated LXC) | All credentials |
No service is accessible anonymously. Guests and IoT have zero access to any internal service.
## Secrets Policy
- No plaintext secrets in any config file committed to the repo
- All secrets referenced by Vaultwarden entry name (e.g., `homelab/pfsense`)
- `.env` files in `.gitignore`
- Vaultwarden lives in its own isolated LXC — no shared container
## Certificate Management
| Domain | Provider | Method | Renewal |
|--------|----------|--------|---------|
| `*.lerkolabs.com` | Let's Encrypt via Cloudflare | DNS-01 challenge | Automatic (Caddy) |
Caddy handles all cert issuance and renewal automatically. No manual action unless Cloudflare API token expires.
## Update Cadence
| System | Frequency | Method |
|--------|-----------|--------|
| pfSense | Monthly | Manual — System → Update |
| Proxmox | Monthly | `apt update && apt dist-upgrade` |
| Pi-hole | Monthly | `pihole -up` |
| Docker services | Weekly | `docker compose pull && docker compose up -d` |
| Omada firmware | Quarterly | Omada Controller → Devices |
| AT&T Gateway | Automatic | AT&T pushes updates |
| WireGuard keys | Annually (or on peer change) | Rotate in pfSense VPN config |
## Known Technical Debt
| Item | Risk | Priority | Notes |
|------|------|----------|-------|
| IoT VLAN rules too broad | Medium | Medium | Currently allows all outbound internet; should restrict to known ports/destinations per device type |
| No IDS/IPS | Medium | Low | pfSense supports Suricata — not deployed |
| No automated patching | Low | Low | All updates are manual; no Watchtower or unattended-upgrades on most services |
| Cloudflare API token scope | Low | Low | Verify token is scoped to DNS-edit only, not zone-admin |
| Beszel agent coverage | Low | Low | Confirm all LXCs have Beszel agents deployed |
+90 -2
View File
@@ -1,3 +1,91 @@
# SERVICES
# Services
_stub_
Full registry of what's running, where it lives, and how to reach it. See [README](../README.md) for compute layout and [Network](NETWORK.md) for VLAN/IP context.
## Status Key
| Symbol | Meaning |
|--------|---------|
| ✅ | Running, healthy |
| ⚠️ | Running, needs attention |
| 🔴 | Down / broken |
| 🚧 | In progress |
| | Decommissioned |
## Core Network (VLAN 1000/1010/1020)
| Service | IP | Port | VLAN | URL | Status | Notes |
|---------|-----|------|------|-----|--------|-------|
| pfSense | 10.1.0.1 / 10.0.0.1 | 443 | LAN/MGMT | https://pfsense.lerkolabs.com | ✅ | Firewall, DHCP, WireGuard VPN |
| Omada Switch | 10.0.0.2 | 443 | MGMT | https://switch.lerkolabs.com | ✅ | Managed switch, VLAN config |
| AT&T Gateway | 192.168.1.254 | 80 | — | http://192.168.1.254 | ✅ | IP Passthrough only, WiFi disabled |
| Pi-hole | 10.2.0.11 | 80/53 | 1020 | https://pihole.lerkolabs.com | ✅ | Primary DNS, ad blocking |
| Caddy (infra) | 10.2.0.20 | 80/443 | 1020 | — | ✅ | Reverse proxy, wildcard SSL via Cloudflare DNS-01 |
| ntfy | 10.2.0.20 | — | 1020 | — | ✅ | Push notifications (infra LXC) |
| Authentik | 10.2.0.25 | 9000 | 1020 | https://auth.lerkolabs.com | ✅ | SSO — OIDC + forward auth |
| Proxmox | 10.2.0.10 | 8006 | 1020 | https://proxmox.lerkolabs.com | ✅ | Hypervisor |
## Observability (monitor LXC — 10.2.0.51)
| Service | URL | Notes |
|---------|-----|-------|
| Grafana | https://grafana.lerkolabs.com | Dashboards, alerting |
| Victoria Metrics | — | Metrics storage |
| Beszel | — | Container + host monitoring |
## Productivity Apps (apps LXC — 10.2.0.60)
All behind Authentik SSO.
| Service | URL | Auth | Purpose |
|---------|-----|------|---------|
| Outline | https://outline.lerkolabs.com | OIDC | Team wiki |
| Vikunja | https://tasks.lerkolabs.com | OIDC | Task management |
| Ghostfolio | https://finance.lerkolabs.com | Forward auth | Portfolio tracking |
| Hoarder | https://hoarder.lerkolabs.com | Forward auth | Bookmark manager |
| Grist | https://grist.lerkolabs.com | Forward auth | Spreadsheets / data |
| Actual Budget | https://budget.lerkolabs.com | Forward auth | Personal budgeting |
| FreshRSS | https://rss.lerkolabs.com | Forward auth | RSS reader |
| Memos | https://memos.lerkolabs.com | Forward auth | Quick notes |
| Traggo | https://time.lerkolabs.com | Forward auth | Time tracking |
| Baikal | https://dav.lerkolabs.com | Forward auth | CalDAV / CardDAV |
| Glance | https://glance.lerkolabs.com | Forward auth | Homepage dashboard |
| Filebrowser | https://files.lerkolabs.com | Forward auth | File management |
| Bytestash | — | Forward auth | Snippet storage |
Shared infrastructure in apps LXC: single Postgres instance (multi-DB) + Redis. See [D004](DECISIONS.md#d004--shared-postgres--redis-in-apps-lxc).
## Secrets (vault LXC — 10.2.0.X)
| Service | URL | Notes |
|---------|-----|-------|
| Vaultwarden | https://vault.lerkolabs.com | Isolated LXC — not shared with apps |
## Media (servarr VM)
| Service | Purpose |
|---------|---------|
| Plex + Jellyfin | Media streaming |
| Sonarr / Radarr / Lidarr | Automated media management |
| Prowlarr + Bazarr | Indexer aggregation + subtitles |
| qBittorrent (via Gluetun) | Downloads — VPN-gated |
| Calibre-Web Automated | Book library with auto-ingest |
| Kavita | E-reader |
## DMZ (VLAN 1 — 10.99.0.0/24)
| Service | IP | URL | Status | Notes |
|---------|----|-----|--------|-------|
| Caddy (DMZ) | 10.99.0.20 | — | ✅ | Public reverse proxy |
| Gitea | 10.99.0.22 | https://gitea.lerkolabs.com | ✅ | Public Git |
| Portfolio | 10.99.0.23 | https://lerkolabs.com | ✅ | Personal site |
## Access Matrix
| Service | LAN | Homelab | Guest | IoT | WFH | VPN |
|---------|-----|---------|-------|-----|-----|-----|
| pfSense Web GUI | ✅ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Pi-hole Admin | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |
| All *.lerkolabs.com | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |
| Proxmox | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |
| Internet | ✅ | limited | ✅ | ✅ | ✅ | optional |