はじめに
いやあ、tailscaleくんがごたごたしてますね。
恥ずかしながら、お行儀の悪さを今まで知りませんでした。
なので、自宅で使っているtailscale構成をすべてNetbirdで置き換えようと思います。
tailscale
まあこの方の記事読んでもらえればなんでゴタついてるかわかると思います。
netbird
WireGuardを基盤にしたVPNソリューションです。
tailscaleがクローズドなリレーネットワークとクローズドソースのコントロールプレーンを使うのに対し、netbirdはWebRTCライクなNATトラバーサルとCoturnを使っています。
コントロールプレーンとシグナリングサーバーが完全にセルフホストできるので、全部自分で持っておきたい、って人にいいと思います。
ただしtailscaleにもコントロールプレーンをセルフホストできるHeadscaleがありますので、一概にどちらが良くてどちらが悪いとかではありません。
コントロールプレーンをセルフホストしたらその分コストもかかるのでね。
Important
なんとなく変えたかっただけなので。
構築
下準備として、コントロールプレーンの公開に使用するドメインとSSL証明書を用意してください(例: vpn.example.com)。
また、今回は構築済みのNginxリバースプロキシを利用する前提で進めます。
ファイアウォールやルーターで、TCP/80番,443番、UDP/3478番ポートを開放して外部からアクセスできるようにしておいてください。
Proxmox LXCの設定
今回はアクセス受け入れ用のピア(Exit Node / Routing Peer)をAlmaLinux 10で構築します。
冗長化のために3つのピアを用意します。
お好みのテンプレートでLXCコンテナを作成した後一度シャットダウンし、各コンテナの設定ファイル(/etc/pve/lxc/CTID.conf)に以下の内容を追加してください。
lxc.cgroup2.devices.allow: c 10:200 rwm
lxc.mount.entry: /dev/net dev/net none bind,create=dir
lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file
追加できたらコンテナを起動しておきます。
コントロールプレーンの構築
公式のセルフホスト用ドキュメントを参考にしながらコントロールプレーンを構築していきます。
専用のVMを用意しDocker、jq、curlをインストールしておいてください。
セットアップスクリプトの実行
公式が提供しているインストールスクリプトを利用します。
# curl -fsSL https://github.com/netbirdio/netbird/releases/latest/download/getting-started.sh | bash
The NETBIRD_DOMAIN variable cannot be empty.
Enter the domain you want to use for NetBird (e.g. netbird.my-domain.com): vpn.example.com
Which reverse proxy will you use?
[0] Traefik (recommended - automatic TLS, included in Docker Compose) <- 既設のリバースプロキシなどがない方はこれ。
[1] Existing Traefik (labels for external Traefik instance)
[2] Nginx (generates config template) <- 今回はこれ。
[3] Nginx Proxy Manager (generates config + instructions)
[4] External Caddy (generates Caddyfile snippet)
[5] Other/Manual (displays setup documentation)
Enter choice [0-5] (default: 0): 2
Should container ports be bound to localhost only (127.0.0.1)?
Choose 'yes' if your reverse proxy runs on the same host (more secure).
Bind to localhost only? [Y/n]: n <- リバースプロキシが別ホストにいるので全ホストからのアクセスを受け入れる
Is Nginx running in Docker?
If yes, enter the Docker network Nginx is on (NetBird will join it).
Docker network (leave empty if not in Docker): <- NginxはDocker環境ではないので空
Dockerコンテナが起動し、セットアップスクリプトが完了すると、次のようなNginxのコンフィグファイルが出力されます。
これを、既設のNginxリバースプロキシの設定に組み込みます。
# NetBird Nginx Configuration
# Generated by getting-started.sh
#
# 1. Update SSL certificate paths below
# 2. Copy to your nginx config directory:
# Debian/Ubuntu: /etc/nginx/sites-available/netbird (then symlink to sites-enabled)
# RHEL/CentOS: /etc/nginx/conf.d/netbird.conf
# 3. Test and reload: nginx -t && systemctl reload nginx
upstream netbird_dashboard {
server <netbird managementをデプロイしたサーバー>:8080;
keepalive 10;
}
upstream netbird_server {
server <netbird managementをデプロイしたサーバー>:8081;
}
server {
listen 80;
server_name vpn.example.com;
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
server_name vpn.example.com;
ssl_certificate /path/to/your/fullchain.pem; # ← 実際のSSL証明書のパスに変更します
ssl_certificate_key /path/to/your/privkey.pem; # ← 秘密鍵のパスに変更します
# Recommended SSL settings
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
# Required for long-lived gRPC connections
client_header_timeout 1d;
client_body_timeout 1d;
# Common proxy headers
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Scheme $scheme;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# WebSocket connections (relay, signal, management)
location ~ ^/(relay|ws-proxy/) {
proxy_pass http://netbird_server;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_read_timeout 1d;
}
# Native gRPC (signal + management)
location ~ ^/(signalexchange\.SignalExchange|management\.ManagementService)/ {
grpc_pass grpc://netbird_server;
grpc_read_timeout 1d;
grpc_send_timeout 1d;
grpc_socket_keepalive on;
}
# HTTP routes (API + OAuth2)
location ~ ^/(api|oauth2)/ {
proxy_pass http://netbird_server;
proxy_set_header Host $host;
}
# Dashboard (catch-all)
location / {
proxy_pass http://netbird_dashboard;
}
}
初期セットアップ
リバースプロキシの設定が完了し指定したドメインへアクセスすると、管理者ユーザーの作成画面が表示されます。
管理者ユーザーの作成後、初期ピアの追加画面が表示されることがありますが、ここはスキップしてダッシュボードへ進んでください。
Note
10.0.0.0/16として進めます。環境に合わせて適宜読み替えてください。
ピアの追加
Routing Peersグループの作成
ここから、Exit Node兼Routing Peerとなるピアを追加していきます。
まずは、左メニューのAccess ControlからRouting Peersグループを作成します。

