iPhoneとFreeBSDでopenvpn

iPhoneのOSで対応しているVPNはIKEv2/IPSec/L2TP。昔はPPTPもあったけど、どこかのバージョンで廃止になった。
うちのネットワーク環境はフレッツ光。ルータRT-S300SEの内側にFreeBSDのサーバがあると言う構成で、RT-S300SEはポートを指定してTCPかUDPのパケットは飛ばすことができるけど、プロトコルを指定して飛ばすことができないので、IPSecとかは使えない。
で、一時期はPPTPを使っていたんだけど、iOSの対応から外された時点で無意味になった。

しかし、最近OpenVPNのクライアントアプリを入れると、iPhoneでもOpenVPNが使えることがわかったので、試してみた。
基本的には数ある参考サイトを見ればやり方は書いてあるのだが、はまりポイントがいくつかあったので一応書いておく。

# サーバ側のアプリのインストール

FreeBSDの場合、portsからsecurity/openvpnをインストールする。
この記事を書いたときは、2.4.3だった。
以上。make configはデフォルトのままで良い。
そうすると、/usr/local/share/examples/openvpnにopenvpnの設定等のサンプルが、/usr/local/share/easy-rsa に easy-rsa の設定等がインストールされる。
openvpnの設定は/usr/local/etc/openvpnなんだが、/usr/local/etc/openvpnディレクトリは作成されない。

# 鍵と証明書の作成

## CAの鍵と証明書の作成

```shell-session
# cd /usr/local/share/easy-rsa
# easyrsa init-pki
# easyrsa build-ca
```
CA鍵のパスフレーズと、CA証明書のCommonNameを聞かれるので、適切に答える。
この行程で、pki/private/ca.key と pki/ca.crt ができる。
ちなみに、/usr/local/share/easy-rsa以外の場所でやりたい場合、/usr/local/bin/easyesaを見た限りでは、環境変数EASYRSAを設定すれば良さそうだが、試してはいない。

## サーバの鍵と証明書の作成

```shell-session
# easyrsa build-server-full server nopass
```

CAのパスフレーズを聞かれるので入力する。
pki/private/server.key と、pki/reqs/server.req、pki/issued/server.crt ができる。
crtの有効期限は10年。

## dhの作成

```shell-session
# easyrsa gen-dh
```

pki/dh.pemができる。

## 失効リストの作成

```shell-session
# easyrsa gen-crl
```

pki/crl.pem ができる。

## クライアント用の鍵と証明書の作成

```shell-session
# easyrsa build-client-full false
```

false の部分は、クライアントを識別する名前。
CAのパスフレーズと、クライアントのパスフレーズを聞いてくるので、それぞれ入力する。
pki/issued/false.crt, pki/private/false.key, pki/reqs/false.req ができる。

# サーバ側の設定

ディレクトリを作成し、設定ファイルのひな形、作成した鍵、証明書をコピーする。

```shell-session
# mkdir /usr/local/etc/openvpn
# cd /usr/local/etc/openvpn
# cp /usr/local/share/examples/openvpn/sample-config-files/server.conf openvpn.conf
# cp /usr/local/share/easy-rsa/pki/ca.crt /usr/local/share/easy-rsa/pki/issued/server.crt /usr/local/share/easy-rsa/pki/private/server.key /usr/local/share/easy-rsa/pki/dh.pem pki/crl.pem /usr/local/etc/openvpn/
# openvpn --genkey --secret ta.key
```

後は、openvpn.confを編集する。

```text:openvpn.conf
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh.pem
crl-verify crl.pem
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist ipp.txt
push "redirect-gateway def1 bypass-dhcp"
push "dhcp-option DNS 192.168.0.1"
keepalive 10 120
tls-auth ta.key 0
cipher AES-256-CBC
comp-lzo
user nobody
group nobody
persist-key
persist-tun
status /var/log/openvpn-status.log
log         /var/log/openvpn.log
log-append  /var/log/openvpn.log
verb 3
explicit-exit-notify 1
```

# クライアント側の設定

必要な鍵と証明書をコピーする。

```shell-session
# mkdir /usr/local/etc/openvpn/client
# cd /usr/local/etc/openvpn/client
# cp /usr/local/share/examples/openvpn/sample-config-files/client.conf wizard-limit.ovpn
# cp ../ca.crt ../ta.crt /usr/local/share/easy-rsa/pki/issued/false.crt /usr/local/share/easy-rsa/pki/private/false.key /usr/local/etc/openvpn/client
```

