commit 6e956f733443544545c5fdf93e409383b55c7ca5 Author: timmy Date: Fri Apr 17 08:41:50 2026 +0800 Initial commit: NetBird self-hosted deployment documentation diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..bf5fdce --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,245 @@ +# NetBird 快速部署指南 + +> 目標:在 Proxmox LXC (Ubuntu 24.04) 上部署 NetBird,使用 IP 直接存取(無域名、無 TLS)。 + +## 先決條件 + +- Proxmox LXC / Ubuntu 24.04 +- root 權限 +- 網際網路存取 +- 內網 IP (例:`192.168.42.127`) + +> ⚠️ 以下步驟中的 `192.168.42.127` 請換成你的實際 IP。 + +--- + +## Step 1: 安裝前置套件 + +```bash +apt-get update +apt-get install -y curl jq ca-certificates gnupg openssl +``` + +## Step 2: 安裝 Docker + +```bash +curl -fsSL https://get.docker.com | sh +systemctl start docker +systemctl enable docker +``` + +## Step 3: 安裝 crun(LXC 必須) + +```bash +curl -sL "https://github.com/containers/crun/releases/download/1.20/crun-1.20-linux-amd64" \ + -o /usr/local/bin/crun +chmod +x /usr/local/bin/crun + +cat > /etc/docker/daemon.json << 'EOF' +{ + "runtimes": { + "crun": { + "path": "/usr/local/bin/crun" + } + }, + "default-runtime": "crun" +} +EOF + +systemctl restart docker +docker run --rm hello-world # 驗證 +``` + +## Step 4: 建立設定目錄與產生金鑰 + +```bash +mkdir -p /opt/netbird && cd /opt/netbird + +RELAY_SECRET=$(openssl rand -base64 32 | sed 's/=//g') +ENCRYPTION_KEY=$(openssl rand -base64 32) +SERVER_IP=192.168.42.127 # 改成你的 IP +``` + +## Step 5: 建立設定檔 + +### docker-compose.yml + +```bash +cat > /opt/netbird/docker-compose.yml << 'EOF' +services: + caddy: + image: caddy:2 + container_name: netbird-caddy + restart: unless-stopped + networks: [netbird] + ports: + - "80:80" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + + dashboard: + image: netbirdio/dashboard:latest + container_name: netbird-dashboard + restart: unless-stopped + networks: [netbird] + env_file: + - ./dashboard.env + + netbird-server: + image: netbirdio/netbird-server:latest + container_name: netbird-server + restart: unless-stopped + networks: [netbird] + ports: + - "3478:3478/udp" + volumes: + - netbird_data:/var/lib/netbird + - ./config.yaml:/etc/netbird/config.yaml + command: ["--config", "/etc/netbird/config.yaml"] + +volumes: + netbird_data: + +networks: + netbird: +EOF +``` + +### Caddyfile + +```bash +cat > /opt/netbird/Caddyfile << 'EOF' +:80 { + @grpc header Content-Type application/grpc* + reverse_proxy @grpc h2c://netbird-server:80 + + @backend path /relay* /ws-proxy/* /api/* /oauth2/* + reverse_proxy @backend netbird-server:80 + + reverse_proxy /* netbird-dashboard:80 +} +EOF +``` + +### config.yaml + +```bash +cat > /opt/netbird/config.yaml << EOF +server: + listenAddress: ":80" + exposedAddress: "http://${SERVER_IP}:80" + stunPorts: + - 3478 + metricsPort: 9090 + healthcheckAddress: ":9000" + logLevel: "info" + logFile: "console" + + authSecret: "${RELAY_SECRET}" + dataDir: "/var/lib/netbird" + + auth: + issuer: "http://${SERVER_IP}/oauth2" + signKeyRefreshEnabled: true + dashboardRedirectURIs: + - "http://${SERVER_IP}/nb-auth" + - "http://${SERVER_IP}/nb-silent-auth" + cliRedirectURIs: + - "http://localhost:53000/" + + reverseProxy: + trustedHTTPProxies: + - "0.0.0.0/0" + + store: + engine: "sqlite" + encryptionKey: "${ENCRYPTION_KEY}" +EOF +``` + +### dashboard.env + +```bash +cat > /opt/netbird/dashboard.env << EOF +NETBIRD_MGMT_API_ENDPOINT=http://${SERVER_IP} +NETBIRD_MGMT_GRPC_API_ENDPOINT=http://${SERVER_IP} +AUTH_AUDIENCE=netbird-dashboard +AUTH_CLIENT_ID=netbird-dashboard +AUTH_CLIENT_SECRET= +AUTH_AUTHORITY=http://${SERVER_IP}/oauth2 +USE_AUTH0=false +AUTH_SUPPORTED_SCOPES=openid profile email groups +AUTH_REDIRECT_URI=/nb-auth +AUTH_SILENT_REDIRECT_URI=/nb-silent-auth +NGINX_SSL_PORT=443 +LETSENCRYPT_DOMAIN=none +EOF +``` + +## Step 6: 啟動服務 + +```bash +cd /opt/netbird +docker compose pull +docker compose up -d +``` + +> ⏳ 首次啟動需等待約 2-5 分鐘下載 GeoLite2 地理資料庫(約 60MB)。 + +## Step 7: 驗證 + +```bash +# 檢查容器狀態 +docker ps + +# 測試 Dashboard +curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/ +# 預期: 200 + +# 測試 OAuth2 端點(伺服器就緒的指標) +curl -s http://127.0.0.1/oauth2/.well-known/openid-configuration | jq .issuer +# 預期: "http://192.168.42.127/oauth2" +``` + +## Step 8: 開始使用 + +### 1️⃣ Web 管理介面 + +瀏覽器開啟:**http://192.168.42.127** + +首次登入會引導你建立管理員帳號。 + +### 2️⃣ 客戶端連線 + +安裝 NetBird 客戶端後執行: + +```bash +netbird up --management-url http://192.168.42.127 +``` + +--- + +## 疑難排解 + +### Docker 容器啟動失敗 + +``` +OCI runtime create failed: runc create failed: ... permission denied +``` + +**原因**:Proxmox LXC 容器 `/proc/sys` 限制。 +**解決**:確認已執行 Step 3 設定 crun。 + +### 伺服器啟動後無回應 (502 Bad Gateway) + +**原因**:正在下載 GeoLite2 資料庫。 +**檢查**:`docker compose logs -f netbird-server` +**處理**:耐心等待,視網速需 2-5 分鐘。 + +### 完全重置 + +```bash +cd /opt/netbird +docker compose down --volumes +docker compose up -d +``` diff --git a/README.md b/README.md new file mode 100644 index 0000000..41dff81 --- /dev/null +++ b/README.md @@ -0,0 +1,76 @@ +# NetBird Self-Hosted @ 192.168.42.127 + +在 Proxmox LXC (Ubuntu 24.04) 上自建 NetBird 的完整部署記錄與設定檔。 + +## 🎯 部署特色 + +- **IP-only 部署**:無需域名,純內網 LAN 使用 +- **HTTP 模式**:使用 Caddy 作為反向代理統一 port 80 +- **內建 IdP**:使用 NetBird Server 內建的 OAuth2(Dex),無需額外 IdP +- **LXC 相容**:使用 crun 取代 runc 繞過 Proxmox 非特權容器限制 + +## 📁 文件說明 + +| 文件 | 用途 | +|------|------| +| [QUICKSTART.md](./QUICKSTART.md) | 快速部署步驟(適合複製貼上執行) | +| [netbird-selfhosted-setup.md](./netbird-selfhosted-setup.md) | 完整安裝指南(含原理說明) | +| [SUMMARY.md](./SUMMARY.md) | 部署摘要與疑難排解記錄 | + +## 🚀 快速開始 + +```bash +# 瀏覽器開啟 +open http://192.168.42.127 + +# 客戶端連線 +netbird up --management-url http://192.168.42.127 +``` + +## 🏗️ 架構 + +``` + ┌───────────────────────┐ + │ 192.168.42.127 │ + │ │ + :80 ────▶│ Caddy (反向代理) │ + │ ├── / ──▶│──▶ dashboard (Web UI) + │ ├── /api/* ──▶│──▶ netbird-server + │ ├── /oauth2/* ──▶│──▶ netbird-server + │ ├── /relay* ──▶│──▶ netbird-server + │ └── gRPC ──▶│──▶ netbird-server + :3478/udp ────────────────────────│──▶ netbird-server (STUN) + └───────────────────────┘ +``` + +## 🔧 環境 + +| 項目 | 值 | +|------|-----| +| 主機 | `192.168.42.127` | +| 系統 | Ubuntu 24.04 LTS | +| 容器類型 | Proxmox LXC (非特權) | +| Docker | 29.4.0 | +| OCI Runtime | crun 1.20 | +| 設定目錄 | `/opt/netbird/` | + +## 📦 服務清單 + +| 容器 | 映像 | 用途 | +|------|------|------| +| `netbird-caddy` | `caddy:2` | 反向代理 (:80) | +| `netbird-dashboard` | `netbirdio/dashboard:latest` | Web 管理介面 | +| `netbird-server` | `netbirdio/netbird-server:latest` | 管理 + Signal + Relay + STUN + IdP | + +## ⚠️ 注意事項 + +- **無 TLS**:僅適合內網,勿暴露至公網 +- **無域名**:使用 IP 作為 issuer,若 IP 變更需重建 +- **LXC 限制**:必須使用 crun,不可使用預設 runc +- **首次啟動慢**:需下載 GeoLite2 資料庫(約 60MB) + +## 🔗 相關連結 + +- [NetBird 官方文件](https://docs.netbird.io/selfhosted/selfhosted-quickstart) +- [NetBird GitHub](https://github.com/netbirdio/netbird) +- [crun](https://github.com/containers/crun) diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 0000000..a012614 --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,142 @@ +# NetBird 部署摘要 + +## 部署結果 + +✅ **NetBird Self-Hosted 成功部署於 `192.168.42.127`** + +- 部署日期:2026-04-17 +- 存取方式:`http://192.168.42.127` +- 部署模式:HTTP-only(無 TLS)、IP-only(無域名) + +## 部署時的關鍵決策 + +### 1. 為何不用官方 `getting-started.sh` 腳本? + +官方腳本是互動式的(讀取 `/dev/tty`),且預設選項需要公網域名與 Let's Encrypt。本次為內網 IP 部署,手動產生設定檔更直接。 + +腳本原始碼中支援 `NETBIRD_DOMAIN=use-ip` 模式(line 308),會自動偵測 IP 並切換為 HTTP、port 80、`rel://` relay 協定。本次部署採用了相同的邏輯但以手動設定檔實現。 + +### 2. 為何選 Caddy 而非 Traefik 作為反向代理? + +- Traefik 內建 Let's Encrypt ACME,但無域名時會反覆嘗試失敗 +- Caddy 設定檔更精簡(約 10 行),純 HTTP 路由對本場景足夠 +- 兩者都能正確處理 gRPC (h2c) 後端 + +### 3. 為何需要 crun 取代 runc? + +**遇到的錯誤**: + +``` +OCI runtime create failed: runc create failed: unable to start container process: +error during container init: open sysctl net.ipv4.ip_unprivileged_port_start file: +reopen fd 8: permission denied +``` + +**根因分析**: +- Proxmox 非特權 LXC 將 `/proc/sys` 掛載為唯讀(`ro`) +- runc 1.2+ 為防止 TOCTOU 攻擊,會用「reopen fd」機制存取 sysctl 檔案 +- 此機制在 LXC 的 `/proc` mount namespace 限制下失敗 + +**嘗試但無效的方法**: +- `--privileged` flag +- `--security-opt apparmor=unconfined --security-opt seccomp=unconfined` +- `mount -o remount,rw /proc/sys` +- apt 安裝的 crun 1.14.1(OCI spec 太舊,Docker 不接受) + +**最終解法**: +下載 GitHub 最新 crun binary (1.20),並設為 Docker 預設 runtime。 + +## 安裝步驟摘要 + +``` +1. apt 安裝前置套件 (curl, jq, openssl) ← 約 1 分鐘 +2. 安裝 Docker Engine ← 約 2 分鐘(網速慢) +3. 下載 crun binary,設定為 Docker 預設 runtime ← 約 30 秒 +4. 建立 /opt/netbird/ 下四個設定檔 ← 約 30 秒 +5. docker compose pull + up -d ← 約 1 分鐘 +6. 等待 netbird-server 下載 GeoLite2 資料庫 ← 約 2-5 分鐘 +``` + +總耗時約 10 分鐘(含 GeoIP 下載)。 + +## 服務驗證 + +所有服務已驗證正常運作: + +| 端點 | 狀態 | 說明 | +|------|------|------| +| `http://192.168.42.127/` | ✅ 200 | Dashboard Web UI | +| `http://192.168.42.127/oauth2/.well-known/openid-configuration` | ✅ 200 | OIDC Discovery | +| STUN `:3478/udp` | ✅ | NAT 穿透 | +| 內部 gRPC (Signal + Management) | ✅ | h2c via Caddy | + +## 疑難排解記錄 + +| 問題 | 原因 | 解法 | +|------|------|------| +| 初次 apt install 卡住 | 背景執行的 apt 行程未結束就被觸發第二個,導致 lock 競爭 | 清除 `/var/lib/dpkg/lock*` 與 `/var/lib/apt/lists/lock`,重啟 apt | +| `curl: command not found` | apt 仍在安裝中 | 等待安裝完成 | +| 所有 Docker 容器啟動失敗 | LXC `/proc/sys` 唯讀 + runc 1.3 的 reopen fd 機制 | 改用 crun 1.20 | +| Docker Compose `crun` runtime 未知 | `/etc/docker/daemon.json` 設定未生效 | `systemctl restart docker` | +| apt 的 crun 1.14.1 報 `unknown version specified` | crun 的 OCI spec 1.0.0 與 Docker 29.4 不相容 | 從 GitHub 下載 crun 1.20 | +| 502 Bad Gateway(所有 API 端點) | netbird-server 下載 GeoLite2 中,尚未開始監聽 port 80 | 等待 2-5 分鐘 | + +## 檔案位置 + +### 本機(/Users/timmy/dev/42_127/) + +``` +├── README.md ← 專案總覽 +├── QUICKSTART.md ← 快速部署步驟 +├── SUMMARY.md ← 本檔(部署摘要) +└── netbird-selfhosted-setup.md ← 完整安裝指南 +``` + +### 遠端伺服器(192.168.42.127:/opt/netbird/) + +``` +├── docker-compose.yml ← 服務編排 +├── Caddyfile ← 反向代理路由 +├── config.yaml ← NetBird Server 設定 +└── dashboard.env ← Dashboard 環境變數 +``` + +### Docker Volume + +- `netbird_netbird_data`:NetBird SQLite 資料庫與 GeoIP 資料 + +## 後續維運 + +### 日常指令 + +```bash +ssh root@192.168.42.127 +cd /opt/netbird + +# 查看日誌 +docker compose logs -f + +# 重啟 +docker compose restart + +# 更新映像 +docker compose pull && docker compose up -d + +# 備份資料庫 +docker compose exec netbird-server sqlite3 /var/lib/netbird/store.db ".backup /tmp/backup.db" +docker cp netbird-server:/tmp/backup.db ./backup-$(date +%F).db +``` + +### 可能的擴展 + +- **加上 TLS**:若之後取得域名,可切換為 Traefik + Let's Encrypt(或本機自簽憑證) +- **外部 IdP**:需支援 OIDC 的 IdP(如 Authentik、Keycloak、Google)可取代內建 Dex +- **高可用性**:SQLite 不支援叢集,需改用 PostgreSQL;Relay 可獨立部署 +- **監控**:已暴露 `metricsPort: 9090` 的 Prometheus 指標 + +## 參考資料 + +- 官方文件:https://docs.netbird.io/selfhosted/selfhosted-quickstart +- Getting Started 腳本:https://github.com/netbirdio/netbird/releases/latest/download/getting-started.sh +- crun release:https://github.com/containers/crun/releases +- Proxmox LXC Docker 相容性:https://pve.proxmox.com/wiki/Linux_Container#_features diff --git a/netbird-selfhosted-setup.md b/netbird-selfhosted-setup.md new file mode 100644 index 0000000..2da8226 --- /dev/null +++ b/netbird-selfhosted-setup.md @@ -0,0 +1,302 @@ +# NetBird Self-Hosted 安裝指南 + +> 環境:Proxmox LXC (CT127) / Ubuntu 24.04 / 內網 IP-only (無域名) + +## 環境資訊 + +| 項目 | 值 | +|------|-----| +| 主機 | 192.168.42.127 | +| 系統 | Ubuntu 24.04 LTS (Noble Numbat) | +| 核心 | 6.8.12-16-pve (Proxmox VE) | +| 容器類型 | LXC (非特權) | + +## 1. 安裝前置套件 + +```bash +apt-get update +apt-get install -y curl jq ca-certificates gnupg openssl +``` + +## 2. 安裝 Docker + +```bash +curl -fsSL https://get.docker.com | sh +systemctl start docker +systemctl enable docker +``` + +## 3. 解決 LXC 容器中 Docker 無法啟動的問題 + +Proxmox LXC 容器中 `/proc/sys` 被掛載為唯讀,導致 runc 1.3+ 嘗試存取 `net.ipv4.ip_unprivileged_port_start` sysctl 時失敗: + +``` +OCI runtime create failed: runc create failed: unable to start container process: +error during container init: open sysctl net.ipv4.ip_unprivileged_port_start file: +reopen fd 8: permission denied +``` + +**解決方案:以 crun 取代 runc 作為 OCI runtime** + +```bash +# 下載 crun 1.20 +curl -sL "https://github.com/containers/crun/releases/download/1.20/crun-1.20-linux-amd64" \ + -o /usr/local/bin/crun +chmod +x /usr/local/bin/crun + +# 設定 Docker 使用 crun +cat > /etc/docker/daemon.json << 'EOF' +{ + "runtimes": { + "crun": { + "path": "/usr/local/bin/crun" + } + }, + "default-runtime": "crun" +} +EOF + +systemctl restart docker + +# 驗證 +docker run --rm hello-world +``` + +## 4. 建立 NetBird 設定檔 + +```bash +mkdir -p /opt/netbird +cd /opt/netbird +``` + +### 4.1 產生金鑰 + +```bash +RELAY_SECRET=$(openssl rand -base64 32 | sed 's/=//g') +ENCRYPTION_KEY=$(openssl rand -base64 32) + +echo "RELAY_SECRET=$RELAY_SECRET" +echo "ENCRYPTION_KEY=$ENCRYPTION_KEY" +``` + +> 記下這兩個值,下方設定檔需要用到。 + +### 4.2 docker-compose.yml + +```yaml +services: + caddy: + image: caddy:2 + container_name: netbird-caddy + restart: unless-stopped + networks: [netbird] + ports: + - "80:80" + volumes: + - ./Caddyfile:/etc/caddy/Caddyfile:ro + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" + + dashboard: + image: netbirdio/dashboard:latest + container_name: netbird-dashboard + restart: unless-stopped + networks: [netbird] + env_file: + - ./dashboard.env + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" + + netbird-server: + image: netbirdio/netbird-server:latest + container_name: netbird-server + restart: unless-stopped + networks: [netbird] + ports: + - "3478:3478/udp" + volumes: + - netbird_data:/var/lib/netbird + - ./config.yaml:/etc/netbird/config.yaml + command: ["--config", "/etc/netbird/config.yaml"] + logging: + driver: "json-file" + options: + max-size: "500m" + max-file: "2" + +volumes: + netbird_data: + +networks: + netbird: +``` + +### 4.3 Caddyfile + +Caddy 作為反向代理,將所有服務統一在 port 80: + +``` +:80 { + @grpc header Content-Type application/grpc* + reverse_proxy @grpc h2c://netbird-server:80 + + @backend path /relay* /ws-proxy/* /api/* /oauth2/* + reverse_proxy @backend netbird-server:80 + + reverse_proxy /* netbird-dashboard:80 +} +``` + +### 4.4 config.yaml + +將 `` 和 `` 替換為步驟 4.1 產生的值: + +```yaml +server: + listenAddress: ":80" + exposedAddress: "http://192.168.42.127:80" + stunPorts: + - 3478 + metricsPort: 9090 + healthcheckAddress: ":9000" + logLevel: "info" + logFile: "console" + + authSecret: "" + dataDir: "/var/lib/netbird" + + auth: + issuer: "http://192.168.42.127/oauth2" + signKeyRefreshEnabled: true + dashboardRedirectURIs: + - "http://192.168.42.127/nb-auth" + - "http://192.168.42.127/nb-silent-auth" + cliRedirectURIs: + - "http://localhost:53000/" + + reverseProxy: + trustedHTTPProxies: + - "0.0.0.0/0" + + store: + engine: "sqlite" + encryptionKey: "" +``` + +### 4.5 dashboard.env + +```env +NETBIRD_MGMT_API_ENDPOINT=http://192.168.42.127 +NETBIRD_MGMT_GRPC_API_ENDPOINT=http://192.168.42.127 +AUTH_AUDIENCE=netbird-dashboard +AUTH_CLIENT_ID=netbird-dashboard +AUTH_CLIENT_SECRET= +AUTH_AUTHORITY=http://192.168.42.127/oauth2 +USE_AUTH0=false +AUTH_SUPPORTED_SCOPES=openid profile email groups +AUTH_REDIRECT_URI=/nb-auth +AUTH_SILENT_REDIRECT_URI=/nb-silent-auth +NGINX_SSL_PORT=443 +LETSENCRYPT_DOMAIN=none +``` + +## 5. 啟動服務 + +```bash +cd /opt/netbird +docker compose up -d +``` + +首次啟動時 netbird-server 會下載 GeoLite2 地理位置資料庫(約 60MB),視網路速度可能需要數分鐘。 + +驗證所有容器正常運行: + +```bash +docker ps +``` + +預期輸出三個容器皆為 `Up` 狀態: + +``` +NAMES STATUS PORTS +netbird-caddy Up 0.0.0.0:80->80/tcp +netbird-dashboard Up 80/tcp, 443/tcp +netbird-server Up 0.0.0.0:3478->3478/udp +``` + +驗證 OAuth2 端點: + +```bash +curl -s http://127.0.0.1/oauth2/.well-known/openid-configuration | jq .issuer +# 預期輸出: "http://192.168.42.127/oauth2" +``` + +## 6. 使用方式 + +### Web 管理介面 + +瀏覽器打開 `http://192.168.42.127`,完成首次設定(建立管理員帳號)。 + +### 客戶端連線 + +```bash +netbird up --management-url http://192.168.42.127 +``` + +## 服務架構 + +``` +Client Request (:80) + | + Caddy (反向代理) + | + +-- / --> netbird-dashboard (Web UI) + +-- /api/* /oauth2/* --> netbird-server (管理 API + 內建 IdP) + +-- /relay* /ws-proxy/* --> netbird-server (Relay + WebSocket) + +-- gRPC (Content-Type 判斷) --> netbird-server (Signal + Management gRPC) + + STUN (:3478/udp) --> netbird-server +``` + +## 開放端口 + +| 端口 | 協定 | 用途 | +|------|------|------| +| 80 | TCP | Dashboard + API + Signal + Relay | +| 3478 | UDP | STUN (NAT 穿透) | + +## 常用維運指令 + +```bash +cd /opt/netbird + +# 查看日誌 +docker compose logs -f netbird-server +docker compose logs -f --tail=50 + +# 重啟服務 +docker compose restart + +# 停止服務 +docker compose down + +# 更新映像 +docker compose pull +docker compose up -d + +# 完全重置(刪除所有資料) +docker compose down --volumes +``` + +## 注意事項 + +- 此為 HTTP-only 內網部署,無 TLS 加密,不適合暴露至公網 +- 如需公網存取,應配置域名並啟用 HTTPS(使用 Traefik + Let's Encrypt) +- Proxmox LXC 容器需使用 crun 替代 runc,否則 Docker 容器無法啟動 +- 設定檔位於 `/opt/netbird/`,資料儲存在 Docker volume `netbird_netbird_data`