Usersグループの作成
次に、一般クライアント用のグループを作成します。
Usersグループを作成しPoliciesタブから以下の画像のように2つのポリシーを設定しておきます。


セットアップキーの取得
グループの作成が完了したらRouting Peersグループの詳細画面を開き、Setup KeysからRouting Peer登録用のキーを作成します。
- Make this key reusable: オン
- Usage limit: 追加するピアの数(今回は3回まで使えるキー)
- Auto-assigned groups: Routing Peers

このキーを使用して追加されたピアは自動的にRouting Peersグループへ割り当てられるようになります。

作成後、Install Netbirdをクリックして次に進みます。
Netbirdクライアントのインストール
インストール方法が画面に表示されるので、その手順に従って各ピアマシンでコマンドを実行します。

curl -fsSL https://pkgs.netbird.io/install.sh | sh
netbird up --management-url https://vpn.example.com --setup-key <作成したセットアップキー>
用意した3台のピアマシンのそれぞれで上記のコマンドを実行するだけでピアの登録は完了です。
ホームネットワークの設定
ネットワークの追加
Routingピアの追加が完了したら、自宅ネットワークへのアクセス設定を行います。
左メニューのNetworksタブを開き、Add Networkから新しいネットワークを追加します。

ネットワークを作成するとResourceを追加するか尋ねられるので、Add Resourceをクリックします。
- Name: 分かりやすい名前(例: Home Network)
- Address: 自宅のサブネット(例:
10.0.0.0/16)
Note

次にAccess Controlタブを開き、Add Policyからアクセスポリシーを設定します。

以下の画像のようにポリシーを設定し、Add Policyをクリックして保存します。

リソース追加画面に戻り設定したポリシーが反映されていることを確認したら、再度Add Resourceをクリックしてリソースの追加を完了させます。

作成したリソースを開き、Routing Peersタブを選択します。
Add Routing Peerをクリックしてピア追加画面を開き、Peer Groupに先ほど作成したRouting Peersグループを選択して追加します


Exit Nodeの追加
Network Routes画面からAdd Exit Nodeをクリックし設定画面を開きます。
- Peer Group: Routing Peers
- Distribution Groups: Users
任意の名前をつけて保存します。
作成後、Exit Nodeのタブを開き、Auto Applyがオフになっていることを確認してください。
もしオンになっていた場合はオフに変更しておきます。

DNS設定
私は自宅にAdGuard Homeをデプロイしているので、どこからでも自宅のDNSサーバーを利用できるように設定します。
左メニューのDNS内のNameserversタブから、Add Nameserver→Custom DNSを選択します。

設定画面でDNSサーバーのIPアドレスを入力します。
このときMatch Domainsを空にしておくと、すべてのドメインのクエリが指定した自宅のDNSサーバーへ転送されるようになります。
これでサーバー側の基本的なセットアップは完了です。
クライアントの追加
例としてAndroid版クライアントからの接続方法を書きます。
アプリ左側のメニューからChange Serverを選択し、構築したコントロールプレーンのURL(今回の例では https://vpn.example.com )を設定します。
サーバーを変更後、Connectボタンをタップすると自動的にブラウザで認証画面が開きます。作成したユーザーでログインすれば、デバイスの登録と接続が完了します。
コントロールプレーン上で新しいピアが追加されたことを確認したら、そのピアをUsersグループに割り当ててください。
またExit Nodeを使用する場合はアプリ下部のタブからNetworksを開き、Exit Nodeのトグルをオンにするだけでルーティングが切り替わります。
おわりに
Tailscaleと比べると自前で設定する項目はやや多いですが、公式ドキュメントが丁寧なので、読みながら進めれば問題なく構築できると思います。
