引子

内网穿透一直是折腾NAS、HomeLab的数码发烧友群体的必备,有了它,在家里部署的存储、影音等内部服务就能够在外面访问了,你能够做到在家里建一个网盘,在办公室能够高速访问。

可以说,内网穿透技术能够为每个人都创造一片互联网的自留地。

内网穿透技术也经历了长期演变。

传统的方案是通过客户端注册、服务端连接保持、端口转发等功能组成的一组对等的网络端口,开放端口、数据传输、内部系统的安全性一直是一个挑战,FRP则是传统内网穿透工具的代表之一。

而如今随着WireGuard技术的发展,使得Full Mesh架构的Overlay组网得到普及,Tail Scale、Net Maker等开源产品陆续涌现,这类基于WireGuard的方案最大进步是提供了更加安全的传输机制以及更加灵活的组网架构,并且契合零信任原则,可以理解成安全的、灵活的、虚拟的局域网。

随着内网穿透技术的发展,内网穿透也从传统的端口到端口穿透,演变成了节点到节点的穿透,更加安全、更加高效、更加灵活、更多可能。

通俗来讲,传统的方案让你得以访问家庭的内部服务,而最新的方案让你安全地访问整个家庭的所有设备!

相比之下,大家肯定更愿意选择新的内穿方案,然而,基于WireGuard的开源产品往往并不像FRP等老牌工具那么成熟,并且对用户技术要求更高。

过去几年里,我尝试了多种开源自建产品,均被我放弃了。

直到我发现了一款更新的产品NetBird。在各类产品底层技术趋同的情况下,NetBird为自建用户提供了更好的用户体验,经过我的测试,NetBird达到预期,正式成为了我的内穿方案!

本文则是我部署NetBird过程的记录,希望对你有所帮助,让更加现代、安全的技术造福更多人!

部署过程

规划与准备

系统规划

在开始前,我们需要将系统的规划确定下来,我的规划如下:

域名一个公网备案域名,例如:netbird.infra.jiaxiang.wang,最终肯定不是这个,大家不要尝试~
部署环境1Panel管理的云主机,Ubuntu系统2C4G
身份认证Auth0免费版(家庭够用了)
反向代理1Panel内置的OpenResty,Web UI操作,简单快捷

对于使用官方一键部署脚本的全新云主机来说,部署十分简单,但是由于我要使用自己的反向代理,所以要对官方的方案进行调整,而网上缺乏相关的资料,因此我写了本篇文章分享经验。

部署过程主要参考了自托管部署的官方文档

安装准备

首先在Linux主机内安装Docker引擎以及需要的软件包

apt install -y jq curl

由于NetBird部署所需的组件和配置较多,无法通过单个docker compsoe文件完成部署,因此,我们需要首先将包含docker compsoe文件和配置在内的代码仓库克隆下来。

首先创建文件夹和持久化存储路径。

mkdir netbird
cd netbird
mkdir signal-db mgmt-db

在终端执行以下命令,创建拉取脚本并下载最新版本。虽然看起来繁琐,但是这种方式将来更容易更新,留下的技术债务更少。

cat << 'EOF' > fetch-netbird.sh
#!/bin/bash
REPO="https://github.com/netbirdio/netbird/"
# this command will fetch the latest release e.g. v0.8.7
LATEST_TAG=$(basename $(curl -fs -o/dev/null -w %{redirect_url} ${REPO}releases/latest))
echo $LATEST_TAG

# this command will clone the latest tag
git clone --depth 1 --branch $LATEST_TAG $REPO
EOF

sh fetch-netbird.sh

cd netbird/infrastructure_files/

准备环境变量配置文件

cp setup.env.example setup.env

根据下文的配置说明,更改setup.env文件内的环境变量,最终的setup.env文件将用于生成docker compose配置文件。

服务端

官方提供了详细的部署文档,但是有些内容已经过时,下面根据我的实际情况对步骤进行记录。

