Linux上でZTNETを用いてZeroTierネットワークコントローラーをセルフホスト:プライベートP2P SD-WANを構築する

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

背景と導入の理由

異なるクラウドプロバイダーに分散したベアメタルサーバー、自宅ラボ(ホームラボ)、エッジノードを相互接続する場合、多くのエンジニアはWireGuardやOpenVPNの採用を検討します。特にWireGuardは圧倒的な通信速度を誇ります。

しかし、動的に変化する40〜50台のエンドポイント間でPoint-to-PointのWireGuardキーやハンドシェイクを管理し続けるのは、運用上すぐに悪夢へと変わります。ZeroTierは分散型仮想スイッチとして機能することで、この課題を解決します。ただし、公式ホスト版のZeroTier Central無料プランではノード数が最大25台に制限されており、ネットワークの調停メタデータもサードパーティのインフラを経由します。

独自のコントローラーをセルフホストすれば、これら両方の制約を排除できます。ネットワークのルートキーを完全に自社管理し、人為的なメンバー数上限を回避して、テレメトリの送信もゼロに抑えられます。内部的には、ZeroTierはレイヤー2(L2)で動作します。各ノードはあたかも同一ラック内のスイッチに直接有線接続されているかのように通信します。コントローラーが初期ハンドシェイクを仲介した後は、デバイス同士がコントローラーを完全にバイパスし、UDPホールパンチングによって暗号化トラフィックをダイレクトにPeer-to-Peer(P2P)転送します。

素のZeroTierコントローラーインスタンスは、低レベルなJSON APIとCLIバイナリしか提供していません。そのため、新しいエンドポイントの承認や手動でのルーティング設定はすぐに煩雑になります。ZTNETはこの課題をスマートに解消します。軽量なNode.jsとSQLiteスタックでZeroTierコントローラーをラップしており、月額4ドル程度の最小構成VPSでもRAM消費量は60MB未満に収まります。直感的なWebダッシュボードを通じて、メンバーの承認、CIDRブロックの割り当て、アクティブなトンネルの監視を簡単に行えます。

この構成は、マルチクラウド環境のKubernetesクラスター、アウトオブバンドのIPMI踏み台サーバー、またはキャリアグレードNAT(CGNAT)をまたぐハイブリッドな開発環境を統合する際に真価を発揮します。

インストール

Docker Composeを使用してZTNETをデプロイすることで、インストールのモジュール性を保ち、ベースとなるLinuxディストリビューションから依存関係を隔離できます。

ステップ1: Dockerと前提パッケージのインストール

DebianまたはUbuntuでローカルリポジトリのインデックスを更新し、DockerエンジンとComposeプラグインを導入します:

sudo apt update && sudo apt upgrade -y
sudo apt install -y curl ca-certificates gnupg lsb-release

# Docker公式リポジトリを設定
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin

sudo usermod -aG docker $USER

ステップ2: ディレクトリ構成の準備とComposeファイルの作成

ホスト上に、コントローラーのIDキーおよびSQLiteデータベースを永続化するための専用ディレクトリを作成します:

mkdir -p ~/ztnet/zt-data ~/ztnet/ztnet-data && cd ~/ztnet

Composeマニフェストファイルを作成します:

nano docker-compose.yml

以下のサービス設定を記述します。コンテナに仮想ネットワークtunインターフェースのプロビジョニング権限を付与するcap_addブロックには特に注意してください:

services:
  ztnet:
    image: ztnet/ztnet:latest
    container_name: ztnet-controller
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - SYS_ADMIN
    devices:
      - /dev/net/tun
    ports:
      - "3000:3000"       # ZTNET Web GUI
      - "9993:9993/udp"   # ZeroTierコントローラー通信
    environment:
      - PORT=3000
      - ZT_ADDR=127.0.0.1:9993
    volumes:
      - ./zt-data:/var/lib/zerotier-one
      - ./ztnet-data:/app/backend/data

ステップ3: スタックの起動

バックグラウンドでコンテナを立ち上げます:

docker compose up -d

起動ログを確認し、ZeroTierデーモンが公開鍵/秘密鍵ペアを正常に初期化したか検証します:

docker compose ps
docker compose logs -f

設定手順

ステップ1: 管理者アカウントの設定

ブラウザでhttp://<サーバーのIPアドレス>:3000にアクセスします。初回起動時、ZTNETは管理者認証情報を設定するウィザードを表示します。強固なパスワードを設定し、ダッシュボードにログインしてください。

