wsshuttle
WSL2 から手軽に sshuttle を行う wsshuttle を作ったので、お試しください。
インストール
sudo apt install -y ipcalc
pip3 install sshuttle # apt によってインストールされるものはバージョンが古くて動かない可能性がある
sudo wget https://raw.githubusercontent.com/yabeenico/wsshuttle/main/wsshuttle -O /usr/local/bin/wsshuttle
sudo chmod +x /usr/local/bin/wsshuttle
実行例
# ルーティングテーブルを変更するため、実行する WSL2 のターミナルに Windows の admin 権限が必要。
wsshuttle -r ssh-server -x 157.0.0.0/8 0/0
dry-run
wsshuttle -r ssh-server -x 157.0.0.0/8 0/0 --dry
route.exe delete 157.0.0.0 mask 255.0.0.0
route.exe delete 3.3.3.3 mask 255.255.255.255
route.exe delete 0.0.0.0 mask 128.0.0.0
route.exe delete 128.0.0.0 mask 128.0.0.0
route.exe add 157.0.0.0 mask 255.0.0.0 192.168.3.1 metric 1 if 7
route.exe add 3.3.3.3 mask 255.255.255.255 192.168.3.1 metric 1 if 7
route.exe add 0.0.0.0 mask 128.0.0.0 172.18.187.223 metric 1 if 46
route.exe add 128.0.0.0 mask 128.0.0.0 172.18.187.223 metric 1 if 46
sshuttle -l 0.0.0.0:0 -x 3.3.3.3 -r ssh-server -x 157.0.0.0/8 0/0
route.exe delete 157.0.0.0 mask 255.0.0.0
route.exe delete 3.3.3.3 mask 255.255.255.255
route.exe delete 0.0.0.0 mask 128.0.0.0
route.exe delete 128.0.0.0 mask 128.0.0.
要約
外出先 win から 自宅 NW に VPNを張りたい。
コマンドは管理者権限を持った wsl2 で実行。
- インストール
sudo apt install -y sshuttle ipcalc
- env: 環境変数を設定
export IF_WIN_MAC=XX-XX-XX-XX-XX-XX # 手動で設定: win の 物理 NIC の MAC アドレス
export HOME_NW=192.168.3.0/24 # 手動で設定: 自宅の NW (LAN)
export HOME_GIP=$(ssh ssh-server curl inet-ip.info 2>/dev/null) # もしくは手動で設定
export HOME_NW_ADDR=$(ipcalc $HOME_NW | grep ^Address | awk '{print $2}')
export HOME_NW_MASK=$(ipcalc $HOME_NW | grep ^Netmask | awk '{print $2}')
export IF_WIN_IDX=$(route.exe print | grep -i "$(echo $IF_WIN_MAC | perl -pe 's/-/ /g')" | awk -v FS=. '{print $1}' | sed 's/ //g')
export IF_WIN_IP=$(arp.exe -a | grep ^Interface | grep $(printf %x $IF_WIN_IDX) | awk '{print $2}')
export VIF_WIN_IP=$(grep -o '172.*' /etc/resolv.conf)
export VIF_WIN_IDX=$(arp.exe -a | grep ^Interface | grep $VIF_WIN_IP | awk '{print $4}' | perl -pe '$_=hex')
export VIF_WSL2_IP=$(hostname -I)
$ env | egrep 'IF_W|HOME_' | sort
HOME_GIP=30.30.30.30
HOME_NW=192.168.3.0/24
HOME_NW_ADDR=192.168.3.0
HOME_NW_MASK=255.255.255.0
IF_WIN_IDX=21
IF_WIN_IP=10.0.0.2
IF_WIN_MAC=XX-XX-XX-XX-XX-XX
VIF_WIN_IDX=33
VIF_WIN_IP=172.21.48.1
VIF_WSL2_IP=172.21.49.46
- lan: 宛先が 192.168.3.0/24 のパケットのみをトンネリング。
route.exe add $HOME_NW_ADDR mask $HOME_NW_MASK $VIF_WSL2_IP metric 1 if $VIF_WIN_IDX
sshuttle -r ssh-server -l 0 $HOME_NW
- global: 全てのパケットをトンネリング。
route.exe delete 0.0.0.0
route.exe add $HOME_GIP mask 255.255.255.255 $IF_WIN_IP metric 1 if $IF_WIN_IDX
route.exe add 0.0.0.0 mask 0.0.0.0 $VIF_WSL2_IP metric 2 if $VIF_WIN_IDX
sshuttle -r user@$HOME_GIP:22 -l 0 0/0 -x $HOME_GIP # ssh 接続先は適宜設定 (ドメインの名前解決は不可)
- default: VPN を解除。
route.exe delete 0.0.0.0
route.exe add 0.0.0.0 mask 0.0.0.0 $IF_WIN_IP metric 1 if $IF_WIN_IDX
目的
外出先 win から自宅の NW (192.168.3.0/24) に簡易 VPN を張る (画像を参照)。
以下、lan と global の2パターンについて解説する。
- lan: 宛先が 192.168.3.0/24 のパケットのみをトンネリング。
- 外出先 win から
curl.exe 192.168.3.2
を叩いて結果を得る。 (or ブラウザで192.168.3.2
を開く)
- 外出先 win から
- global: 全てのパケットをトンネリング。
- 外出先 win から
curl.exe inet-ip.info
(グローバル IP 取得) を叩いて30.30.30.30
の結果を得る。
- 外出先 win から
手順
- 図の
routing
:route.exe
を利用し、 win が送信するパケットを wsl2 を迂回するように、 win のルーティングテーブルを設定する。 (wsl2 のシェルを管理者権限で実行する必要あり) - 図の
shuttle
:sshuttle
を利用し、 wsl2 から ssh 接続可能な NW に対して簡易 VPN を張る。
解説
- wsl2 は、図に示すとおり、ゲスト OS とホスト OS が仮想 NW で接続される。
- VIF_WIN、VIF_WSL2 は、その仮想 NWに接続された仮想 NIC。
- IF_WIN は、win の物理 NIC。
-
route.exe add 192.168.3.0 mask 255.255.255.0 172.21.49.46 metric 1 if 33
- 宛先が
192.168.3.0/24
のパケットを、NIC インデックス ==33
の NIC から172.21.49.46
に向けて転送。 - metric は優先度。小さいほど優先度が高い。
- 宛先が
-
sshuttle -r ssh-server -l 0 192.168.3.0/24 -x 192.168.3.100
- 宛先が
192.168.3.0/24
のパケットを ssh-server 経由でトンネリング。 - ただし、宛先が
192.168.3.100
のパケットはトンネリングしない。 -
-l 0
は-l 0.0.0.0/0.0.0.0
の略で、全ての送信元のパケットを許可。
- 宛先が
実用的なスクリプト
要旨に記述したコマンドに初期化処理や後始末を付け加えたもの。
check.sh
VPN が機能しているかを確認する、
- グローバル IP の確認
- 20.20.20.20: 外出先
- 30.30.30.30: 自宅
- web-server (192.168.3.2) への疎通確認
check.sh
#!/bin/bash
curl inet-ip.info
curl -sS --connect-timeout 1 192.168.3.2 | head -n 1
curl.exe inet-ip.info
curl.exe -sS --connect-timeout 1 192.168.3.2 | head -n 1
env.sh
env.sh
#!/bin/bash
export IF_WIN_MAC=XX-XX-XX-XX-XX-XX # 手動で設定: win の 物理 NIC の MAC アドレス
export HOME_NW=192.168.3.0/24 # 手動で設定: 自宅の NW (LAN)
export HOME_GIP=$(ssh ssh-server curl inet-ip.info 2>/dev/null) # もしくは手動で設定
export HOME_NW_ADDR=$(ipcalc $HOME_NW | grep ^Address | awk '{print $2}')
export HOME_NW_MASK=$(ipcalc $HOME_NW | grep ^Netmask | awk '{print $2}')
export IF_WIN_IDX=$(route.exe print | grep -i "$(echo $IF_WIN_MAC | perl -pe 's/-/ /g')" | awk -v FS=. '{print $1}' | sed 's/ //g')
export IF_WIN_IP=$(arp.exe -a | grep ^Interface | grep $(printf %x $IF_WIN_IDX) | awk '{print $2}')
export VIF_WIN_IP=$(grep -o '172.*' /etc/resolv.conf)
export VIF_WIN_IDX=$(arp.exe -a | grep ^Interface | grep $VIF_WIN_IP | awk '{print $4}' | perl -pe '$_=hex')
#export VIF_WIN_IP=$(arp -a | awk -v FS='[()]' '{print $2}' | sort | head -n 1)
export VIF_WSL2_IP=$(hostname -I)
$ source env.sh
$ env | egrep 'IF_W|HOME_' | sort
HOME_GIP=30.30.30.30
HOME_NW=192.168.3.0/24
HOME_NW_ADDR=192.168.3.0
HOME_NW_MASK=255.255.255.0
IF_WIN_IDX=21
IF_WIN_IP=10.0.0.2
IF_WIN_MAC=XX-XX-XX-XX-XX-XX
VIF_WIN_IDX=33
VIF_WIN_IP=172.21.48.1
VIF_WSL2_IP=172.21.49.46
default.sh
VPNを解除。
default.sh
#!/bin/bash
source env.sh
# set -x
route.exe delete $HOME_GIP
route.exe delete $HOME_NW_ADDR
route.exe delete 0.0.0.0
route.exe add 0.0.0.0 mask 0.0.0.0 $IF_WIN_IP metric 1 if $IF_WIN_IDX
$ ./default.sh # set -x をアンコメントして実行した結果
+ route.exe delete 30.30.30.30
The route deletion failed: Element not found.
+ route.exe delete 192.168.3.0
The route deletion failed: Element not found.
+ route.exe delete 0.0.0.0
OK!
+ route.exe add 0.0.0.0 mask 0.0.0.0 172.28.100.51 metric 1 if 21
OK!
$ ./check.sh
20.20.20.20
curl: (28) Connection timed out after 1001 milliseconds
20.20.20.20
curl: (28) Connection timed out after 1000 milliseconds
グローバル IP が外出先 (20.20.20.20) かつ、web-server とも疎通していない。
lan.sh
宛先が HOME_NW == 192.168.3.0/24
のパケットのみをトンネリング。
lan.sh
#!/bin/bash
source env.sh
set -x
./default.sh &>/dev/null
route.exe add $HOME_NW_ADDR mask $HOME_NW_MASK $VIF_WSL2_IP metric 1 if $VIF_WIN_IDX
sshuttle -r ssh-server -l 0 $HOME_NW
./default.sh &>/dev/null
$ ./lan.sh
+ ./default.shl
+ route.exe add 192.168.3.0 mask 255.255.255.0 172.21.49.46 metric 1 if 33
OK!
+ sshuttle -r ssh-server -l 0 192.168.3.0/24
client: Connected.
^Cclient:
client: Keyboard interrupt: exiting.
+ ./default.sh
$ ./check.sh # sshuttle 実行中に別のシェルで実行
20.20.20.20
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
20.20.20.20
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
グローバル IP は外出先 (20.20.20.20) だが、web-serverとは疎通している。
global.sh
全てのパケットをトンネリング。
global.sh
#!/bin/bash
source env.sh
set -x
./default.sh &>/dev/null
route.exe delete 0.0.0.0
route.exe add $HOME_GIP mask 255.255.255.255 $IF_WIN_IP metric 1 if $IF_WIN_IDX
# 宛先が HOME_GIP (30.30.30.30) のパケットは例外として、物理 NIC (IF_WIN_IDX == 21) から送信する。
route.exe add 0.0.0.0 mask 0.0.0.0 $VIF_WSL2_IP metric 2 if $VIF_WIN_IDX
# 上記例外以外の全てのパケットは win の 仮想 NIC (VIF_WIN_IDX == 33) 経由で WSL2 の 仮想 NIC (VIF_WSL2_IP == 172.21.49.46) に転送。
sshuttle -r user@$HOME_GIP:22 -l 0 0/0 -x $HOME_GIP
# 全てのパケットをトンネリング。
# ただし、宛先が HOME_GIP (30.30.30.30) のパケットを除く。
# HOME_GIP をトンネリング対象から除外しないと、HOME_GIP に ssh 接続できなくなる。
# また、名前解決の問い合わせパケットもやり取りできなくなるため、ssh の接続先は IP アドレス直打ちにする。
./default.sh &>/dev/null
$ ./global.sh
+ ./default.sh
+ route.exe delete 0.0.0.0
OK!
+ route.exe add 30.30.30.30 mask 255.255.255.255 172.28.100.51 metric 1 if 21
OK!
+ route.exe add 0.0.0.0 mask 0.0.0.0 172.21.49.46 metric 2 if 33
OK!
+ sshuttle -r user@30.30.30.30:22 -l 0 0/0 -x 30.30.30.30
client: Connected.
^Cclient:
client: Keyboard interrupt: exiting.
+ ./default.sh
$ ./check.sh # sshuttle 実行中に別のシェルで実行
30.30.30.30
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
30.30.30.30
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
グローバル IP が自宅 (30.30.30.30) かつ、web-server とも疎通している。