WireGuradでVPSと自宅サーバーを拠点間VPN接続し外部から自宅リソースにアクセスする

  • インターネットから自宅のリソースにアクセスしたい
  • 賃貸の無料ネットを使用しているのでポート開放ができない
  • 個人的な通信は自前のインフラだけで完結させたい(Cloudflare Tunnelとか使いたくない)
  • 自宅サーバーとVPSと独自ドメインならある

ということで、インターネットから自宅のリソースにアクセスするための環境を構築してみましょう!

今回はhttps://example.comというドメインにアクセスしたら、自宅LANの192.168.1.253:8123で動いているHome Assistantに繋がるようにするという体で進めていきます。

なお、IPv6ベースの通信ももちろん可能ですが、記事で扱わなければならないことが増えるため今回はIPv4のみを考慮した設定にしています。

IPv6でも通信したい方はこの記事の設定ファイルをLLMとかに食わせれば教えてくれると思うのでやってみてくださいね。

技術スタック

WireGuardを使用します。

frp(Fast Reverse Proxy)でも同様のことを実現できるようですが、セキュリティのレベルがWireGuardの方が高そうだったので今回は選びませんでした🤔

WireGuardはL3で動作するVPNプロトコル、frpはリバースプロキシとしてHTTP/HTTPS(L7)を扱えるほか、TCP/UDP(L4)の転送もサポートしているアプリケーションと、それぞれ動作レイヤーが異なります。

登場人物の整理

No名前IP (グローバル・プライベート併記)内容
1エンドデバイス動的グローバルIPキャリアの回線に繋がっているスマホなど。このデバイスから自宅のHome Assistantにアクセスしたい。
2VPS固定グローバルIP (WireGuard上では10.0.0.1)インターネットから自宅への通信の受け口。WireGuardのサーバープロセスが動いている。
3自宅サーバー192.168.1.254 (WireGuard上では10.0.0.2)WireGuardのクライアントプロセスが動いている
4Home Assistantサーバー192.168.1.253Home Assistantが実際に動いているサーバー

自宅サーバーとHome Assistantサーバーをあえて別のアドレスとして扱っていますが、同一システム上で(同一IPとして)動かすことももちろん可能です👌

DNSの設定を済ませておく

10年ぐらい前に比べたら最近はDNSの設定が一瞬で反映されるような気がするんですけど気のせいでしょうか? (まあ実際は使用しているネームサーバーに浸透速度が依存するのでしょうが)

とはいえDNSは先に設定しておくことに越したことはないので、使用するドメインとVPSのグローバルIPを紐づけておきましょう。

WireGuardの環境構築

まずはVPSと自宅サーバーをWireGuardで繋ぐ準備をしていきます。

WireGuradのインストール

(VPSと自宅サーバー両方で行う)

$ sudo apt update
$ sudo apt install wireguard

IPフォワーディングの有効化

(VPSと自宅サーバー両方で行う)

$ sudo nano /etc/sysctl.conf

設定ファイルを開き、以下の行をコメントアウト or 無ければ追記する。

net.ipv4.ip_forward=1

設定を反映させる。

$ sudo sysctl -p

鍵生成

公開鍵と秘密鍵をそれぞれVPSと自宅サーバー用に生成します。つまり4つの鍵を生成するということですね。

この作業はどのPCでも行えるので、例としてMac用の作業手順を書いておきます。

$ brew install wireguard-tools
$ wg genkey | tee privatekey | wg pubkey > publickey

privatekeypublickeyという2つのファイルが生成されるのでそれぞれの内容をメモ。

これをVPS側の秘密鍵・公開鍵として扱いましょうか。

改めて鍵生成のコマンドを実行し、同じようにしてメモ、今度は自宅サーバー側の秘密鍵・公開鍵とします。

設定ファイル作成

(VPSと自宅サーバー両方で行う)

$ sudo touch /etc/wireguard/wg0.conf

