FreeBSDでVPNサーバー

ZRouterにはracoon,racoon2,openvpnのprofileが用意されていてVPNサーバーもターゲットの一つであったようです。

利用しているISPがグローバルアドレスを配ってくれているので、せっかくなので[自宅アクセス](http://qiita.com/yamori813/items/c53f349c513259868bf8)を試してみようと思いました。

2017/2/17にあったFreeBSD workshopで話して教えてもらった事も反映しました。

FreeBSDで使えるVPNサーバを提供するOpenSrouceは以下の用なものがある。

|名称|仕様|ライセンス|説明| 
|:---|:---|:---|:---|
|racoon/[mpd](http://mpd.sourceforge.net/)|IPSec|BSD|racoonはWIDEのKAMEプロジェクトの成果でIPSecのスタックである。IPv6ではIPSecが必須でそれのための実装なのだが、副産物としてIPv4でのIPSecでも使える。racoonはユーザランドのプログラムとカーネル内のコード(sys/netipsec)で機能する。racoonは現在はSourcefogeの[ipsec-tools](http://ipsec-tools.sourceforge.net/)でメンテナンスされている。mpdはL2TPやPPPをサポートするスタックでFreeBSDのNetGraphを使ったプログラムで、実行コマンドはバージョンをつけてmpd5となっている。racoonはクライアントプログラムとしてMac OS Xなどでも使われている。|
|[racoon2](http://www.racoon2.wide.ad.jp/w/)/mpd|IPSec|BSD|racoonはikev1だけだがIPSecのikev2にも対応したプログラム。racoonとはまったく別の作り込みで設定ファイルやプログラムの構成が違う。|
|[OpenVPN](https://openvpn.net/)|独自|GPLv2|racoon/racoon2は専用のカーネル内のコードを必要にしたがOpenVPNは汎用のtapデバイスでVPNを提供する仕組みの模様。あまり詳しく調べてません。|
|[strongSwan](https://www.strongswan.org/)|IPSec|GPLv2|いろいろ調べていて見つけましたが、ZRouterのprofileに追加して[試して](https://qiita.com/yamori813/items/55a072369fd9e931dc6a)みました。|
|[SoftEtherVPN](https://ja.softether.org/)|独自|GPLv2|後発のかなり大きなプログラムでユーザランドの実装で機能している模様。いろいろな種類のVPNに対応しているようだ。CLIでの設定コマンドも用意されているがWindowsのGUIの設定プログラムもある。このプログラムは筑波大学で産学連携で作られたようです。組み込み用のソリューションは製品になっているようです。|
|OpenIKED|IPSec|BSD|ikev2なOpenBSDで作られている軽量な実装のようです。|
|OpenIKEv2|IPSec|Apache|スペインの方が作ったもののようです|

|タイプ|サーバ|クライアント|
|:--|:--|:--|
|L2TP/IPSec ikev1|racoon,strongSwan|win,mac,iphone,android|
|Xauth/IPSec ikev1|strongSwan|mac,iphone,android|
|IPSec ikev2|strongSwan, racoon2, openiked,OpenIKEv2|win,mac,iphone|

VPNにはIPSec/L2TPやPPTPなどがあるが、AppleはPPTPのサポートを止めたようだ。

IPSec ikev2はAndroidではstrondswanのアプリを入れれば使えるようになるようだ。

ikev2は証明書が必要で、オレオレ認証局で運用する場合は、クライアントにcaのkeyのインストールが必要になります。

SoftEtherVPNがamd64で簡単に動いたので、ZRouterにつっこんで動かしてみようと思った。この[メモ](http://qiita.com/yamori813/items/71f9cc799fb05a58885a)はこの時にiconvが動作しなかったため調べた内容でした。どうにかこうにかビルドが通ってiconvも使える環境にして試したがBus Errorで動かなかった。メモリが32MバイトのRT3050で試したのだが、メモリが足りないのかもしれない。

次にracoon2を試してみたのだが、もともとZRouterに入っていたソースではなくて[portsをコピー](http://qiita.com/yamori813/items/dc251ec7a9af8f0c9d36)してビルドしてみた。ビルドは通ったが設定ファイルが分からず断念。racoon2はracoonに比べ極端に情報が少ない上に、互換がないので、かなり頑張らないと設定は難しいかも。racoon/racoon2の開発時にはL2TPを通して使われる事を想定していなかったようで説明が無いのも難解にしている要因な気がします。racoonはいろいろなところで使われているので、なにかあったら誰かが対応してくれると思うが、racoon2はあまり使われていないので、どうなるか心配になる。

結局racoonを使う事にした。racoonとmpd5もZRouterのportsでビルドできるようにしました。必要なコマンドを入れたイメージは6Mくらいになっています。

FreeBSD 9でのよい説明がここにあったのでほとんどこれに従った。

http://oichinote.com/plus/2013/04/l2tp-vpn-server-in-freebsd-9-1.html

FreeBSDではドライバはカーネルビルド時に入れ込む事もできますが、カーネルモジュールとしてブート後に組み込み事ができます。ipfwなどはこれに該当します。sys/netipsecにあるIPSecのコードはカーネルビルド時に組み込まないと機能しません。

上記のページでは何らかの理由でpfを設定していますが、IPSec/L2TPの動作にはpfは必要ありません。また2017/2現在の12-CURRENTでは  IPSEC_FILTERTUNNELは無くなっているようです。ちょうど[IPSEC_NAT_Tも無くなってデフォルト](https://svnweb.freebsd.org/base?view=revision&sortby=file&revision=313330)になっていました。

pfを知らなかったのですが、OpenBSDから移植されたパケットフィルターでipfwと同等の機能を持つようです。

カーネルがブートしたらちゃんとカーネルにIPSecが入っているか確認します。

```
# dmesg | grep -i ipsec
IPsec: Initialized Security Association Processing.
```

インターフェースは以下のような状態です。

```
# ifconfig -a
rt0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=b<RXCSUM,TXCSUM,VLAN_MTU>
        ether 00:18:84:00:00:00
        inet 10.0.1.19 netmask 0xffffff00 broadcast 10.0.1.255 
        media: Ethernet 100baseTX <full-duplex>
        status: active
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
        options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6>
        inet 127.0.0.1 netmask 0xff000000 
        groups: lo 
enc0: flags=0<> metric 0 mtu 1536
        groups: enc 
```

ユーザランドのプログラムは以下のように実行します。

```
        /usr/local/sbin/mpd5 -b

        /usr/local/sbin/setkey -f /usr/local/etc/racoon/setkey.conf

        /usr/local/sbin/racoon
```

mpd5を実行するとnetgraphで必要なカーネルモジュールがロードされます。

```
# kldstat
Id Refs Address    Size     Name
 1   29 0x80001000 3a97a0   kernel
 2    1 0xc0247000 5080     geom_md.ko
 3    1 0xc024d000 2f4e4    ufs.ko
 4    1 0xc027d000 92d8     unionfs.ko
 5    1 0xc0287000 2230     ng_socket.ko
 6    9 0xc028a000 a0bc     netgraph.ko
 7    1 0xc0295000 3c20     ng_mppc.ko
 8    1 0xc0299000 268      rc4.ko
 9    1 0xc029a000 3020     ng_l2tp.ko
10    1 0xc029e000 2560     ng_ksocket.ko
11    1 0xc02a1000 ae0      ng_tee.ko
12    1 0xc02a2000 1610     ng_iface.ko
13    1 0xc02a4000 51f0     ng_ppp.ko
14    1 0xc02aa000 243c     ng_vjc.ko
15    1 0xc02ad000 a60      ng_tcpmss.ko
```

unionfs.koまでZRouter由来で、これ以降がmpd5に必要なkoになります。

以下はユーザランドプログラム実行後のnetstatの抜粋です。

```
# netstat -a
Active Internet connections (including servers)
Proto Recv-Q Send-Q Local Address          Foreign Address        (state)
tcp4       0      0 *.5006                 *.*                    LISTEN
tcp4       0      0 localhost.5005         *.*                    LISTEN
udp4       0      0 10.0.1.19.isakmp       *.*                    
udp4       0      0 10.0.1.19.4500         *.*                    
udp4       0      0 10.0.1.19.l2f          *.*                    
Netgraph sockets
Type  Recv-Q Send-Q Node Address   #Hooks
ctrl       0      0 mpd73-stats:      0
ctrl       0      0 mpd73-eso:        0
ctrl       0      0 mpd73-cso:        0
ctrl       0      0 mpd73-lso:        0
data       0      0 mpd73-eso:        0
data       0      0 mpd73-cso:        0
data       0      0 mpd73-lso:        0
```

ポート5005,5006,l2f(1701)がmpd5が握っていて、ポートisakmp(500),4500がracoonが握っています。

![IPSec.png](https://qiita-image-store.s3.amazonaws.com/0/104066/4cdb7703-25f5-761e-ec8f-cce59f930b01.png)

# 内々の接続テスト

同一ネット内での接続確認はMac OS X 10.6で行い下記のように設定しました。

![MacVPN2.png](https://qiita-image-store.s3.amazonaws.com/0/104066/70a46c30-8254-1402-8b34-2e82ff7d9664.png)

![MacVPN.png](https://qiita-image-store.s3.amazonaws.com/0/104066/477e3079-fcf2-78d8-357c-6d9f3365c68e.png)

ターミナルでtail -f /var/log/system.logして接続すると、どこまで進んでいるかが分かります。VPNのエラーはセキュリティ上の配慮で明確なエラーを返さずだんまりなので、ログで確認するしかありません。

![スクリーンショット（2017-02-06 11.27.23）.png](https://qiita-image-store.s3.amazonaws.com/0/104066/c669e425-b7f3-0740-1e2f-af34e614a6f4.png)

接続中にサーバ側でインターフェースを確認すると以下のようになっている。

```
# ifconfig -a
rt0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=b<RXCSUM,TXCSUM,VLAN_MTU>
        ether 00:18:84:00:00:00
        inet 10.0.1.19 netmask 0xffffff00 broadcast 10.0.1.255 
        media: Ethernet 100baseTX <full-duplex>
        status: active
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
        options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6>
        inet 127.0.0.1 netmask 0xff000000 
        groups: lo 
enc0: flags=0<> metric 0 mtu 1536
        groups: enc 
ng0: flags=88d1<UP,POINTOPOINT,RUNNING,NOARP,SIMPLEX,MULTICAST> metric 0 mtu 128
0
        inet 10.0.1.19 --> 10.0.1.220 netmask 0xffffffff 
```

# 外からの接続テスト

外からアクセスするにはDDNSなどを利用してIPアドレスがわかるようにする必要があります。私はdhclient-exit-hooksにDDNSへの登録の処理を入れてあります。

外部から内部のテストをFirewallを入れ替えて試してみようとおもったところ、2017/2現在の12-CURRENTのipfw_natが調子が悪い。パケットは通るのだが、Mac OS Xのブラウザから開けるページもあるのだがいろいろなページが開けない。sys/netpfil/ipfwはv6対応のためか、いじられているのでそれの影響かな。。。

とりあえず11RなコードでビルドしてFirewallを焼き直して外からのアクセスを試してみた。Fierwallの設定がおこなわれているホストで追加で以下を実行した。

```
# kldload ipdivert
# natd -f natd.conf
# ipfw add 5 divert natd all from any to any
# ipfw add 6 allow esp from any to any
# ipfw add 7 allow udp from any to any
```

5番のルールを設定したときに接続切れるのですが、もう一度接続すると問題なく入れます。ipfwは失敗すると入れなくなったりするので、注意した方が良いです。ちょっと設定が雑なので、実際に使うときには穴ができないようにもう少し細かく設定した方が良いかもしれません。
ah(51)はMac OS Xなどでは使っていないようなので設定していません。

内から外と外から内の設定はipfw_natだけ、natdだけ、ipfw_nat/natdを両方使っての設定方法がありますが、良くネットで見かける最後のパターンで設定してみました。

```natd.conf
interface vlan2
port natd
redirect_port udp 10.0.1.19:500 500
redirect_port udp 10.0.1.19:4500 4500
redirect_proto esp 10.0.1.19
```
![VPNLAN.png](https://qiita-image-store.s3.amazonaws.com/0/104066/710c7705-339b-b6cc-2ec4-287cdd1641cb.png)

IPSec/L2TPなサーバでdefault route設定していなくて3時間くらいはまりました。


停電対策で自動起動するようにしたら、ポートフォワードが効かなくなりはまりました。上記の処理を自動起動の最後のほうにしたところ問題なくなりました。

Android 9な端末で確認していたのですが、この端末は一度VPN接続でこけると、次に試したときに正常に処理しなくなります。

FirewallとIPSec/L2TPを同じホストで動かす事もできると思いますが、分けた方がipfwの設定が分かりやすい気もします。またいじるときにFirewallの設定に失敗すると、ネットにつながらなくなり調べるにも不自由するので、分けた方が良いような気がします。

この設定をしていて、CURRENTは開発中のコードが入っていて、不安定になっている可能性もあり、Firewallのような実用品はReleaseを使った方が良い事も勉強になりました。

Android 7で接続できず調べてみた所ng_vjcでカーネルがクラッシュしていました。ng_vjcを調べてみたのですが、よくわからなかったので、mpd.confでvjcを無効にした所、正常に接続できるようになりました。

```
# workaound crash at ng_vjc by android 7
        set ipcp disable vjcomp
        set ipcp deny vjcomp
```

FreeBSD 12.0のリリース前にOpenSSLが1系になり、racoonなどがコンパイルできなくなっていたのですが、リリース時にはちゃんとportsがアップデートされて、コンパイルできるようになっていました。12でも問題なく使えています。

IOデータのWN-G300Rを使った環境からVPNを張る場合、設定の「IPSecパススルー」を有効にする必要がありました。

アクセスの記録は下記を追加すると、/var/log/utx.logに残るようになります。

```
l2tp_server:
	set auth enable system-acct
```

```
# last
foo     L_l2tp-1 10.0.1.144             Tue Mar 24 08:19 - 08:20  (00:00)
```

正常に動くようになれば通常のログは必要ないので、以下を設定すると良いです。

```
startup:
	log -all
```

当初全くログが出てなくて、調べてみたところhostnameが設定されてないとsyslogdがまったくログを吐かないためでした。

MPD5のWEBサーバはこんな表示でした。

![スクリーンショット 2020-03-23 10.01.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/7e310ad9-f547-c294-4fb7-d6874411efe6.png)

時々入れなくなる事があるが、しばらくして試すと大丈夫でインターネット側の問題のような気もする。

落雷などで停電するのは4年に一度くらいだが、停電して復旧後にサービスが開始できるようにしておくのが良い。

お金をかけて二重化することもできるが、今のところ致命的な障害は発生していない。

Windows 7でVPNを張る場合にダイアログを出さずに接続させるには

C:\Users\ユーザー\AppData\Roaming\Microsoft\Network\Connections\Pbk\rasphone.pbk

のPreviewUserPwを0にするとできました。

VPNサーバはいろいろありますが、ここで設定した、L2TP/IPSec(ikev1)が一番いろいろな端末でサポートされているようです。

たいていのFreeWiFiからVPN接続できますが、たまに接続できないところもあります。