Wireguad VPNを利用しVPSを介して外部から自宅サーバへアクセスする

経緯

自宅サーバに外部からアクセスしたいが自宅サーバを直接公開するのは怖い。 ということで、借りたVPSを出島として自宅サーバとその他スマホなどの端末でVPNを構築することでアクセスできるようにした。

目的

クライアントのノートPC(Windows)やスマホ(Android)からWireGuard VPNを経由して自宅サーバーにアクセスする。 自宅サーバーを外部へ公開で済むように、中継点としてVPSを経由する。

構成について

WireGuard

VPNを構築するソフトウェアとしてWireGuardを利用した。

これにした理由は以下のような感じ

  • オープンソース
  • (OpenVPNとくらべ)設定が楽そう
  • Linuxのカーネルに統合されている

なお、以降このVPNを構成する各サーバーまたは端末のいずれかを公式に倣ってpeerと記載することがある。

サーバー、端末について

  • VPS(AWS Lightsail)
    • OS: Ubuntu 20.04
    • 用途:自宅サーバにアクセスするための中継点
    • グローバルIP: 198.51.100.1/24
    • VPN内IP: 192.168.100.1/24, fd00::1/64
  • 自宅サーバー
    • OS: Manjaro
    • 用途: ファイルサーバー
    • VPN内IP: 192.168.100.2/24, fd00::2/64
  • その他クライアント
    • OS: Android/Windows
    • VPN内IP: 192.168.100.3/24, fd00::3/64
  • 共通事項
    • ポート: 51820

以下補足

  • 外部からグローバルIPでアクセスできるのはVPSのみ。
  • クライアントについてはWindowsとAndroidを使っているが、とくに区別して説明する必要がなさそうだったので本記事ではまとめてその他クライアントとして扱っている。
  • IPとポートについては念のため実際のとは変えている。
  • 上記のポートである51820はWireGuardのデフォルトらしい。 実際の構築の際はランダムに生成した別のポート番号を使っていたため、以降の例でも(デフォルトのポートではあるが)あえて明示している。
  • IPv6アドレスとipV4アドレスは今回は両方設定したが、別にIPv4だけでもいい。
  • ファイアーウォールについては省略

Step1 WireGuard の Install

VPS(Ubuntu 20.4)

普通にapt installでインストールできる。

$ sudo apt install wireguard-tools

自宅サーバー(Manjaro)

$ sudo pacman -S wireguard-tools

これだけだと動かなかった。

Manjaro の自宅鯖のほうは去年から運用している関係上VPSよりもカーネルが古く、そのままでは対応していない模様。

念のためカーネルの確認

$ uname -all
Linux host-name 4.19.144-1-MANJARO #1 SMP Wed Sep 9 18:43:01 UTC 2020 x86_64 GNU/Linux

host-nameの次にある4.19というのがカーネル(のはず) 4.19はそのままではWireguardに対応していないので、4.19用にDynamic Kernel Module Supportでモジュールを追加する必要がある

$ sudo pacman -S wireguard-dkms linux419-headers

これでwgコマンドが使えるようになった。

Step2: WireGuardの初期設定

鍵の生成

秘密鍵・共有鍵

$ umask 0077 # umaskでこれから作成する鍵の権限を制限する。これしないと次のコマンドがエラーになる
$ wg genkey > vps.key # 秘密鍵の生成
$ wg pubkey < vps.key > vps.pub # 公開鍵の生成

これを各端末ごとに繰り返して先に作成しておく。

事前共有鍵

任意で事前共有鍵を作成しておくことでより安全になる。

$ genpsk > vps-homeserver.psk # 事前共有鍵の生成(任意)

これも各組み合わせで作成しておく。 ちなみに今回のような、VPSを中継点とした構成では必要なのはVPSと各端末の組み合わせ(例:vps-homeserver.pskvps-smartphone.psk)、だけで、端末間の共有鍵(例:homeserver-smartphone.psk)は必要ない

設定

まずはサーバー自身のIP、秘密鍵、ポートを設定していく。

$ sudo ip link add dev wg0 type wireguard # wireguard用のデバイスwg0の追加
$ sudo ip addr add 10.0.0.1/24 dev wg0 # IPv4アドレスの設定
$ sudo addr add fdc9:281f:04d7:9ee9::1/64 dev wg0 # IPv6アドレスの設定
$ sudo set wg0 listen-port 51871 private-key /path/to/vps.key #ポートとプライベートキーの設定

