本文へ移動
購読
GUIDE#security更新 2026.09.24

WireGuardでVPNサーバーを構築する:設定ファイルの書き方からクライアント配布・運用まで

Ubuntu 24.04にWireGuardを構築し、Windows・スマートフォンから接続するまでの手順。wg0.confの各行の意味、nftablesのNAT、MTU・DNSの切り分け、鍵の失効までを扱います。

level
中級
time
約30分
stack
WireGuard / Ubuntu 24.04 / Windows・iOS クライアント
author
@ryoma_t
// 前提条件
Linuxの基本操作とsudoが使えること / グローバルIPを持つサーバーまたはUDPポートを開放できるルーター
WireGuardサーバー側でトンネル越しのpingとwg showを実行し、ノートPCとのハンドシェイクを確認したターミナル
画像:nagoya.dev(実際の画面・実行結果をもとに作成)

WireGuardとは、Linuxカーネルに標準で組み込まれた、公開鍵ベースの軽量なVPNプロトコルとその実装です。設定に必要なのは「自分の秘密鍵」「相手の公開鍵」「相手に許可するIP範囲」の3点がほぼすべてで、OpenVPNのように証明書基盤(PKI)を組む必要がありません。

扱う構成は、Ubuntu 24.04のサーバー1台に、Windowsのノートパソコンとスマートフォン(iOS/Android)をつなぐ形です。NATはnftables、スマートフォンへの配布はQRコードを使い、スプリットトンネルの設計、MTUやDNSの切り分け、鍵の失効までを一続きの手順にしています。

この記事でわかること

  • WireGuardが暗号方式を固定し、AllowedIPsで「経路」と「許可リスト」を兼ねる仕組み(cryptokey routing)
  • wg0.confの各行の意味と、nftablesでNAT・IPフォワーディングを設定する手順
  • Windows・iOS/Androidクライアントの設定と、QRコードでの安全な配布方法
  • ハンドシェイク失敗・MTU・DNS・NATタイムアウトの原因別の切り分け方
  • 鍵の失効・ローテーション・ログの取り方と、Tailscaleとの使い分けの判断基準

WireGuardの仕組みとOpenVPNとの違い

WireGuardは暗号方式を交渉しません。鍵交換はCurve25519、暗号化と認証はChaCha20-Poly1305、ハッシュはBLAKE2s、鍵導出はHKDFと、使うアルゴリズムがプロトコル自体に固定されています。選べないのは不便に見えますが、OpenVPNやIPsecで繰り返されてきた「弱い暗号への格下げ」や「設定ミスで暗号化が実質無効になる」事故が構造的に起きない、という設計判断です。アルゴリズムに問題が見つかったときは、設定ではなくプロトコルのバージョンごと更新する方針が取られています。

通信はUDPのみで、ハンドシェイクは1往復で完了します。セッション鍵は約2分ごとに作り直され、認証できないパケットには一切応答しないため、UDP 51820にポートスキャンをかけてもWireGuardが動いていることは外から判別できません。クライアント側のIPアドレスが変わっても(Wi-Fiからモバイル回線に切り替えても)、次に届いた認証済みパケットの送信元でサーバー側のEndpointが更新されるので、セッションは切れません。これがWireGuardの「ローミング」です。

もう1つ理解しておきたいのがcryptokey routingです。各ピアの公開鍵にAllowedIPsを結びつけ、送信時は「この宛先IPへのパケットはこの鍵で暗号化してこのピアへ送る」、受信時は「このピアから復号できたパケットの送信元IPがAllowedIPsに含まれていなければ捨てる」という判断を、経路選択と認証を一体化して行います。

項目WireGuardOpenVPN
暗号スイート固定(Curve25519 / ChaCha20-Poly1305 / BLAKE2s)設定で選択(TLSベース、各種暗号)
トランスポートUDPのみUDP / TCP(TCP 443で制限の厳しい網も通せる)
実装場所Linuxカーネル内(5.6以降)。他OSは公式アプリユーザー空間のデーモン
認証公開鍵ペア(+任意の事前共有鍵)X.509証明書(PKI)+任意でユーザー認証
設定量数行〜十数行CAの運用を含めると多い
ローミング自動再接続が必要になることが多い
接続ログ設計上持たない(最終ハンドシェイク時刻のみ)詳細な接続ログあり
不得意な状況UDPが遮断される網、動的な設定配布スループット、設定の複雑さ

