ホームラボ WireGuard VPN は何をしますか?
WireGuardは、高速で現代的なVPNプロトコルです。ホームネットワークへのリモートアクセスに最適な選択肢であり、OpenVPNよりも設定が簡単で、ほとんどの代替手段よりも高速です。
すべての設定例は一般的な構成を示しています。各コマンド(特にiptables転送ルールと鍵ファイルのパーミッション)をシステムに適用する前に確認し、メンテナンス時間帯に変更を行ってください。
使用するタイミング
- Raspberry Pi、Linuxホスト、pfSense、またはルーター上でWireGuardサーバーをセットアップする場合
- WireGuardの鍵ペアを生成し、ピア設定ファイルを作成する場合
- スマートフォンやラップトップからホームネットワークにリモートアクセスできるように設定する場合
- スプリットトンネリング(ホームトラフィックのみルーティング)とフルトンネル(全トラフィックをルーティング)の違いを説明する場合
- WireGuardの接続が確立しない問題をトラブルシューティングする場合
- 複数クライアント向けにピア設定の生成を自動化する場合
WireGuardの仕組み
スマートフォン (WireGuard クライアント)
│
│ 暗号化UDPトンネル (ポート51820)
│
ホームルーター (WireGuard サーバー — パブリックIPまたはDDNSが必要)
│
ホームネットワーク (192.168.1.0/24, NAS, Pi など)
すべてのデバイスは鍵ペア(公開鍵+秘密鍵)を持ちます。
サーバーは各クライアントの公開鍵を把握します。
クライアントはサーバーの公開鍵とエンドポイント(IP:ポート)を把握します。
トラフィックは、中央サーバーや認証局なしでエンドツーエンドで暗号化されます。
サーバー設定 (Linux)
# WireGuardのインストール
sudo apt update && sudo apt install wireguard -y
# サーバー鍵ペアの生成 — 最初から秘密パーミッションでファイルを作成
sudo mkdir -p /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server_private.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server_private.key > /etc/wireguard/server_public.key'
# サーバー設定の書き出し — 実際の秘密鍵の値に置き換えてください
# 秘密鍵をバージョン管理や共有してはいけません
sudo tee /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.8.0.1/24 # VPNサブネット — サーバーは .1 を取得
ListenPort = 51820
PrivateKey = <paste_server_private_key_here>
# スコープ制限された転送ルール:VPNトラフィックの出入力を許可し、包括的なFORWARD ACCEPTは行わない
PostUp = iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT
PostUp = iptables -A FORWARD -i eth0 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -o eth0 -j ACCEPT
PostDown = iptables -D FORWARD -i eth0 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# スマートフォン — 実際のスマートフォンの公開鍵に置き換えてください
PublicKey = <phone_public_key>
AllowedIPs = 10.8.0.2/32
[Peer]
# ラップトップ — 実際のラップトップの公開鍵に置き換えてください
PublicKey = <laptop_public_key>
AllowedIPs = 10.8.0.3/32
EOF
sudo chmod 600 /etc/wireguard/wg0.conf
# eth0 を実際のアウトバウンドインターフェース名に置き換えてください
# 確認コマンド: ip route show default
# IP転送の有効化(サーバーを介したトラフィックルーティングに必要)
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
# WireGuardの起動と自動起動の有効化
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0
クライアント設定
# クライアントデバイスごとに一意の鍵ペアを生成
# クライアント上で実行するか、サーバー上で生成して秘密鍵を安全に転送 — 平文では絶対に転送しない
umask 077
wg genkey | tee phone_private.key | wg pubkey > phone_public.key
# クライアント設定ファイル (phone_wg0.conf):
[Interface]
PrivateKey = <phone_private_key>
Address = 10.8.0.2/32
DNS = 192.168.1.2 # オプション: トンネル経由でPi-holeをDNSとして使用
[Peer]
PublicKey = <server_public_key>
Endpoint = your-home-ip.ddns.net:51820 # パブリックIPまたはDDNSホスト名
AllowedIPs = 192.168.1.0/24 # スプリットトンネル: ホームネットワークトラフィックのみ
# AllowedIPs = 0.0.0.0/0, ::/0 # フルトンネル: すべてのトラフィックをVPN経由
PersistentKeepalive = 25 # NATホールを維持(モバイルクライアントに必須)
スプリットトンネル vs フルトンネル
# スプリットトンネル: AllowedIPs = 192.168.1.0/24
ホームネットワーク宛てのトラフィックのみがVPNを通過します。
YouTubeやSpotifyなどのインターネットトラフィックは直接送信されます — モバイルでのパフォーマンスが向上します。
最適な用途: 「NASとPiにどこからでもアクセスしたいだけ」
# フルトンネル: AllowedIPs = 0.0.0.0/0, ::/0
すべてのトラフィックがホームインターネット接続を経由します。
用途: ホームDNS/Pi-hole広告ブロックの利用。
欠点: ホームのアップロード速度がボトルネックになり、どこでも遅くなります。
# マルチサブネットスプリットトンネル(最も一般的なホームラボのユースケース):
AllowedIPs = 192.168.10.0/24, 192.168.20.0/24, 192.168.30.0/24, 10.8.0.0/24
すべてのVLANトラフィックをトンネル経由でルーティングし、インターネットは直接接続します。
鍵生成とピア管理
import subprocess
def generate_keypair() -> tuple[str, str]:
"""WireGuard鍵ペアを生成します。 (秘密鍵, 公開鍵) を返します。"""
private = subprocess.check_output(["wg", "genkey"]).decode().strip()
public = subprocess.run(
["wg", "pubkey"], input=private.encode(), capture_output=True
).stdout.decode().strip()
return private, public
def generate_preshared_key() -> str:
return subprocess.check_output(["wg", "genpsk"]).decode().strip()
def build_client_config(
client_private_key: str,
client_vpn_ip: str, # 例: "10.8.0.3"
server_public_key: str,
server_endpoint: str, # 例: "home.example.com:51820"
allowed_ips: str = "192.168.1.0/24",
dns: str = "",
) -> str:
dns_line = f"DNS = {dns}\n" if dns else ""
return f"""[Interface]
PrivateKey = {client_private_key}
Address = {client_vpn_ip}/32
{dns_line}
[Peer]
PublicKey = {server_public_key}
Endpoint = {server_endpoint}
AllowedIPs = {allowed_ips}
PersistentKeepalive = 25
"""
def build_server_peer_block(
client_public_key: str,
client_vpn_ip: str,
comment: str = "",
) -> str:
comment_line = f"# {comment}\n" if comment else ""
return f"""
{comment_line}[Peer]
PublicKey = {client_public_key}
AllowedIPs = {client_vpn_ip}/32
"""
秘密鍵はソース管理外に保管してください。このスクリプトを使用する場合は、鍵素材をモード600のファイルに書き込み、決してログ出力や表示をしないでください。
pfSense / OPNsense WireGuard
# pfSense: VPN → WireGuard → トンネル追加
インターフェースキー: 生成(鍵ペアを自動生成)
リッスンポート: 51820
インターフェースアドレス: 10.8.0.1/24
# ピア追加(クライアントごとに1つ):
公開鍵: <クライアントの公開鍵>
許可IP: 10.8.0.2/32
# WireGuardインターフェースの割り当て:
インターフェース → 割り当て → 追加 (wg0を選択)
インターフェースを有効化、IPは不要(トンネル設定で設定済み)
# ファイアウォールルール:
WAN → UDPポート51820のインバウンドを許可(クライアントがサーバーに到達できるように)
WireGuardインターフェース → アクセス可能にしたいLANネットワークへのトラフィックを許可
DDNS(ダイナミックDNS)のホームサーバー設定
ほとんどのホームインターネット接続は動的IPです。DDNSを使用して、IP変更後もVPNエンドポイントに到達可能にします。
# オプション1: Cloudflare DDNS — クレデンシャルはシークレットファイルに保存し、インラインには記述しない
# docker-composeのエントリ(envファイル使用):
ddns-updater:
image: qmcgaw/ddns-updater
env_file: ./ddns.env # zone_id と token はここに保存し、composeファイルには記述しない
restart: unless-stopped
# ddns.env (chmod 600、gitにはコミットしない):
# SETTINGS_CLOUDFLARE_ZONE_ID=your_zone_id
# SETTINGS_CLOUDFLARE_TOKEN=your_api_token
# オプション2: DuckDNS(無料、シンプル)
duckdns.org にサインアップ → トークンとサブドメインを取得 (myhome.duckdns.org)
トークンを /etc/ddns.env (モード600) に保存し、root所有の小さなスクリプトを使用:
# /usr/local/bin/update-duckdns
#!/bin/sh
set -eu
. /etc/ddns.env
curl --fail --silent --show-error --max-time 10 \
--get "https://www.duckdns.org/update" \
--data-urlencode "domains=myhome" \
--data-urlencode "token=${DUCKDNS_TOKEN}" \
--data-urlencode "ip="
# cronジョブ:
*/5 * * * * /usr/local/bin/update-duckdns >/dev/null 2>&1
トラブルシューティング
# WireGuardのステータスと最終ハンドシェイクを確認
sudo wg show
# 「latest handshake」が表示されない、または非常に古い場合、トンネルは接続されていません。
# 確認事項:
# 1. ルーター/ファイアウォールでUDPポート51820が開放されていますか?
sudo ufw status # またはpfSense/UniFiのファイアウォールルールを確認
# 2. クライアント設定のサーバー公開鍵は正しいですか?
sudo wg show wg0 public-key # クライアント設定のものと比較
# 3. サーバーでIP転送が有効になっていますか?
cat /proc/sys/net/ipv4/ip_forward # 1 であるべき
# 4. クライアントのAllowedIPs は、到達しようとしているIPをカバーしていますか?
# AllowedIPs = 192.168.1.0/24 で 192.168.3.5 に到達しようとしている場合、ルーティングされません。
# カーネルログでWireGuardエラーを確認
dmesg | grep wireguard
# WireGuardの再起動
sudo wg-quick down wg0 && sudo wg-quick up wg0
アンチパターン
# 悪い例: 秘密鍵をバージョン管理に保存したり、共有したりする
# 秘密鍵はパスワードと同等です — gitにコミットしてはいけません
# 悪い例: モバイルで AllowedIPs = 0.0.0.0/0 を使用し、影響を考慮しない
# フルトンネルはすべてのモバイルトラフィックをホームのアップロード経由でルーティングします — 通常は遅い
# 悪い例: モバイルクライアントでPersistentKeepaliveを設定しない
# NATの背後にあるモバイルクライアントは、これがないとアイドルトンネルが切断されます
# 悪い例: ファイアウォールでポート51820を開放したが、サーバーでIP転送を忘れる
# トンネルは接続されるがトラフィックはルーティングされない — デバッグが混乱します
# 悪い例: 複数のクライアントデバイスで鍵ペアを共有する
# 各デバイスは独自の一意の鍵ペアを持つ必要があります — 共有鍵はセキュリティモデルを破壊します
# 悪い例: 包括的な「FORWARD ACCEPT」iptablesルールを使用する
# 転送ルールはwg0インターフェースと方向にのみスコープ制限する
ベストプラクティス
- クライアントデバイスごとに一意の鍵ペアを生成 — 鍵を再利用しない
- モバイルではスプリットトンネリング(
AllowedIPs = <ホームサブネット>)を使用 - すべてのモバイルクライアントで
PersistentKeepalive = 25を設定 - ISPが動的IPを割り当てる場合はDDNSを使用し、クレデンシャルはインラインではなく env ファイルに保存
- 包括的なFORWARD ACCEPTではなく、スコープ制限されたiptables転送ルール(wg0のインバウンドのみ)を使用
- クライアント設定にPi-holeのIPを
DNS =として追加し、VPN経由で広告ブロックを取得 - サーバー鍵ペアを定期的にローテーションし、すべてのクライアント設定を更新
関連スキル
- homelab-network-setup
- homelab-vlan-segmentation
- homelab-pihole-dns