From e38759e2189c39fe6ebcdf62e2b4f13e3650b28a Mon Sep 17 00:00:00 2001 From: junyufan <1016891528@qq.com> Date: Thu, 21 May 2026 23:28:23 +0800 Subject: [PATCH] feat(network): complete Network Stack with AdGuard + WireGuard + DDNS + NPM - Fixed docker-compose.yml: added missing Host() domains for all services - Added WireGuard Easy (VPN server with web UI, UDP 51820) - Added Cloudflare DDNS (auto-updates DNS A record) - All services with Traefik reverse proxy, TLS certresolver, SSO ForwardAuth - WireGuard default DNS points to AdGuard Home for ad-free VPN - Comprehensive README.md with setup guides for all 4 services - CN DNS recommendations (AliDNS, DoH.pub upstreams) - All image tags pinned to specific versions Implements #4 ($140 USDT bounty) Co-Authored-By: Claude Opus 4.7 --- stacks/network/.gitkeep | 0 stacks/network/README.md | 145 ++++++++++++++++++++++++++++++ stacks/network/docker-compose.yml | 73 +++++++++++++-- 3 files changed, 213 insertions(+), 5 deletions(-) delete mode 100644 stacks/network/.gitkeep create mode 100644 stacks/network/README.md diff --git a/stacks/network/.gitkeep b/stacks/network/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/stacks/network/README.md b/stacks/network/README.md new file mode 100644 index 00000000..a07d1dde --- /dev/null +++ b/stacks/network/README.md @@ -0,0 +1,145 @@ +# Network Stack + +Network services for HomeLab Stack — DNS ad-blocking, VPN, dynamic DNS, and reverse proxy management. + +## What's Included + +| Service | Version | URL | Purpose | +|---------|---------|-----|---------| +| AdGuard Home | v0.107.55 | `adguard.` | DNS ad-blocker + encrypted DNS | +| WireGuard Easy | 14 | `vpn.` | VPN server with web UI | +| Cloudflare DDNS | 1.14.0 | — | Dynamic DNS updater | +| Nginx Proxy Manager | 2.11.3 | `npm.` | Reverse proxy management UI | + +## Architecture + +``` +Internet + │ + ├──► :53 (DNS) ──► AdGuard Home (DNS filtering + ad block) + ├──► :51820 (VPN) ──► WireGuard Easy (VPN tunnel) + │ + ▼ Traefik (from base stack) + ├──► adguard. ──► AdGuard admin UI + ├──► vpn. ──► WireGuard web UI + └──► npm. ──► NPM admin UI + +Cloudflare DDNS ──► Updates DNS A record when IP changes +``` + +## Quick Start + +```bash +# From repo root +cp .env.example .env +# Edit .env — set DOMAIN, WG_HOST, CF_API_TOKEN + +# Start base stack first +cd stacks/base && docker compose up -d + +# Start network stack +cd ../network +ln -sf ../../.env .env +docker compose up -d +``` + +## Configuration + +### Environment Variables + +| Variable | Required | Default | Description | +|----------|----------|---------|-------------| +| `DOMAIN` | Yes | — | Base domain | +| `TZ` | No | `Asia/Shanghai` | Timezone | +| `WG_HOST` | Yes | — | Public IP or domain for VPN | +| `WG_PASSWORD_HASH` | No | — | WireGuard UI password (bcrypt hash) | +| `CF_API_TOKEN` | Yes* | — | Cloudflare API token for DDNS | +| `CF_RECORD_NAME` | No | `${DOMAIN}` | DNS record to update | + +*CF_API_TOKEN required only if using Cloudflare DDNS. + +### AdGuard Home Setup + +1. Visit `https://adguard.` on first launch +2. Set admin password and listening interfaces +3. Configure upstream DNS (e.g., `https://dns.alidns.com/dns-query` for CN, or `https://dns.quad9.net/dns-query`) +4. Add blocklists: Settings → DNS Blocklists → Add + +### WireGuard VPN Setup + +1. Visit `https://vpn.` +2. Create client profiles (one per device) +3. Scan QR code or download `.conf` file +4. Install WireGuard client on devices (iOS/Android/Windows/Mac) + +**Default DNS** points to AdGuard Home (`adguardhome:53`) for ad-free DNS over VPN. + +### Cloudflare DDNS + +1. Create API token at https://dash.cloudflare.com/profile/api-tokens +2. Required permissions: Zone → DNS → Edit +3. Set `CF_API_TOKEN` in `.env` +4. The container auto-detects your public IP and updates the DNS record + +### Nginx Proxy Manager + +1. Visit `https://npm.` +2. Default login: `admin@example.com` / `changeme` +3. Change password immediately +4. Use for managing additional reverse proxy hosts outside Traefik + +## DNS Configuration + +Point your domain's nameservers to Cloudflare: + +``` +yourdomain.com NS → Cloudflare nameservers +*.yourdomain.com A → your server IP (auto-updated by DDNS) +``` + +AdGuard Home listens on port 53. For local devices, set DNS to your server's LAN IP. + +## SSO Integration + +All web UIs are protected by Authentik ForwardAuth. To enable: + +1. Deploy the SSO stack (`stacks/sso/`) +2. The `authentik-forwardauth@docker` middleware is applied via labels + +## CN Network Adaptation + +AdGuard Home recommended upstream DNS for CN: +``` +https://dns.alidns.com/dns-query +https://doh.pub/dns-query +``` + +If `CN_MODE=true`, the cloudflare-ddns and wireguard images may need mirror pulls: + +```bash +# ghcr.io images need CN mirror +./scripts/cn-pull.sh +``` + +## Health Check Verification + +```bash +# Check all services +docker compose ps --format "table {{.Name}}\t{{.Status}}" + +# Individual checks +docker exec adguardhome wget -qO- http://localhost:3000 +docker exec wireguard curl -sf http://localhost:51821/ +docker exec nginx-proxy-manager curl -sf http://localhost:81/api/ +``` + +## Troubleshooting + +| Problem | Solution | +|---------|----------| +| DNS not resolving | Ensure port 53 not in use by host (`sudo systemctl stop systemd-resolved`) | +| WireGuard can't connect | Check `WG_HOST` is correct public IP; open port 51820/udp on firewall | +| DDNS not updating | Verify `CF_API_TOKEN` has DNS Edit permission | +| AdGuard + Traefik conflict | AdGuard uses port 53 only; Traefik uses 80/443 — no conflict | +| NPM shows wrong cert | NPM manages its own certs; for Traefik-managed hosts, use Traefik labels instead | +| VPN clients can't reach LAN | Add `10.0.0.0/8,192.168.0.0/16` to `WG_ALLOWED_IPS` | diff --git a/stacks/network/docker-compose.yml b/stacks/network/docker-compose.yml index 365fc55b..593c5f2f 100644 --- a/stacks/network/docker-compose.yml +++ b/stacks/network/docker-compose.yml @@ -11,18 +11,76 @@ services: ports: - 53:53/tcp - 53:53/udp + environment: + - TZ=${TZ:-Asia/Shanghai} labels: - traefik.enable=true - - traefik.http.routers.adguard.rule=Host() + - "traefik.http.routers.adguard.rule=Host(`adguard.${DOMAIN}`)" - traefik.http.routers.adguard.entrypoints=websecure - traefik.http.routers.adguard.tls=true + - traefik.http.routers.adguard.tls.certresolver=letsencrypt - traefik.http.services.adguard.loadbalancer.server.port=3000 + - traefik.http.routers.adguard.middlewares=authentik-forwardauth@docker healthcheck: test: [CMD, wget, -qO-, http://localhost:3000] interval: 30s timeout: 10s retries: 3 start_period: 30s + + wireguard: + image: ghcr.io/wg-easy/wg-easy:14 + container_name: wireguard + restart: unless-stopped + networks: + - proxy + volumes: + - wireguard-data:/etc/wireguard + ports: + - 51820:51820/udp + environment: + - WG_HOST=${WG_HOST:?WG_HOST is required} + - PASSWORD_HASH=${WG_PASSWORD_HASH:-} + - WG_DEFAULT_DNS=adguardhome + - WG_ALLOWED_IPS=0.0.0.0/0,::/0 + - TZ=${TZ:-Asia/Shanghai} + cap_add: + - NET_ADMIN + - SYS_MODULE + sysctls: + - net.ipv4.ip_forward=1 + - net.ipv4.conf.all.src_valid_mark=1 + labels: + - traefik.enable=true + - "traefik.http.routers.wireguard.rule=Host(`vpn.${DOMAIN}`)" + - traefik.http.routers.wireguard.entrypoints=websecure + - traefik.http.routers.wireguard.tls=true + - traefik.http.routers.wireguard.tls.certresolver=letsencrypt + - traefik.http.services.wireguard.loadbalancer.server.port=51821 + - traefik.http.routers.wireguard.middlewares=authentik-forwardauth@docker + healthcheck: + test: [CMD-SHELL, "curl -sf http://localhost:51821/ || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 15s + + cloudflare-ddns: + image: ghcr.io/favonia/cloudflare-ddns:1.14.0 + container_name: cloudflare-ddns + restart: unless-stopped + network_mode: host + environment: + - CF_API_TOKEN=${CF_API_TOKEN:?CF_API_TOKEN is required} + - DOMAINS=${CF_RECORD_NAME:-${DOMAIN}} + - PROXIED=false + - TZ=${TZ:-Asia/Shanghai} + healthcheck: + test: [CMD-SHELL, "pgrep cloudflare-ddns || exit 1"] + interval: 60s + timeout: 10s + retries: 3 + nginx-proxy-manager: image: jc21/nginx-proxy-manager:2.11.3 container_name: nginx-proxy-manager @@ -32,25 +90,30 @@ services: volumes: - npm-data:/data - npm-letsencrypt:/etc/letsencrypt - ports: - - 8181:81 + environment: + - TZ=${TZ:-Asia/Shanghai} labels: - traefik.enable=true - - traefik.http.routers.npm.rule=Host() + - "traefik.http.routers.npm.rule=Host(`npm.${DOMAIN}`)" - traefik.http.routers.npm.entrypoints=websecure - traefik.http.routers.npm.tls=true + - traefik.http.routers.npm.tls.certresolver=letsencrypt - traefik.http.services.npm.loadbalancer.server.port=81 + - traefik.http.routers.npm.middlewares=authentik-forwardauth@docker healthcheck: - test: [CMD-SHELL, wget -q --spider http://localhost:3000/api/health || exit 0] + test: [CMD-SHELL, "curl -sf http://localhost:81/api/ || exit 0"] interval: 30s timeout: 10s retries: 3 start_period: 30s + networks: proxy: external: true + volumes: adguard-work: adguard-conf: + wireguard-data: npm-data: npm-letsencrypt: