Initial commit: NetBird self-hosted deployment documentation

This commit is contained in:
2026-04-17 08:41:50 +08:00
commit 6e956f7334
4 changed files with 765 additions and 0 deletions

245
QUICKSTART.md Normal file
View File

@@ -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: 安裝 crunLXC 必須)
```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
```

76
README.md Normal file
View File

@@ -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 內建的 OAuth2Dex無需額外 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)

142
SUMMARY.md Normal file
View File

@@ -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.1OCI 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 不支援叢集,需改用 PostgreSQLRelay 可獨立部署
- **監控**:已暴露 `metricsPort: 9090` 的 Prometheus 指標
## 參考資料
- 官方文件https://docs.netbird.io/selfhosted/selfhosted-quickstart
- Getting Started 腳本https://github.com/netbirdio/netbird/releases/latest/download/getting-started.sh
- crun releasehttps://github.com/containers/crun/releases
- Proxmox LXC Docker 相容性https://pve.proxmox.com/wiki/Linux_Container#_features

302
netbird-selfhosted-setup.md Normal file
View File

@@ -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
`<RELAY_SECRET>``<ENCRYPTION_KEY>` 替換為步驟 4.1 產生的值:
```yaml
server:
listenAddress: ":80"
exposedAddress: "http://192.168.42.127:80"
stunPorts:
- 3478
metricsPort: 9090
healthcheckAddress: ":9000"
logLevel: "info"
logFile: "console"
authSecret: "<RELAY_SECRET>"
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: "<ENCRYPTION_KEY>"
```
### 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`