WindowsでLinuxなどを扱えるWSL2ですが、 WSL1とは結構構成が変わっています。
ネットワーク周りも変わっていて改めて設定を行ったのでそれについて。
WSL1でのネットワーク設定
WSL1ではWindowsのネットワークをそのまま使う形で、 例えばWSL内でsshサーバーを建てるとWindowsホストの22番ポートなどを 直接使う形になっていました。
Windows側でファイアウォールなどの設定を外部からアクセスできるようにすれば アクセスできる状態でした。
WSL2でのネットワーク設定(Windows内)
昨年リリースされたWSL2。 より普通のLinuxぽい使い方が出来るようになりました。
WSL2では立ち上がるUbuntuなどが仮想サーバーの様な 振る舞いをするようになり、 独自のIPアドレスを持っています。
Windows内ではこのアドレスを知っていればアクセスできるわけですが、 このIPアドレスが起動するたびに変わるため結構面倒。
これを回避するために、WSL2へはlocalhost
でアクセス出来る仕様があります。
これはWSL2の開発途中で追加されたものですが、現在は デフォルトで有効になっています。
WSL2ではWindowsの C:\Users\<ユーザー名>\フォルダの中に .wslconfig という名前のファイルを作って設定を書いておくとWSLの起動時にこのファイルの中身を読みこんでその設定が有効になります。
ここへ、
1 2 |
|
としておくとWSL2へのlocalhost
でアクセスが無効になります。
これをTrue
にすると有効になる、という記事がありますが、
これは当初この機能が導入された当時はデフォルト無効な状態で
有効にする必要があったからで、今は以下にあるようにデフォルトがTrueで有効になっています。
この機能によってWSL2の中でhttpdサーバーをたてて、http://localhost
とかに
アクセスするとWSL2で動かしているhttpdサーバーにアクセス出来るようになっています。
勿論、WSL2のIPアドレスを調べてそのアドレスを指定してもアクセスできます。
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: bond0: <BROADCAST,MULTICAST,MASTER> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether 2a:8f:ed:30:01:5b brd ff:ff:ff:ff:ff:ff
3: dummy0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether ee:62:ad:a6:3e:0a brd ff:ff:ff:ff:ff:ff
4: sit0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/sit 0.0.0.0 brd 0.0.0.0
5: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 00:15:5d:30:e9:41 brd ff:ff:ff:ff:ff:ff
inet 172.18.229.192/20 brd 172.18.239.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::215:5dff:fe30:e941/64 scope link
valid_lft forever preferred_lft forever
もしくは
$ ip r
default via 172.18.224.1 dev eth0
172.18.224.0/20 dev eth0 proto kernel scope link src 172.18.229.192
でeth0
で見えている172.18.229.192
がWSL2のアドレス。
(172で始まるクラスBのプライベートアドレスが割り振られている。)
これを使ってhttp://172.18.229.192
でアクセスできます。
これであれば、localhostForwarding=False
でもアクセスできます。
ちなみに、WSL2の中でWindows自体のIPアドレスを知りたい場合は、
Windows側のコマンドである
ipconfig.exe
(/mnt/c/Windows/system32/ipconfig.exe
)
が使えるのでこれを使うと
$ ipconfig.exe
Windows IP 構成
イーサネット アダプター vEthernet (WSL):
接続固有の DNS サフィックス . . . . .:
リンクローカル IPv6 アドレス. . . . .: fe80::b5af:1841:475a:8a27%50
IPv4 アドレス . . . . . . . . . . . .: 172.18.224.1
サブネット マスク . . . . . . . . . .: 255.255.240.0
デフォルト ゲートウェイ . . . . . . .:
イーサネット アダプター イーサネット:
接続固有の DNS サフィックス . . . . .:
リンクローカル IPv6 アドレス. . . . .: fe80::2d90:1387:f61b:2f48%5
IPv4 アドレス . . . . . . . . . . . .: 192.168.11.2
サブネット マスク . . . . . . . . . .: 255.255.255.0
デフォルト ゲートウェイ . . . . . . .: 192.168.11.1
Wireless LAN adapter ローカル エリア接続* 9:
...
な感じでWindowsのIPアドレス(192.168.130.1)とは別にWSLがvEthernetとして認識されて仮想ネットワークが作られていることが分かります。
WSL2でのネットワーク設定(Windows外部から)
localhostForwarding=True
の設定の時点で
localhost
でアクセスできる様になるのでWindowsのIPアドレスにも直結している様な感じもしますが
実際にはそうはなっていません。
Windows内からでもhttp://192.168.11.2
としてもWSLで動かしているサーバーにはアクセス出来ない状態です。
sshサーバーを立ててWSL内からssh 192.168.11.2
としてもアクセスできません。
これはhttpdやsshで使っている80番や22番のポートが192.168.11.2とは結びついていないからです。
この辺なんとなくアドレスとポートはまとめて1つで、
アドレスの呼び方(localhost:80
なのか192.168.11.2:80
なのか)が違っても
同じものと思っていましたが、
実際にはこれらはすべて別のものとして管理されている、ということです。
localhostForwarding=True
とすることで、WSL側のlocalhost
とWindows側のlocalhost
で
ポートが統一される、という状態にはなっていますが、
外部向けのWindowsのIPアドレスのポートには結びついてない状態です。
これを結びつけるためにはWSLの外から作業をしてやる必要があります。
具体的には以下のようなPowerShellのスクリプトを実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
以下のものを参考にしてちょっとアップデートしたスクリプトです。
[WSL 2] NIC Bridge mode 🖧 (Has TCP Workaround🔨) · Issue #4150 · microsoft/WSL
WSL側のIPアドレスは変わるので固定では出来ず、毎回上の様に何らかの方法で取得する必要があります。
追記: 2021/05/16
3行目のところで、
Windowsが複数のネットワークインターフェースを持っている場合などはtail -n1
だと別のものを選んでしまう可能性もあるので、
状況によって
bash -c "ip route |grep 'eth0 proto'|cut -d ' ' -f9"
などインターフェースを指定するなどする必要があります。
(Thanks to jx @ comment)
追記ここまで
netsh interface portproxy add
というところでポートをつないでいます。
古い接続がある場合にdelete
してから、という記述も見かけましたが、
少なくとも手元の環境ではdelete
しなくても新しいadd
をすれば上書きされていたので
特にdelete
をする必要はありません。
これをwsl_port.ps1とかで保存して実行すれば 22, 3000, 8080番のポートをWSLとWindowsの外部向けIPアドレスとでつないでいます。 (最初の行にあるのが管理者として実行するようにするおまじないなので、ファイルを右クリックで出てくるメニューでPowerShellで実行を実施すればOK。)
また、同時にファイアウォールの設定もこれらのポートについて外すようにしています。
これで、外部からもhttp://192.168.11.2:8080
とかでWSLのサーバーにアクセスすることができます。
(この例だとクラスCのプライベートアドレスなので同じプライベートネットワーク内にあるものからであれば、ですが。)
Windowsの中からも自分のhttp://192.168.11.2:8080
でWSLのサーバーにアクセス出来るようになります。
Windows起動時につなげる
外部からの接続を常時する必要がなければ必要な時に上のスクリプトを実行すれば良いわけですが、 常時使いたい、という場合はタスクスケジューラーに仕込んで ログイン時に上のスクリプトを実行すると楽できます。
Windowsの検索でtask
とかしてタスクスケジューラーを見つけて立ち上げます。
操作基本タスクの作成からタスクを作ります。
- 名前、説明: 適当に
wsl
など。 - トリガー: ログオン時
- 操作: プログラムの開始
- プログラム/スクリプト:
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
- PowerShellの実態へのパス。
- 引数の追加:
-ExecutionPolicy RemoteSigned C:\Users\User\wsl_port.ps1
-ExecutionPolicy RemoteSigned
を追加することで自作スクリプトの実行を許可。その後にスクリプトへのパスを追記。
- プログラム/スクリプト:
- 完了: [完了]をクリックしたときに、このタスクの[プロパティ]ダイアログを開くにチェックして完了を押す。
- 出てきたプロパティの全般タブにある最上位の特権で実行するにチェックしてOK。
これでログイン時にWSLの特定のポートをWindows側の外部IPアドレスにつなげることが出来ます。
この方法だと起動中にWSLを再起動した場合にはIPアドレスが変更されるため、 再びこのスクリプトを実行する必要があります。
必要であれば、再起動コマンドを上のスクリプトに追加して 再起動と同時にポートを繋げる作業をするスクリプトにしても良いかもしれません。
WSLの再起動
WSLの再起動の話をしたのでそれについて少し。
PowerShell(管理者権限)上で
PS C:\WINDOWS\system32> wsl -l -v
NAME STATE VERSION
* Ubuntu Running 2
docker-desktop Stopped 2
docker-desktop-data Stopped 2
みたいにすると今動いているディストリビューションが分かります。
ここではUbuntu
が動いています。
ここで
PS C:\WINDOWS\system32> wsl --terminate Ubuntu
とすると、Ubuntu
を停止しますが、即座に再起動を行います。
--terminate
の代わりに-t
でも可。
これをスクリプトの先頭に書いて、少しスリープさせてポートを付けなおす様な ことをすれば良いとは思うのですが、 使っているLinuxのディストリビューションが変わる可能性があり、そのたびに 書き換えないといけません。
現在はUbuntu
というバージョンなしの名前になっているので、Ubuntuを使い続けるのであれば
そこま気にしなくても良いのですが、
バージョン名のついている状態や、他のLinuxを使ったりする場合には使ってるものをちゃんと把握して
やってもらいたいところ。
そこで、wsl -l -v
の出力を見て使っているものを把握することが考えられますが、
この出力が現状、とくに日本語環境だとうまく使えない状態になっています。
wsl.exe outputting unicode to stdout · Issue #4607 · microsoft/WSL
上のIssueにありますが、例えばPowerShell版grepのSelect-String
を使って
PS C:\WINDOWS\system32> wsl -l |Select-String Ubuntu
とかやっても何も出ません。
PS C:\WINDOWS\system32> wsl -l
Linux 用 Windows サブシステム ディストリビューション:
Ubuntu (既定)
docker-desktop
docker-desktop-data
とちゃんと出てそうなのに。
試しに出力をパイプに渡してmore
とかで見てみると
PS C:\WINDOWS\system32> wsl -l |more
L
i
n
u
x
(u
W
i
n
d
o
w
s
オ0ヨ0キ0ケ0ニ0・
ヌ0」0ケ0ネ0・モ0・・キ0・・:
U
b
u
n
t
u
(
稙喙)
こんな感じになっていて、日本語の文字化けもそうなんですが、
そもそも各文字が1行ずつ出力されている状態なので、
Select-String
では捕まらない状態になっています。
なので現状日本語環境でwsl -l
などを使って使っているディストリビューションを確認して
どうこうするのは出来ません。
必要なら英語環境にしてしまう、とかですね。