From 62a6267026677358c79e6d815af1c8c475c333a0 Mon Sep 17 00:00:00 2001 From: jazzymc Date: Sun, 18 Jan 2026 16:57:25 +0200 Subject: [PATCH] Add infrastructure documentation --- docs/00-CURRENT-STATE.md | 265 +++++++++++ docs/01-PHASE1-DNS-PORTABILITY.md | 208 +++++++++ docs/02-PHASE2-FOSSORIAL-STACK.md | 391 +++++++++++++++++ docs/03-PHASE3-AUTHENTIK-ZEROTRUST.md | 196 +++++++++ docs/04-PHASE4-REMOTE-GAMING.md | 465 ++++++++++++++++++++ docs/05-PHASE5-RUSTDESK.md | 187 ++++++++ docs/06-CHANGELOG.md | 39 ++ docs/06-PHASE6-PORTAINER-MANAGEMENT.md | 159 +++++++ docs/07-CHANGELOG.md | 580 +++++++++++++++++++++++++ docs/08-PHASE7-GITEA-GITOPS.md | 168 +++++++ 10 files changed, 2658 insertions(+) create mode 100644 docs/00-CURRENT-STATE.md create mode 100644 docs/01-PHASE1-DNS-PORTABILITY.md create mode 100644 docs/02-PHASE2-FOSSORIAL-STACK.md create mode 100644 docs/03-PHASE3-AUTHENTIK-ZEROTRUST.md create mode 100644 docs/04-PHASE4-REMOTE-GAMING.md create mode 100644 docs/05-PHASE5-RUSTDESK.md create mode 100644 docs/06-CHANGELOG.md create mode 100644 docs/06-PHASE6-PORTAINER-MANAGEMENT.md create mode 100644 docs/07-CHANGELOG.md create mode 100644 docs/08-PHASE7-GITEA-GITOPS.md diff --git a/docs/00-CURRENT-STATE.md b/docs/00-CURRENT-STATE.md new file mode 100644 index 0000000..5ba2c88 --- /dev/null +++ b/docs/00-CURRENT-STATE.md @@ -0,0 +1,265 @@ +# Infrastructure Upgrade Proposal: xtrm-lab.org (v2) + +## Current Infrastructure State + +**Document Updated:** 2026-01-18 +**Target Domain:** xtrm-lab.org + +--- + +## Network Topology + +### MikroTik hAP ax³ Router (192.168.31.1) + +| Parameter | Value | +|-----------|-------| +| RouterOS Version | 7.20.6 (stable) | +| WAN IP (Static) | 62.73.120.142 | +| LAN Subnet | 192.168.31.0/24 | +| Docker Bridge | 172.17.0.0/24 | +| SSH Access | `ssh -i /root/.ssh/mikrotik_key -p 2222 unraid@192.168.31.1` | + +**Interfaces:** +- `ether1` - WAN (62.73.120.142/23) +- `bridge` - LAN (192.168.31.1/24) +- `docker-bridge` - Container network (172.17.0.1/24) +- `back-to-home-vpn` - WireGuard VPN (192.168.216.1/24) + +**Running Containers on MikroTik:** +| Container | IP | Purpose | +|-----------|-----|---------| +| pihole:latest | 172.17.0.2 | DNS sinkhole (Pi-hole v6) | +| unbound:latest | 172.17.0.3 | Recursive DNS resolver | + +### Unraid Server (192.168.31.2) + +**Tailscale IP:** 100.100.208.70 + +**Key Services:** + +| Service | Container Name | Port(s) | Network | External URL | +|---------|---------------|---------|---------|--------------| +| Portainer | portainer | 9002→9000, 9444→9443 | bridge | http://100.100.208.70:9002 (Tailscale) | +| Pi-hole | binhex-official-pihole | 53, 80, 67 | br0 (192.168.31.4) | ph1.xtrm-lab.org | +| Unbound | unbound | 53 | br0 (192.168.31.5) | - | +| Traefik | traefik | 8001→80, 44301→443 | dockerproxy | traefik.xtrm-lab.org | +| Authentik | authentik | 9000, 9443 | dockerproxy | auth.xtrm-lab.org | +| Authentik Worker | authentik-worker | - | authentik | - | +| Vaultwarden | vaultwarden | 4743→80 | bridge | vault.xtrm-lab.org | +| Plex | plex | 32400 | host | plex.xtrm-lab.org | +| Home Assistant | HomeAssistant_inabox | 8123 | host (192.168.31.15) | ha.xtrm-lab.org | +| Transmission | transmission | 9091, 51413 | bridge | - | +| Nextcloud | Nextcloud | 8666→80 | bridge | - | +| PostgreSQL | postgresql17 | 5432 | bridge | - | +| Redis | Redis | 6379 | bridge | - | +| Uptime Kuma | UptimeKuma | 3001 | bridge | - | +| NetAlertX | NetAlertX | 20211 | host | netalert.xtrm-lab.org | +| UrBackup | UrBackup | 55414 | host | urbackup.xtrm-lab.org | +| Homarr | homarr | 10004→7575 | bridge | - | +| Nebula Sync | nebula-sync | - | - | Pi-hole sync | +| DoH Server | DoH-Server | 8053 | dockerproxy | doh.xtrm-lab.org | +| stunnel DoT | stunnel-dot | 853 | bridge | dns.xtrm-lab.org:853 | +| Pangolin | pangolin | 3003→3001, 3004→3002 | bridge | Fossorial controller | +| Gitea | gitea | 3005→3000, 2222→22 | dockerproxy | git.xtrm-lab.org | +| Woodpecker Server | woodpecker-server | 8008→8000 | dockerproxy | ci.xtrm-lab.org | +| Woodpecker Agent | woodpecker-agent | - | dockerproxy | - | +| RustDesk ID | rustdesk-hbbs | 21115-21116, 21118-21119 | bridge | rustdesk.xtrm-lab.org | +| RustDesk Relay | rustdesk-hbbr | 21117 | bridge | rustdesk.xtrm-lab.org | + +--- + +## Current NAT/Port Forwarding (MikroTik) + +| Rule | Protocol | WAN Port | Destination | Purpose | +|------|----------|----------|-------------|---------| +| Forward HTTP | TCP | 80 | 192.168.31.2:8001 | Traefik HTTP | +| Forward HTTPS | TCP | 443 | 192.168.31.2:44301 | Traefik HTTPS | +| Plex | TCP | 32400 | 192.168.31.2:32400 | Plex Media Server | +| Transmission | TCP/UDP | 51413 | 192.168.31.2:51413 | BitTorrent | +| DoT | TCP | 853 | 172.17.0.2:853 | DNS over TLS | +| DoH | TCP/UDP | 5443 | 172.17.0.2:443 | DNS over HTTPS | +| DNS Force | UDP/TCP | 53 | 172.17.0.2:53 | Force LAN DNS to Pi-hole | +| RustDesk NAT Test | TCP | 21115 | 192.168.31.2:21115 | RustDesk NAT Test | +| RustDesk ID TCP | TCP | 21116 | 192.168.31.2:21116 | RustDesk ID Server | +| RustDesk ID UDP | UDP | 21116 | 192.168.31.2:21116 | RustDesk ID Server | +| RustDesk Relay | TCP | 21117 | 192.168.31.2:21117 | RustDesk Relay | + +--- + +## Current WireGuard Configuration + +**Interface:** `back-to-home-vpn` +- Listen Port: 59188 +- Address: 192.168.216.1/24 +- Public Key: `3e+p++SJ6f5EURt6WCKApOLMQHWpURm/vn/0s9+EKzs=` + +**Existing Peers:** +1. hAP ax³ (secondary device) +2. Kaloyan's S25 Ultra (mobile) +3. Additional peer (unnamed) + +--- + +## Traefik Configuration + +**Entry Points:** +- HTTP (:80) → Redirects to HTTPS +- HTTPS (:443) + +**Certificate Resolver:** Cloudflare DNS Challenge +- Email: admin@xtrm-lab.org +- DNS Provider: Cloudflare + +**Existing Middlewares:** +- `default-headers` - Security headers (HSTS, XSS protection, etc.) +- `authentik-forward-auth` - Forward auth to Authentik (configured but not applied) +- `pihole1-redirect` / `pihole2-redirect` - Redirect root to /admin/ + +--- + +## Authentik Configuration + +| Parameter | Value | +|-----------|-------| +| Version | 2025.8.1 | +| URL | auth.xtrm-lab.org | +| PostgreSQL Host | postgresql17 | +| Database | authentik_db | +| Redis Host | redis | +| Network | dockerproxy | + +**Status:** Deployed but not yet integrated with services + +--- + +## Portainer Configuration (Phase 6) + +| Parameter | Value | +|-----------|-------| +| Version | CE Latest | +| HTTP Port | 9002 | +| HTTPS Port | 9444 | +| Data Path | /mnt/user/appdata/portainer | +| Tailscale URL | http://100.100.208.70:9002 | +| Local URL | http://192.168.31.2:9002 | + +**Status:** Deployed, awaiting initial setup and MikroTik connection (Phase 6.2/6.3) + +--- + +## DNS Architecture + +``` + ┌─────────────────────────────────────┐ + │ Internet │ + └───────────────┬─────────────────────┘ + │ + ┌───────────────▼─────────────────────┐ + │ MikroTik hAP ax³ (192.168.31.1) │ + │ WAN: 62.73.120.142 │ + └───────────────┬─────────────────────┘ + │ + ┌────────────────────────┼────────────────────────┐ + │ │ │ + ▼ ▼ ▼ +┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐ +│ Pi-hole (Router) │ │ Unraid Server │ │ LAN Devices │ +│ 172.17.0.2 │ │ 192.168.31.2 │ │ 192.168.31.x │ +│ Primary DNS │ │ │ │ │ +└────────┬─────────┘ └────────┬─────────┘ └──────────────────┘ + │ │ + ▼ ▼ +┌──────────────────┐ ┌──────────────────┐ +│ Unbound (Router) │ │ Unbound (Unraid) │ +│ 172.17.0.3 │ │ 192.168.31.5 │ +│ Recursive DNS │ │ Recursive DNS │ +└──────────────────┘ └──────────────────┘ + │ + ▼ + ┌──────────────────┐ + │ Pi-hole (Unraid) │ + │ 192.168.31.4 │ + │ Secondary DNS │ + └──────────────────┘ +``` + +--- + +## Service Interruption Risk Assessment + +| Phase | Component | Interruption Risk | Mitigation | +|-------|-----------|-------------------|------------| +| 1 | Tailscale Integration | LOW | Add-on service, no changes to existing | +| 1 | DoH Endpoint | LOW | New endpoint, existing DNS unaffected | +| 2 | Pangolin/Gerbil | MEDIUM | New containers, may conflict with WG port 51820 | +| 2 | Newt Connector | LOW | Outbound only | +| 3 | Authentik Forward Auth | HIGH | Will gate all services - test thoroughly | +| 4 | Sunshine/Moonlight | LOW | New service, Tailscale-only access | +| 5 | RustDesk | MEDIUM | New ports required on MikroTik | +| 6 | Portainer | LOW | Management tool only, no service impact | + +--- + +## Ports Required for Full Implementation + +### New MikroTik Port Forwards Needed: + +| Service | Protocol | Port(s) | Destination | Phase | +|---------|----------|---------|-------------|-------| +| WireGuard (Fossorial) | UDP | 51820 | 192.168.31.2:51820 | 2 | +| RustDesk ID TCP | TCP | 21115-21117 | 192.168.31.2:21115-21117 | 5 | +| RustDesk Relay | TCP | 21118-21119 | 192.168.31.2:21118-21119 | 5 | +| RustDesk NAT | UDP | 21116 | 192.168.31.2:21116 | 5 | + +--- + +## Next Steps + +Proceed to individual phase documents: +1. [Phase 1: Global DNS Portability](./01-PHASE1-DNS-PORTABILITY.md) +2. [Phase 2: Fossorial Tunnel Stack](./02-PHASE2-FOSSORIAL-STACK.md) +3. [Phase 3: Identity & Zero Trust](./03-PHASE3-AUTHENTIK-ZEROTRUST.md) +4. [Phase 4: Remote Gaming](./04-PHASE4-REMOTE-GAMING.md) +5. [Phase 5: RustDesk Setup](./05-PHASE5-RUSTDESK.md) +6. [Phase 6: Portainer Management](./06-PHASE6-PORTAINER-MANAGEMENT.md) + +--- + +## Completed Infrastructure Tasks + +### Static IP Assignment for Critical Services + +**Status:** COMPLETED (2026-01-18) +**Priority:** High +**Reason:** Critical services should have static IPs outside DHCP/dynamic lease range to prevent IP conflicts and ensure reliable inter-container communication. + +#### dockerproxy Network (172.18.0.0/16) +Static IP range: 172.18.0.2 - 172.18.0.50 + +| Service | Static IP | +|---------|-----------| +| dockersocket | 172.18.0.2 | +| traefik | 172.18.0.3 | +| authentik | 172.18.0.11 | +| authentik-worker | 172.18.0.12 | +| postgresql17 | 172.18.0.13 | +| Redis | 172.18.0.14 | +| vaultwarden | 172.18.0.15 | + +#### bridge Network (172.17.0.0/16) +Static IP range: 172.17.0.2 - 172.17.0.50 + +| Service | Static IP | +|---------|-----------| +| portainer | 172.17.0.2 | +| rustdesk-hbbs | 172.17.0.3 | +| rustdesk-hbbr | 172.17.0.4 | + +#### Implementation Steps +1. [x] Update Docker network IPAM config to reserve static range +2. [x] Recreate critical containers with --ip flag or docker-compose static IP +3. [x] Update any hardcoded references to old IPs +4. [x] Test inter-container connectivity +5. [x] Document final IP assignments + +**Note:** IPs assigned via `docker network connect --ip`. To persist across container recreation, update Unraid Docker templates or use docker-compose. diff --git a/docs/01-PHASE1-DNS-PORTABILITY.md b/docs/01-PHASE1-DNS-PORTABILITY.md new file mode 100644 index 0000000..75ee104 --- /dev/null +++ b/docs/01-PHASE1-DNS-PORTABILITY.md @@ -0,0 +1,208 @@ +# Phase 1: Global DNS Portability + +## Status: ✅ COMPLETED (2026-01-18) + +Pi-hole ad-blocking works on all devices via Tailscale MagicDNS and DoH/DoT endpoints. + +--- + +## Tailscale Configuration + +| Parameter | Value | +|-----------|-------| +| Unraid Tailscale IP | 100.100.208.70 | +| Hostname | xtrm-unraid | +| Subnet Route | 192.168.31.0/24 (advertised & approved) | +| Global DNS | Pi-hole via MagicDNS | +| Override Local DNS | Enabled | + +### Connected Devices +| Device | Tailscale IP | Status | +|--------|--------------|--------| +| xtrm-unraid | 100.100.208.70 | Online | +| kaloyans-macbook-air | 100.68.118.59 | Active | +| mikrotik-tailscale-1 | 100.75.93.123 | Online | +| samsung-sm-s938b | 100.111.64.56 | Offline | + +--- + +## DNS Services + +### Pi-hole Instances + +| Instance | Location | IP | Web UI | Status | +|----------|----------|-----|--------|--------| +| Primary | MikroTik Container | 172.17.0.2 | ph2.xtrm-lab.org | ✅ Running | +| Secondary | Unraid (macvlan br0) | 192.168.31.4 | ph1.xtrm-lab.org | ✅ Running | + +**Sync:** nebula-sync (healthy) syncs Unraid → MikroTik every 5 minutes + +### Unbound Instances (Recursive DNS) + +| Instance | Location | IP | Status | +|----------|----------|-----|--------| +| Primary | MikroTik Container | 172.17.0.3 | ✅ Running | +| Secondary | Unraid (macvlan br0) | 192.168.31.5 | ✅ Running | + +### Pi-hole Upstream Configuration + +**Unraid Pi-hole (192.168.31.4):** +``` +upstreams = ["172.17.0.3#53", "192.168.31.5#53"] +``` + +--- + +## DoH Endpoint (DNS over HTTPS) + +| Parameter | Value | +|-----------|-------| +| URL | `https://doh.xtrm-lab.org/dns-query` | +| Container | DoH-Server (ghcr.io/ich777/doh-server) | +| Listen Port | 8053 | +| Upstream DNS | udp:192.168.31.1:53 | +| Network | dockerproxy | + +**Traefik Route (dynamic.yml):** +```yaml +doh-secure: + rule: "Host(`doh.xtrm-lab.org`)" + entryPoints: [https] + tls: + certResolver: cloudflare + service: doh # → http://DoH-Server:8053 +``` + +**Test:** +```bash +curl -H 'accept: application/dns-json' 'https://doh.xtrm-lab.org/dns-query?name=google.com&type=A' +``` + +--- + +## DoT Endpoint (DNS over TLS) + +| Parameter | Value | +|-----------|-------| +| Hostname | doh.xtrm-lab.org:853 | +| Container | stunnel-dot (dweomer/stunnel) | +| Accept Port | 853 | +| Forward To | 192.168.31.4:53 (Unraid Pi-hole) | + +**MikroTik NAT:** WAN:853 → 192.168.31.2:853 + +**Android Private DNS:** Settings → Private DNS → `doh.xtrm-lab.org` + +--- + +## DNS Architecture + +``` + ┌─────────────────────────────────────┐ + │ External Clients │ + └──────────────┬──────────────────────┘ + │ + ┌─────────────────────────┼─────────────────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ + │ Tailscale │ │ DoH │ │ DoT │ + │ MagicDNS │ │ doh.xtrm-lab.org│ │ :853 │ + │ 100.100.100.100 │ │ (Traefik→DoH) │ │ (stunnel) │ + └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ + │ │ │ + └────────────────────────┼────────────────────────┘ + │ + ▼ + ┌─────────────────────────────────────────────────────────────────────┐ + │ LAN (192.168.31.0/24) │ + │ │ + │ ┌─────────────────────────┐ ┌─────────────────────────┐ │ + │ │ MikroTik Pi-hole │ │ Unraid Pi-hole │ │ + │ │ 172.17.0.2 │ │ 192.168.31.4 │ │ + │ │ (NAT forced for LAN) │ │ (Direct access allowed) │ │ + │ └───────────┬─────────────┘ └───────────┬─────────────┘ │ + │ │ │ │ + │ ▼ ▼ │ + │ ┌─────────────────────────┐ ┌─────────────────────────┐ │ + │ │ MikroTik Unbound │ │ Unraid Unbound │ │ + │ │ 172.17.0.3 (recursive) │ │ 192.168.31.5 (recursive)│ │ + │ └─────────────────────────┘ └─────────────────────────┘ │ + └─────────────────────────────────────────────────────────────────────┘ +``` + +--- + +## DHCP DNS Servers (MikroTik) + +| Priority | Server | Notes | +|----------|--------|-------| +| Primary | 192.168.31.1 | MikroTik (NAT forces to Pi-hole 172.17.0.2) | +| Secondary | 192.168.31.4 | Unraid Pi-hole (direct, for failover) | + +--- + +## MikroTik DNS NAT Rules + +| Rule | Action | Description | +|------|--------|-------------| +| 3 | ACCEPT | Traffic TO 192.168.31.4:53 (allows Unraid Pi-hole) | +| 7 | DST-NAT | Force LAN DNS to 172.17.0.2 (MikroTik Pi-hole) | +| 21 | DST-NAT | DoT WAN:853 → 192.168.31.2:853 | + +--- + +## Failover Behavior + +| Scenario | Behavior | +|----------|----------| +| MikroTik Pi-hole down | Clients use secondary DNS (192.168.31.4) | +| MikroTik Unbound down | Pi-holes use Unraid Unbound (192.168.31.5) | +| Unraid down | MikroTik services continue independently | + +--- + +## Client Configuration + +### macOS (DoH) +- Firefox/Chrome: Settings → Security → Custom DNS → `https://doh.xtrm-lab.org/dns-query` +- System-wide: Install DNS profile with DoH URL + +### Android (DoT) +- Settings → Network → Private DNS → `doh.xtrm-lab.org` + +### Tailscale Clients +- Automatic via MagicDNS (no configuration needed) + +--- + +## Verification Commands + +```bash +# Test DoH endpoint +curl -H 'accept: application/dns-json' 'https://doh.xtrm-lab.org/dns-query?name=google.com&type=A' + +# Test ad-blocking via DoH +curl -H 'accept: application/dns-json' 'https://doh.xtrm-lab.org/dns-query?name=ads.google.com&type=A' +# Expected: 0.0.0.0 + +# Test Pi-holes directly +dig +short google.com @172.17.0.2 # MikroTik Pi-hole +dig +short google.com @192.168.31.4 # Unraid Pi-hole + +# Test Unbound directly +dig +short google.com @172.17.0.3 # MikroTik Unbound +dig +short google.com @192.168.31.5 # Unraid Unbound + +# Check Tailscale ad-blocking +dig +short ads.google.com @100.100.100.100 +# Expected: 0.0.0.0 +``` + +--- + +## Known Issues + +| Issue | Status | Notes | +|-------|--------|-------| +| Certificate renewal failing | ⚠️ Open | Cloudflare API token needs Zone:DNS:Edit permission. Certs expire Feb 11, 2026. | diff --git a/docs/02-PHASE2-FOSSORIAL-STACK.md b/docs/02-PHASE2-FOSSORIAL-STACK.md new file mode 100644 index 0000000..6da91c2 --- /dev/null +++ b/docs/02-PHASE2-FOSSORIAL-STACK.md @@ -0,0 +1,391 @@ +# Phase 2: The "Fossorial" Tunnel Stack (Pangolin, Gerbil, Newt) + +## Goal +Deploy the Fossorial tunnel stack using your MikroTik's static IP (62.73.120.142) to host tunnels without requiring a VPS, with MikroTik container fallback for resilience. + +--- + +## What is Fossorial? + +Fossorial is a self-hosted tunnel solution consisting of: + +| Component | Purpose | Role | +|-----------|---------|------| +| **Pangolin** | Central controller/dashboard | Manages tunnels, provides web UI | +| **Gerbil** | WireGuard manager | Handles WireGuard peer configuration | +| **Newt** | Tunnel connector | Lightweight agent that "dials out" to establish tunnels | + +**Why Fossorial over plain WireGuard?** +- Automatic peer management +- Web-based tunnel configuration +- Self-healing connections via Newt +- Easier certificate/identity management + +--- + +## Current WireGuard State (MikroTik) + +``` +Interface: back-to-home-vpn +├── Listen Port: 59188 (non-standard - good!) +├── Address: 192.168.216.1/24 +├── Public Key: 3e+p++SJ6f5EURt6WCKApOLMQHWpURm/vn/0s9+EKzs= +└── Peers: 3 configured +``` + +**Port 51820 Status:** NOT in use - available for Fossorial + +--- + +## Architecture Overview + +``` + Internet + │ + ┌────────────▼────────────┐ + │ MikroTik (62.73.120.142)│ + │ Port Forward: │ + │ UDP 51820 → Unraid │ + │ TCP 443 → Traefik │ + └────────────┬────────────┘ + │ + ┌──────────────────┼──────────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ + │ Pangolin │ │ Gerbil │ │ Newt │ + │ (Controller) │ │ (WG Manager) │ │ (Connector) │ + │ :3000 web UI │ │ :51820 WG │ │ Outbound only │ + │ │ │ :8080 API │ │ │ + └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ + │ │ │ + └───────────────────┴───────────────────┘ + Internal Network + 192.168.31.0/24 +``` + +--- + +## Implementation Steps + +### Step 2.1: Create Docker Network for Fossorial + +```bash +docker network create --driver bridge fossorial +``` + +--- + +### Step 2.2: Deploy Pangolin (Controller) + +**Unraid Docker Template:** + +```xml + + + pangolin + fossoriumtech/pangolin:latest + https://hub.docker.com/r/fossoriumtech/pangolin + fossorial + sh + false + Pangolin - Fossorial tunnel controller and dashboard + Network:VPN + https://pangolin.xtrm-lab.org + --restart unless-stopped + + + 3000 + + + /mnt/user/appdata/pangolin/data + /mnt/user/appdata/pangolin/config + + + https://pangolin.xtrm-lab.org + GENERATE_A_SECURE_32_CHAR_KEY + + + true + Host(`pangolin.xtrm-lab.org`) + https + cloudflare + default-headers@file + 3000 + dockerproxy + + + https://pangolin.xtrm-lab.org + Pangolin Dashboard + http + + + /app/data + +``` + +**Generate Secret Key:** +```bash +openssl rand -hex 32 +``` + +--- + +### Step 2.3: Deploy Gerbil (WireGuard Manager) + +**Unraid Docker Template:** + +```xml + + + gerbil + fossoriumtech/gerbil:latest + https://hub.docker.com/r/fossoriumtech/gerbil + fossorial + sh + true + Gerbil - Fossorial WireGuard manager + Network:VPN + --cap-add=NET_ADMIN --cap-add=SYS_MODULE --sysctl net.ipv4.ip_forward=1 --sysctl net.ipv4.conf.all.src_valid_mark=1 + + + 51820 + 8080 + + + /mnt/user/appdata/gerbil/wireguard + /mnt/user/appdata/gerbil/data + + + http://pangolin:3000 + 62.73.120.142 + 51820 + wg0 + SAME_AS_PANGOLIN_SECRET + + + http://192.168.31.2:8080/health + Gerbil WireGuard + http + +``` + +--- + +### Step 2.4: Deploy Newt (Connector) + +**Unraid Docker Template:** + +```xml + + + newt + fossoriumtech/newt:latest + https://hub.docker.com/r/fossoriumtech/newt + fossorial + sh + false + Newt - Fossorial tunnel connector (dials out to establish tunnels) + Network:VPN + --restart unless-stopped + + + /mnt/user/appdata/newt/data + + + https://pangolin.xtrm-lab.org + 62.73.120.142:51820 + GENERATE_VIA_PANGOLIN_UI + unraid-local + + + docker + Newt Connector + docker + +``` + +--- + +### Step 2.5: MikroTik Port Forward for WireGuard + +**Add NAT rule for Fossorial WireGuard:** + +```routeros +# Connect via SSH +ssh -i /root/.ssh/mikrotik_key -p 2222 unraid@192.168.31.1 + +# Add port forward +/ip/firewall/nat add chain=dstnat \ + action=dst-nat \ + to-addresses=192.168.31.2 \ + to-ports=51820 \ + protocol=udp \ + dst-address=62.73.120.142 \ + dst-port=51820 \ + comment="Fossorial WireGuard" + +# Add firewall rule to allow +/ip/firewall/filter add chain=forward \ + action=accept \ + protocol=udp \ + dst-address=192.168.31.2 \ + dst-port=51820 \ + comment="Allow Fossorial WireGuard" \ + place-before=14 +``` + +**Verification:** +```routeros +/ip/firewall/nat print where comment~"Fossorial" +``` + +--- + +### Step 2.6: Connect Networks (fossorial ↔ dockerproxy) + +Pangolin needs to be accessible via Traefik. Either: + +**Option A: Connect Pangolin to both networks** +```bash +docker network connect dockerproxy pangolin +``` + +**Option B: Use Traefik external routing in dynamic.yml** +```yaml +# Add to /mnt/user/appdata/traefik/dynamic.yml +http: + routers: + pangolin-secure: + rule: "Host(`pangolin.xtrm-lab.org`)" + entryPoints: + - https + middlewares: + - default-headers + tls: + certResolver: cloudflare + service: pangolin + + services: + pangolin: + loadBalancer: + servers: + - url: "http://192.168.31.2:3000" +``` + +--- + +### Step 2.7: MikroTik Container Fallback (Optional) + +Deploy a lightweight Gerbil instance on MikroTik for resilience: + +**Prerequisites:** +- USB storage connected to MikroTik (already present: `usb1`) +- Container mode enabled + +**MikroTik Commands:** + +```routeros +# Create container for Gerbil fallback +/container/config set registry-url=https://registry-1.docker.io tmpdir=usb1/tmp + +# Pull gerbil image +/container add \ + remote-image=fossoriumtech/gerbil:latest \ + interface=docker-bridge \ + root-dir=usb1/gerbil \ + start-on-boot=yes \ + comment="Fossorial Gerbil Fallback" + +# Configure environment +/container/envs add name=gerbil-env key=GERBIL_PUBLIC_IP value="62.73.120.142" +/container/envs add name=gerbil-env key=GERBIL_PUBLIC_PORT value="51821" +/container/envs add name=gerbil-env key=GERBIL_MODE value="standalone" +``` + +**Note:** MikroTik containers have limited resources. This is a fallback for critical services only (Pi-hole access, Authentik). + +--- + +## Service Interruption Assessment + +| Action | Risk | Impact | Mitigation | +|--------|------|--------|------------| +| Deploy Pangolin/Gerbil/Newt | NONE | New containers | - | +| Port forward 51820 | LOW | New port, existing WG on 59188 unaffected | - | +| Connect fossorial network | LOW | Container networking | Test connectivity | +| MikroTik container | MEDIUM | Router resources | Monitor CPU/memory | + +**Existing WireGuard (back-to-home-vpn) Impact:** NONE +- Uses port 59188, not 51820 +- Completely separate interface + +--- + +## Verification Checklist + +- [ ] All three containers running: `docker ps | grep -E "pangolin|gerbil|newt"` +- [ ] Pangolin web UI accessible: https://pangolin.xtrm-lab.org +- [ ] Gerbil API responding: `curl http://192.168.31.2:8080/health` +- [ ] MikroTik NAT rule in place: `ssh ... "/ip/firewall/nat print"` +- [ ] External WireGuard test: Connect from external network to 62.73.120.142:51820 +- [ ] Newt connected in Pangolin dashboard + +--- + +## Initial Pangolin Setup + +1. Navigate to https://pangolin.xtrm-lab.org +2. Create admin account +3. Add Gerbil node: + - Name: `unraid-gerbil` + - API URL: `http://gerbil:8080` + - API Key: (same as GERBIL_API_KEY) +4. Create a tunnel: + - Name: `home-services` + - Assign to Gerbil node +5. Generate Newt API key in Pangolin UI +6. Update Newt container with the API key + +--- + +## Rollback Procedure + +1. **Stop containers:** + ```bash + docker stop newt gerbil pangolin + docker rm newt gerbil pangolin + ``` + +2. **Remove MikroTik NAT:** + ```routeros + /ip/firewall/nat remove [find comment="Fossorial WireGuard"] + ``` + +3. **Remove network:** + ```bash + docker network rm fossorial + ``` + +4. **Clean up data (if desired):** + ```bash + rm -rf /mnt/user/appdata/pangolin /mnt/user/appdata/gerbil /mnt/user/appdata/newt + ``` + +--- + +## Files Modified + +| File/System | Change | Backup Required | +|-------------|--------|-----------------| +| MikroTik NAT | Add UDP 51820 forward | N/A (can remove) | +| /mnt/user/appdata/traefik/dynamic.yml | Add pangolin route | YES | +| New directories created | /mnt/user/appdata/pangolin,gerbil,newt | N/A | + +--- + +## Dependencies for Next Phase + +Phase 3 (Authentik) can now use Fossorial tunnels to: +- Expose Authentik externally without Cloudflare dependency +- Create secure tunnels for mobile OIDC authentication diff --git a/docs/03-PHASE3-AUTHENTIK-ZEROTRUST.md b/docs/03-PHASE3-AUTHENTIK-ZEROTRUST.md new file mode 100644 index 0000000..69da74c --- /dev/null +++ b/docs/03-PHASE3-AUTHENTIK-ZEROTRUST.md @@ -0,0 +1,196 @@ +# Phase 3: Identity & Zero Trust (Authentik) + +## Status: ✅ COMPLETED + +**Last Verified:** 2026-01-18 + +--- + +## Goal +Gate every `*.xtrm-lab.org` service behind OIDC authentication using Authentik, implementing Zero Trust access control. + +--- + +## Current Authentik State + +| Parameter | Value | +|-----------|-------| +| Container | authentik (+ authentik-worker) | +| Version | 2025.8.1 | +| Network | dockerproxy | +| Ports | 9000 (HTTP), 9443 (HTTPS) | +| URL | https://auth.xtrm-lab.org | +| PostgreSQL | postgresql17 (authentik_db) | +| Redis | redis | +| Status | ✅ Running (healthy) | + +--- + +## Verified Configuration + +### Users + +| Username | Name | Status | +|----------|------|--------| +| akadmin | authentik Default Admin | Active | +| admin | Admin User | Active | +| jazzymc | Kaloyan Danchev | Active | + +### Groups + +| Group Name | Purpose | +|------------|---------| +| authentik Admins | Administrative access | +| authentik Read-only | Read-only access | + +### Outpost + +| Name | Type | Status | +|------|------|--------| +| authentik Embedded Outpost | proxy | ✅ Running | + +### Applications + +| Application | Slug | +|-------------|------| +| XTRM-Lab Protected Services | xtrm-lab-protected | +| Actual Budget | actual-budget | + +### Proxy Provider + +| External Host | Mode | +|---------------|------| +| https://auth.xtrm-lab.org | forward_domain | + +### 2FA Status + +| Type | Count | +|------|-------| +| TOTP Devices | 2 | +| WebAuthn Devices | 0 | + +--- + +## Services Protected by Authentik Forward Auth + +The following services require Authentik authentication: + +| Service | Domain | +|---------|--------| +| n8n | n8n.xtrm-lab.org | +| Traefik Dashboard | traefik.xtrm-lab.org | +| NetAlertX | netalert.xtrm-lab.org | +| UrBackup | urbackup.xtrm-lab.org | +| Pi-hole 1 | ph1.xtrm-lab.org | +| Pi-hole 2 | ph2.xtrm-lab.org | +| Unimus | unimus.xtrm-lab.org | +| Homarr | xtrm-lab.org | +| Uptime Kuma | uptime.xtrm-lab.org | +| Transmission | transmission.xtrm-lab.org | + +**Total: 12 protected routes** (including root redirects for Pi-holes) + +--- + +## Services WITHOUT Authentik Protection + +These services have their own authentication or are public: + +| Service | Domain | Reason | +|---------|--------|--------| +| Authentik | auth.xtrm-lab.org | Self (would cause redirect loop) | +| Plex | plex.xtrm-lab.org | Has own Plex authentication | +| Vaultwarden | vault.xtrm-lab.org | Has own authentication | +| Home Assistant | ha.xtrm-lab.org | Has own authentication | +| Karakeep | karakeep.xtrm-lab.org | Public/own auth | +| RustFS CDN | cdn.xtrm-lab.org | Public CDN (S3 auth) | +| Pangolin API | pangolin.xtrm-lab.org | API access | +| Nextcloud | nextcloud.xtrm-lab.org | Has own authentication | + +--- + +## Traefik Forward Auth Middleware + +Configured in `/mnt/user/appdata/traefik/dynamic.yml`: + +```yaml +authentik-forward-auth: + forwardAuth: + address: "http://authentik:9000/outpost.goauthentik.io/auth/traefik" + trustForwardHeader: true + authResponseHeaders: + - X-authentik-username + - X-authentik-groups + - X-authentik-email + - X-authentik-name + - X-authentik-uid +``` + +--- + +## Verification Checklist + +- [x] Authentik initial setup completed (admin password set) +- [x] Outpost running and connected (embedded outpost) +- [x] User groups created (authentik Admins, authentik Read-only) +- [x] Application/provider pairs configured (2 applications) +- [x] Traefik config updated with forward auth middleware +- [x] Services tested successfully (302 redirect to login) +- [x] All planned services protected (12 routes) +- [x] 2FA enabled for admin accounts (2 TOTP devices) + +--- + +## Architecture + +``` + Internet User + │ + ┌────────────▼────────────┐ + │ Traefik (Reverse Proxy)│ + │ *.xtrm-lab.org:443 │ + └────────────┬────────────┘ + │ + ┌────────────▼────────────┐ + │ Forward Auth Check │ + │ → Authentik Outpost │ + └────────────┬────────────┘ + │ + ┌──────────────────┴──────────────────┐ + │ │ + ┌─────────▼─────────┐ ┌─────────▼─────────┐ + │ Authenticated? │ │ Login Required │ + │ YES → Pass │ │ Redirect to │ + │ through to │ │ auth.xtrm-lab.org│ + │ backend service │ └───────────────────┘ + └───────────────────┘ +``` + +--- + +## Maintenance Notes + +### Database +- PostgreSQL database: `authentik_db` +- User: `authentik_user` +- Host: `postgresql17` container +- Data path: `/mnt/user/appdata/postgresql` + +### Backup Recommendation +Regularly backup: +- PostgreSQL database (contains all Authentik config) +- `/mnt/user/appdata/traefik/dynamic.yml` + +### Rollback Procedure + +**Remove all protection (emergency):** +1. Edit `/mnt/user/appdata/traefik/dynamic.yml` +2. Remove `authentik-forward-auth` from all router middlewares +3. Traefik will auto-reload + +--- + +## Related Documents + +- [00-CURRENT-STATE.md](./00-CURRENT-STATE.md) - Infrastructure overview +- [02-PHASE2-FOSSORIAL-STACK.md](./02-PHASE2-FOSSORIAL-STACK.md) - Pangolin integration diff --git a/docs/04-PHASE4-REMOTE-GAMING.md b/docs/04-PHASE4-REMOTE-GAMING.md new file mode 100644 index 0000000..69780f5 --- /dev/null +++ b/docs/04-PHASE4-REMOTE-GAMING.md @@ -0,0 +1,465 @@ +# Phase 4: Remote Gaming (Sunshine + Moonlight) + +## Goal +Enable low-latency 60FPS game streaming from Nobara Linux (AMD GPU) to MacBook and Android devices, using Tailscale for optimal network pathing. + +--- + +## Prerequisites + +- **Gaming PC:** Nobara Linux with AMD GPU (VA-API support) +- **Tailscale:** Installed on gaming PC (from Phase 1) +- **Clients:** MacBook and Android devices with Tailscale + +--- + +## Architecture Overview + +``` + ┌─────────────────────────────────┐ + │ Tailscale Mesh Network │ + │ (Encrypted, P2P when possible) │ + └─────────────────┬───────────────┘ + │ + ┌────────────────────────────┼────────────────────────────┐ + │ │ │ + ▼ ▼ ▼ +┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ +│ Nobara Gaming PC│ │ MacBook │ │ Android Device │ +│ Sunshine Host │ │ Moonlight Client│ │ Moonlight Client│ +│ 100.x.x.x (TS) │ │ 100.x.x.x (TS) │ │ 100.x.x.x (TS) │ +│ AMD VA-API │ │ │ │ │ +└─────────────────┘ └─────────────────┘ └─────────────────┘ +``` + +**Why Tailscale for Gaming?** +- Automatic P2P connection when on same LAN +- Encrypted tunnel when remote +- No manual port forwarding required +- MagicDNS for easy hostname resolution +- NAT traversal handled automatically + +--- + +## Implementation Steps + +### Step 4.1: Install Sunshine on Nobara + +**Method A: Flatpak (Recommended for Nobara)** +```bash +# Install via Flatpak +flatpak install flathub dev.lizardbyte.app.Sunshine + +# Enable autostart +flatpak run --command=sunshine dev.lizardbyte.app.Sunshine & +``` + +**Method B: Native Package** +```bash +# Add Sunshine repository +sudo dnf copr enable lizardbyte/stable +sudo dnf install sunshine + +# Enable service +sudo systemctl enable --now sunshine +``` + +**Post-Install:** +1. Access Sunshine web UI: `https://localhost:47990` +2. Set initial admin password +3. Complete setup wizard + +--- + +### Step 4.2: Configure AMD VA-API Hardware Encoding + +**Verify AMD GPU:** +```bash +# Check for AMD GPU +lspci | grep -i vga + +# Verify VA-API support +vainfo +``` + +**Expected output:** +``` +vainfo: VA-API version: 1.x +vainfo: Driver version: Mesa Gallium driver ... +vainfo: Supported profile and entrypoints: + VAProfileH264Main : VAEntrypointEncSlice + VAProfileHEVCMain : VAEntrypointEncSlice +``` + +**Configure Sunshine for VA-API:** + +1. Open Sunshine Web UI → Configuration → Video +2. Set: + - **Encoder:** `vaapi` + - **Adapter:** `/dev/dri/renderD128` (default AMD) + - **Codec:** `H.265/HEVC` (better compression, lower latency) + +**Sunshine config file** (`~/.config/sunshine/sunshine.conf`): +```ini +[video] +encoder = vaapi +adapter_name = /dev/dri/renderD128 +hevc_mode = 2 # Always use HEVC when client supports + +[stream] +fps = [30, 60, 120] +resolutions = [ + 1920x1080, + 2560x1440, + 3840x2160 +] +``` + +--- + +### Step 4.3: Add Applications to Sunshine + +**Access:** Sunshine Web UI → Applications + +**Recommended Applications:** + +| Name | Command | Working Directory | +|------|---------|-------------------| +| Desktop | - | - | +| Steam Big Picture | `steam -bigpicture` | - | +| Lutris | `lutris` | - | +| Heroic Games | `heroic` | - | + +**For specific games:** +``` +Name: Cyberpunk 2077 +Command: steam steam://rungameid/1091500 +Detached: ["steam"] +``` + +--- + +### Step 4.4: Install Tailscale on Nobara + +```bash +# Install Tailscale +curl -fsSL https://tailscale.com/install.sh | sh + +# Authenticate +sudo tailscale up + +# Get Tailscale IP +tailscale ip -4 +``` + +**Note the Tailscale IP** (e.g., `100.64.x.x`) - clients will connect to this. + +--- + +### Step 4.5: Configure Sunshine for Tailscale + +**Sunshine listens on all interfaces by default.** For security, restrict to Tailscale: + +**Option A: Bind to Tailscale interface only** +```ini +# ~/.config/sunshine/sunshine.conf +[network] +address_family = both +origin_address = 100.64.x.x # Your Tailscale IP +``` + +**Option B: Firewall rules (recommended)** +```bash +# Allow Sunshine ports only from Tailscale +sudo firewall-cmd --permanent --zone=trusted --add-source=100.64.0.0/10 +sudo firewall-cmd --permanent --zone=trusted --add-port=47984-48010/tcp +sudo firewall-cmd --permanent --zone=trusted --add-port=47998-48010/udp +sudo firewall-cmd --permanent --zone=trusted --add-port=47989/tcp # Web UI +sudo firewall-cmd --permanent --zone=trusted --add-port=47990/tcp # Web UI HTTPS +sudo firewall-cmd --reload +``` + +--- + +### Step 4.6: Install Moonlight Clients + +#### MacBook +```bash +# Homebrew +brew install --cask moonlight + +# Or download from: https://moonlight-stream.org/ +``` + +#### Android +- Install "Moonlight Game Streaming" from Google Play Store + +--- + +### Step 4.7: Pair Moonlight with Sunshine + +1. **On Moonlight client:** + - Add PC manually: Enter Tailscale IP (e.g., `100.64.x.x`) + - Or use MagicDNS hostname: `nobara-pc` (if enabled in Tailscale) + +2. **Enter PIN:** + - Moonlight displays a 4-digit PIN + - Enter in Sunshine Web UI → PIN Pairing + +3. **Verify connection:** + - Moonlight should show your configured applications + +--- + +### Step 4.8: MikroTik QoS for Gaming (Optional but Recommended) + +**Goal:** Prioritize Nobara PC traffic to prevent bufferbloat during gaming sessions. + +**SSH to MikroTik:** +```bash +ssh -i /root/.ssh/mikrotik_key -p 2222 unraid@192.168.31.1 +``` + +**Create Simple Queue for Gaming PC:** +```routeros +# First, find Nobara's IP (replace with actual) +# Assuming Nobara is at 192.168.31.50 + +# Create queue for gaming priority +/queue simple add \ + name="Gaming-Priority" \ + target=192.168.31.50 \ + max-limit=0/0 \ + priority=1/1 \ + queue=default-small/default-small \ + comment="Nobara Gaming PC Priority" + +# Alternative: Use queue tree for more control +/queue tree add \ + name="Gaming-Upload" \ + parent=global \ + packet-mark=gaming-upload \ + priority=1 \ + max-limit=50M + +/queue tree add \ + name="Gaming-Download" \ + parent=global \ + packet-mark=gaming-download \ + priority=1 \ + max-limit=100M + +# Mark gaming traffic +/ip firewall mangle add \ + chain=prerouting \ + src-address=192.168.31.50 \ + action=mark-packet \ + new-packet-mark=gaming-upload \ + passthrough=yes + +/ip firewall mangle add \ + chain=postrouting \ + dst-address=192.168.31.50 \ + action=mark-packet \ + new-packet-mark=gaming-download \ + passthrough=yes +``` + +--- + +### Step 4.9: Optimize Streaming Settings + +**Sunshine Settings (Server):** + +| Setting | LAN Value | Remote Value | +|---------|-----------|--------------| +| Bitrate | 50-80 Mbps | 20-40 Mbps | +| FPS | 60-120 | 60 | +| Resolution | Native | 1080p | +| Codec | HEVC | HEVC | + +**Moonlight Settings (Client):** + +| Setting | LAN Value | Remote Value | +|---------|-----------|--------------| +| Video Codec | HEVC (if supported) | HEVC | +| Frame Pacing | V-Sync | On | +| Bitrate | Auto or 50+ Mbps | 20 Mbps | +| Resolution | Match display | 1080p | + +--- + +## Network Path Analysis + +**Tailscale P2P (Same Network):** +``` +MacBook → Router → Nobara PC +Latency: <1ms additional overhead +``` + +**Tailscale Relayed (Different Network):** +``` +MacBook → Tailscale DERP → Nobara PC +Latency: ~20-50ms additional overhead +``` + +**With Tailscale Direct (NAT Traversal Success):** +``` +MacBook (Office) → Internet → Home Router → Nobara PC +Latency: RTT/2 + encoding latency (~30-80ms typical) +``` + +--- + +## Service Interruption Assessment + +| Action | Risk | Impact | Mitigation | +|--------|------|--------|------------| +| Install Sunshine | NONE | Nobara only | - | +| Install Tailscale | NONE | Nobara only | - | +| MikroTik QoS | LOW | May affect other traffic briefly | Test during low usage | +| Firewall rules | LOW | Nobara only | Can revert | + +--- + +## Verification Checklist + +- [ ] Sunshine installed and running on Nobara +- [ ] VA-API encoding verified: `vainfo` shows HEVC support +- [ ] Tailscale running on Nobara: `tailscale status` +- [ ] Sunshine Web UI accessible: `https://:47990` +- [ ] Moonlight paired successfully +- [ ] Desktop streaming works (low latency) +- [ ] Game streaming works at 60 FPS +- [ ] Remote streaming works (via Tailscale from external network) +- [ ] MikroTik QoS queue active (optional) + +--- + +## Troubleshooting + +### High Latency / Stuttering + +1. **Check Tailscale connection type:** + ```bash + tailscale status + # Look for "direct" vs "relay" + ``` + +2. **Force direct connection:** + ```bash + tailscale ping + ``` + +3. **Lower bitrate in Moonlight** + +### Encoding Errors + +1. **Verify VA-API:** + ```bash + sudo vainfo + # Should show HEVC/H264 encode support + ``` + +2. **Check Sunshine logs:** + ```bash + journalctl -u sunshine -f + # Or: ~/.config/sunshine/sunshine.log + ``` + +3. **Fall back to software encoding:** + ```ini + # sunshine.conf + encoder = software + ``` + +### No Audio + +1. **Check PulseAudio/PipeWire:** + ```bash + pactl list sinks + ``` + +2. **Set Sunshine audio sink:** + - Web UI → Audio → Select correct output + +--- + +## Security Considerations + +1. **Tailscale-only access:** Sunshine is only reachable via Tailscale network + +2. **PIN pairing:** Each client must be manually paired + +3. **Web UI protection:** Consider adding Authentik forward auth (optional) + +4. **Firewall:** Block Sunshine ports from non-Tailscale interfaces + +--- + +## Optional: Expose Sunshine Web UI via Traefik + +If you want to manage Sunshine remotely via browser: + +**Add to Traefik dynamic.yml:** +```yaml +http: + routers: + sunshine-secure: + rule: "Host(`sunshine.xtrm-lab.org`)" + entryPoints: + - https + middlewares: + - default-headers + - authentik-forward-auth # Protect with Authentik + tls: + certResolver: cloudflare + service: sunshine + + services: + sunshine: + loadBalancer: + servers: + - url: "https://192.168.31.50:47990" # Nobara IP +``` + +**MikroTik hairpin NAT (if needed):** +```routeros +# Only if accessing from LAN via external hostname +/ip/firewall/nat add chain=srcnat \ + action=masquerade \ + src-address=192.168.31.0/24 \ + dst-address=192.168.31.50 \ + dst-port=47990 \ + protocol=tcp +``` + +--- + +## Files Modified + +| File/System | Change | Backup Required | +|-------------|--------|-----------------| +| Nobara: ~/.config/sunshine/ | Sunshine config | No (new install) | +| Nobara: firewalld | Allow Tailscale ports | Can revert | +| MikroTik: Queue | Gaming priority | N/A | +| Traefik dynamic.yml (optional) | Sunshine route | YES | + +--- + +## Performance Expectations + +| Scenario | Expected Latency | FPS | +|----------|------------------|-----| +| LAN (same network) | 5-15ms | 60-120 | +| Remote (Tailscale direct) | 30-60ms | 60 | +| Remote (Tailscale relay) | 50-100ms | 60 | + +--- + +## Dependencies for Next Phase + +Phase 5 (RustDesk) provides: +- Alternative remote access when gaming not required +- Lower resource usage for general desktop access +- Additional fallback for remote management diff --git a/docs/05-PHASE5-RUSTDESK.md b/docs/05-PHASE5-RUSTDESK.md new file mode 100644 index 0000000..d79a335 --- /dev/null +++ b/docs/05-PHASE5-RUSTDESK.md @@ -0,0 +1,187 @@ +# Phase 5: Hardened RustDesk Self-Hosted Setup + +## Status: ✅ SERVER-SIDE COMPLETE + +**Last Verified:** 2026-01-18 + +--- + +## Goal +Deploy a high-security, self-hosted RustDesk infrastructure with custom ID server, relay server, and end-to-end encryption using your own keypair. + +--- + +## Current State + +### Server Components + +| Component | Container | Status | Ports | +|-----------|-----------|--------|-------| +| ID Server | rustdesk-hbbs | ✅ Running | TCP 21115-21116, UDP 21116, WS 21118-21119 | +| Relay Server | rustdesk-hbbr | ✅ Running | TCP 21117 | + +### Configuration + +| Parameter | Value | +|-----------|-------| +| Public Key | `+Xlxh96tqwh9tD58ctOmB05Qpfs0ByCoLQcF+yCw0J8=` | +| ID Server | rustdesk.xtrm-lab.org:21116 | +| Relay Server | rustdesk.xtrm-lab.org:21117 | +| DNS | rustdesk.xtrm-lab.org → 62.73.120.142 | +| Data Path | /mnt/user/appdata/rustdesk-server | + +### MikroTik NAT Rules + +| Rule | Protocol | WAN Port | Destination | +|------|----------|----------|-------------| +| RustDesk NAT Test | TCP | 21115 | 192.168.31.2:21115 | +| RustDesk ID Server | TCP | 21116 | 192.168.31.2:21116 | +| RustDesk ID Server | UDP | 21116 | 192.168.31.2:21116 | +| RustDesk Relay | TCP | 21117 | 192.168.31.2:21117 | + +### Port Connectivity (Verified) + +| Port | Protocol | Status | +|------|----------|--------| +| 21116 | TCP | ✅ Accessible | +| 21117 | TCP | ✅ Accessible | + +--- + +## Client Configuration + +To connect RustDesk clients to your self-hosted server: + +### Settings +``` +ID Server: rustdesk.xtrm-lab.org +Relay Server: rustdesk.xtrm-lab.org +Key: +Xlxh96tqwh9tD58ctOmB05Qpfs0ByCoLQcF+yCw0J8= +``` + +### Connection String (for quick setup) +``` +rustdesk.xtrm-lab.org,+Xlxh96tqwh9tD58ctOmB05Qpfs0ByCoLQcF+yCw0J8= +``` + +--- + +## Verification Checklist + +### Server-Side (Complete) +- [x] Keypair generated: `/mnt/user/appdata/rustdesk-server/id_ed25519*` +- [x] hbbs container running +- [x] hbbr container running +- [x] MikroTik NAT rules configured (4 rules) +- [x] DNS resolves: rustdesk.xtrm-lab.org → 62.73.120.142 +- [x] Port 21116 accessible from external +- [x] Port 21117 accessible from external + +### Client-Side (Pending User Testing) +- [ ] Client connects with public key +- [ ] Remote session works between two clients +- [ ] Relay works when direct P2P fails + +--- + +## Architecture + +``` + Internet + │ + ┌────────────▼────────────┐ + │ MikroTik (62.73.120.142)│ + │ NAT Rules: │ + │ TCP 21115-21117 │ + │ UDP 21116 │ + └────────────┬────────────┘ + │ + ┌──────────────────┼──────────────────┐ + │ │ │ + ▼ ▼ ▼ + ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ + │ hbbs (ID Server)│ │ hbbr (Relay) │ │ RustDesk Client │ + │ TCP 21115-21116 │ │ TCP 21117 │ │ Your devices │ + │ UDP 21116 │ │ │ │ │ + │ WS 21118-21119 │ │ │ │ │ + └─────────────────┘ └─────────────────┘ └─────────────────┘ +``` + +--- + +## Container Details + +### hbbs (ID/Rendezvous Server) + +``` +Image: rustdesk/rustdesk-server:latest +Command: hbbs -r rustdesk.xtrm-lab.org:21117 -k _ +Volume: /mnt/user/appdata/rustdesk-server:/root +Ports: 21115, 21116 (TCP+UDP), 21118, 21119 +``` + +### hbbr (Relay Server) + +``` +Image: rustdesk/rustdesk-server:latest +Command: hbbr -k _ +Volume: /mnt/user/appdata/rustdesk-server:/root +Ports: 21117 +``` + +**Note:** The `-k _` flag enforces encrypted connections using the keypair. + +--- + +## Security Features + +1. **End-to-End Encryption:** All connections encrypted with Ed25519 keypair +2. **Key Verification:** Clients must have correct public key to connect +3. **Self-Hosted:** No third-party servers involved +4. **Encrypted-Only Mode:** Unencrypted connections rejected + +--- + +## Maintenance + +### View Logs +```bash +docker logs rustdesk-hbbs --tail 50 +docker logs rustdesk-hbbr --tail 50 +``` + +### Restart Services +```bash +docker restart rustdesk-hbbs rustdesk-hbbr +``` + +### Key Rotation +```bash +# Generate new keypair +docker run --rm -v /mnt/user/appdata/rustdesk-server:/data rustdesk/rustdesk-server hbbs -g + +# Restart containers +docker restart rustdesk-hbbs rustdesk-hbbr + +# Update all clients with new public key +``` + +--- + +## Rollback Procedure + +```bash +# Stop and remove containers +docker stop rustdesk-hbbs rustdesk-hbbr +docker rm rustdesk-hbbs rustdesk-hbbr + +# Remove MikroTik NAT rules (via SSH) +/ip/firewall/nat remove [find comment~RustDesk] +``` + +--- + +## Related Documents + +- [00-CURRENT-STATE.md](./00-CURRENT-STATE.md) - Infrastructure overview +- [04-PHASE4-REMOTE-GAMING.md](./04-PHASE4-REMOTE-GAMING.md) - Sunshine/Moonlight setup diff --git a/docs/06-CHANGELOG.md b/docs/06-CHANGELOG.md new file mode 100644 index 0000000..5876669 --- /dev/null +++ b/docs/06-CHANGELOG.md @@ -0,0 +1,39 @@ +# Changelog + +## 2026-01-18 + +- [PHASE 1] DNS Portability - COMPLETED + - Added DoH route to Traefik dynamic.yml (doh.xtrm-lab.org) + - Verified DoH endpoint working with ad-blocking + - Updated verification checklist - all items complete + - Fixed hostname in docs: dns.xtrm-lab.org → doh.xtrm-lab.org + - Updated nebula-sync status: unhealthy → healthy +- [SERVICE] DoH-Server: Now routed via Traefik at doh.xtrm-lab.org +- [SERVICE] stunnel-dot: Confirmed running for DoT on port 853 +- [ISSUE] Certificate renewal failing - Cloudflare API token needs Zone:DNS:Edit permission (certs expire Feb 11, 2026) +- [PHASE 1] DNS Redundancy verified: + - 2x Pi-hole: MikroTik (172.17.0.2) + Unraid (192.168.31.4) + - 2x Unbound: MikroTik (172.17.0.3) + Unraid (192.168.31.5) + - nebula-sync: Healthy, syncing every 5 minutes + - NAT rules: Properly configured for failover + - Added DNS Redundancy Architecture section to Phase 1 doc +- [DOC] Rewrote 01-PHASE1-DNS-PORTABILITY.md - removed implementation guides, kept only current state + +## 2026-01-18 +- [INFRA] Updated static IP proposal: dockersocket→172.18.0.2, traefik→172.18.0.3, vaultwarden→172.18.0.15 +- [INFRA] Static IP assignment for critical services - COMPLETED + +## 2026-01-18 (Phase 7 Deployment) +- [PHASE 7] Gitea deployed - git.xtrm-lab.org - COMPLETED +- [PHASE 7] Woodpecker CI Server deployed - ci.xtrm-lab.org - COMPLETED +- [PHASE 7] Woodpecker CI Agent deployed and connected - COMPLETED +- [SERVICE] gitea: PostgreSQL database (gitea_db) created +- [SERVICE] woodpecker-server: Port 8008, OAuth via Gitea +- [SERVICE] woodpecker-agent: Connected to server, 2 parallel workflows +- [DNS] Added git.xtrm-lab.org and ci.xtrm-lab.org A records + +## 2026-01-18 (Woodpecker Update) +- [PHASE 7] Woodpecker Server updated to v3.13.0 +- [PHASE 7] Woodpecker Agent updated to v3.13.0 +- [SERVICE] Fixed SQLite database permissions for migration +- [CI] First pipeline test successful (infrastructure repo) diff --git a/docs/06-PHASE6-PORTAINER-MANAGEMENT.md b/docs/06-PHASE6-PORTAINER-MANAGEMENT.md new file mode 100644 index 0000000..2bfdcf9 --- /dev/null +++ b/docs/06-PHASE6-PORTAINER-MANAGEMENT.md @@ -0,0 +1,159 @@ +# Phase 6: Multi-Host Docker Management with Portainer + +## Overview + +**Goal:** Unified container management dashboard for Unraid Docker. + +| Component | Role | +|-----------|------| +| Portainer CE | Management hub (runs on Unraid) | +| Unraid Docker | Local host via Unix socket | + +> **Note:** MikroTik RouterOS containers cannot be managed via Portainer - see [Limitation](#mikrotik-limitation) section. + +--- + +## Phase 6.1: Unraid Server Setup ✅ COMPLETED + +**Goal:** Install and configure the Portainer controller. + +### Tasks + +- [x] Install Portainer CE container via Docker CLI +- [x] Configure container settings: + - Network Type: **Bridge** + - Port Mapping: Container **9000** → Host **9002** (changed due to Authentik conflict) + - Port Mapping: Container **9443** → Host **9444** + - Path Mappings: + - Host `/var/run/docker.sock` → Container `/var/run/docker.sock` + - Host `/mnt/user/appdata/portainer` → Container `/data` +- [x] Add Unraid labels (`net.unraid.docker.managed`, `net.unraid.docker.icon`) +- [x] Add Tailscale labels (`tailscale.expose`, `tailscale.host`, `tailscale.port`) +- [x] Start container +- [x] Initialize Portainer via web UI + +### Container Configuration + +```bash +docker run -d \ + --name=portainer \ + --restart=unless-stopped \ + -p 9002:9000 \ + -p 9444:9443 \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v /mnt/user/appdata/portainer:/data \ + --label 'net.unraid.docker.managed=dockerman' \ + --label 'net.unraid.docker.icon=https://raw.githubusercontent.com/lllllllillllllillll/Dashboard-Icons/main/png/portainer.png' \ + --label 'net.unraid.docker.webui=http://100.100.208.70:9002' \ + --label 'tailscale.expose=true' \ + --label 'tailscale.host=100.100.208.70' \ + --label 'tailscale.port=9002' \ + portainer/portainer-ce:latest +``` + +### Access URLs +- LAN: `http://192.168.31.2:9002` +- Tailscale: `http://100.100.208.70:9002` +- HTTPS LAN: `https://192.168.31.2:9444` +- HTTPS Tailscale: `https://100.100.208.70:9444` + +### Verification +- [x] Portainer container running +- [x] Portainer UI accessible +- [x] Local Unraid environment connected + +--- + +## Phase 6.2 & 6.3: MikroTik Integration ❌ NOT FEASIBLE + +### MikroTik Limitation + +**MikroTik RouterOS does not use Docker.** It has its own proprietary container runtime that: + +- Does NOT have a Docker daemon +- Does NOT expose `/var/run/docker.sock` +- Does NOT support Docker API +- Can ONLY be managed via RouterOS CLI/API + +### What Was Attempted + +1. Created veth interface (`veth-socat` at 172.17.0.5) +2. Added bridge port to `docker-bridge` +3. Created mount for `/var/run/docker.sock` +4. Deployed `alpine/socat` container +5. Added firewall and NAT rules + +### Why It Failed + +``` +socat[2] E connect(, AF=1 "/var/run/docker.sock", 22): No such file or directory +``` + +The socket doesn't exist because MikroTik's container system is not Docker-based. + +### Cleanup Performed + +All MikroTik changes were reverted: +- Removed socat container +- Removed veth-socat interface +- Removed docker_sock mount +- Removed firewall/NAT rules + +--- + +## MikroTik Container Management Alternatives + +Since Portainer cannot connect to MikroTik, use these methods instead: + +### 1. RouterOS CLI (SSH) + +```bash +# From Unraid +ssh -i /root/.ssh/mikrotik_key -p 2222 unraid@192.168.31.1 + +# List containers +/container/print + +# Start/stop containers +/container/start 0 +/container/stop 0 + +# View logs +/log/print where topics~"container" +``` + +### 2. WinBox/WebFig + +Access MikroTik web interface at `http://192.168.31.1` to manage containers via GUI. + +### 3. RouterOS REST API + +MikroTik RouterOS 7+ has a REST API that can be used for automation: +``` +GET https://192.168.31.1/rest/container +``` + +--- + +## Current Status Summary + +| Component | Status | Access | +|-----------|--------|--------| +| Portainer (Unraid) | ✅ Running | http://100.100.208.70:9002 | +| Unraid Docker | ✅ Connected | Via Portainer | +| MikroTik Containers | ⚠️ Separate | Via RouterOS CLI | + +--- + +## Rollback Plan + +If Portainer issues occur: +```bash +docker stop portainer && docker rm portainer +``` + +--- + +## Related Documents + +- [00-CURRENT-STATE.md](./00-CURRENT-STATE.md) - Infrastructure overview diff --git a/docs/07-CHANGELOG.md b/docs/07-CHANGELOG.md new file mode 100644 index 0000000..dc7fac9 --- /dev/null +++ b/docs/07-CHANGELOG.md @@ -0,0 +1,580 @@ +# Infrastructure Changelog +## 2026-01-18 +- [INFRA] Added pending task: Static IP assignment for critical services on dockerproxy and bridge networks +- [SERVICE] postgresql17: Recreated container (was stopped due to port conflict) +- [SERVICE] authentik + authentik-worker: Restarted after PostgreSQL fix +- [TEMPLATE] Added RustDesk container templates with icons +- [TEMPLATE] Updated Pi-hole template with proper Unraid CA metadata + + +Track all changes to services, configurations, and phase progress. + +--- + +## 2026-01-17 - Homarr + Portainer Integration + +### Portainer App Added to Homarr +- [SERVICE] homarr: Added Portainer app to dashboard +- Section: Monitoring +- URL: http://100.100.208.70:9002 (Tailscale) +- Ping URL: http://192.168.31.2:9002 (LAN) + +### Docker Integration Added +- [SERVICE] homarr: Added Docker integration via socket +- Integration name: Docker (Unraid) +- Socket: unix:///var/run/docker.sock +- Linked to Portainer app for container status display + +### Database Changes +- Added app record for Portainer +- Added item and item_layout for Monitoring section +- Added integration record for Docker +- Linked integration to Portainer item + +### Access +- Homarr: https://xtrm-lab.org +- Portainer visible in Monitoring section + +--- + +## 2026-01-17 - Phase 6.2/6.3 Cancelled: MikroTik Incompatible + +### Discovery +- MikroTik RouterOS containers are NOT Docker-based +- No `/var/run/docker.sock` exists on MikroTik +- Portainer cannot connect to MikroTik's container runtime + +### What Was Attempted +- Created veth-socat interface (172.17.0.5) +- Deployed alpine/socat container +- Added firewall and NAT rules for port 2375 +- Socat failed: `No such file or directory` for docker.sock + +### Cleanup Performed +- Removed socat container +- Removed veth-socat interface and bridge port +- Removed docker_sock mount +- Removed firewall/NAT rules for port 2375 + +### Conclusion +- Phase 6.2 and 6.3 are NOT FEASIBLE +- MikroTik containers must be managed via RouterOS CLI/WebFig +- Portainer remains useful for Unraid-only container management + +### Status Update +- [PHASE 6.1] COMPLETED - Portainer managing Unraid +- [PHASE 6.2] CANCELLED - MikroTik incompatible +- [PHASE 6.3] CANCELLED - MikroTik incompatible + +--- + +## 2026-01-17 - Unraid Container Labels Fixed + +### Containers Updated +- [SERVICE] unbound: Added Unraid labels (`net.unraid.docker.managed`, `net.unraid.docker.icon`) +- [SERVICE] portainer: Added Unraid labels + Tailscale labels + +### Portainer Labels +- `net.unraid.docker.managed=dockerman` +- `net.unraid.docker.icon` - Portainer icon +- `net.unraid.docker.webui=http://100.100.208.70:9002` +- `tailscale.expose=true` +- `tailscale.host=100.100.208.70` +- `tailscale.port=9002` + +### Unbound Labels +- `net.unraid.docker.managed=dockerman` +- `net.unraid.docker.icon` - Unbound icon + +### Note +Both containers recreated to apply labels. Services verified working after recreation. + +--- + +## 2026-01-17 - Phase 6.1 Completed: Portainer CE Deployed + +### Portainer CE Installation +- [PHASE 6.1] Portainer CE deployed on Unraid - COMPLETED +- Container: `portainer/portainer-ce:latest` +- HTTP Port: **9002** (changed from 9000 due to Authentik conflict) +- HTTPS Port: **9444** +- Data: `/mnt/user/appdata/portainer` +- LAN URL: `http://192.168.31.2:9002` +- Tailscale URL: `http://100.100.208.70:9002` + +### Port Conflict Resolution +- Original plan: port 9000 +- Conflict: Authentik already using port 9000 +- Resolution: Mapped to port 9002 (HTTP) and 9444 (HTTPS) + +### Next Steps +- Phase 6.2: Deploy Socat proxy on MikroTik (port 2375) +- Phase 6.3: Connect MikroTik environment to Portainer + +### Status +- [PHASE 6.1] COMPLETED - Portainer running, needs initial setup via web UI +- [PHASE 6.2] NOT STARTED +- [PHASE 6.3] NOT STARTED + +--- + +## 2026-01-17 - Phase 6 Added: Multi-Host Docker Management + +### New Documentation +- [PHASE 6] Created 06-PHASE6-PORTAINER-MANAGEMENT.md +- Portainer CE deployment plan for unified Docker management +- Covers Unraid local setup and MikroTik remote API via Socat + +### Phase 6 Components +- Phase 6.1: Portainer CE installation on Unraid (port 9002) +- Phase 6.2: MikroTik Socat proxy for Docker API exposure (port 2375) +- Phase 6.3: Unified dashboard connection + +### Security Considerations +- MikroTik firewall rules to restrict Docker API access to Unraid only +- Unauthenticated API requires network-level security + +### Status +- [PHASE 6] IN PROGRESS - Phase 6.1 completed + +--- + + +## 2026-01-17 - Status Audit + +### Verified Working +- [PHASE 1] Tailscale on Unraid - WORKING (100.100.208.70) +- [PHASE 1] nebula-sync Pi-hole sync - HEALTHY (was unhealthy, now fixed) +- [PHASE 1] stunnel-dot (DoT on 853) - WORKING +- [PHASE 2] Pangolin controller - RUNNING (13 days uptime) +- [PHASE 3] Authentik main container - HEALTHY + +### Issues Found +- [PHASE 1] DoH-Server - RUNNING but BROKEN (can't reach Pi-hole upstream from dockerproxy network) +- [PHASE 2] gerbil - CRASHED (Pangolin returns empty CIDR for WG config) +- [PHASE 3] authentik-worker - CRASHED (PostgreSQL DNS resolution failure) +- [PHASE 5] RustDesk - NOT DEPLOYED + +### MikroTik NAT Status +- Port 853 (DoT) -> Unraid stunnel - OK +- Port 5443 (DoH) -> MikroTik Pi-hole (wrong target, should be Unraid DoH-Server) +- Port 51820 (Fossorial WG) - NOT CONFIGURED +- Ports 21115-21119 (RustDesk) - NOT CONFIGURED + +--- + +## Template for Future Entries + +## YYYY-MM-DD - Description + +### Changes +- [PHASE X] description - STATUS +- [SERVICE] name: what changed + +### Issues +- description of any problems found + +### Notes +- any relevant context + +## 2026-01-17 - DNS Infrastructure Fixes + +### DoH-Server +- [PHASE 1] DoH-Server - WORKING at `doh.xtrm-lab.org` (not `dns.xtrm-lab.org` as documented) +- [ISSUE] Infrastructure docs reference `dns.xtrm-lab.org` but container uses `doh.xtrm-lab.org` +- [ACTION NEEDED] Either update docs OR add Traefik route for `dns.xtrm-lab.org` + +### Unraid Unbound - FIXED +- [PHASE 1] Replaced broken klutchell/unbound with mvance/unbound:latest +- [ROOT CAUSE 1] Original image missing root.hints/root.key files (distroless image issue) +- [ROOT CAUSE 2] MikroTik NAT rules were hijacking Unbound's outbound DNS (192.168.31.0/24 -> Pi-hole) +- [ROOT CAUSE 3] IPv6 not working on br0 macvlan, causing timeout loops + +### MikroTik NAT Changes +- Added rule 6: "Allow Unraid Unbound" - accept UDP from 192.168.31.5 port 53 +- Added rule 8: "Allow Unraid Unbound TCP" - accept TCP from 192.168.31.5 port 53 +- These rules placed BEFORE the "Force DNS to Pi-hole" rules + +### Unbound Configuration +- Location: /mnt/user/appdata/unbound-mvance/ +- Custom config: a-records.conf (disables IPv6, sets logging) +- Image: mvance/unbound:latest +- Network: br0 (macvlan) at 192.168.31.5 + +### Verified Working +- Unraid Unbound (192.168.31.5) - RESOLVED google.com, github.com, cloudflare.com +- Unraid Pi-hole upstreams: 172.17.0.3 (MikroTik Unbound) + 192.168.31.5 (Unraid Unbound) +- DoH endpoint working at doh.xtrm-lab.org +- stunnel-dot (DoT) - already working + +### Still Pending +- MikroTik Pi-hole upstream config needs verification (check if it uses both Unbounds) +- Docs need update: dns.xtrm-lab.org vs doh.xtrm-lab.org + +### MikroTik Pi-hole Upstreams - FIXED +- [PHASE 1] MikroTik Pi-hole was using Google DNS (8.8.8.8, 8.8.4.4) instead of local Unbounds +- Changed upstreams via Pi-hole v6 API to: + - 172.17.0.3#53 - MikroTik local Unbound + - 192.168.31.5#53 - Unraid Unbound +- DNS resolution tested and working + +### Full DNS Redundancy Now Achieved +- Unraid Pi-hole upstreams: 172.17.0.3, 192.168.31.5 +- MikroTik Pi-hole upstreams: 172.17.0.3, 192.168.31.5 +- Both Unbounds working as recursive resolvers +- nebula-sync keeps blocklists in sync between Pi-holes + +--- + +## 2026-01-17 - Gerbil Investigation: Feature Not Available + +### Issue +- Gerbil kept crashing with "invalid CIDR address" error +- Exit node was correctly configured in database +- API returned empty data despite valid configuration + +### Root Cause +- **Pangolin 1.14 Community Edition does not include Exit Nodes feature** +- Exit Nodes / Gerbil functionality requires paid Pangolin license +- The API endpoint exists but returns empty data for CE users + +### Resolution +- Removed gerbil container (feature not available) +- Existing MikroTik WireGuard VPN provides equivalent remote access functionality +- Phase 2 (Fossorial Stack) marked as blocked pending license upgrade + +### Status +- [PHASE 2] gerbil - REMOVED (requires paid Pangolin license) +- [PHASE 2] Pangolin controller - RUNNING (limited to CE features) + +--- + +## 2026-01-18 - Actual Budget OIDC Integration with Authentik + +### Problem +- Actual Budget OIDC login failing with multiple errors + +### Fixes Applied + +#### 1. DNS Resolution (EAI_AGAIN) +- **Issue:** Container couldn't resolve auth.xtrm-lab.org +- **Fix:** Added `--add-host=auth.xtrm-lab.org:` to container +- **Template:** /boot/config/plugins/dockerMan/templates-user/my-actual-budget.xml + +#### 2. JWT Signing Algorithm (HS256 vs RS256) +- **Issue:** Authentik signed tokens with HS256, Actual Budget expected RS256 +- **Root Cause:** OAuth2 provider had no signing key configured +- **Fix:** Set signing_key_id to 'authentik Internal JWT Certificate' in database +- **SQL:** `UPDATE authentik_providers_oauth2_oauth2provider SET signing_key_id = '48203833-f562-4ec6-b782-f566e6d960d5' WHERE client_id = 'actual-budget';` + +#### 3. Insufficient Scope +- **Issue:** Provider had no scope mappings assigned +- **Fix:** Added openid, email, profile scopes to provider +- **SQL:** `INSERT INTO authentik_core_provider_property_mappings (provider_id, propertymapping_id) VALUES (3, 'a24eea06-...'), (3, '4394c150-...'), (3, '7272ab52-...');` + +### Traefik Static IP +- **Issue:** Traefik IP was dynamic, would break actual-budget on restart +- **Fix:** Assigned static IP 172.18.0.10 to Traefik on dockerproxy network +- **Template:** Added `--ip=172.18.0.10` to ExtraParams in my-traefik.xml + +### Final Configuration + +| Component | Setting | +|-----------|---------| +| Traefik | 172.18.0.10 (static) on dockerproxy | +| Actual Budget | --add-host=auth.xtrm-lab.org:172.18.0.10 | +| Authentik Provider | actual-budget with RS256 signing + scopes | + +### Actual Budget OIDC Environment +``` +ACTUAL_OPENID_DISCOVERY_URL=https://auth.xtrm-lab.org/application/o/actual-budget/.well-known/openid-configuration +ACTUAL_OPENID_CLIENT_ID=actual-budget +ACTUAL_OPENID_CLIENT_SECRET= +ACTUAL_OPENID_SERVER_HOSTNAME=https://actual.xtrm-lab.org +``` + +### Status +- [PHASE 3] Actual Budget OIDC - WORKING +- [SERVICE] traefik: Static IP 172.18.0.10 configured +- [SERVICE] actual-budget: OIDC login via Authentik working + +## 2026-01-18 - Phase 5 Completed: RustDesk Self-Hosted Deployment + +### Keypair Generation +- [PHASE 5] Generated Ed25519 keypair for encrypted connections +- Public Key: `+Xlxh96tqwh9tD58ctOmB05Qpfs0ByCoLQcF+yCw0J8=` +- Data directory: /mnt/user/appdata/rustdesk-server/ + +### Containers Deployed +- [SERVICE] rustdesk-hbbs: ID/Rendezvous server on ports 21115-21116 (TCP), 21116 (UDP), 21118-21119 +- [SERVICE] rustdesk-hbbr: Relay server on port 21117 +- Both containers configured with `-k _` for mandatory encryption +- AutoKuma labels added for Uptime Kuma monitoring + +### MikroTik Configuration +- Added NAT rules 24-27 for RustDesk ports +- Added firewall forward rules (Allow RustDesk TCP/UDP) +- Ports forwarded: 21115 (NAT test), 21116 (TCP+UDP), 21117 (Relay) + +### DNS +- rustdesk.xtrm-lab.org already resolving to 62.73.120.142 (DNS only, no proxy) + +### Verification +- All TCP ports (21115, 21116, 21117) accessible externally +- Both containers running healthy +- Logs show successful startup with keypair loaded + +### Client Configuration +| Setting | Value | +|---------|-------| +| ID Server | rustdesk.xtrm-lab.org | +| Relay Server | rustdesk.xtrm-lab.org | +| Public Key | +Xlxh96tqwh9tD58ctOmB05Qpfs0ByCoLQcF+yCw0J8= | + +### Status +- [PHASE 5] RustDesk Self-Hosted - COMPLETED + +## 2026-01-18 - Vaultwarden 502 Fix + +### Issue +- Vaultwarden returning unexpected error when creating new logins +- Traefik logs showed 502 Bad Gateway errors + +### Root Cause +- Traefik config pointed to `http://192.168.31.2:4743` +- Vaultwarden container had no port 4743 mapping (port 80/tcp was not published) +- Both containers on `dockerproxy` network but config used host IP + +### Fix +- Updated `/mnt/user/appdata/traefik/dynamic.yml` +- Changed: `url: "http://192.168.31.2:4743"` → `url: "http://vaultwarden:80"` +- Uses Docker internal DNS which resolves to container IP on dockerproxy network + +### Status +- [SERVICE] vaultwarden: Working - can create/edit logins + +## 2026-01-18 - Progress Summary + +### Completed Phases +- [PHASE 1] DNS Portability - COMPLETE (DoH, DoT, Unbound redundancy) +- [PHASE 5] RustDesk Self-Hosted - COMPLETE (hbbs/hbbr deployed) +- [PHASE 6] Portainer Management - COMPLETE (6.2/6.3 cancelled - MikroTik incompatible) + +### In Progress +- [PHASE 3] Authentik Zero Trust - Actual Budget integrated, more services pending + +### Blocked +- [PHASE 2] Fossorial Stack - Gerbil requires paid Pangolin license + +### Not Started +- [PHASE 4] Remote Gaming (Sunshine/Moonlight) - Starting now + +### Known Issues +- HomeAssistant_inabox: Exited (1) 3 days ago +- pgAdmin4: Exited (137) 2 weeks ago + +## 2026-01-18 - Phase 4 Started: MacBook Prepared + +### MacBook Setup Complete +- [PHASE 4] Moonlight v6.1.0 already installed +- [PHASE 4] Tailscale connected (100.68.118.59) + +### Pending - Nobara Setup +- Install Sunshine on Nobara +- Configure VA-API encoding +- Pair with Moonlight + +### Instructions Saved +- MacBook: ~/Documents/NOBARA-SUNSHINE-SETUP.md + +### Status +- [PHASE 4] MacBook client ready, awaiting Nobara server setup + +## 2026-01-18 - NetAlertX & Uptime Kuma Fixes (Partial) + +### Uptime Kuma - FIXED +- [SERVICE] Added Traefik route for uptime.xtrm-lab.org +- Protected with Authentik forward auth +- Service URL: http://192.168.31.2:3001 + +### NetAlertX - IN PROGRESS +- [ISSUE] Container not scanning network - shows 0 devices +- [ROOT CAUSE] Multiple config files exist: + - /app/config/app.conf (mounted from host) - updated correctly + - /app/back/app.conf (container internal) - has old value '--localnet' +- [ATTEMPTED] Updated /mnt/user/appdata/netalertx/config/app.conf +- [ATTEMPTED] Updated database Settings table +- [ATTEMPTED] Deleted database to force reload +- [DISCOVERED] App reads from /app/back/app.conf which is generated at startup + +### NetAlertX Fix Required +1. The /app/back/app.conf needs to be updated to: + SCAN_SUBNETS=['192.168.31.0/24 --interface=br0'] +2. This file is regenerated on container start from /app/config/app.conf +3. May need to use Settings UI at https://netalert.xtrm-lab.org to change SCAN_SUBNETS + +### Manual ARP Scan Test - WORKS +Command: docker exec NetAlertX arp-scan --localnet --interface=br0 +Result: Found 20 devices on 192.168.31.0/24 + +### Pending Tasks +- Fix NetAlertX to use correct subnet config +- Add Tailscale network scanning (may not work - ARP doesn't work over tunnels) +- User requested: RustFS for personal CDN (assets hosting) + +### Status +- [SERVICE] uptime.xtrm-lab.org - WORKING +- [SERVICE] netalertx - PARTIALLY BROKEN (config issue) + +## 2026-01-18 - NetAlertX FIXED + +### Resolution +- [SERVICE] NetAlertX now scanning network correctly - found 21 devices +- [FIX] Updated config in multiple locations: + - /data/config/app.conf (runtime config inside container) + - /app/back/app.conf (plugin reads from here) + - /mnt/user/appdata/netalertx/config/app.conf (host mount for persistence) + +### Config Change +``` +SCAN_SUBNETS=['192.168.31.0/24 --interface=br0'] +``` + +### Root Cause Summary +- NetAlertX has complex config handling with multiple config file locations +- /app/config (mounted) -> copied to /data/config on startup +- /data/config/app.conf is read by the app +- /app/back/app.conf is read by plugins at runtime +- All three needed to be updated + +### Verified Working +- ARP scan found 21 devices on 192.168.31.0/24 +- Devices visible at https://netalert.xtrm-lab.org/devices.php + +### Note on Tailscale Scanning +- ARP scanning does NOT work over Tailscale (point-to-point tunnel, no broadcast) +- Tailscale devices need to be added manually or via different discovery method + +## 2026-01-18 - RustFS CDN Deployed + +### Service Details +- [SERVICE] RustFS - S3-compatible object storage for personal CDN +- Image: rustfs/rustfs:latest +- Ports: 9010 (S3 API), 9011 (Console) +- Data: /mnt/user/appdata/rustfs/data +- Logs: /mnt/user/appdata/rustfs/logs + +### Access URLs +- S3 API: https://cdn.xtrm-lab.org +- Console: http://192.168.31.2:9011/rustfs/console/ +- Credentials stored in: /mnt/user/appdata/rustfs/CREDENTIALS.txt + +### Traefik Route +- Host: cdn.xtrm-lab.org +- No Authentik protection (public CDN for assets) +- S3 authentication handles access control + +### Usage +Create bucket and upload assets via: +- RustFS Console at port 9011 +- S3-compatible CLI tools (aws-cli, rclone, etc.) +- Direct S3 API calls + +### Example S3 CLI Usage +```bash +# Configure aws-cli +aws configure set aws_access_key_id +aws configure set aws_secret_access_key + +# Create bucket +aws --endpoint-url https://cdn.xtrm-lab.org s3 mb s3://assets + +# Upload file +aws --endpoint-url https://cdn.xtrm-lab.org s3 cp image.png s3://assets/ + +# Public URL (after setting bucket policy) +https://cdn.xtrm-lab.org/assets/image.png +``` + +### Status +- [SERVICE] rustfs - RUNNING +- [PHASE N/A] Personal CDN - COMPLETED + +## 2026-01-18 - PostgreSQL Data Path Restored & Phase 3 Verified + +### Root Cause Analysis +- [INCIDENT] PostgreSQL container was recreated at 07:40 UTC with wrong data path +- [CAUSE] Container used default path `/mnt/user/appdata/postgresql17` instead of configured `/mnt/user/appdata/postgresql` +- [IMPACT] Authentik started with empty database, all configuration appeared lost +- [DATA] Original data was safe in `/mnt/user/appdata/postgresql/` the entire time + +### Resolution +- [FIX] Stopped postgresql17 container +- [FIX] Recreated container with correct volume mount: `/mnt/user/appdata/postgresql:/var/lib/postgresql/data` +- [FIX] Restarted Authentik containers +- [VERIFIED] All Authentik data restored (users, groups, applications, providers) + +### Other Fixes This Session +- [SERVICE] Uptime-Kuma-API: Added missing ADMIN_PASSWORD environment variable +- [TRAEFIK] Added Docker provider constraint to filter broken container labels +- [TRAEFIK] Added missing routes: authentik, transmission, nextcloud to dynamic.yml + +### Phase 3 Verification - COMPLETED +Verified Authentik Zero Trust configuration: +- Users: akadmin, admin, jazzymc (3 active users) +- Groups: authentik Admins, authentik Read-only +- Outpost: Embedded Outpost running (proxy type) +- Applications: XTRM-Lab Protected Services, Actual Budget +- Proxy Provider: forward_domain mode for auth.xtrm-lab.org +- 2FA: 2 TOTP devices configured +- Protected Services: 12 routes using authentik-forward-auth middleware + +### Services Status +- [SERVICE] auth.xtrm-lab.org - WORKING (302 redirect to login) +- [SERVICE] uptime.xtrm-lab.org - WORKING (forward auth active) +- [SERVICE] ph2.xtrm-lab.org - WORKING (forward auth active) +- [SERVICE] All forward-auth protected services - WORKING + +### Documentation Updated +- [DOC] 03-PHASE3-AUTHENTIK-ZEROTRUST.md - Marked as COMPLETED with verified state + +## 2026-01-18 - Phase 5 RustDesk Verified + +### Server-Side Verification Complete +- [x] Keypair exists: /mnt/user/appdata/rustdesk-server/id_ed25519* +- [x] Public Key: +Xlxh96tqwh9tD58ctOmB05Qpfs0ByCoLQcF+yCw0J8= +- [x] hbbs container: Up 10+ hours +- [x] hbbr container: Up 10+ hours +- [x] MikroTik NAT: 4 rules configured (21115-21117 TCP, 21116 UDP) +- [x] DNS: rustdesk.xtrm-lab.org → 62.73.120.142 +- [x] Port 21116 TCP: Externally accessible (verified via nc) +- [x] Port 21117 TCP: Externally accessible (verified via nc) + +### Client Configuration +ID Server: rustdesk.xtrm-lab.org +Relay Server: rustdesk.xtrm-lab.org +Key: +Xlxh96tqwh9tD58ctOmB05Qpfs0ByCoLQcF+yCw0J8= + +### Documentation Updated +- [DOC] 05-PHASE5-RUSTDESK.md - Rewritten with verified state, marked SERVER-SIDE COMPLETE +- [PENDING] Client-side testing (user to verify remote sessions work) + +## 2026-01-18 - Phase 7 GitOps Plan Created + +### New Phase: Gitea + Woodpecker CI +- [DOC] Created 08-PHASE7-GITEA-GITOPS.md +- [PLAN] Lightweight GitOps stack for infrastructure management +- [COMPONENTS] Gitea (~200MB) + Woodpecker Server/Agent (~200MB) +- [TOTAL RESOURCES] ~400MB RAM, ~700MB storage + +### Planned Features +- Git version control for all configs +- Automated YAML validation +- CI/CD pipeline for deployments +- Auto-rollback on health check failure +- Authentik SSO integration +- Safe AI agent integration + +### URLs (Planned) +- git.xtrm-lab.org - Gitea web UI +- ci.xtrm-lab.org - Woodpecker CI dashboard diff --git a/docs/08-PHASE7-GITEA-GITOPS.md b/docs/08-PHASE7-GITEA-GITOPS.md new file mode 100644 index 0000000..4b6929a --- /dev/null +++ b/docs/08-PHASE7-GITEA-GITOPS.md @@ -0,0 +1,168 @@ +# Phase 7: Gitea + Woodpecker CI (GitOps for Homelab) + +## Status: ✅ COMPLETED + +**Deployed:** 2026-01-18 + +--- + +## Deployed Components + +| Service | Container | Version | Port | URL | Status | +|---------|-----------|---------|------|-----|--------| +| Gitea | gitea | 1.25.3 | 3005→3000, 2222→22 | https://git.xtrm-lab.org | ✅ Running | +| Woodpecker Server | woodpecker-server | 3.13.0 | 8008→8000 | https://ci.xtrm-lab.org | ✅ Running | +| Woodpecker Agent | woodpecker-agent | 3.13.0 | - | - | ✅ Running | + +--- + +## Configuration + +### Gitea + +| Parameter | Value | +|-----------|-------| +| Admin User | jazzymc | +| Database | PostgreSQL (gitea_db @ 172.18.0.13) | +| DB User | gitea | +| SSH Port | 2222 | +| Data Path | /mnt/user/appdata/gitea/data | +| Network | dockerproxy | + +### Woodpecker CI + +| Parameter | Value | +|-----------|-------| +| Version | 3.13.0 | +| Admin User | jazzymc (via Gitea OAuth) | +| Server IP | 172.18.0.134 | +| gRPC Port | 9000 | +| HTTP Port | 8000 (mapped to 8008) | +| Max Workflows | 2 (parallel) | +| Data Path | /mnt/user/appdata/woodpecker/server | +| Agent Secret | 564a5716400532874a8e02313a491b4f3864ce9b77a5122ce0eb14777749e740 | + +### Gitea OAuth App (for Woodpecker) + +| Parameter | Value | +|-----------|-------| +| Client ID | 924b3300-b607-4a48-bc26-35b06dbf18c7 | +| Redirect URI | https://ci.xtrm-lab.org/authorize | + +--- + +## Network Configuration + +All services on `dockerproxy` network: + +| Service | Internal IP | DNS Name | +|---------|-------------|----------| +| Gitea | Dynamic | gitea | +| Woodpecker Server | 172.18.0.134 | woodpecker-server | +| PostgreSQL | 172.18.0.13 | postgresql17 | + +### Traefik Routes + +| Domain | Service | Port | +|--------|---------|------| +| git.xtrm-lab.org | gitea | 3000 | +| ci.xtrm-lab.org | woodpecker-server | 8000 | + +--- + +## Verification Checklist + +- [x] Gitea container running +- [x] Gitea accessible at https://git.xtrm-lab.org +- [x] Admin account created (jazzymc) +- [x] OAuth app created for Woodpecker +- [x] Woodpecker Server v3.13.0 running +- [x] Woodpecker Agent v3.13.0 running and connected +- [x] Woodpecker accessible at https://ci.xtrm-lab.org +- [x] Gitea OAuth login working +- [x] CI pipeline tested successfully + +--- + +## Usage + +### Git Operations + +```bash +# Clone via HTTPS +git clone https://git.xtrm-lab.org/jazzymc/infrastructure.git + +# Clone via SSH (port 2222) +git clone ssh://git@git.xtrm-lab.org:2222/jazzymc/infrastructure.git +``` + +### CI Pipeline (.woodpecker.yml) + +```yaml +steps: + - name: test + image: alpine + commands: + - echo 'Hello from Woodpecker CI!' + - date +``` + +### Example: Node.js Pipeline + +```yaml +steps: + - name: install + image: node:20 + commands: + - npm install + + - name: test + image: node:20 + commands: + - npm test + + - name: build + image: node:20 + commands: + - npm run build + when: + branch: main +``` + +### Example: Docker Build + +```yaml +steps: + - name: build + image: docker + commands: + - docker build -t myapp . + volumes: + - /var/run/docker.sock:/var/run/docker.sock +``` + +--- + +## Maintenance + +### Update Woodpecker + +```bash +docker pull woodpeckerci/woodpecker-server:v3 +docker pull woodpeckerci/woodpecker-agent:v3 +docker restart woodpecker-server woodpecker-agent +``` + +### Backup + +Important paths to backup: +- /mnt/user/appdata/gitea/data +- /mnt/user/appdata/woodpecker/server +- PostgreSQL database: gitea_db + +--- + +## Related Documents + +- [00-CURRENT-STATE.md](./00-CURRENT-STATE.md) - Infrastructure overview +- [03-PHASE3-AUTHENTIK-ZEROTRUST.md](./03-PHASE3-AUTHENTIK-ZEROTRUST.md) - SSO setup