基础配置

setup.env文件中,将NETBIRD_DOMAIN的值改为公网访问的域名,例如app.domain.com

NETBIRD_DOMAIN=app.domain.com

登录认证服务配置

本章节篇幅较长,但操作简单,请耐心阅读~

部署NetBird的最重要一步就是决定采用哪种安全认证方法,NetBird采用单点登录领域的OIDC协议,官方支持以下的选项:

我个人选择的是Auth0,原因很简单,因为整套组网方案的安全性至关重要!在网络层面,我们可以依赖WireGuard的原生加密通讯,而系统的入口(用户登录)反而成为了公开暴露的最大的攻击平面,保证登录服务的安全就是保证整套系统的安全。

在工作中,我比较倾向于私有化部署的方案,然而在生活中,我更倾向于将关键且非隐私的服务迁到信任的云服务之上。因为我在NAS或HomeLab部署的服务整体处于缺少积极维护的状态,经过几次WatchTower把服务升级挂了的尴尬之后,我就把大部分服务的自动更新停止了。

家庭自建服务的这种缺乏维护的状态让我如鲠在喉,从而让我不敢放心享受NAS服务的强大和公网访问的便捷性,这也是我最终采用这套零信任组网的方案原因之一,让所有自建服务只在这套零信任网络中可被访问,而选择Auth0,我得以将入口的安全责任也卸下。

扯远了,配置使用Auth0虽然步骤较多,但十分简单,推荐阅读官方文档

下面是详细的操作步骤,在完成之后,你应该能够获取下列环境变量的信息。

认证相关环境变量表

名称非必须
NETBIRD_AUTH_CLIENT_ID下文基础应用部分的Client ID
NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT下文的OpenID Configuration
NETBIRD_USE_AUTH0true
NETBIRD_AUTH_AUDIENCE下文的Identifier
NETBIRD_AUTH_DEVICE_AUTH_CLIENT_ID下文交互式SSO部分的Client ID
NETBIRD_MGMT_IDPauth0
NETBIRD_IDP_MGMT_CLIENT_ID下文Machine to Machine认证部分的Client ID
NETBIRD_IDP_MGMT_CLIENT_SECRET下文Machine to Machine认证部分的Client Secret
NETBIRD_IDP_MGMT_EXTRA_AUDIENCE下文Machine to Machine认证部分的DOMAIN

正式开始!

  1. 注册Auth0,地址在https://auth0.com/
  2. 创建NetBird的基础应用,请参考下图的步骤。注意,务必选择Native类型。

image.png

  1. 配置Auth0应用

将下图的3号Client ID标注的值配置到环境变量。

image.png

向下翻页,填写下图中1、2、3号对应的内容,多个域名用英文逗号,分隔。

  • https://YOUR DOMAINhttp://localhost:53000填入1号
  • https://YOUR DOMAINhttp://localhost填入2、3、5号,需要打开4号开关

image.png

image.png

向下翻页,展开Advanced Settings,将3号OpenID Configuration配置到环境变量。

image.png

  1. 配置Auth0 API

参考下图进行配置,Identifier填写域名即可,Identifier的值同样要被配置到环境变量。

image.png

  1. 配置交互式SSO认证

参照下图创建一个新的应用,名称填写Interactive Login

image.png

将下图2号Client ID配置到环境变量NETBIRD_AUTH_DEVICE_AUTH_CLIENT_ID

image.png

下翻到Advanced Settings,启用Device Code并保存。

image.png

  1. 配置Machine to Machine认证

参照下图创建一个新的应用,名称填写Netbird API

image.png

在提示选择API时,选择上文创建名为netbird的API

在下一步界面选择Auth0 Management API,然后选中以下权限并继续:read:users, update:users, create:users, read:users_app_metadata, update:users_app_metadata, create:users_app_metadata

image.png