新規にVPNを立てるなら、UDPが通らない環境(一部のホテルWi-Fiや厳格なプロキシ配下)を除いてWireGuardを選んで困ることはほとんどありません。逆に、出先の回線がTCP 443しか通らない、ユーザー名とパスワードやRADIUS連携による認証が監査要件になっている、という条件があるならOpenVPNや商用製品の方が素直です。WireGuardは「鍵を持っている端末」を認証するものであり、「人」を認証する仕組みは持っていません。

前提とアドレス設計

この記事の想定は次の通りです。サーバーはUbuntu 24.04で、グローバルIPを持つVPS、または自宅・オフィスのルーター背後(UDP 51820をポートフォワード)に置きます。WAN側のインターフェース名は ens3 と仮定するので、実際の名前を先に確認してください。

terminal
ip route get 1.1.1.1
# 1.1.1.1 via 203.0.113.1 dev ens3 src 203.0.113.10 uid 1000
#   → "dev" の後がWAN側インターフェース名

VPN内のアドレスは 10.8.0.0/24、サーバーが 10.8.0.1、クライアントは 10.8.0.2 から順に割り当てます。接続先のLANは 192.168.10.0/24 とします。

VPN用のサブネットと接続先LANは、クライアントが普段いるネットワークと重複しないものを選んでください。家庭用ルーターの初期値である 192.168.0.0/24 や 192.168.1.0/24 を社内LANにしていると、自宅から接続した人だけ「つながるのに社内サーバーに届かない」状態になります。OS側で自宅LANのon-link経路が優先されるためで、WireGuard側では直せません。

インストールと鍵ペアの生成

Ubuntu 24.04のカーネル(6.8系)にはWireGuardモジュールが含まれているので、必要なのは wg と wg-quick を含むツール群だけです。鍵は32バイトのランダム値をBase64にした44文字の文字列で、ファイルの権限が 600 になるよう先に umask 077 を設定してから生成します。

terminal
sudo apt update
sudo apt install -y wireguard qrencode
sudo -i
cd /etc/wireguard
umask 077

# サーバーの鍵ペア
wg genkey | tee server.key | wg pubkey > server.pub

# クライアントごとの鍵ペアと事前共有鍵(PSK)
wg genkey | tee laptop.key | wg pubkey > laptop.pub
wg genpsk > laptop.psk
wg genkey | tee phone.key  | wg pubkey > phone.pub
wg genpsk > phone.psk

ls -l
# -rw------- 1 root root 45 ... server.key   ← 600 になっていること

事前共有鍵(PresharedKey)は省略可能ですが、付けない理由がありません。ハンドシェイクに対称鍵をもう1段混ぜることで、将来Curve25519が量子計算機で破られた場合でも、PSKを知らない攻撃者は過去の通信を復号できなくなります。クライアントごとに別のPSKを生成してください。

原則は「秘密鍵は使う端末で生成し、公開鍵だけをサーバーに渡す」です。上の手順はQRコード配布の都合でサーバー側でクライアント鍵を作っていますが、配布が終わったらサーバー上の laptop.key や phone.key は shred -u で消してください。サーバーに残った秘密鍵は、サーバー侵害時にそのままクライアントのなりすましに使えます。

サーバーのwg0.confを書く

wg-quick はインターフェース名と同じ名前の設定ファイルを /etc/wireguard/ から読みます。[Interface] が自分、[Peer] が接続相手で、クライアントの数だけ [Peer] を並べます。鍵はファイルパスではなく中身の文字列を書きます。

