WireGuardでVPN接続を利用してプライベートネットワークにアクセスする

# はじめに

プライベートネットワークへ外部からVPNで繋げるようにしてみた。
VPNには最近流行りのWireGuardを用いた。

![introduction.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914607/353145fc-3eab-93d0-4b85-a8b5b3323d0e.png)


## WireGuard

[WireGuard](https://www.wireguard.com/)は比較的新しいL3VPNプロトコル。
UDPベースで[速い](https://www.wireguard.com/performance/)のが特徴。
しかも、設定がSSHの設定と同じくらい簡単だと謳っている。
たしかに、[wg](http://manpages.ubuntu.com/manpages/hirsute/en/man8/wg.8.html)と[wg-quick](http://manpages.ubuntu.com/manpages/hirsute/en/man8/wg-quick.8.html)のmanページを見ると、設定できるパラメータが少なくてシンプルなことがわかる。

## 環境

インターネット越しにプライベートネットワークへアクセスする。
詳細な構成は以下の通り。

- サーバー
    - Ubuntu 18.04（Public IP付き）
- クライアント
    - Ubuntu 18.04
    - Android

![architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914607/194b7899-3e55-1aa1-3c28-0b68aac21b30.png)


アクセスしたいサーバーのアドレスは`172.16.6.1/24`。
プライベートアドレスなのでインターネットから直接アクセスすることはできない。
せっかくなのでAndroidからの接続も試した。

# 設定

クライアントだけでなくサーバーも公開鍵を作り、お互いの公開鍵を事前に知っておく必要があるのがSSHと違うところ。

## サーバー (Ubuntu 18.04)

まずはインストールから。
昔はPPAの設定が必要だったが今はいらない。
`/etc/apt/sources.list`に以下のような記述があれば大丈夫。
`universe`の前後に`main`, `multiverse`, `restricted`のような単語が入っていても問題ない。
該当箇所が見つからない場合は以下の内容を追記する。

```:/etc/apt/sources.list
deb http://jp.archive.ubuntu.com/ubuntu/ bionic-updates universe
```

リポジトリの設定ができたらインストール。

```bash
sudo apt update
sudo apt install wireguard
```

次は鍵の作成。
rootで作業する。
秘密鍵は`/etc/wireguard/privatekey`に保存され、公開鍵は`/etc/wireguard/publickey`に保存される。

```bash
cd /etc/wireguard
wg genkey | tee privatekey | wg pubkey > publickey
```

作った鍵を使ってサーバーのコンフィグ(`/etc/wireguard/wg0.conf`)を作成

```ini:/etc/wireguard/wg0.conf
[Interface]
ListenPort = 51820
PrivateKey = [さっき作成したサーバーのPrivate Key]
Address = 10.0.1.1/24

PostUp = ufw limit proto udp from any to [サーバーのPublic IP] port 51820
PostUp = ufw route allow in on %i out on [プライベートネットワークと接続しているインターフェース名]
PostUp = iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -d 172.16.6.0/24 -o [プライベートネットワークと接続しているインターフェース名] -j MASQUERADE
PostDown = ufw delete limit proto udp from any to [サーバーのPublic IP] port 51820
PostDown = ufw route delete allow in on %i out on [プライベートネットワークと接続しているインターフェース名]
PostDown = iptables -t nat -D POSTROUTING -s 10.0.1.0/24 -d 172.16.6.0/24 -o [プライベートネットワークと接続しているインターフェース名] -j MASQUERADE

[Peer]
PublicKey = [これから作成するUbuntuクライアントのPublic Key]
AllowedIPs = 10.0.1.2/32


[Peer]
PublicKey = [これから作成するAndroidクライアントのPublic Key]
AllowedIPs = 10.0.1.3/32
```

- `Address`
    - WireGuardの起動時に作成される`wg0`インターフェースに振るアドレス
    - 好きなPrivateアドレスを使えば良いが、NATの設定もそれに応じて変更する
- `PostUp`, `PostDown`
    - ファイアウォール
        - `ufw limit proto udp from any to [サーバーのPublic IP] port 51820`
            - 外部からVPNサーバーにアクセスできるようにするための設定
    - NAT
        - `iptables -t nat -A POSTROUTING -s 10.0.1.0/24 -d 172.16.6.0/24 -o [プライベートネットワークと接続しているインターフェース] -j MASQUERADE`
            - プライベートネットワーク(この場合は172.16.6.0/24)で通信するときにちゃんと送信元アドレスをそのサブネット内のアドレスにするための設定
    - IPフォワード
        - `ufw route allow in on %i out on [外部と接続しているインターフェース]`
            - `wg0`からインターフェースからのパケットが外へ出られるようにするための設定
        - カーネルの設定
            - IPフォワードは本当はカーネルの設定でも許可する必要がある
                - 今回はサーバーに`dockerd`が入っているので、カーネルの設定をする必要はない
                - `dockerd`の[デフォルトの設定](https://docs.docker.com/engine/reference/commandline/dockerd/)では自動的に設定してくれる
            - `dockerd`が入っていない場合や明示的に`dockerd`のIPフォワードを無効化している場合は、`echo 1 > /proc/sys/net/ipv4/ip_forward`でIPフォワードを有効化する必要がある
- `AllowedIPs`
    - `Peer`へフォワードするサブネット
    - サーバ側の設定では対向の`Peer`のプライベートアドレスを書いておけば良い

## クライアント (Ubuntu 18.04)

サーバーと同じようにWireGuardをインストールし、鍵を作成する。

```bash
sudo apt update
sudo apt install wireguard
# ここからroot
cd /etc/wireguard
wg genkey | tee privatekey | wg pubkey > publickey
```

作った鍵をもとにコンフィグ(`/etc/wireguard/wg0.conf`)を書く

```ini:/etc/wireguard/wg0.conf
[Interface]
ListenPort = 51820
PrivateKey = [いま作成したUbuntuクライントの秘密鍵]
Address = 10.0.1.2/24

[Peer]
PublicKey = [さっき作成したサーバーの公開鍵]
AllowedIPs = 10.0.1.0/24, 172.16.6.0/24
Endpoint = [サーバーのPublic IP]:51820
PersistentKeepalive = 15
```

- `AllowedIPs`
    - サーバーとは設定が少し違う
    - WireGuardのVPNネットワークとプライベートネットワークとの通信を許可する
- `PersistendKeepAlive`
    - NATの裏からクライアントがVPN接続した際に、接続が途切れないようにするための設定

最後に今作ったクライアントの公開鍵の情報を**サーバーの**`/etc/wireguard/wg0.conf`に書き込む。

## クライアント (Android)

一旦パソコンでコンフィグを書いて、QRで読み込むのが楽。
WireGuardとqrencodeがインストールしてあるパソコンなら何でもOK。

```bash
sudo apt update
sudo apt install wireguard　qrencode
# ここからroot
cd /etc/wireguard
wg key | tee android_privatekey | wg pubkey > android_publickey
```

鍵を作成したらコンフィグを書く。
Ubuntuクライアントと異なる箇所は`PrivateKey`と`Address`。

```ini:/etc/wireguard/android.conf
[Interface]
ListenPort = 51820
PrivateKey = [いま作成したAndroidクライントの秘密鍵]
Address = 10.0.1.3/24

[Peer]
PublicKey = [さっき作成したサーバーの公開鍵]
AllowedIPs = 10.0.1.0/24, 172.16.6.0/24
Endpoint = [サーバーのPublic IP]:51820
PersistentKeepalive = 15
```

以下のコマンドで表示されたQRコードをAndroidから読み込む。
ターミナルにQR表示できるのすごい！

```bash
qrencode -t ansiutf8 < /etc/wireguard/android.conf 
```

こちらも、今作ったクライアントの公開鍵の情報を**サーバーの**`/etc/wireguard/wg0.conf`に書き込むのを忘れずに。

# 接続

VPNをつなげて疎通を確認してみる。

## サーバー (Ubuntu 18.04)

以下のコマンドでVPNサーバーを立ち上げる。
再起動時も自動で立ち上がる。

```bash
systemctl enable wg-quick@wg0
systemctl start wg-quick@wg0
```

## クライアント (Ubuntu 18.04)

こちらは常にVPN接続している必要がないのでenableしない。

```bash
systemctl start wg-quick@wg0
```

サーバーのPrivate IP(10.0.1.1)と疎通できるのが確認できる。
VPNはきちんと繋がっているのがわかる。

```bash
$ ping 10.0.1.1 -c 3
PING 10.0.1.1 (10.0.1.1) 56(84) bytes of data.
64 bytes from 10.0.1.1: icmp_seq=1 ttl=64 time=267 ms
64 bytes from 10.0.1.1: icmp_seq=2 ttl=64 time=267 ms
64 bytes from 10.0.1.1: icmp_seq=3 ttl=64 time=267 ms

--- 10.0.1.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 267.387/267.566/267.858/0.632 ms
```

次はプライベートネットワークとの疎通の確認。
UFWはデフォルトでICMPのフォワードを許可しているので、プライベートネットワークとの疎通はTCPで確認する。

```bash
$ mtr 172.16.6.1 -r -c 3 --tcp -P 22
Start: 2020-12-03T12:12:25+0200
HOST: XXXXXXXXXXX                 Loss%   Snt   Last   Avg  Best  Wrst StDev
  1.|-- 10.0.1.1                   0.0%     3  260.6 260.5 260.4 260.7   0.2
  2.|-- 172.16.6.1                 0.0%     3  261.2 261.2 261.1 261.3   0.1
```

## クライアント (Android)

VPNの接続はトグルボタンを押すだけ。

<img width="400" alt="wireguard-android.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914607/25381488-5b98-a931-90f5-197dfc29dfc2.jpeg">

同様にサーバーのPrivate IP(10.0.1.1)と疎通できるのが確認できる。
このとき「トンネル対象アプリ」にPingのアプリを選択するのを忘れずに。

<img width="400" alt="ping-android.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/914607/54af762f-b58d-97c1-26df-237212204a10.jpeg">

TCPの疎通確認はUbuntuで試せたので割愛。

## おわりに

VPN接続してプライベートネットワーク内のホストと通信できるのが確認できました。
Androidは設定をQRで読み込め、しかも、VPNで通信するアプリを選べるというのは便利ですね。

この記事を読んで「面白かった」「学びがあった」と思っていただけた方、よろしければ LGTM、Twitter や Facebook、はてなブックマークにてコメントをお願いします！

また [DeNA 公式 Twitter アカウント @DeNAxTech](https://twitter.com/DeNAxTech) では、 Blog 記事だけでなく色々な勉強会での登壇資料も発信しています。ぜひフォローして下さい！
<a href="https://twitter.com/DeNAxTech?ref_src=twsrc%5Etfw" class="twitter-follow-button" data-size="large" data-lang="ja" data-show-count="false">Follow @DeNAxTech</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>