おは代々木ダイアリー

いろいろ試したメモを書きます

【WireGuard】VPNで自宅サーバーを公開する

最近マイクラを始めまして。もう周回遅れどころの騒ぎではないレベルで遅れているのはわかっているんですが、
なるほど自鯖を立てるのは楽しいなぁというところまで来ましたのでちょっとまとめてみます。

想定読者

以下のような人にはまぁ役に立つのかなと思います。

  • サーバーは公開したいがお金はかけたくない!
  • サーバーは公開したいが自宅のIPは公開したくない!
  • サーバーは公開したいがそもそもグローバルIPが降ってこない!

やること

VPS上にVPNサーバーを立てて、VPNにつないだ自宅サーバーに対してTCPなりUDPなりをフォワーディングします。

今回は題材として、VPNサーバーとしてWireGuardを使用し、統合版マイクラのサーバーである Bedrock Dedicated Server (以下、BDS)を自宅サーバー(Ubuntu 18.04)で動かして、自宅IPを公開せずに公開する、ということをやります。

ちなみに BDS の導入は本筋から外れるので先に書いておきますが、ここ からダウンロードしたのを使います。

とりあえず、BDSを動かすサーバーとして Core i5-2520M と 8GB RAMを積んだ ThinkPad X220 を、マインクラフト統合版のクライアントとしてKindle Fire HD10 を使ってます。(i5-2520Mって単体で売ってるんだ・・・)

Fire HD 10 タブレット ブラック (10インチHDディスプレイ) 32GB

Fire HD 10 タブレット ブラック (10インチHDディスプレイ) 32GB

  • 発売日: 2019/10/30
  • メディア: エレクトロニクス

まぁこんな構成ですが、SwitchとPS4スマホでワイワイガヤガヤとマルチできています。

パターン1: 自宅サーバーを直接VPNでつなぐ

一番簡単なパターンです。自宅サーバー自体をVPNのクライアントとし、VPSで受けた通信を自宅サーバー側に流してあげます。

f:id:ohayoyogi:20210318145711p:plain
イメージ

今回は、設定にあたり、VPNにおいてVPSのIPを 10.0.0.1 とし、自宅サーバーのIPを10.0.3.1 を割り当てることにしました。

VPNの設定:VPS側の設定

まずは、各クライアントとつなぐ中心になるVPS(ここではVPNサーバーと呼びます)の設定をします。

wireguard の導入

秘密鍵/公開鍵の生成については他のサイトを参考にしてください。

sudo apt-get install wireguard
sudo vim /etc/wireguard/wg0.conf
[Interface]
PrivateKey = <サーバーの秘密鍵>
Address = 10.0.0.1/16
ListenPort = <wireguard用に開くport>

[Peer]
# Peer 1
PublicKey = <クライアントの公開鍵>
AllowedIPs = 10.0.3.0/24
sudo wg-quick up wg0

こんな感じでしょうか。 [Peer] 以降の行をコピペすると接続するクライアント設定を増やせます。サイトによってはPostUp, PostDownを設定しているところもあるんですが、ufwで別途設定するので省略したいと思います。

ufw の設定

wireguard 用のポートを開きます。

ufw allow <wireguard用に開くport>/udp
ufw reload

VPNの設定:自宅サーバー側の設定

同様に自宅サーバー側にもwireguardを導入していきます。

wireguard の導入

sudo apt-get install wireguard

サーバーもクライアントもこれでいけるの楽っすね。

sudo vim /etc/wireguard/wg0.conf
[Interface]
PrivateKey = <クライアントの秘密鍵>
Address = 10.0.3.1/32

[Peer]
PublicKey = <サーバーの公開鍵>
Endpoint = <サーバーのIP>:<wireguard用に開くport>
AllowedIPs = 10.0.0.0/16
sudo wg-quick up wg0

サーバー公開の設定

ここまでで、VPNは接続できたと思います。(互いにping飛ばしたり、ip addr で確認してください)。続いて、このVPNを通して自宅サーバーにリクエストをフォワーディングする設定をしてサーバーを外から到達できるようにしていきます。

ファイアウォールに穴をあける