/etc/wireguard/wg0.conf
[Interface]
Address = 10.8.0.1/24
ListenPort = 51820
PrivateKey = (server.key の中身)
PostUp = nft add table ip wgnat
PostUp = nft add chain ip wgnat postrouting '{ type nat hook postrouting priority srcnat; policy accept; }'
PostUp = nft add rule ip wgnat postrouting ip saddr 10.8.0.0/24 oifname "ens3" masquerade
PostDown = nft delete table ip wgnat

# ノートPC(Windows)
[Peer]
PublicKey = (laptop.pub の中身)
PresharedKey = (laptop.psk の中身)
AllowedIPs = 10.8.0.2/32

# スマートフォン(iOS)
[Peer]
PublicKey = (phone.pub の中身)
PresharedKey = (phone.psk の中身)
AllowedIPs = 10.8.0.3/32
  • Address: サーバー自身のトンネル内アドレス。/24 で書くと、10.8.0.0/24 宛の経路が wg0 に自動で入ります。
  • ListenPort: 待ち受けるUDPポート。51820は慣例で、変えても安全性は変わりません(未認証パケットには応答しないため)。
  • PostUp / PostDown: インターフェースの起動・停止時に実行するコマンド。ここではNATテーブルの作成と削除に使います(次の章)。
  • AllowedIPs(サーバー側): そのピアに割り当てたIPを /32 で書きます。「このピアからの復号後パケットは送信元が 10.8.0.2 のときだけ受け入れる」「10.8.0.2 宛はこのピアへ送る」の両方を意味します。

サーバー側の [Peer] に AllowedIPs = 0.0.0.0/0 を書いてはいけません。クライアント用の設定をコピーしたときによく起きる事故で、サーバーの全通信(他のクライアント宛やインターネット向けの戻りパケットまで)がそのピアに向けて暗号化され、VPN全体が沈黙します。0.0.0.0/0 を書くのはクライアント側だけです。

IPフォワーディングとNAT(nftables)

クライアントからLANやインターネットへ出るには、サーバーが「自分宛でないパケットを転送する」設定と、戻りのパケットがサーバーまで帰ってくる仕組みが必要です。転送はカーネルの ip_forward、戻りはNAT(masquerade)で確保します。masqueradeは 10.8.0.x の送信元をサーバーの ens3 のアドレスに書き換えるので、LAN側の機器やインターネット上のサーバーは 10.8.0.0/24 という経路を知らなくても応答を返せます。

terminal
echo 'net.ipv4.ip_forward = 1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
sysctl net.ipv4.ip_forward
# net.ipv4.ip_forward = 1

# ufw を使っている場合はUDPポートの許可と転送の許可が別に必要
sudo ufw allow 51820/udp
sudo ufw route allow in on wg0 out on ens3

NATのルールは wg0.conf の PostUp に書いた3行が作ります。wgnat という専用テーブルに閉じ込めているので、ufwや他のnftablesルールと干渉せず、PostDown でテーブルごと消せば後始末も確実です。オフィスのLANにサーバーを置く場合は、masqueradeの代わりにルーターへ「10.8.0.0/24 はサーバーのLAN側IP経由」という静的経路を1行足す方法もあります。こちらならLAN側のログにVPNクライアントの実IP(10.8.0.2 など)が残り、ファイルサーバーのアクセス制御も端末単位でかけられるので、社内用途ではこちらを勧めます。VPSに置く場合は経路を足せる相手がいないのでmasqueradeが必須です。

起動とクライアント設定(Windows / iOS・Android)

terminal
sudo systemctl enable --now wg-quick@wg0
sudo systemctl status wg-quick@wg0 --no-pager
sudo ss -ulnp | grep 51820
# UNCONN 0 0 0.0.0.0:51820 0.0.0.0:* ...

クライアント側の設定は、サーバー側と鍵の役割が逆になります。Endpoint はサーバーのグローバルIPまたはDDNS名、AllowedIPs は「VPNに流す宛先」、PersistentKeepalive = 25 はNATの背後にいるクライアントがNATテーブルのエントリを維持するための空パケットの間隔です。多くの家庭用ルーターやモバイル回線はUDPのマッピングを30秒程度で破棄するため、それより短い25秒が公式に推奨されています。

