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

**2020/10/12wg-quickについて追記**
**2021/1/18 タイトルと本文の誤字を修正しました**

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

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

## 構成について
### WireGuard
VPNを構築するソフトウェアとして[WireGuard](https://www.wireguard.com/)を利用した。

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

- オープンソース
- （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](https://wiki.archlinux.jp/index.php/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 # 公開鍵の生成
```
これを各端末ごとに繰り返して先に作成しておく。

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

```
$ wg genpsk > vps-homeserver.psk # 事前共有鍵の生成（任意）
```

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



### 設定 

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

```bash
$ sudo ip link add dev wg0 type wireguard # wireguard用のデバイスwg0の追加
$ sudo ip addr add 10.0.0.1/24 dev wg0 # IPv4アドレスの設定
$ sudo ip addr add fdc9:281f:04d7:9ee9::1/64 dev wg0 # IPv6アドレスの設定
$ sudo wg 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`へ飛ぶことになる。

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

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

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


### 起動
```
# ip link set wg0 up
```
これでつながるはず。つながらなかったら`ファイアーウォール`とか確認する。

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

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

```
# sysctl -w net.ipv4.ip_forward=1
# sysctl -w net.ipv6.conf.all.forwarding=1
```
これで中継できるようになる

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

```conf:/etc/sysctl.conf
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
```


## 設定の永続化
### 設定ファイルの保存、読込 (`wg showconf`, `wg setcnf`について）


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

```
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アドレスとドメインを使いこなす | ギークを目指して](http://equj65.net/tech/documentationip/)
[WireGuard - ArchWiki](https://wiki.archlinux.org/index.php/WireGuard)
[IPv6アドレスにおける「インターフェース識別子」という名称の謎とModified EUI-64によるIPv6アドレス生成:Geekなぺーじ](https://www.geekpage.jp/blog/?id=2020-7-21-1)
[第614回　WireGuardでVPNサーバーを構築する：Ubuntu Weekly Recipe｜gihyo.jp … 技術評論社](https://gihyo.jp/admin/serial/01/ubuntu-recipe/0614?page=2)
[WireGuard で VPS 経由で家庭内に入る - Qiita](https://qiita.com/false-git@github/items/bf3884030a954751cd09)