然后,将2、3、4号配置到环境变量。注意:4号的值需要拼装成https://<DOMAIN>/api/v2/格式的字符串。

image.png

至此,身份认证部分的配置全部结束,环境变量最终被配置如下:

NETBIRD_AUTH_OIDC_CONFIGURATION_ENDPOINT="https://<DOMAIN>/.well-known/openid-configuration"
NETBIRD_USE_AUTH0=true
NETBIRD_AUTH_CLIENT_ID="<Client_ID>"
NETBIRD_AUTH_SUPPORTED_SCOPES="openid profile email offline_access api email_verified"
NETBIRD_AUTH_AUDIENCE="<IDENTIFIER>"
NETBIRD_AUTH_DEVICE_AUTH_CLIENT_ID="<INTERACTIVE_CLIENT_ID>"
NETBIRD_MGMT_IDP="auth0"
NETBIRD_IDP_MGMT_CLIENT_ID="<NETBIRD_API_CLIENT_ID>"
NETBIRD_IDP_MGMT_CLIENT_SECRET="<NETBIRD_API_CLIENT_SECRET>"
NETBIRD_IDP_MGMT_EXTRA_AUDIENCE="https://<DOMAIN>/api/v2/"

因为认证服务的配置步骤比较繁琐,为了确保配置的正确性,请仔细核对配置内容!核对无误后把配置更新到setup.env文件中

代理配置

因为使用我们自建的反向代理,也就是1panel中的默认代理OpenResty软件,所以我们需要禁用NetBird的自动证书功能,并对公网端口进行调整。setup.env文件调整如下:

NETBIRD_DISABLE_LETSENCRYPT=true

# 修改成反向代理的TLS/HTTPS端口
NETBIRD_RELAY_PORT=443
NETBIRD_MGMT_API_PORT=443
NETBIRD_SIGNAL_PORT=443

其他配置

建议将setup.env文件中以下配置进行调整:

  • 禁用使用情况跟踪
NETBIRD_DISABLE_ANONYMOUS_METRICS=true

启动运行

首先根据setup.env的配置生成docker compose文件,文件被放在artifacts文件夹。

./configure.sh

cd artifacts

(可选)修改docker-compose.yml文件,添加国内的Docker镜像加速代理,此文撰写时,使用的代理地址是:docker.xuanyuan.me

(可选但推荐)修改docker-compose.yml文件,配置持久化存储路径,改成类似以下的格式。

volumes:
  netbird-mgmt:
    driver: local
    driver_opts:
      type: none
      device: "/path/to/netbird/mgmt-db"
      o: bind
  netbird-signal:
    driver: local
    driver_opts:
      type: none
      device: "/path/to/netbird/signal-db"
      o: bind
  netbird-letsencrypt:

修改docker-compose.yml文件,更新服务端口,根据下表的外部端口更新内部端口(外部端口可以根据自己的实际情况修改,只要和后面保持一致即可)。

路径协议内部端口外部端口
/HTTPdashboard:8010080
/signalexchange.SignalExchange/gRPCsignal:8010081
/apiHTTPmanagement:44310083
/management.ManagementService/gRPCmanagement:44310083
/relayHTTPrelay:44310084

参考配置如下,只需修改ports的端口。

services:
  # UI dashboard
  dashboard:
……
    ports:
      - 10080:80
……

可选:修改docker-compose.yml文件,更新management服务启动命令--disable-geolite-update禁用地理信息数据库更新。如果你的服务器位于国内,地理信息数据库更新会一直卡住,导致management服务无法启动。

  # Management
  management:
    ……
    command: [
    ……
      "--disable-geolite-update",
    ……
      ]

启动!

docker compose up -d

防火墙配置