wizard-limit.ovpn(前半はOpenVPNサーバを識別する文字列、後半はovpnにする)を編集する。

```text:wizard-limit.ovpn
client
dev tun
proto udp
remote www.wizard-limit.net 1194
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert false.crt
key false.key
; remote-cert-tls server
tls-auth ta.key 1
cipher AES-256-CBC
comp-lzo
verb 3
```

`remote-cert-tls server` は、サンプルのファイルにはあるのだが、これがあるとうまく繋がらない。
調べてみたところ、[Important Note on possible "Man-in-the-Middle" attack if clients do not verify the certificate of the server they are connecting to.](https://openvpn.net/index.php/open-source/documentation/howto.html#mitm) に説明があるらしい。
この情報は、[OpenVPN と easy-rsa 3 使う](http://yutamoty.hatenablog.com/entry/2014/05/14/151408)にあった。

後は、このディレクトリにあるファイルを全てクライアントに転送すれば良い。

# 周りの設定

うちのようにVPNサーバがNATを使った家庭内LANにある場合、ルータの設定で1194/udpをVPNサーバに飛ばす必要がある。以前試したときはudpではなくtcpで実験していたので、1194/tcpしか飛ばしていなかったので、今回設定しなおした。
RT-S300SEの場合、`詳細設定`→`静的NAT設定`で、1194/udpをVPNサーバのIPアドレス宛に飛ばすようにする。

あと、VPNクライアントと家庭内LAN内と通信したい場合、デフォルトルータのルーティングテーブルに、10.8.0.0/24宛の通信をVPNサーバに向ける設定を追加する必要がある。
RT-S300SEの場合、`詳細設定`→`LAN側静的ルーティング設定` で、10.8.0.0/24(VPNのネットワークアドレス。openvpn.confのserverで指定したもの)宛を、VPNサーバのIPアドレス宛に飛ばすようにする。

# どこではまったのか？

最初、動作確認をサーバ/クライアント共にFreeBSDで行った。
クライアント側は、openvpn-client コマンドにovpnファイルを引数として与えてやれば、比較的簡単に接続までは行く。

## ta.key の存在

サンプルの設定ファイルだと、tls-authと言う行が生きている。
これは、サーバとクライアントで同じta.key を持っている必要があるらしい。
設定ファイルのコメントに従って、`openvpn --genkey --secret ta.key`で作成したら繋がるようになった。

## remote-cert-tls

クライアントの設定に remote-cert-tls があるとうまくいかない。これは、上述の通り。
本来は、設定した方が良さそうではある。

## 静的ルーティングの追加

設定ファイルのコメントには、以下のように書いてある。

```text
# Push routes to the client to allow it
# to reach other private subnets behind
# the server.  Remember that these
# private subnets will also need
# to know to route the OpenVPN client
# address pool (10.8.0.0/255.255.255.0)
# back to the OpenVPN server.
```

家庭内LANのサブネットは一つだけで、VPNクライアントのデフォルトルートは10.8.0.1に向いているので、ルーティングのpushは必要ない。
後は、家庭内LAN側が10.8.0.0/24宛をVPNサーバに向けると言うことがわかれば良いはずなので、上述の通りデフォルトルータのルーティングテーブルに追加した。

## クライアント側のDNS問題

何も考えずに、クライアント側でopenvpn-clientを動かしたら、/etc/resolv.confが空っぽになってしまった。
手動でクライアント側のプロバイダのDNSを指定してみても、VPNが繋がっているとDNSが引けない。
そこで、サーバ側の設定に`push "dhcp-option DNS 192.168.0.1"`を追加することで、VPN接続中にDNSが引けるようになった。
192.168.0.1はRT-300SEのLAN側のIPアドレスである。
本当は、家庭内向けには192.168.0.2にDNSサーバがいるのだが、そっちだと外の名前が引けなかったので。
VPNを切ってもクライアント側のresolv.confは復活しないので、本来は何らかの接続/切断スクリプトを書く必要があると思うのだが、FreeBSDはテストで使っただけで、実際のクライアントはiPhoneを使うので追求しないことにした。

# 参考サイト

- [OpenVPNのインストールとセットアップからインターネット接続までのガイドブック](http://qiita.com/noraworld/items/2fe6be489e1d93c748b8)
- [OpenVPN と easy-rsa 3 使う](http://yutamoty.hatenablog.com/entry/2014/05/14/151408)
- [iPhoneやiPad (iOS) でOpenVPNを使ってみよう！](https://www.openvpn.jp/document/ios-openvpn/)