部署 Netbird 但不使用 docker
1. 环境准备
- 准备 Linux 服务器、域名、HTTPS 证书,以及支持 HTTP/2、gRPC 的 Nginx。
- Linux 节点需具备 WireGuard 运行条件,如果安装了系统防火墙,需允许所需通信
- 放行 TCP 443 UDP 3478 端口
| 组件 | 地址 / 端口 | 用途 |
|---|---|---|
| Nginx | test.example.com:443 |
HTTPS、API、gRPC、WebSocket、静态页面 |
| NetBird Server | 127.0.0.1:8081 |
Management、Signal、Relay、OIDC |
| STUN | UDP 3478 |
NAT 穿透,直接访问 Server |
| Dashboard | /html |
Nginx 本地静态文件 |
test.example.com 批量替换
2. 安装和启动 NetBird Server
下载 Linux amd64 二进制:
https://fileshare-1253968452.cos.ap-shanghai.myqcloud.com/netbird-server
chmod +x ./netbird-server
./netbird-server --config ./config.yaml
# 网络条件不佳的时候执行下面的指令跳过 geoip 库的下载否则服务器不会启动
NB_DISABLE_GEOLOCATION=true ./netbird-server --config ./config.yaml
NB_DISABLE_GEOLOCATION=true 只作用于这次命令启动的进程。NetBird 默认会在地理位置数据库不存在或需要更新时下载 GeoLite2,网络不佳时可能卡在数据库下载阶段,加上这个选项会跳过地理位置服务初始化和下载。IP 国家/城市识别和基于地理位置的策略不可用,路由 /api/locations/countries 返回 412
配置文件
将下面内容保存为 Server 启动目录下的 config.yaml
server:
listenAddress: ":8081" # 服务端监听端口;生产环境限制公网直接访问
exposedAddress: "https://test.example.com:443" # 对外地址:协议://域名:端口
stunPorts:
- 3478 # STUN 监听端口(UDP)
metricsPort: 9090 # 监控指标端口
healthcheckAddress: ":9000" # 健康检查端口
logLevel: "info"
logFile: "console" # 日志输出到 stdout
authSecret: "<新装生成的 authSecret>" # 服务端认证密钥
dataDir: "./data" # 数据目录
# NetBird 内置 OIDC 修改对应域名即可(无法关闭;可禁用本地账号密码认证)
auth:
issuer: "https://test.example.com/oauth2" # 内置 OIDC Issuer
localAuthDisabled: false # 完成外部 OIDC 管理员迁移后可设为 true,禁用本地密码登录
signKeyRefreshEnabled: true
sessionCookieEncryptionKey: "<新装生成的 sessionCookieEncryptionKey>"
dashboardRedirectURIs:
- "https://test.example.com/nb-auth" # Dashboard 登录回调
- "https://test.example.com/nb-silent-auth" # Dashboard 静默登录回调
cliRedirectURIs:
- "http://localhost:53000/" # 客户端本地 OAuth2 回调
# Nginx 反向代理来源;需按实际代理地址收紧
reverseProxy:
trustedHTTPProxies:
- "0.0.0.0" # 替换成本地受信任地址如 127.0.0.1
store:
engine: "sqlite" # 当前部署使用 SQLite Mysql 支持已被移除 仅支持 sqlite与 postgres
encryptionKey: "<新装生成的 encryptionKey>" # 数据加密密钥
# store:
# engine: "postgres"
# dsn: "host=<PG_HOST> user=<PG_USER> password=<PG_PASSWORD> dbname=<PG_DB_NAME> port=<PG_PORT>"
authSecret 和加密密钥可分别使用 openssl rand -base64 32 生成
合并式 NetBird Server 的内置 OIDC 服务 。外部 OIDC 通过 Dashboard 的 Settings → Identity Providers 添加
3. 配置 Nginx
3.1 模块化版本(nginx.conf + conf.d/netbird.conf)
/etc/nginx/nginx.conf
user ubuntu;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections auto;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server_tokens off;
sendfile on;
tcp_nopush on;
client_max_body_size 10m;
access_log /var/log/nginx/access.log;
gzip on;
gzip_vary on;
gzip_comp_level 5;
gzip_types text/plain text/css application/javascript application/json application/wasm image/svg+xml;
include /etc/nginx/conf.d/*.conf;
}
/etc/nginx/conf.d/netbird.conf
upstream netbird_server {
server 127.0.0.1:8081;
}
server {
listen 80;
server_name test.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2; # Nginx 1.24 的 HTTP/2 语法
# http2 on Nginx1.25以上语法
server_name test.example.com;
ssl_certificate /cert/fullchain.pem; # 证书公钥
ssl_certificate_key /cert/privkey.pem; # 证书密钥
ssl_protocols TLSv1.2 TLSv1.3;
root /html; # Dashboard 静态目录
index index.html;
client_header_timeout 1d;
client_body_timeout 1d;
# Relay / WebSocket
location ~ ^/(relay|ws-proxy/) {
proxy_pass http://netbird_server;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-Proto https;
proxy_read_timeout 1d;
proxy_buffering off;
}
# 原生 gRPC:Signal / Management / ProxyService(上游 h2c)
location ~ ^/(signalexchange\.SignalExchange|management\.(ManagementService|ProxyService))/ {
grpc_pass grpc://netbird_server;
grpc_set_header Host $host;
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
grpc_read_timeout 1d;
grpc_send_timeout 1d;
grpc_socket_keepalive on;
}
# API / OIDC
location ~ ^/(api|oauth2)/ {
proxy_pass http://netbird_server;
proxy_http_version 1.1;
proxy_set_header Connection "close";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_read_timeout 120s;
}
location = /netbird.wasm {
default_type application/wasm;
try_files $uri =404;
}
location = /ironrdp-pkg/ironrdp_web_bg.wasm {
default_type application/wasm;
try_files $uri =404;
}
location / {
try_files $uri $uri.html $uri/ =404;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: ws: wss: https:;" always;
}
error_page 404 /404.html;
location = /404.html {
internal;
}
}
3.2 普通版本(单文件 nginx.conf)
user ubuntu;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log warn;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 2048;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
server_tokens off;
sendfile on;
tcp_nopush on;
client_max_body_size 10m;
access_log /var/log/nginx/access.log;
gzip on;
gzip_vary on;
gzip_comp_level 5;
gzip_types text/plain text/css application/javascript application/json application/wasm image/svg+xml;
upstream netbird_server {
server 127.0.0.1:8081;
}
server {
listen 80;
server_name test.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2; # Nginx 1.24 的 HTTP/2 语法
# http2 on Nginx1.25以上语法
server_name test.example.com;
ssl_certificate /cert/fullchain.pem; # 证书公钥
ssl_certificate_key /cert/privkey.pem; # 证书密钥
ssl_protocols TLSv1.2 TLSv1.3;
root /html; # Dashboard 静态目录
index index.html;
client_header_timeout 1d;
client_body_timeout 1d;
# Relay / WebSocket
location ~ ^/(relay|ws-proxy/) {
proxy_pass http://netbird_server;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-Proto https;
proxy_read_timeout 1d;
proxy_buffering off;
}
# 原生 gRPC:Signal / Management / ProxyService(上游 h2c)
location ~ ^/(signalexchange\.SignalExchange|management\.(ManagementService|ProxyService))/ {
grpc_pass grpc://netbird_server;
grpc_set_header Host $host;
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
grpc_read_timeout 1d;
grpc_send_timeout 1d;
grpc_socket_keepalive on;
}
# API / OIDC
location ~ ^/(api|oauth2)/ {
proxy_pass http://netbird_server;
proxy_http_version 1.1;
proxy_set_header Connection "close";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_read_timeout 120s;
}
# WASM 资源
location = /netbird.wasm {
default_type application/wasm;
try_files $uri =404;
}
location = /ironrdp-pkg/ironrdp_web_bg.wasm {
default_type application/wasm;
try_files $uri =404;
}
location / {
try_files $uri $uri.html $uri/ =404;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Strict-Transport-Security "max-age=31536000" always;
add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: ws: wss: https:;" always;
}
error_page 404 /404.html;
location = /404.html {
internal;
}
}
}
4. 部署 Dashboard 静态文件
下载静态文件压缩包:
https://fileshare-1253968452.cos.ap-shanghai.myqcloud.com/html-v2.96.1.zip
然后根据以下占位符替换 _next\static\chunks\2crj5kse9ebp_.js文件,用编辑器搜索替换方便点
4.1 占位符对照表
$ 占位符 |
替换内容 | 说明 |
|---|---|---|
$USE_AUTH0 |
false |
使用非 Auth0 认证 |
$AUTH_AUTHORITY |
https://test.example.com/oauth2 |
内置 OIDC Issuer |
$AUTH_CLIENT_ID |
netbird-dashboard |
Dashboard OIDC 客户端 |
$AUTH_CLIENT_SECRET |
空字符串 | 浏览器端不填密钥 |
$AUTH_AUDIENCE |
netbird-dashboard |
OIDC Audience |
$AUTH_SUPPORTED_SCOPES |
openid profile email groups |
请求的 Scope |
$AUTH_REDIRECT_URI |
/nb-auth |
登录回调 |
$AUTH_SILENT_REDIRECT_URI |
/nb-silent-auth |
静默回调 |
$NETBIRD_MGMT_API_ENDPOINT |
https://test.example.com |
API 地址 |
$NETBIRD_MGMT_GRPC_API_ENDPOINT |
https://test.example.com |
gRPC 地址 |
$NETBIRD_TOKEN_SOURCE |
accessToken |
Token 来源 |
$NETBIRD_DRAG_QUERY_PARAMS |
false |
不传递 URL 查询参数 |
$NETBIRD_HOTJAR_TRACK_ID |
空字符串 | 不使用 Hotjar |
$NETBIRD_GOOGLE_ANALYTICS_ID |
空字符串 | 不使用 Google Analytics |
$NETBIRD_GOOGLE_TAG_MANAGER_ID |
空字符串 | 不使用 Google Tag Manager |
$NETBIRD_AUTH_SERVICE_URL |
空字符串 | 留空 |
$NETBIRD_WASM_PATH |
空字符串 | 使用前端默认路径 |
$NETBIRD_LICENSED |
false |
不启用授权功能 |
$NETBIRD_CLOUD |
false |
自托管模式 |
$NETBIRD_AGENT_NETWORK_ONLY |
false |
不启用仅 Agent Network 模式 |
$NETBIRD_AGENT_NETWORK_ENABLED |
false |
不启用 Agent Network |
$NETBIRD_HUBSPOT_PORTAL_ID |
空字符串 | 不使用 HubSpot |
$NETBIRD_HUBSPOT_SIGNUP_FORM_ID |
空字符串 | 不使用 HubSpot |
$NETBIRD_HUBSPOT_ONBOARDING_FORM_ID |
空字符串 | 不使用 HubSpot |
$NETBIRD_HUBSPOT_SURVEY_FORM_ID |
空字符串 | 不使用 HubSpot |
$NETBIRD_ANALYTICS_EXCLUDED_EMAILS |
空字符串 | 留空 |
$NETBIRD_ANNOUNCEMENT |
空字符串 | 不显示公告 |
4.2 逐条 sed 替换
把下面每条命令末尾的 实际配置文件.js 改为上一步查到的文件路径。如果查到多个前端配置文件,则分别替换。示例中保留原有 JS/JSON 引号,如 "$USE_AUTH0" 替换为 "false";不要把开关改成没有引号的布尔值。注意 Browser Dashboard 的 Client Secret 必须留空。
sed -i 's|\$USE_AUTH0|false|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$AUTH_AUTHORITY|https://test.example.com/oauth2|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$AUTH_CLIENT_ID|netbird-dashboard|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$AUTH_CLIENT_SECRET||g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$AUTH_AUDIENCE|netbird-dashboard|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$AUTH_SUPPORTED_SCOPES|openid profile email groups|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$AUTH_REDIRECT_URI|/nb-auth|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$AUTH_SILENT_REDIRECT_URI|/nb-silent-auth|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_MGMT_API_ENDPOINT|https://test.example.com|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_MGMT_GRPC_API_ENDPOINT|https://test.example.com|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_TOKEN_SOURCE|accessToken|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_DRAG_QUERY_PARAMS|false|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_HOTJAR_TRACK_ID||g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_GOOGLE_ANALYTICS_ID||g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_GOOGLE_TAG_MANAGER_ID||g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_AUTH_SERVICE_URL||g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_WASM_PATH||g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_LICENSED|false|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_CLOUD|false|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_AGENT_NETWORK_ONLY|false|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_AGENT_NETWORK_ENABLED|false|g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_HUBSPOT_PORTAL_ID||g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_HUBSPOT_SIGNUP_FORM_ID||g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_HUBSPOT_ONBOARDING_FORM_ID||g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_HUBSPOT_SURVEY_FORM_ID||g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_ANALYTICS_EXCLUDED_EMAILS||g' /html/_next/static/chunks/2crj5kse9ebp_.js
sed -i 's|\$NETBIRD_ANNOUNCEMENT||g' /html/_next/static/chunks/2crj5kse9ebp_.js
如存在 OidcTrustedDomains.js.tmpl,手动创建正式文件,必须执行:
cp /html/OidcTrustedDomains.js.tmpl /html/OidcTrustedDomains.js
sed -i 's|\$NETBIRD_MGMT_API_ENDPOINT|https://test.example.com|g' /html/OidcTrustedDomains.js
升级 Dashboard 后,需要在新版本原始静态文件上重新替换。如果静态 HTML 中仍无条件加载 Hotjar,需手动移除对应脚本引用。
5. 验证及常见问题
# Server 监听状态
ss -lntup | grep -E ':8081|:443|:3478'
# NetBird 内置 OIDC(绕开 Nginx)
curl -i http://127.0.0.1:8081/oauth2/.well-known/openid-configuration
# Nginx 对外 OIDC
curl -i https://test.example.com/oauth2/.well-known/openid-configuration
# Dashboard 静态回调
curl -I https://test.example.com/nb-auth
# Nginx 错误日志
tail -f /var/log/nginx/error.log
在初次使用时如遇到 peer之间无法访问的时候,请配置默认的Access Control Policies 规则 ALL 双向访问
在初次访问 peer 的时候遇到延迟高建立连接慢的时候请关闭 Lazy connection 按需链接,保持长连接,该问题只会出现在无头客户端如 linux client,gui不会出现这种问题
netbird service reconfigure --service-env NB_LAZY_CONN=off
使用以下指令查看连接状态
netbird status -d
连接回退路径
LAN -> P2P NAT0 -> P2P NAT2 -> UDP -> WEBSOCKET
常用指令
从 docker 镜像复制出静态文件
docker cp dashboard:/usr/share/nginx/html .
从 docker 镜像复制出服务端二进制文件
docker cp netbird:/go/bin/netbird-server ./netbird-server