接続先の設定

次に接続先の設定。 wg setコマンドで宛先peer別の設定をする。

だいぶ長々と書いているが、これらの内容は最終的には設定ファイルを直接いじれるため、このコマンド自体はそこまで必要ではなかったりする。とはいえ各オプションについては設定ファイルをいじるためにも結局把握する必要があるので省略するわけにもいかずこんな感じに…。

以下は全体像を説明するため今回使った一通りのオプションを記載しているが、実際には後述のようにサーバー・端末によって必要だったり必要なかったりする。実際のpeer別の設定については後述する。

$ sudo wg set wg0 \
    peer [接続先の公開鍵の中身] \
    preshared-key /path/to/peer-otherpeer.psk \
    endpoint 198.51.100.1/64:51902 \
    persistent-keepalive 25 \
    allowed-ips 192.168.100.2/24, fd00::2/64

以下各引数について

peer

公開鍵を指定する。接続先の名前の設定がないことから、この公開鍵が事実上の接続先を指定する名前(ID)として役割もある模様。 なぜかほかの鍵ファイルと違いパスで渡すとエラーになるのでキーの文字列を取り出して引数にとる必要がある。取り出すとわかるがSSHのキーなどと比べるとかなり短い。

preshared-key

設定しているサーバー(クライアント)と接続先との事前共有鍵。パスで指定。任意ではあるが、WireGuardの鍵は(16進数ではないためか)SSHなどほかの鍵とくらべるとかなり短いため、気分的には設定したほうが安心できる。

endpoint

グローバルIP。2つのpeerのうちどちらかがもう一方へendpoint宛でパケットを送れる必要がある。 今回であればVPS以外のpeerからVPS宛にendpointを設定すればいい。 ちなみにこのendpointはパケットのやり取りを通じて自動更新されるため、気づくとVPSにスマホのendpointが設定されていたりする。

allowed-ips

ここに指定した宛先のパケットをこのpeerに送る。特定のpeer宛のIPを指定すればそのpeer宛のパケットだけが飛ぶが、ここでネットワークを指定することで、該当ネットワークへのほかのパケットもこのpeerへ送るようになる

具体的にいうと、192.168.100.2/32とすると192.168.100.2/32宛だけだが、192.168.100.0/24とすると192.168.100.0/24のネットワーク内のほかの端末へのアクセスもこのpeerへ飛ぶことになる。

VPS192.168.100.1/32経由で自宅サーバー192.168.100.2/32へアクセスする今回のケースの場合、その他クライアントの設定でVPS宛のarrowed-ips192.168.100.0/24と指定することで自宅サーバー宛のパケットもVPSに飛ばせるようになる。

persistent-keepalive 25

定期的に対象のpeerにパケットを飛ばして接続を維持させる設定。 これが必要なのは今回の自宅サーバーのように中継点を介して外部からアクセスするケース。自宅サーバーが公開されていない以上中継点側から接続を開始することができないため、自宅サーバー側から中継点へ接続を維持し続ける必要がある。

今回の場合、自宅サーバーのVPS宛の設定でのみ指定する必要がある。

起動

# ip link set wg0 up

これでつながるはず。つながらなかったらファイアーウォールとか確認する。

なお、LightsailWireGuardが有効になってもpingが届かないので心配になるが、wg showで最終接続時間が見れるので、接続ができていればとりあえずこれで確認できる。

IPforwardの設定

そのままだと自宅サーバーあてのパケットがVPSに届いてもそこでせき止められてしまうので、ちゃんと中継できるよう設定をする

# sysctl -w net.ipv4.ip_forward=1
# sysctl -w net.ipv6.conf.all.forwarding=1

これで中継できるようになる

この設定を永続化するには/etc/sysctl.confの以下をコメントアウトする。

net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1

設定の永続化

設定ファイルの保存、読込 (wg showconf, wg setcnfについて)

2020/10/12wg-quickについて追記

以下のコマンドで設定が保存ができる

wg showconf wg0 > /etc/wireguard/wg0.conf

できる設定ファイルはこんな感じ

[Interface]
ListenPort = 51820
PrivateKey = [略]

[Peer] 
PublicKey = [略]
PresharedKey = [略]
AllowedIPs = 192.168.100.2/32, fd00::1/128