laptop.conf(Windows用・フルトンネル)
[Interface]
PrivateKey = (laptop.key の中身)
Address = 10.8.0.2/32
DNS = 192.168.10.1
MTU = 1420

[Peer]
PublicKey = (server.pub の中身)
PresharedKey = (laptop.psk の中身)
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
phone.conf(iOS/Android用・スプリットトンネル)
[Interface]
PrivateKey = (phone.key の中身)
Address = 10.8.0.3/32
DNS = 192.168.10.1

[Peer]
PublicKey = (server.pub の中身)
PresharedKey = (phone.psk の中身)
Endpoint = vpn.example.com:51820
AllowedIPs = 10.8.0.0/24, 192.168.10.0/24
PersistentKeepalive = 25

Windowsは公式クライアントをインストールし、「トンネルの追加」から laptop.conf をインポートします。トンネル名はファイル名から取られるので、英数字とハイフンで短くしておきます。AllowedIPsに 0.0.0.0/0 と ::/0 の両方を含めると「トンネル外のトラフィックをブロック(キルスイッチ)」のチェックボックスが現れ、有効にするとVPNが落ちた瞬間に素の回線へ抜ける通信を遮断できます。公衆Wi-Fi対策でフルトンネルにするなら必ず有効にしてください。

iOS/Androidは設定ファイルをQRコードにしてカメラで読み込ませるのが最も安全で速い方法です。ターミナルに直接描画すれば、ファイルを送る必要がありません。

terminal
sudo qrencode -t ansiutf8 < /etc/wireguard/phone.conf
# 端末のWireGuardアプリ →「+」→「QRコードから作成」で読み取る

設定ファイルは秘密鍵を含む「そのまま使える認証情報」です。メールやチャットで送らない、QRコードを画面に出すのは本人の目の前だけ、読み取りが済んだらサーバー上の .conf と .key を削除する、の3点を運用ルールにしてください。

AllowedIPsとスプリットトンネルの設計

クライアント側の AllowedIPs は、wg-quickや公式アプリがそのままルーティングテーブルに追加する経路です。0.0.0.0/0 なら全通信がVPNを通り(フルトンネル)、社内LANだけを書けばそれ以外は普段の回線から出ます(スプリットトンネル)。

目的クライアント側 AllowedIPsDNS の指定向く場面・注意点
社内LANだけVPN経由(スプリット)10.8.0.0/24, 192.168.10.0/24社内DNS(例: 192.168.10.1)動画会議などの帯域をサーバーが負担しない。自宅LANと社内LANのサブネット重複に弱い
すべてVPN経由(フル)0.0.0.0/0, ::/0VPN経由で到達できるDNS公衆Wi-Fiでの盗聴・改ざん対策。サーバーの回線帯域とMTU問題の影響を受けやすい
フルトンネルから特定宛先を除外除外分を差し引いたCIDRの列挙同上WireGuardに「除外」構文はないため、計算したCIDRリストを並べる。可読性が落ちる

IPv6には注意が必要です。クライアントの回線にIPv6があり、AllowedIPsに ::/0 がないと、IPv6で到達できる宛先への通信はVPNを迂回して素の回線から出ます。サーバー側にIPv6を用意しない場合でも、フルトンネルなら ::/0 を書いてIPv6をトンネルに吸い込み、サーバーで落とす(漏らさない)方が安全です。

選び方の目安は、目的が「社内リソースへのアクセス」ならスプリット、「出先の回線を信用しない」ならフルトンネル+キルスイッチです。スプリットの方がサーバー負荷・MTU問題・DNSの取り違えがすべて軽くなるので、要件がない限りスプリットを既定にしています。

ポート開放とDDNS

自宅やオフィスのルーター背後に置く場合、必要なのは「UDP 51820 をサーバーのLAN側IPへ転送する」設定だけです。TCPは不要で、サーバーのLAN側IPはDHCPの固定割り当てにしておきます。グローバルIPが変わる回線ではDDNSサービスにホスト名を登録し、クライアントの Endpoint にそのホスト名を書きます。