wg0.confwg0はWireGuardの通信に使用するネットワークインターフェース名になります。好きな名前に変更できますが、やはりwg0という名前が広く使われているようです。

VPS側の設定ファイルを編集する

コメントを参考に適宜編集したものを設定ファイルに書き込みます。

[Interface]
PrivateKey = {先ほど生成したVPS側の秘密鍵}
Address = 10.0.0.1/24 # wg0に割り当てるプライベートIP
ListenPort = 55555 # セキュリティ的にデフォルトの51820から変えておいたほうが無難
# 「eth0」は実際のネットワークIFの名前を環境に合わせて変える
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

[Peer]
PublicKey = {先ほど生成した自宅サーバー側の公開鍵}
AllowedIPs = 10.0.0.2/32, 192.168.1.0/24
PersistentKeepalive = 25 # クライアントがNATの内側にいたりして接続が不安定になる場合

ちなみにVPS側の設定ファイルのAllowedIPsの自宅サーバーを指すIPだけなぜサブネットマスクが24ではなく32なのか気になってGeminiに聞いてみたのですが、

Gemini
Gemini

[Interface] セクションの Address = 10.0.0.1/24 の意味:

これは、WireGuardサーバーの仮想ネットワークインターフェース(wg0)自体に 10.0.0.1 というIPアドレスを割り当て、このインターフェースが 10.0.0.0/24 というネットワーク範囲全体(10.0.0.0 から 10.0.0.255 まで)を管理下に置くことを意味します。
サーバーはこのVPNネットワークにおけるゲートウェイのような役割を果たし、このサブネット内の通信や、外部(インターネットなど)への通信の中継点となります。OSはこの設定を見て、10.0.0.x 宛てのパケットを wg0 インターフェースに送るべきだと判断します。

[Peer] セクションの AllowedIPs = 10.0.0.2/32 の意味:

このパラメータは、「この公開鍵を持つPeer(クライアント)からどの送信元IPアドレスを持つパケットを受け入れるか」、そして「どの宛先IPアドレスを持つパケットをこのPeerに転送するか」という2つの重要な役割を定義します。
10.0.0.2/32 と指定することで、以下のようになります。

受信時(アクセス制御): このPeer(クライアント)から送られてくるパケットは、送信元IPアドレスが 10.0.0.2 であるものだけを許可します。これにより、クライアントが不正に別のIPアドレスを名乗ることを防ぎます。

送信時(ルーティング): サーバーから見て、宛先IPアドレスが 10.0.0.2 であるパケットは、このPeer(クライアント)に転送します。
つまり、このPeerには 10.0.0.1/24 というネットワーク内の 10.0.0.2 という特定の単一IPアドレスを割り当て、そのIPアドレスでのみ通信を許可する、という意味合いになります。/32 (IPv4) や /128 (IPv6) は、単一のホストアドレスを指定するためのCIDR表記です。

まとめ

Interface の Address は、サーバー自身が管理するネットワーク範囲全体を定義します。
Peer の AllowedIPs は、その特定のPeerに割り当てる(そして許可する)IPアドレスを具体的に指定します。多くの場合、クライアントには単一のIPアドレスを割り当てるため /32 や /128 を使います。

とのことでした。納得。

自宅サーバー側の設定ファイルを編集する

[Interface]
PrivateKey = {先ほど生成した自宅サーバー側の秘密鍵}
Address = 10.0.0.2/24
# 自分の環境では設定ファイルでDnsディレクティブを指定するとOS全体で名前解決できなくなったので注意。指定を消したら直った。
# 「eth0」は実際のネットワークIFの名前を環境に合わせて変える
PostUp = iptables -t nat -A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE

[Peer]
PublicKey = {先ほど生成したVPS側の公開鍵}
EndPoint = {VPSのグローバルIP}:55555
AllowedIPs = 10.0.0.0/24
PersistentKeepalive = 25