官方文档要求在云服务上开放端口,调整云主机的安全组:

  • 在服务器上开启以下TCP端口:80、443、33073、10000、33080(分别用于DashBoard HTTP/HTTPS、Management gRPC/HTTP API、Signal gRPC API和中继服务)。
  • Coturn通过STUN/TURN协议实现中继功能,需要开启监听端口UDP 3478及动态中继连接所需的UDP端口范围49152-65535。这些配置在安装文件中已设为默认值,但您可根据实际需求进行调整。

本文由于采用了1Panel的反向代理,大部分NetBird的非标端口都被代理到1Panel反向代理的443端口了,因此这里只需要添加NetBird的新端口即可。注意:请确保1Panel的反向代理端口(默认80443)已被放开,否则无法访问!

端口协议备注
443UDP补充:relay服务quic入口
3478UDPcoturn P2P
49152-65535UDPcoturn P2P

添加后效果如下图

image.png

1Panel代理配置

在1Panel中,可以像添加普通网站一样添加NetBird服务,步骤如下图所示。

因为我们将NetBird的端口改为了10080,所以这里代理地址需要填写127.0.0.1:10080

image.png

创建完成后,开启HTTPS访问,步骤如下图所示。

image.png

配置代理路径,方法及步骤如下图所示。

image.png

根据下表的映射关系,添加完所有的配置。

路径协议代理地址备注
/HTTP127.0.0.1:10080默认存在,无需添加
/apiHTTP127.0.0.1:10083
/relayHTTP127.0.0.1:10084

添加所有的代理路径后,页面应该如下图所示。

image.png

由于1Panel的配置UI不支持添加gRPC类型的代理,因此需要将gRPC类型的代理手动添加到1Panel配置中,配置内容如下:

    location ^~ /management.ManagementService/ {
        grpc_read_timeout 300s;
        grpc_send_timeout 300s;
        grpc_socket_keepalive on;
        grpc_pass grpc://127.0.0.1:10083;
        access_log off;
    }

    location ^~ /signalexchange.SignalExchange/ {
        grpc_read_timeout 300s;
        grpc_send_timeout 300s;
        grpc_socket_keepalive on;
        grpc_pass grpc://127.0.0.1:10081;
        access_log off;
    }

按下图所示,将配置粘贴到配置文件中并保存。

image.png

此时,通过浏览器能够访问你的NetBird域名了!

客户端

NAS/HomeLab

在自建服务器使用NetBird最方便的方法就是通过docker compose文件部署一个netbird client容器服务。

首先需要在NetBird系统上创建setup key,然后用setup key注册到NetBird的网络中。

我的docker compose文件如下:

services:
    netbird:
        cap_add:
            - NET_ADMIN
        environment:
            - NB_SETUP_KEY=<setup key>
            - NB_MANAGEMENT_URL=https://<domain>:443
        volumes:
            - netbird:/etc/netbird
        # network_mode: host
        image: netbirdio/netbird:latest
volumes:
    netbird:

手机/电脑

手机电脑的使用方式更为简单,只需要通过官网下载对应平台的客户端即可,这里不再赘述。

使用

创建用户

回到Auth0,创建用于登录NetBird的用户(这个用户不是Auth0的用户,而是Auth0提供的身份认证服务的子用户,可以帮你的家人、朋友创建),步骤如下图所示。创建完成后,会提示验证邮箱等,按照提示操作即可。

image.png

登录Netbird

重头戏来啦,在浏览器访问域名,使用上一步创建的用户就能够顺利登录进去了!

image.png

如果你在这一步遇到任何问题,比如页面加载不出来等,请跳转到填坑章节。

更多使用方法建议参照官方文档,我也会整理自己的最佳实践,如果大家感兴趣,欢迎留言~

维护

备份

参考官方文档

将artifacts文件夹内容备份。

cd netbird/infrastructure_files/artifacts/
mkdir backup
cp docker-compose.yml turnserver.conf management.json backup/

将数据进行持久化备份。

docker compose stop management
docker compose cp -a management:/var/lib/netbird/ backup/
docker compose start management

在本文中,我已经提前将数据进行了持久化存储,作为一道额外的保障。