VPS では当然 BDSで使用するポートを開かなければならないので

ufw allow 19132/udp
ufw reload

として、UDP用に19132ポートを開けます(公開するサーバーに合わせて適宜読み替えてください)。

iptables 設定の記入

続いて、iptables の設定で自宅サーバーへフォワーディングします。ufw を使って設定をするので /etc/ufw/before.rules に記入します。

# NAT
*nat
-F
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# minecraft
-A PREROUTING -i eth0 -p udp --dport 19132 -j DNAT --to-destination 10.0.3.1:19132
-A POSTROUTING -o wg0 -p udp --dport 19132 -d 10.0.3.1 -j MASQUERADE

COMMIT

# minecraft の行から下がミソです。DNAT ってなかなか聞き慣れないかもしれませんが、「eth0宛の--dportに指定したポートに来たパケットは --to-destinationに転送する」設定と考えてください。民生用ルーターの「ポートマッピング」と同じです。(と、考えてよいのかな?)

次に MASQUERADE ですが、正しくはNAPTで、ルーターのNATといえばこれって感じのアレです(どれ)。
例えば自宅のルーターには割り振られるグローバルIPは1つですが、複数のプライベートIPをぶら下げて通信することができます。それを実現しているのがMASQUERADEで、ルーターを出るときに使用したポートと送信元のプライベートIPアドレスを記録しておいて、ルーターにレスポンスが返ってきた時にそのプライベートIPにレスポンスを流してあげる、みたいなことをしているのです。

今回それがなぜ必要か?というと今回はVPNサーバーが「BDSにスマホからリクエストがきたよ!」といってフォワーディングしてくるパケットには

送信先: 10.0.3.1:19132
送信元:<スマホのIP>:<スマホのポート>

といった情報が記録されています。これを受けとった自宅サーバーはレスポンスを

送信元: 10.0.3.1:19132
送信先: <スマホのIP>:<スマホのポート>

として返します。この時の「送信先: <スマホのIP>」となってしまうのがミソで、普通にルーティングされてしまうと自宅用ルーターでNAPTされたパケットがスマホに飛んでいってしまい「VPSサーバーにリクエストしたはずが全然知らんIPから返信来たがな」となって正しく受け取られません(はず。確かめてない)

パターン2: ルーターVPNクライアントさせる

OpenWRTにwireguard導入してサブネットまるごとVPNみたいなのやろうとも思ったんですが、力尽きたのでここまで。

パターン3: VPNサーバーにnginxを導入しリバプロさせる

iptables でやっているところを nginx でリバプロさせます。どのレイヤでフォワーディングするか、というところが違うところです。

終わりに

力尽きてしまいましたが、目的は達せられたのでこれにて終わります。
リクエストがあれば書きます・・・書きたい・・・ 。

同様な内容を試みているサイトを見かけたんですが「iptablesではよくわからないけどうまくいかなかったので nginxで!」みたいなサイトが多く、もやもやしたので頑張ってiptablesでやってみました。

追記

IPv6にも対応した。けど思ったよりしんどかったのでメモ。

IPv4のプライベートIPに対応する概念として、IPv6には ユニークローカルアドレス というものがあるらしいです。ユニークローカルアドレスに割り当てられている領域は fd00::/8 で、めっちゃ広いなーと思って適当に fd00:3::1/32 とかを適当に自宅サーバーにあてがってみたんですが、pingすら通りませんでした。

ちゃんと調べてみると、以下Wikipediaより引用

ブロック fd00::/8 は、40ビットのランダムなビット列を後につけて/48のプリフィックスとして使用することと定められている。この結果、この範囲のプリフィックスは fdxx:xxxx:xxxx:: という形式になる。RFC 4193 では、高精度の乱数生成の方法が利用できない場合であっても、最小限度の品質を保ってランダムなビット列を得られる方法を、ユーザに提案している。

といった感じで、 /48プリフィックスとして使用すること、とあり、fd に続いて10文字ものランダムな列を入れなければならないそうです。なるべく短いほうがいいぜーと思ってやったんですが、こんな罠があったとは・・・(プレフィックス /64 とかのIPをあてたらいけました)。