OpenConnectによるVPN接続
概要
- Cisco AnyConnectではなく、互換クライアントのOpenConnectを利用してVPN接続を行う方法についてのメモ
- Cisco AnyConnectはVirtualboxの仮想NICと相性が悪く、VPN接続中はルーティングを全てトンネル側に向けてしまうため、仮想環境のネットワークに接続できない問題がある
- Virtualboxのネットワークに接続できないと、Vagrant上の開発環境や、Genymotion(Androidエミュレータ)が利用できなくなるので困る
- 問題の報告は以前から上がっているようだけれど、改善の気配はないみたい
- 動作確認環境は Mac OS X 10.10.5 (Yosemite)
事前準備
必要なアプリケーションのインストール
仮想ネットワークデバイス TUN/TAP
- TunTap - Home
- Yosemite以降、未署名のKernel Extensionがインストールできなくなったため sudo nvram boot-args="kext-dev-mode=1" などの回避手段が取られていたが、2014/11/18から署名済パッケージが配布されているため、現在はインストール上の問題は発生しない
$ brew cask install tuntap
Cisco互換のVPNクライアント(?) vnpc
$ brew install vpnc
OpenConnect
$ brew install openconnect
各種証明書のエクスポート
- クライアント証明をKeyChainからエクスポートする
- サーバ証明書をKeyChainからエクスポートする
- KeyChain Access.app の login にある認証局ドメイン(ca.example.comなど)の証明書を選択し、右クリックメニューなどからExportする
- 出力先は ~/.cisco/certificate/server/ca.example.com.cer に設置した
接続方法
以下の様なコマンドで接続できる。
$ sudo openconnect \ --user=username \ --authgroup=vpn \ --script=/usr/local/etc/vpnc-script \ --cafile=~/.cisco/certificate/server/ca.example.com.cer \ --certificate=~/.cisco/certificate/client/cert.p12 \ vpn.example.com
--user
は、各自接続ユーザ名に置き換える--script
は、vpncでインストールされたパスを指定する--cafile
と--certificate
は、前項でエクスポートした各証明書パスを指定するvpn.example.com
は、接続先のVPNサーバ名に置き換える--authgroup
は環境によって不要かもしれない(指定値は接続先VPN管理者に確認する)- 設定項目はファイルに切り出して接続時に読み込ませることもできる
接続終了
- 接続終了は Ctrl + C などで切断する
- 起動時に -b オプションを付加するとバックグランドで実行されるので、その場合は kill などで殺す
問題点など
signer not found のエラーが出る
- CA証明書指定が間違っているかもしれない
- とりあえず yes とか答えてスルーすることもできるが、当然微妙
- 雑に済ますなら --no-cert-check オプションでスキップすることもできる
SSL negotiation with vpn.example.com Server certificate verify failed: signer not found Certificate from VPN server "vpn.example.com" failed verification. Reason: signer not found Enter 'yes' to accept, 'no' to abort; anything else to view: yes
パスワード入力を最大3回求められる
- 「sudo」「クライアント証明書」「VPNサーバへのログイン」のそれぞれで、パスワード入力が求められる
- セキュリティ上のリスクを承知の上で、以下の様なパスワード回避手段を採用することができる
openconnect実行にsudoパスワードを省略する
$ sudo visudo -f /etc/sudoers --- # 下記の1行を追加する %admin ALL=(ALL) NOPASSWD: /usr/local/bin/openconnect
クライアント証明書からパスワードを除去する
$ cd ~/.cisco/certificate/client/ $ openssl pkcs12 -in cert.p12 -nodes -out temp.pem Enter Import Password: (エクスポート時のパスワードを入力) MAC verified OK $ openssl pkcs12 -export -in temp.pem -out cert_unprotected.p12 Enter Export Password: (何も入力せずにEnter) Verifying - Enter Export Password: (何も入力せずにEnter) $ rm temp.pem
VPNサーバへのログインパスワードを標準入力で渡す
- 標準入力からパスワードを受け取ることができるので、起動時のコマンドに含めてしまう
echo -n "password" | sudo openconnect --passwd-on-stdin vpn.example.com
切断後にネット接続できなくなることがある
- 何故か自然復旧することもあるけれど、たいてい接続できない状態のままになる
- pingを打っても下記のようなエラーが表示される感じ
$ ping 192.168.0.1 PING 192.168.0.1 (192.168.0.1): 56 data bytes ping: sendto: No route to host
- 原因はDefault Gatewayに
RTF_IFSCOPE
が立っているので、通常の接続時にgatewayとして認識してくれないみたい- ルーティングテーブルを出力すると、
Flags
にI
が含まれていることが確認できる
- ルーティングテーブルを出力すると、
$ netstat -nr Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default 192.168.211.254 UGScI 0 0 en1 127 127.0.0.1 UCS 0 0 lo0 127.0.0.1 127.0.0.1 UH 2 327379 lo0 ...
- netstatのドキュメントによると、
I
はRTF_IFSCOPE
を示すとのことI RTF_IFSCOPE Route is associated with an interface scope
- 説明はちゃんと理解できていないけど、そのインターフェース専用のgatewayとして設定されるということなのかな…
A route which is marked with the RTF_IFSCOPE flag is instantiated for the corresponding interface.
- いずれにしてもこれがあると都合が悪いので正常に戻したい
- 復旧手段
$ route -n get -ifscope en1 default route to: default destination: default mask: default gateway: 192.168.211.254 interface: en1 flags: <UP,GATEWAY,DONE,STATIC,PRCLONING> recvpipe sendpipe ssthresh rtt,msec rttvar hopcount mtu expire 0 0 0 0 0 0 1500 0
- 削除〜追加のコマンドは、このような感じ(
en1
は各環境のインターフェース名にあわせる)
$ DEFAULT_GATEWAY=`route -n get -ifscope en1 default | grep gateway | awk '{ print $2 }'` $ sudo route delete default -ifscope en1 $ sudo route add default $DEFAULT_GATEWAY
- ルーティングテーブルを出力すると、
Flags
からI
がなくなっていることが分かる
$ netstat -nr Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default 192.168.211.254 UGSc 210 2 en1 127 127.0.0.1 UCS 0 0 lo0 127.0.0.1 127.0.0.1 UH 2 104 lo0 ...
それでは、快適なVPNライフをお過ごしください。