ホスト名の名前解決は、トンネルを有効化した時点で1回だけ行われ、カーネルはIPアドレスしか保持しません。接続中に自宅側のグローバルIPが変わると、約3分後にハンドシェイクが失敗し始め、そのままでは復旧しません。スマートフォンではトンネルをオフ・オンすれば再解決されます。Linuxクライアントを常時接続で使うなら、wireguard-toolsのソースリポジトリに含まれる contrib/reresolve-dns スクリプトをcronで回す運用が定番です。

もう1つ、日本の回線固有の事情として、IPv4 over IPv6(MAP-E方式やDS-Lite方式)の契約ではポート開放が制限されます。MAP-Eでは使えるポート番号が割り当てられた範囲に限られ、DS-Liteではそもそも開放できません。この場合は ListenPort を割り当て範囲内の番号に変えるか、VPSに中継サーバーを置くか、NAT越えを肩代わりしてくれるTailscaleに切り替えるのが現実的です。

確認方法:接続できているかを見る

判断の起点は必ず wg show です。latest handshake が数十秒〜2分程度で更新され続けていれば、鍵とUDPの到達性は正しいと確定できます。

sudo wg show の出力例
interface: wg0
  public key: (サーバー公開鍵)
  private key: (hidden)
  listening port: 51820

peer: (ノートPCの公開鍵)
  preshared key: (hidden)
  endpoint: 203.0.113.45:52133
  allowed ips: 10.8.0.2/32
  latest handshake: 14 seconds ago
  transfer: 1.24 MiB received, 9.87 MiB sent
terminal
# クライアント(Linux/macOS)から:トンネル内、LAN、名前解決の順に確認
ping -c 3 10.8.0.1
ping -c 3 192.168.10.1
nslookup fileserver.example.local 192.168.10.1

# サーバー側:UDPパケットが届いているか(届かなければルーター/クラウドのFWの問題)
sudo tcpdump -ni ens3 udp port 51820 -c 5

# サーバー側:NATテーブルが存在するか
sudo nft list table ip wgnat

Windowsでは tracert -d 192.168.10.1 の1ホップ目が 10.8.0.1 になっていれば経路はVPNに向いています。フルトンネルの場合は、ブラウザでグローバルIPを表示するサービスを開き、サーバー側の回線のIPが出ることを確認してください。

つまずきポイント:MTU・DNS・Keepaliveの切り分け

「Handshake did not complete after 5 seconds, retrying」が続く

クライアントのログにこの行が繰り返され、サーバー側の wg show に latest handshake が出ない状態です。原因は上から順に、UDPがサーバーに届いていない(ポートフォワード漏れ、ufw、クラウドのセキュリティグループ)、鍵の貼り間違い(PublicKeyの欄に秘密鍵を貼った、サーバーとクライアントで別のPSK)、そして端末の時刻が過去に戻ったこと、の3つです。WireGuardのハンドシェイクにはリプレイ防止のタイムスタンプが含まれ、前回より古い時刻を名乗るハンドシェイクは拒否されます。まず tcpdump で到達性を切り分け、届いているなら鍵と時刻を疑ってください。

10.8.0.1 にはpingが通るが、LANにもインターネットにも出られない

トンネル自体は張れているので、転送側の問題です。sysctl net.ipv4.ip_forward が 0 なら sysctl の設定漏れ、nft list ruleset に wgnat テーブルがなければ PostUp の失敗(journalctl -u wg-quick@wg0 にエラーが出ます)、ufwを使っていて route allow を入れていない、のいずれかがほとんどです。クラウドのVMでは、インスタンス側の「送信元/宛先チェック」がNAT転送を落とす場合があるので無効化します。

SSHやpingは通るのに、Webページが途中で止まる・ファイル転送が固まる

典型的なMTUの問題です。WireGuardは1パケットあたりIPヘッダー(IPv4で20、IPv6で40バイト)+UDP 8バイト+WireGuard固有の32バイトを外側に足すため、外側の回線が1500バイトなら内側に流せる最大は IPv6経由で 1500−80=1420 バイトになります。これが既定値1420の根拠です。外側がPPPoE(1492)だったり、途中の経路でさらに小さいMTUがあり、かつ経路MTU探索(PMTUD)のICMPが遮断されていると、大きなパケットだけが黙って落ちます。小さなやり取り(SSH、ping)は通り、大きな応答(画像を含むページ)で固まるのはそのためです。クライアント側で MTU = 1380、それでも駄目なら 1280(IPv6の最小MTU)にして再現しなくなれば確定です。