※自宅サーバー側の設定ファイルのAllowedIPsのサブネットマスクは/24なので注意

VPS側のポート開放

もしUFWなどを使用してファイアウォールを設定している場合はWireGuardの待ち受けポートの穴開けをしておきましょう。

先ほど設定ファイルの中で55555を待ち受けポートとして指定したので

$ sudo ufw allow 55555/udp

と実行します。

WireGuard起動 & 永続化

(VPSと自宅サーバー両方で行う)

$ sudo systemctl enable wg-quick@wg0 --now

疎通確認

家からVPS

$ ping 10.0.0.1

VPSから家

$ ping 10.0.0.2

それぞれのレスポンスを確認して問題無さそうであれば無事WireGuardによる拠点間VPNの構築&接続は完了です!

リバースプロキシの構築

前提としてVPSにてnginx + Let’s Encryptが既に利用可能なものとします。(ネットにいくらでも転がっている情報なので本記事では環境構築方法を割愛します)

設定ファイルの作成

$ sudo touch /etc/nginx/sites-available/example.com
$ sudo ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/

ファイルの用意ができたら、/etc/nginx/sites-available/example.comを以下のように書き換えます。

server {
  if ($host = example.com) {
    return 301 https://$host$request_uri;
  }

  listen 80;
  listen [::]:80;

  server_name example.com;
}

server {
  listen 443 ssl;
  listen [::]:443 ssl;

  server_name example.com;

  location / {
    proxy_pass http://192.168.1.253:8123;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }

  ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # 証明書ファイルのパスは適宜置きかえる
  ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}

Home AssistantはWebSocketのコネクションも張るのでそれ用の設定も追加しています (proxy_set_header Connection...辺り)

設定後はnginxの再起動を忘れずに!

# 必要ならシンタックスチェック → sudo nginx -t
$ sudo systemctl restart nginx

Home Assistantの設定

おそらく、今の状態のままexample.comにアクセスしてもエラーになるでしょう。

これはHome Assistantが知らないアドレスからのアクセスを弾いているからで、configuration.yamlhttpディレクティブを

http:
  use_x_forwarded_for: true
  trusted_proxies:
    - 10.0.0.1

のようにして、VPSのWireGuard上のプライベートIPを追加してあげると見れるようになるかと思います👍

余談 – MTUの設定

インターネットの世界にはMTUという概念があり、簡単に言えば1回の通信で転送可能な最大パケットサイズのことを指し、通常であれば1500が使用されます。(単位はバイト)

ところがISPの回線方式などによってはパケットにヘッダーが追加されたりする影響でMTUが1500以下になる場合もあります。

例えばSpeedGuideというサイトから使用中の回線のMTU値を確認することができるのですが、実家のNUROやVPSは1500だったのに対し、自宅の賃貸ネットやpovoは1454でした。

自分の環境にてWireGuardを経由した通信のパケットロスが頻発するので色々調べてみたところ、異なるMTUの回線同士をデフォルト設定で繋げていたのが原因ということが判明しました…。

こういったケースでは適切なMTU値を以下のように計算・WireGuardの設定ファイルに記述する必要があります。

  1. 自宅回線とVPN回線の両方のMTUを確認し、値の小さい方の数字を控えます。
  2. そして、IPv4で接続している場合は60、IPv6で接続している場合は80を控えたMTUから引きます。
  3. それをwg0.conf[Interface]ディレクティブにMTU = 1374のように追記します。

自分の場合はVPS回線のMTUが1500、自宅回線のMTUが1454でIPv6で接続しているので1454-80=1374をWireGuardの接続に使用するMTUとして設定したところ、通信が安定するようになりました😌

コメント

  1. […] WireGuardを使ったVPSとの拠点間VPNの構築方法や、LinuxからケースファンのLEDの色を変える方法についてのHowTo記事も執筆しましたので、興味があればぜひ見てみてくださいね。 […]

タイトルとURLをコピーしました