VPSを3台以上借りてサーバーを運用していると、「全台を直接つないで内部通信したい」という場面が出てきます。WireGuardのフルメッシュ構成は、すべてのノード間に直接の暗号化トンネルを張る構成で、中継サーバーなしにどのノードからどのノードへでもプライベートIPで通信できます。
この記事では、Ubuntu 24.04 LTS の3台構成を例に、WireGuardメッシュVPNの設定ファイルを1から作る手順を解説します。インストールログと鍵生成コマンドは docker run ubuntu:24.04 で実際に実行した出力を使います。wg show や ping の出力は実際の3台接続環境がないと取得できないため、公式ドキュメントに基づく出力フォーマットの例を示します。
前提
本記事の手順は Ubuntu 24.04 LTS で確認しています。Ubuntu 22.04 でも同じ手順が使えますが、wireguard-tools のパッケージバージョンと systemd ユニットのパスが一部異なります(末尾の比較図を参照)。WireGuardのカーネルモジュールは Ubuntu 22.04 以降ではビルトインなので、DKMS は不要です。
この記事のポイント
apt install wireguard-toolsだけでwgとwg-quickが入る(DKMS 不要)wg genkey | wg pubkeyとwg genpskで鍵を生成し、ノードごとにwg0.confを作る- 3ノードのフルメッシュは「C(3,2)=3 ペア」――各ノードの設定に
[Peer]ブロックが2つ入る systemctl enable --now wg-quick@wg0で自動起動、wg showで接続状態を確認する- ノードが増えると設定量が n*(n-1)/2 で増えるため、4台以上では自動生成スクリプトが現実的
目次
- 動作確認済み環境
- WireGuardとフルメッシュトポロジー
- wireguard-toolsをインストールする
- 3ノード分の鍵を生成する
- 各ノードのwg0.confを作成する
- インターフェースを起動して接続を確認する
- systemdで自動起動を設定する
- 4台以上のメッシュを自動生成する
- よくあるエラーと解決策
- まとめ
動作確認済み環境
| 項目 | 確認値 |
|---|---|
| OS | Ubuntu 24.04 LTS (Noble Numbat) |
| wireguard-tools | 1.0.20210914-1ubuntu4 |
| カーネルモジュール | ビルトイン(DKMS 不要) |
| VPN アドレス帯 | 10.0.0.0/24(本記事の例) |
| WireGuard ポート | UDP 51820 |
| 検証方法 | docker run –rm ubuntu:24.04 で実際に実行・確認 |
WireGuardとフルメッシュトポロジー
WireGuardは Linux カーネル 5.6 からメインラインに取り込まれた VPN プロトコルです。設定ファイルの構造がシンプルで、「自分の秘密鍵」と「相手の公開鍵」を並べるだけでトンネルが張れます。
フルメッシュ(full mesh)は、すべてのノードが他のすべてのノードと直接 P2P で接続するトポロジーです。中継サーバー(ハブノード)を置かず、node1 から node3 へ通信するとき、node2 を経由しません。VPS を複数台運用する場合に多く使われます。
デメリットは設定量の増え方です。n 台のフルメッシュでは接続ペア数が n*(n-1)/2 になります。3台なら3ペアですが、5台になると10ペア、10台で45ペアと急増します。

wireguard-toolsをインストールする
Ubuntu 24.04 では wireguard-tools パッケージを apt で入れるだけです。カーネルモジュールはカーネルにビルトインなので、別途インストール不要です。
Reading package lists… Done
The following NEW packages will be installed:
wireguard-tools nftables libmnl0 libxtables12 …
Setting up wireguard-tools (1.0.20210914-1ubuntu4) …
$ wg –version
wireguard-tools v1.0.20210914 – https://git.zx2c4.com/wireguard-tools/
インストールに成功すると /usr/bin/wg(鍵管理・状態確認コマンド)と /usr/bin/wg-quick(設定ファイルからのインターフェース起動スクリプト)の2つのコマンドが入ります。また、/usr/lib/systemd/system/wg-quick@.service の systemd ユニットが同梱されており、後で自動起動を設定するときに使います。

3台すべてで同じコマンドを実行してください。
Ubuntu 22.04 の場合
wireguard-tools のパッケージバージョンは 1.0.20210914-1ubuntu2 で、systemd ユニットのパスが /lib/systemd/system/wg-quick@.service になります(Ubuntu 24.04 は /usr/lib/)。機能面の差はありません。

3ノード分の鍵を生成する
WireGuard の鍵は Curve25519 を使います。秘密鍵は wg genkey で生成し、そこから公開鍵を wg pubkey で導出します。フルメッシュでは、加えてノードペアごとに事前共有鍵(PSK)を1本作ります。PSK は必須ではありませんが、量子コンピュータに対する追加の保護層になります。
鍵はどのノードで作っても構いません。作業用のマシン1台で3ノード分をまとめて生成し、各ノードに配布するのが一般的です。
$ NODE1_PUB=$(echo “$NODE1_PRIV” | wg pubkey)
$ NODE2_PRIV=$(wg genkey)
$ NODE2_PUB=$(echo “$NODE2_PRIV” | wg pubkey)
$ NODE3_PRIV=$(wg genkey)
$ NODE3_PUB=$(echo “$NODE3_PRIV” | wg pubkey)
# ノードペアごとにPSKを生成(3ノード = C(3,2) = 3ペア)
$ PSK_12=$(wg genpsk) # node1 ↔ node2
$ PSK_13=$(wg genpsk) # node1 ↔ node3
$ PSK_23=$(wg genpsk) # node2 ↔ node3
$ echo $NODE1_PUB
oOigv2jHUXtrAeWfX8b4o0obHfiZb7/lv52H/mdQu3Q=
$ echo -n “$NODE1_PRIV” | base64 -d | wc -c
32

実測で確認した通り、wg genkey の出力は Base64 エンコードされた44文字の文字列で、デコードすると32バイト(256ビット)の Curve25519 秘密鍵になります。公開鍵も同じく44文字です。
秘密鍵の扱いに注意
生成した秘密鍵(NODE1_PRIV など)は絶対に外部に漏らさないでください。設定ファイルに書き込んだ後は chmod 600 /etc/wireguard/wg0.conf でパーミッションを制限します。公開鍵は他ノードに渡すために相手に教えて問題ありません。
各ノードのwg0.confを作成する
各ノードに /etc/wireguard/wg0.conf を作ります。設定の構造は「[Interface](自分の設定)+ [Peer](相手ノードの数だけ繰り返し)」です。
①node1 の wg0.conf
node1 は VPN アドレス 10.0.0.1/24 を持ち、node2 と node3 を [Peer] として登録します。
[Interface]
PrivateKey = <NODE1_PRIV>
Address = 10.0.0.1/24
ListenPort = 51820
[Peer]
PublicKey = <NODE2_PUB>
PresharedKey = <PSK_12>
Endpoint = <node2の実IPまたはFQDN>:51820
AllowedIPs = 10.0.0.2/32
PersistentKeepalive = 25
[Peer]
PublicKey = <NODE3_PUB>
PresharedKey = <PSK_13>
Endpoint = <node3の実IPまたはFQDN>:51820
AllowedIPs = 10.0.0.3/32
PersistentKeepalive = 25
以下は docker run ubuntu:24.04 で実際に生成した設定ファイルの例です(鍵は実際の wg genkey 出力)。

設定ファイルを作ったら、権限を変更します。
$ ls -la /etc/wireguard/
-rw——- 1 root root 627 Jun 21 15:59 wg0.conf
②node2 の wg0.conf
node2 は 10.0.0.2/24 を持ち、node1 と node3 を [Peer] に登録します。PSK に注意してください。node1 ↔ node2 のペアには PSK_12、node2 ↔ node3 のペアには PSK_23 を使います。
PrivateKey = <NODE2_PRIV>
Address = 10.0.0.2/24
ListenPort = 51820
[Peer]
PublicKey = <NODE1_PUB>
PresharedKey = <PSK_12> # ← node1↔node2 共通
Endpoint = <node1の実IP>:51820
AllowedIPs = 10.0.0.1/32
PersistentKeepalive = 25
[Peer]
PublicKey = <NODE3_PUB>
PresharedKey = <PSK_23> # ← node2↔node3 共通
Endpoint = <node3の実IP>:51820
AllowedIPs = 10.0.0.3/32
PersistentKeepalive = 25
③node3 の wg0.conf
node3 は 10.0.0.3/24 を持ち、node1 と node2 を [Peer] に登録します。PSK は PSK_13 と PSK_23 を使います。
PrivateKey = <NODE3_PRIV>
Address = 10.0.0.3/24
ListenPort = 51820
[Peer] # node1
PublicKey = <NODE1_PUB>
PresharedKey = <PSK_13>
Endpoint = <node1の実IP>:51820
AllowedIPs = 10.0.0.1/32
PersistentKeepalive = 25
[Peer] # node2
PublicKey = <NODE2_PUB>
PresharedKey = <PSK_23>
Endpoint = <node2の実IP>:51820
AllowedIPs = 10.0.0.2/32
PersistentKeepalive = 25
インターフェースを起動して接続を確認する
3台すべての設定ファイルを配置したら、各ノードで wg-quick up wg0 を実行します。
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.0.0.1/24 dev wg0
[#] ip link set mtu 1420 up dev wg0
(公式ドキュメントに基づく出力フォーマット例)
$ sudo wg show
interface: wg0
public key: oOigv2jHUXtrAeWfX8b4o0obHfiZb7/lv52H/mdQu3Q=
listening port: 51820
peer: dwyEqSDiQFrnNKJPPRen3XjuH3eCCwiH1s1pgNj5/kc=
endpoint: 192.168.1.2:51820
allowed ips: 10.0.0.2/32
latest handshake: (接続が確立すると秒数が表示される)
transfer: (受送信バイト数が表示される)
latest handshake: 〜 seconds ago が表示されれば接続が確立しています。ハンドシェイクが見えない場合はファイアウォールで UDP 51820 が開いていない可能性があります。上の端末ブロックは wg show の出力フォーマットを公式ドキュメントに基づいて示したものです。実際の transfer 値やハンドシェイク時間は環境によって異なります。

$ ping -c 3 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=X.XX ms
3 packets transmitted, 3 received, 0% packet loss
$ ping -c 3 10.0.0.3
64 bytes from 10.0.0.3: icmp_seq=1 ttl=64 time=X.XX ms
3 packets transmitted, 3 received, 0% packet loss
3台すべてから相互に ping が通れば、フルメッシュの完成です。実際のレイテンシはノード間の物理距離によって異なります(同一データセンター内なら1ms未満、異リージョン間なら数十ms程度が一般的です)。
systemdで自動起動を設定する
サーバーが再起動したときに WireGuard を自動で起動するには systemctl enable します。
Created symlink /etc/systemd/system/wg-quick.target.wants/wg-quick@wg0.service
→ /usr/lib/systemd/system/wg-quick@.service.
(シンボリックリンクの作成先は Ubuntu 24.04 での実測値)
$ sudo systemctl status wg-quick@wg0
● wg-quick@wg0.service – WireGuard via wg-quick(8) for wg0
Loaded: loaded (/usr/lib/systemd/system/wg-quick@.service; enabled)
Active: active (exited)(接続確立後の正常状態)
active (exited) は正常な状態です。wg-quick はインターフェースを設定した後に終了するタイプのサービスなので、実行中(running)ではなく exited になります。

4台以上のメッシュを自動生成する
4台以上になると手作業での設定ミスが増えます。Python スクリプトで自動生成するのが現実的です。以下は3台分を例にした最小構成のスクリプトです。
#!/usr/bin/env python3
import subprocess, itertools, os
NODES = [
{“name”: “node1”, “endpoint”: “192.168.1.1”, “vpn_ip”: “10.0.0.1”},
{“name”: “node2”, “endpoint”: “192.168.1.2”, “vpn_ip”: “10.0.0.2”},
{“name”: “node3”, “endpoint”: “192.168.1.3”, “vpn_ip”: “10.0.0.3”},
]
def wg_genkey():
priv = subprocess.check_output([“wg”, “genkey”]).decode().strip()
pub = subprocess.check_output([“wg”, “pubkey”],
input=priv.encode()).decode().strip()
return priv, pub
# 鍵を生成
for n in NODES: n[“priv”], n[“pub”] = wg_genkey()
# PSK をペアごとに生成
psks = {}
for a, b in itertools.combinations(NODES, 2):
key = (a[“name”], b[“name”])
psks[key] = subprocess.check_output([“wg”, “genpsk”]).decode().strip()
# 各ノードの設定を出力
for n in NODES:
conf = f”[Interface]\n”
conf += f”PrivateKey = {n[‘priv’]}\n”
conf += f”Address = {n[‘vpn_ip’]}/24\n”
conf += f”ListenPort = 51820\n”
for peer in NODES:
if peer[“name”] == n[“name”]: continue
key = tuple(sorted([n[“name”], peer[“name”]]))
conf += f”\n[Peer]\n”
conf += f”PublicKey = {peer[‘pub’]}\n”
conf += f”PresharedKey = {psks[key]}\n”
conf += f”Endpoint = {peer[‘endpoint’]}:51820\n”
conf += f”AllowedIPs = {peer[‘vpn_ip’]}/32\n”
conf += f”PersistentKeepalive = 25\n”
print(f”# === {n[‘name’]} ===\n{conf}”)
$ python3 gen_mesh.py | head -20
# === node1 ===
[Interface]
PrivateKey = …
Address = 10.0.0.1/24
ListenPort = 51820
…
NODES リストにエントリを追加するだけで4台、5台のメッシュにも対応できます。生成した設定ファイルを各サーバーに scp で配布したあと、sudo wg-quick up wg0 を実行します。
よくあるエラーと解決策
①RTNETLINK answers: Operation not permitted
原因:sudo なしで実行した、またはカーネルモジュールが読み込まれていない。sudo wg-quick up wg0 と先頭に sudo を付けて再実行してください。Ubuntu 22.04/24.04 ではモジュールはビルトインなので読み込み失敗は通常発生しません。
②latest handshake が表示されない(接続できない)
原因の大半は UDP 51820 ポートがファイアウォールでブロックされていることです。VPS を使っている場合、VPS のコントロールパネル側(セキュリティグループ)と OS 側(ufw)の両方を確認します。
Rule added
$ sudo ufw status
51820/udp ALLOW Anywhere
③AllowedIPs 設定で他のノードに到達できない
AllowedIPs = 10.0.0.0/24 と書いた場合、そのピアが「VPN 内の全パケットの出口」になります。フルメッシュでは複数ピアがこの設定を持つと競合します。必ず AllowedIPs = <相手のVPN IP>/32 と指定してください。
④設定変更後に反映されない
wg syncconf でダウンタイムなしに設定を更新することもできますが、まずは down → up のほうが確実です。
まとめ
手順のまとめ
- 全ノードで
apt install wireguard-toolsを実行(Ubuntu 22.04/24.04 は DKMS 不要) wg genkey | wg pubkeyで各ノードの鍵ペアを生成、wg genpskでノードペアごとの PSK を生成- 各ノードの
/etc/wireguard/wg0.confに [Interface] と [Peer] × (n-1) を書く AllowedIPsは/32で指定すること(フルメッシュでは/24にしない)wg-quick up wg0→wg showで handshake を確認 →systemctl enable --now wg-quick@wg0で自動起動- 4台以上では Python スクリプトで設定ファイルを自動生成するのが現実的
VPS を複数借りてサーバーを運用するなら、Vultr や DigitalOcean は複数リージョンに東京・シンガポール・欧米のリージョンを持ちつつ月5〜6ドルから始められます。WireGuardメッシュを組む前提なら、各ノードに独立した固定 IP があるプランが適しています。
複数 VPS の選び方については もあわせて読んでみてください。



コメント