ステップ2: 最初の仮想ネットワークを作成

管理画面から直接オーバーレイメッシュネットワークを定義します:

  1. Create Networkをクリックします。
  2. ZTNETによって16桁の16進数Network ID(例: 8e02d60bc4a19f02)が発行されます。クライアント設定で使用するため、この文字列を控えておきます。
  3. IPv4プライベートサブネット(例: 10.147.17.0/24)を選択し、自動割り当て範囲(例: 10.147.17.10〜10.147.17.250)を定義します。
  4. ネットワークアクセスルールはPrivateのままにしておきます。これにより、未承認ノードがオーバーレイネットワーク内にパケットを送信するのを防ぎます。

ステップ3: クライアントノードをセルフホストネットワークに参加させる

リモートのLinuxクライアント(遠隔地のバックアップワーカーや自宅のワークステーションなど)にSSH接続し、公式クライアントエージェントをインストールします:

curl -s https://install.zerotier.com | sudo bash

ローカルデーモンに指示を出し、コントローラーに対してネットワークへの参加リクエストを送信します:

sudo zerotier-one -q join 8e02d60bc4a19f02

ターミナルに200 join OKと表示されます。これでクライアントは登録されましたが、コントローラーで承認されるまではトラフィックを送受信できません。

ステップ4: ZTNET上でノードを承認する

ZTNETのダッシュボードをリロードし、対象ネットワークの詳細画面を開きます。Membersテーブルに、10文字のNode IDを持つ未承認クライアントが表示されます。

  • Authorizedスイッチをオンにします。
  • 判別しやすいホスト名タグ(例: db-replica-frankfurt)を付与します。
  • 指定したIPプールから自動的にIPアドレス(例: 10.147.17.10)がリースされます。該当ノードが公開サービスをホストしている場合は、固定IPを割り当てることも可能です。

疎通確認とモニタリング

クライアント側でのインターフェースとルーティングの確認

リモートクライアントのターミナルに戻ります。カーネルが新しい仮想tapインターフェース(通常はzt*という名称)を初期化し、リースされたIPアドレスを取得していることを確認します:

ip addr show dev $(ip -br link | awk '$1 ~ /^zt/ {print $1}')

カーネルのルーティングテーブルを確認し、10.147.17.0/24宛てのトラフィックがZeroTierインターフェース経由でルーティングされているか検証します:

ip route | grep zt

ピア間通信のテスト

2台目のリモートマシンを同様に参加させてZTNET側で承認し、割り当てられた内部IP(例: 10.147.17.11)を確認します。1台目のマシンからICMP pingを送信します:

ping -c 4 10.147.17.11

通常の往復レイテンシが確認できるはずです。近隣リージョン間のダイレクトピア接続であれば、ベアメタルの生ping速度とほぼ同等(差分1〜3ms以内)で通信できます。

ピアリンクの状態とトポロジの確認

ZeroTierランタイムエンジンに問い合わせて、ノード同士が直接通信しているか、あるいはリレールートサーバーにフォールバックしているかを確認します:

sudo zerotier-one -q peers

コマンドの出力結果には、各ピアの情報とともにレイテンシメトリクス、リンク種別、アクティブな通信パスが表示されます:

<ztaddr>   <ver>  <role> <lat> <link>   <lastTX> <lastRX> <path>
8e02d60bc4 1.14.0 LEAF      14 DIRECT   4        4        198.51.100.42/9993
3a88b201f9 1.14.0 LEAF      38 DIRECT   2        2        203.0.113.15/41882

リンクステータスがDIRECTになっていれば、パケットが外部リレーを経由せず、暗号化されたUDPトンネルを通ってPoint-to-Pointで直接届いている証拠です。もしRELAYEDと表示される場合は、ローカルファイアウォールやNATゲートウェイがポート9993のアウトバウンドUDP通信をブロックしていないか確認してください。

コントローラーのバックアップ運用

ネットワークコントローラーのID情報は~/ztnet/zt-dataディレクトリ内に保存されています。これらのIDファイルを紛失するとネットワークIDが完全に失われ、すべてのエンドポイントで手動再参加が必要になってしまいます。このディレクトリは定期的にバックアップしてください:

sudo tar -czvf ztnet-backup-$(date +%F).tar.gz -C ~/ztnet zt-data ztnet-data

作成したアーカイブは、S3やオフサイトの暗号化リポジトリへ転送しておきます。このバックアップがあれば、新しいホスト上でも5分未満でマルチノードSD-WAN環境全体を復旧できます。

Share: