はじめに
WSL2ネットワークを新機能でブリッジモードに変更する(IPv6も利用可)で紹介したように、WSL preview 0.51にて外部仮想スイッチを使ったブリッジ接続の導入が試されています。この手法を使うことでいくつかの不満を解消できると思いますが、Windowsホストとは論理的に独立した接続となることでいままで出来ていたことができなくなることもあります。
その一つがWindowsホストで接続したVPNを経由できなくなることです。
default
vEthernet(WSL) Ethernet/WiFi等物理インタフェース
WSL2(Linux) ---------- Windows ------------------------------ ルーター
|
VPN-----------------------------------------VPN endpoint
WSL2はWindows内の仮想スイッチに接続されるため、外部との通信はWindowsホストを経由する。
そのため、WindowsでVPN接続を行うとWSL2からのトラフィックもVPNを経由することができる。
ところが、ブリッジ接続に切り替えると、WSL2からのトラフィックは論理的にはWindows内部
を経由しない。このため、WindowsでVPN接続を行ってもすべてのトラフィックが接続ルーター
にながれてしまう。
bridge
vEthernet(外部)
WSL2(Linux) ------------------------------------------------- ルーター
Windows ------------------------------------------------- ルーター
|
VPN------------------------------------------------------------------VPN endpoint
これを回避する方法として思いつくものをいくつか挙げると。。
- WSL2を標準のネットワーク、ブリッジの両方に接続する
- WSL2のネットワークをdefaultに戻す
- VPNに頼らないセキュアな接続を構築する
- WSL2自身でVPN接続を行う
こんなところでしょうか。
現在実験的にブリッジ接続が提供されていますが、これに加えてこれまでの方法と両立できれば解決しそうな気がします。しかし、そのためには、Microsoftの対応を待つ必要があります。更に、できることが増えれば利便性は増しますが、同時にメンテナンスコスト、機能の複雑性を増すことになるためこういう機能が積極的に採用されるのは難しいと考えています。
そう考えると、VPNが必要なシーンではネットワークの接続をデフォルトに戻すのが一番素直です。幸い、.wslconfigにてnetworkingMode
を変更するだけでモードが切り替わるので特に難しいこともありません。
そういう意味ではわざわざWSL2自身でVPN接続を行うのは複雑さを増すだけに、お勧めできる方法ではないのですが、興味の対象として試してみたので備忘録として共有することにします。
WSL2(Linux) ------------------------------------------------- ルーター
|
VPN------------------------------------------------------------------VPN endpoint
今回の条件
VPN接続にもいくつかのツールがあるため、それぞれツールによって使い方が異なると思いますが、今回はL2TP/IPSecでのVPN接続を試します。
単なる実験ということもありますし、WSL2のインスタンス内に直接設定するよりは管理が楽なので、Dockerコンテナで構築します。DockerHUBにいくつかクライアントイメージがあるのでそれを活用するのが良いでしょう。
今回はubergarm/l2tp-ipsec-vpn-clientを試してみましょう。このコンテナはprivilegedを用いてホスト(=WSL2のLinux)のネットワークに直接手を加えるものようなので、パッケージ化されているだけで実際にはホストのデバイスを直接変更するのと変わりありません。
手順としては
- WSL2にDockerの実行環境を用意する
- カーネルをカスタマイズする
- 実行環境をカスタマイズする
筆者環境はWSL2にDocker Engineをインストールして利用していますが、おそらくDocker Desktopでも同様に利用可能と思います。
l2tp-ipsec-vpn-clientコンテナはいくつかカーネルモジュールを必要としますのでカーネルをカスタマイズします。
さらに、筆者環境では接続IDの齟齬でエラーが出たのでスクリプトを調整しています。
カーネルをカスタマイズする
カーネルのカスタマイズは塩田氏の記事「WSL(Windows Subsystem for Linux)のカーネルは差し替えられる」がわかりやすいと思います。他にも参考にできる記事はネットに沢山あるので環境に合った情報に従ってください。
一点、.configの生成方法は/proc/config.gz
を使うよりもMicrosoft/config-wsl
にひな形があるのでそちらを使った方が良いと思います。
zcat /proc/config.gz | sudo tee .config >/dev/null
↓ ↓ ↓
cp Microsoft/config-wsl .config
ディストロにより多少違いはあるようですが、基本的には開発用のグループパッケージをインストールして、make中にエラーが出たら足りないツールをいくつか追加するという作業で解決するので、さほど難しくないと思います。
カーネルの生成に成功するところまで出来たら、.configに以下の3行を追記してカーネルを再構築してください。
CONFIG_NET_KEY=y
CONFIG_L2TP=y
CONFIG_PPPOL2TP=y
これらのオプションが含まれたカーネルがあれば、先のコンテナを実行できるようです。.wslconfigで再構築したカーネルを指定して、カーネルが置き換わっていることを確認してください。
コンテナの実行
コンテナの実行はubergarm/l2tp-ipsec-vpn-clientに例示があるように環境変数に接続情報をセットして実行すれば完了です。
筆者環境ではVPNサーバがNATの内側にあるために、IDがあわず接続エラーとなっていました。そこで、/startup.shに以下の1行を加えています。
echo " rightid=$VPN_SERVER_ID" >> /etc/ipsec.conf
同様のエラーが発生した場合には、イメージから/startup.shを取り出して、
compose.yamlで/startup.shを上書きしてしまうのが良いでしょう。
services:
l2tp:
image: ubergarm/l2tp-ipsec-vpn-client
environment:
VPN_SERVER_IPV4: <VPN Server IP address>
VPN_SERVER_ID: <VPN Server ID>
VPN_PSK: 'xxxxxxxxxxxxxxxxxx'
VPN_USERNAME: 'xxxxxxxxxxxxx'
VPN_PASSWORD: 'xxxxxxxxxxxxx'
volumes:
- ./startup.sh:/startup.sh:ro
privileged: true
network_mode: "host"
接続が完了するとppp0
にIPアドレスが割り当てられるので、ubergarm/l2tp-ipsec-vpn-clientの例にあるように必要な経路をppp0に向ければVPN接続の出来上がりです。