見ての通りwgコマンドで設定した内容がだいたいそのまま書かれているので、マニュアル通り進めていれば問題なく読めると思う。

これで設定ファイルを読み込む。

wg setconf wg0 /etc/wireguard/wg0.conf

アドレスの追記

wg showconfで作成した設定ファイルにはサーバー自身のアドレスの記載がない。改めて上のコマンドを見返しても分かるが、ipアドレスの設定にはwgコマンドが関わっていないので、そもそもwgコマンドの管理外の模様。 ではいちいち別でwg0を設定しなければいけないかというとそんなことはなく、ネットワークデバイスも一緒に設定してくれるwg-quickというコマンドがある。 これで自動でデバイスを設定できるが、そのためには以下のように[Interface]内にAddressを追記する必要がある。

[Interface]
Address = 192.168.100.1/24, fd00::1/64 # 追記
ListenPort = 51820
PrivateKey = [略]

[Peer] 
PublicKey = [略]
PresharedKey = [略]
AllowedIPs = 192.168.100.2/32, fd00::1/128

これで設定ファイルが完成。

一度設定ファイルを作ったらあとはpeer別に必要な個所を適宜修正して使いまわせる。 というか見比べながら設定しないとどれがどれだかわからなくなる

WindowsやAndroidのクライアントでもこの設定ファイルを読み込んで設定することになる。

wg-quickによる自動起動

/etc/wireguard/wg0.confがある状態で以下のコマンドでネットワークデバイスの作成とwireguardの設定をまとめて行える。

sudo wg-quick up wg0

削除するにはupの代わりにdownを使う

sudo wg-quick down wg0

以下でsystemctlによる自動起動ができる。

sudo systemctl enable wg-quick@wg0.service

peer別の設定ファイルの例

各項目についてはだいたい上記のコマンドで設定した通り。 唯一上記で触れていない例外はDNSで、これはNextcloudに内部用のドメインでアクセスできるようにしたかったため、設定したがあまり必要なケースはないと思うので省略。後日DNSについて記事を書く際についでに少し触れるかもしれない。

スマホ等の端末

[Interface]
Address = 192.168.100.3/24, fd00::3/64
ListenPort = 51820
PrivateKey = [略]
DNS = 192.168.100.2,fd00::2

[Peer] ; VPS
PublicKey = [略]
PresharedKey = [略]
;ここで端末ではなくネットワークを指定することで、VPS以外の該当ネットワークへのパケットもVPSへ送るようになる
AllowedIPs = 192.168.100.0/24,fd00::/64 
Endpoint = 198.51.100.1:51820 ; VPSの外部IP

VPS

[Interface]
Address = 192.168.100.1/24, fd00::1/64
ListenPort = 51820
PrivateKey = [略]

[Peer] ; 自宅サーバー
PublicKey = [略]
PresharedKey = [略]
AllowedIPs = 192.168.100.2/32, fd00::1/128

[Peer] ; スマホ
PublicKey = [略]
PresharedKey = [略]
AllowedIPs = 192.168.100.3/32, fd00::3/128

自宅鯖

[Interface]
Address = 192.168.100.2/24, fd00::2/64
ListenPort = 51820
PrivateKey = [略]

[Peer] ; VPS
PublicKey = [略]
PresharedKey = [略]
AllowedIPs = 192.168.100.1/32, fd00::1/128
Endpoint = [VPSのIP]
PersistentKeepalive = 25 ; これを入れておくことで自宅サーバから定期的にVPSへハンドシェイクを行い、通信を維持できる。 

余談

IPv4かIPv6か

IPv4のほうが間違いなく無難とはいえ個人的には数が多くてランダムで設定すれば重複を気にしなくてもいいという1点でipv6アドレスのほうが好き。IPv6は端末によって対応していない可能性があるが、とはいえ最近の機器であれば大概対応しているはずだし現時点で接続したい機器は一通りつなげられたので結局のところ好みで選んでいいと思われる。

その他参考サイト

例示専用のIPアドレスとドメインを使いこなす | ギークを目指して WireGuard - ArchWiki IPv6アドレスにおける「インターフェース識別子」という名称の謎とModified EUI-64によるIPv6アドレス生成:Geekなぺーじ 第614回 WireGuardでVPNサーバーを構築する:Ubuntu Weekly Recipe|gihyo.jp … 技術評論社 WireGuard で VPS 経由で家庭内に入る - Qiita