部署 Netbird 但不使用 docker

部署 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 
Licensed under CC BY-NC-SA 4.0
路漫漫其修远兮,吾将上下而求索。
使用 Hugo 构建
主题 Stack 由 Jimmy 设计