terminal
# 断片化禁止で通るサイズを探る(Linuxクライアント)。1372 + 28 = 1400 バイトのパケット
ping -M do -s 1372 -c 3 10.8.0.1
# "message too long" や無応答なら、そのサイズは通らない

接続はできるのに、社内サーバーの名前が引けない

DNS = の指定先がVPN経由で到達できるアドレスか(スプリットトンネルなら AllowedIPs に含まれているか)を確認します。Linuxクライアントで /usr/bin/wg-quick: line NN: resolvconf: command not found と出る場合、wg-quickはDNS設定を resolvconf コマンド経由で行うため、openresolv を入れるか、systemd-resolved環境で resolvectl への互換リンクがあるかを確認してください。Windowsでは、社内ドメインの名前だけをVPNのDNSに向けたい場合でも、公式クライアントは指定したDNSをアダプター全体に適用するので、社内DNSが外部の名前も再帰解決できる構成にしておく必要があります。

数十秒放置すると切れる、サーバー側から端末に届かない

クライアント側の PersistentKeepalive = 25 が抜けています。NATの背後にいる側だけが設定すればよく、サーバー側の [Peer] には不要です。モバイルでバッテリーが気になるなら、iOSの「オンデマンド有効化」で必要なときだけ張る運用にします。

「RTNETLINK answers: Operation not supported」「Line unrecognized」

wg-quick up で RTNETLINK answers: Operation not supported が出るのはカーネルにWireGuardモジュールがないときで、古いカーネルのVPSや、ホスト側でモジュールを読み込んでいないLXCコンテナで起きます。Proxmox上のLXCなら、ホストで modprobe wireguard を実行すれば非特権コンテナでも動きます。Line unrecognized: 'Address=...' は、wg setconf にwg-quick専用のキー(Address、DNS、PostUp)を含む設定を渡したときのエラーで、wg-quick を使うか wg-quick strip で除去した設定を渡します。Key is not the correct length or format は鍵の貼り付け時に末尾の改行や空白が混ざったか、ファイルパスを貼ったときに出ます。

運用:鍵ローテーション・失効・ログ

端末の紛失や退職で接続を止めるときは、サーバーの設定ファイルから該当の [Peer] を消したうえで、稼働中のインターフェースにも即時反映させます。wg syncconf を使えば、他のクライアントを切断せずに差分だけ適用できます。

terminal
# 即時に失効(再起動すると wg0.conf の内容に戻るので、conf からも削除しておく)
sudo wg set wg0 peer (失効させる公開鍵) remove

# wg0.conf を編集した後、接続を切らずに反映
sudo wg syncconf wg0 <(sudo wg-quick strip wg0)

# ピアごとの最終ハンドシェイク(UNIX時刻)を記録する。cronで毎分回せば簡易的な接続ログになる
sudo wg show wg0 latest-handshakes

ローテーションは「新しい鍵ペアで [Peer] を追加→配布→ハンドシェイクを確認→古い [Peer] を削除」の順で行えば、切り替え中も接続が途切れません。頻度は年1回程度と人の異動時を目安にし、PSKだけの更新なら鍵ペアより手間が小さいので半年ごとにしても負担になりません。

WireGuardは接続ログを持ちません。誰がいつ接続したかを残したいなら、上の latest-handshakes を定期的に記録するか、wg0 を通る通信をnftablesのログルールやフローログで取ります。カーネルのデバッグ出力(echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control で有効化、-p で無効化)はハンドシェイク失敗の原因を追うときに役立ちますが、常時オンにするものではありません。

