Cover Image for Proxmox + LXC + netbirdでかんたんリモートアクセスVPNを作ろう

Proxmox + LXC + netbirdでかんたんリモートアクセスVPNを作ろう

はじめに

いやあ、tailscaleくんがごたごたしてますね。
恥ずかしながら、お行儀の悪さを今まで知りませんでした。
なので、自宅で使っているtailscale構成をすべてNetbirdで置き換えようと思います。

tailscale

Tailscaleやめたい - まいの雑記帳
Tailscaleはネットワークの知識がない人が使うものだと思っていて、内心ずっと冷笑しているのですが、悪い噂をあまり聞かないので本当に悲しいです。用途に応じて使い分けろという話なんだろうとは思います。思いますが、他人の環境にアクセスしたいときに「まずTailscaleを入れて〜」みたいなことを言わ
Tailscaleやめたい - まいの雑記帳 favicon https://mq1.dev/entry/j7zvrsp48lb
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をインストールしておいてください。

Self-Hosting Quickstart Guide (5 min) - NetBird Docs
Self-Hosting Quickstart Guide (5 min) - NetBird Docs favicon https://docs.netbird.io/selfhosted/selfhosted-quickstart

セットアップスクリプトの実行

公式が提供しているインストールスクリプトを利用します。

# 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リバースプロキシの設定に組み込みます。

nginx-netbird.conf
# 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グループを作成します。

image.png

Usersグループの作成

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

image.png

image.png

セットアップキーの取得

グループの作成が完了したらRouting Peersグループの詳細画面を開き、Setup KeysからRouting Peer登録用のキーを作成します。

  • Make this key reusable: オン
  • Usage limit: 追加するピアの数(今回は3回まで使えるキー)
  • Auto-assigned groups: Routing Peers

image.png

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

image.png

作成後、Install Netbirdをクリックして次に進みます。

Netbirdクライアントのインストール

インストール方法が画面に表示されるので、その手順に従って各ピアマシンでコマンドを実行します。

image.png

curl -fsSL https://pkgs.netbird.io/install.sh | sh
netbird up --management-url https://vpn.example.com --setup-key <作成したセットアップキー>

用意した3台のピアマシンのそれぞれで上記のコマンドを実行するだけでピアの登録は完了です。

ホームネットワークの設定

ネットワークの追加

Routingピアの追加が完了したら、自宅ネットワークへのアクセス設定を行います。

左メニューのNetworksタブを開き、Add Networkから新しいネットワークを追加します。
image.png

ネットワークを作成するとResourceを追加するか尋ねられるので、Add Resourceをクリックします。

  • Name: 分かりやすい名前(例: Home Network)
  • Address: 自宅のサブネット(例: 10.0.0.0/16)

Note

image.png

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

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

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

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

image.png

image.png

Exit Nodeの追加

Network Routes画面からAdd Exit Nodeをクリックし設定画面を開きます。

  • Peer Group: Routing Peers
  • Distribution Groups: Users
    任意の名前をつけて保存します。

作成後、Exit Nodeのタブを開き、Auto Applyがオフになっていることを確認してください。
もしオンになっていた場合はオフに変更しておきます。
image.png

DNS設定

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

image.png

設定画面でDNSサーバーのIPアドレスを入力します。
このときMatch Domainsを空にしておくと、すべてのドメインのクエリが指定した自宅のDNSサーバーへ転送されるようになります。

これでサーバー側の基本的なセットアップは完了です。

クライアントの追加

例としてAndroid版クライアントからの接続方法を書きます。
アプリ左側のメニューからChange Serverを選択し、構築したコントロールプレーンのURL(今回の例では https://vpn.example.com )を設定します。
サーバーを変更後、Connectボタンをタップすると自動的にブラウザで認証画面が開きます。作成したユーザーでログインすれば、デバイスの登録と接続が完了します。

コントロールプレーン上で新しいピアが追加されたことを確認したら、そのピアをUsersグループに割り当ててください。

またExit Nodeを使用する場合はアプリ下部のタブからNetworksを開き、Exit Nodeのトグルをオンにするだけでルーティングが切り替わります。

おわりに

Tailscaleと比べると自前で設定する項目はやや多いですが、公式ドキュメントが丁寧なので、読みながら進めれば問題なく構築できると思います。

Self-Hosting Quickstart Guide (5 min) - NetBird Docs
Self-Hosting Quickstart Guide (5 min) - NetBird Docs favicon https://docs.netbird.io/selfhosted/selfhosted-quickstart