更新配置

如果需要对NetBird的配置进行较大范围调整,建议先备份后,重新生成docker compose文件。

cd netbird/infrastructure_files/
./configure.sh

生成后记得重复之前的修改~

升级

参考官方文档

首先进行备份!

然后阅读官方的版本说明,仔细判断是否兼容以及所需的配置调整。

容器的升级十分简单,只需要拉取最新镜像,重新启动即可。

cd netbird/infrastructure_files/artifacts/
# 拉取
docker compose pull
# 重启
docker compose up -d --force-recreate

总结

虽然花费了不少精力,但是NetBird的最终效果的确达到了我的预期,其零信任的原则能够大大提升网络传输的安全性,尤其是在用户体验方面相较其他开源方案优势十分明显。

和其他基于WireGuard技术的开源组网/VPN/SDN产品相比,NetBird的主要功能同样是基于WireGuard的能力,大家的水平差不多,但是用户体验确实是我体验下来最棒的了。

其他产品的开源版往往阉割了UI方便的功能,导致大家的用户体验较差,以往缺少竞品,大家只能捏着鼻子忍了,然而NetBird出现了!如果其他开源产品不反思改进,那么他们的用户但凡接触过NetBird,就会立马坚定投入NetBird的拥抱中!

在网络产品领域,我认为开源与商业化并不冲突,反而会对品牌价值产生长期的助力,逻辑很简单:

  • 对于个人用户,喜欢折腾、能够折腾成功的人总归是少数,并且会随着时间推移放弃折腾,转用商业产品。引用NAS界的名言:黑群晖的尽头是白群晖!
  • 对于企业用户,只有至关重要的业务才会愿意付钱,只有至关重要的业务才需要专业技术支持,尤其是存储、网络方面。对于私有化部署的服务,运维人员承担很大的风险和责任,他们会想尽方法让企业付费把风险与责任转嫁。如果没有,说明企业对这块的需求不大,哪怕放弃也不愿付费的。

不出意外的话,NetBird应该会成为长期的内网穿透方案,希望NetBird的开源与商业化都能够越来越好!大家有任何想法欢迎评论区交流~

填坑

部署过程中遇到了许多坑,为了不影响阅读,此章节放在最后,并且会持续更新。

NetBird的私有化部署体验相较于其其他竞品还算不错,但是代码的灵活性有待提升,对各种奇怪场景(如国内网络)的支持不够,一旦遇到问题,需要具备编程能力的人才能快速处理,对于项目开源推广不利。

不禁反思,我们的项目不只需要跑通开发的场景/环境,越是开源开放的项目,越需要提前规划,做好工程化,支持更多场景。

无法访问management服务,登录后浏览器控制台/api/user等接口报错502

如果你的docker日志显示如下,则说明你遇到了网络问题。

image.png

这个问题是因为现在数据库的过程中遇到了网络问题,在腾讯云很容易出现该问题。

你需要手动下载以下文件:

  • 单击该链接,下载数据库文件,你会获得一个包含日期的压缩文件,解压后将GeoLite2-City.mmdb文件重命名为GeoLite2-City_<日期>.mmdb,如GeoLite2-City_20250615.mmdb
  • 单击该链接,下载另一个数据库文件,你会获得一个包含日期的压缩文件,解压后将GeoLite2-City-Locations-en.csv文件转换成SQLite数据库geonames_<日期>.db,如为geonames_20250615.db。Github找到一个工具可以帮我们快速完成转换,如需下载成品,请到我的Folk中下载

至此,你得到了GeoLite2-City_<日期>.mmdb文件和geonames_<日期>.db文件,将这个文件放到management容器的持久化路径内,如果你参考本文档的步骤部署,则你应该放在/path/to/netbird/mgmt-db/文件夹。

重启management容器即可。

docker restart artifacts-management-1 

容器应该能够正常工作了。附上参考原文地址~