アクセス制御は「鍵とIPが1対1で固定されている」性質を利用します。サーバー側で 10.8.0.3/32 をスマートフォンの鍵に結びつけてあるので、転送ルールで 10.8.0.3 からの通信先を制限すれば、それがそのまま端末単位のアクセス制御になります。たとえばufwなら sudo ufw route allow in on wg0 from 10.8.0.3 to 192.168.10.20 port 443 proto tcp のように書け、他の宛先への転送はデフォルトのDROPで落ちます。VPNサーバー自体のOSはLinuxサーバーの基本ハードニングに沿って固めておいてください。インターネットに直接さらす機器なので、SSHの鍵認証と自動更新は最低条件です。

Tailscaleとの使い分け

TailscaleはWireGuardをデータプレーンに使い、鍵配布・NAT越え・ACL・SSO連携をコーディネーションサーバー側で肩代わりするサービスです。選択の軸は「鍵配布と経路設定を自分でやるか、任せるか」です。

観点WireGuard(自前)Tailscale
鍵の配布・失効設定ファイルを手動で配布し、Peerを編集アカウントにログインした端末に自動配布、管理画面で失効
NAT越え・ポート開放サーバー側にUDPの開放が必須。MAP-E/DS-Liteでは制約あり不要(STUN/リレーで越える)
アクセス制御AllowedIPs+ファイアウォールでIP単位ユーザー・グループ・タグ単位のACL
外部依存なし(自分のサーバーだけ)コーディネーションサーバー(鍵配布と接続情報)
拠点間接続(Site-to-Site)得意。固定IP同士なら最小構成Subnet Routerで可能
向くケース固定サーバー1台、少数の端末、外部サービスに依存したくない、通信経路を完全に把握したい端末が多い、ポート開放できない、SSOと連動した人単位の制御がほしい

判断基準は端末数と管理の主体です。端末が10台以内で、サーバーが固定IPを持ち、管理者が一人で鍵を把握できるなら、この記事の構成で十分ですし、依存先がない分だけ障害の切り分けが楽です。端末が増えて退職や機種変更のたびにPeerを編集するのが負担になった時点、あるいはSSOのグループでアクセス先を決めたくなった時点が、Tailscaleへ移る合図です。ゼロトラストの考え方で言えば、後者は「人と端末の状態でアクセスを決める」方向への一歩でもあります。

よくある質問

WireGuardは安全ですか?

プロトコルは固定された現代的な暗号方式で構成され、ハンドシェイクは形式検証の対象にもなっています。実運用で問題になるのはプロトコルではなく鍵の扱いです。設定ファイルが漏れればそのまま接続でき、二要素認証の仕組みも持たないので、配布経路と端末の保護、失効の手順まで含めて初めて「安全」と言えます。

OpenVPNから乗り換えるべき?

安定して動いているものを急いで替える必要はありません。乗り換えの動機になるのは、スループットの不足、モバイル端末での再接続の多さ、証明書更新の運用負荷の3つです。逆にTCP 443で通す必要がある、ユーザー名とパスワードやRADIUS連携の認証が要件、という場合はOpenVPNを残す方が合理的です。両方を並行稼働させて段階的に移すこともできます。

NAT越えはできますか?

クライアントがNATの背後にいる分には問題ありません。クライアントから接続を始め、PersistentKeepalive でマッピングを維持します。両側がNATの背後でどちらもポートを開けられない場合は、WireGuard単体では接続できません。VPSに中継用のサーバーを置くか、NAT越えを仲介するTailscaleのようなサービスを使います。

まとめ

WireGuardの設定は、鍵ペアとAllowedIPsの意味さえ押さえれば十数行で完結します。サーバー側の [Peer] は必ず /32、クライアント側の AllowedIPs は用途に応じてスプリットかフルかを選び、NAT背後の端末には PersistentKeepalive = 25 を入れる。この3点でほとんどの構成は動きます。動かないときは wg show の latest handshake を起点に、到達性→鍵→転送→MTU→DNSの順で切り分けてください。設定ファイルは認証情報そのものなので、配布と失効の手順を決めてから運用に入ることが、後々の事故を防ぐ最短経路です。

次に読む

$ subscribe --weekly
毎週月曜、企業ITとホームラボの要点を5分で。