Yet Another WSLに外部から接続できない問題

WSLを便利に使っているのだが、新しいPCをセットアップしたところWSL2になった事で、外部からWSLで立ち上げたサービスへの通信ができくなった。
調べてみると、この問題を解決するには、netshでportproxy 使う方法と、ブリッジモードを使う方法があるようだ。ブリッジモードは、うまくいかなかったので、netsh portproxyを使った設定を記載する。
portproxyを使う場合、いくつかのポイントがあるので、問題の切り分けの仕方をまとめた。
トラブル対応の一つとして、Windows Firewall Defenderのログについても記載した。

- Windows Firewall Defender
    - 許可の設定とブロックの設定
- portproxyの設定
- サービスの問題

## wslからDNSが引けない！
2022/12/01に突然、wslからDNSが引けない状況になった
こちらの記事を参考にWindows Firewallの設定を行って解決。
Windows 11, version 22H2を当てた直後なので、これが関係するかも

https://qiita.com/sunasaji/items/55d6d74041d1d4e9bce2

 
## 参考:Portproxyとブリッジの設定

- [WSL2 は外部からのアクセスが面倒だった](http://var.blog.jp/archives/84997759.html)

# 基本的な流れ

ポートフォワードが起動時に実施するために、以下の対応を行っている


1. タスクスケジューラーからポートフォワードスクリプトを実行する
1. ~/.local/bin/start.sh からサーバープロセスを起動する



# WSLのIPアドレスを確認する
portproxyを設定するためには、WSLに割り当てられているIPアドレスを知る必要がある。WSLのIPアドレスは動的に割り当てられるので、固定のIPアドレスで書くことが出来ない。
WSLで、ifconfigやhostnameを使うことで確認できるが、スクリプトで自動化するためには、PowershellからWSLのIPアドレスを確認できる必要がある。

``` 
Powershellから見る場合
> wsl hostname -I

該当するIPだけを抜き出す
> bash -c "ip route |grep 'eth0 proto'|cut -d ' ' -f9"　# Unbuntu 10
> bash -c "ip route |grep 'eth0' |grep -v '/' |head -1 |cut -d ' ' -f2"  # Ubuntu 20 

```

WSL2のIPアドレスがわかれば、Powershell で設定が可能（管理者モードで実行する必要がある）
対象とするWindows側のIPアドアレスを明記する方法もあるが、"\*"を設定することで、複数のインタフェースがある場合も対応できる。面倒も少ないので、ここでは"\*"を指定。

```
$WSL_IP = xxx.xxx.xxx  
$WIN_IP = "*"          # 実装しているすべてのIPアドレス
$PORT = xxxx           # Listenするポート

> netsh.exe interface portproxy add v4tov4 listenaddress=$WIN_IP listenport=$PORT connectaddress=$WSL_IP connectport=$PORT
> netsh.exe interface portproxy show all
> netsh.exe interface portproxy delete v4tov4 listenaddress=$WIN_IP listenport=$PORT # Delete 
```


# WSL2のportproxyを自動設定するスクリプト

ありがたいことに同じ悩みを持つ先人がWindows Defender Firewallの設定を含めたスクリプトを公開してくれていた（大変参考になりました）。

- [Windows WSL2に外部から直接アクセスするための設定](https://rcmdnk.com/blog/2021/03/01/computer-windows-network/)　rcmdnk's blog から

以下のスクリプトは、WSL2のバージョンで、"ip route"出力フォーマットが違うので、どちらのバージョンでも動くように修正したもの。すべてのパターンでうまくいくとは限らない。
このスクリプトを、タスクスケジューラで起動時に呼び出すようにする。

```
# Source: https://rcmdnk.com/blog/2021/03/01/computer-windows-network/
# WSLのIPアドレス取得部分を修正したもの
#
# Original work on Ubuntu 16.04.7 LTS
# % ip route
# default via 172.26.240.1 dev eth0
# 172.26.240.0/20 dev eth0 proto kernel scope link src 172.26.254.202
#
# Added: for Ubuntu 20.04.4 LTS
# % ip route
# none 172.17.64.0/20 dev eth0  proto none  metric 256
# none 172.17.64.1 dev eth0  proto none  metric 256
#
# 【WSL】UbuntuのOSの基本的な情報(OS名、バージョン名、アーキテクチャ、カーネルバージョン)を調べる方法
# https://mylife8.net/post-1184/ 
#

if (!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Administrators")) { Start-Process powershell.exe "-File `"$PSCommandPath`"" -Verb RunAs; exit }

$ip  = bash.exe -c "ip route |grep 'eth0 proto'|cut -d ' ' -f9"
if( ! $ip ){
    $ip  = bash.exe -c "ip route  | grep 'eth0' | grep -v '/' | head -1 | cut -d ' ' -f2" # added
    if(! $ip){
        echo "The Script Exited, the ip address of WSL 2 cannot be found";
        exit;
    }
}

# All the ports you want to forward separated by comma
$ports=@(22,3000,8080);   # set ports you want to forward
$ports_a = $ports -join ",";

# Remove Firewall Exception Rules
iex "Remove-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' ";

# Adding Exception Rules for inbound and outbound Rules
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Outbound -LocalPort $ports_a -Action Allow -Protocol TCP";
iex "New-NetFireWallRule -DisplayName 'WSL 2 Firewall Unlock' -Direction Inbound -LocalPort $ports_a -Action Allow -Protocol TCP";

for( $i = 0; $i -lt $ports.length; $i++ ){
  $port = $ports[$i];
  iex "netsh interface portproxy add v4tov4 listenport=$port listenaddress=* connectport=$port connectaddress=$ip";
}
# Show proxies
iex "netsh interface portproxy show v4tov4";
```

# それでも接続できない

全て正しく設定したはずなのに、それでも通信ができない場合がある。
投げ出したい気持ちになるのだが、冷静に考えると以下の要因が考えられる。

- 送信元の問題
1. 接続するIPアドレスかポートが間違っている（DHCP環境下ではよくある問題）
1. 送信元のPCからパケットが出ていない
（送信元のWindows Defender Firewallのブロックなど）

- 受信側の問題
1. Windows Defender FirewallでDROPしている
1. 上記portproxy が適切に動いておらず、WSL2のサービスに到達していない
1. サーバーが立ち上がっていないか、狙った設定になっていない

送信の問題は、色々と調べられると思うので割愛する。
受信側の問題は、なかなか厄介だが以下の切り分けが出来る。

- WSLのIPアドレスで接続できる場合は、サーバーは狙った通りに動いている
- WindowsのIPアドレスでも接続で接続できる場合は、portproxy の設定もあっている
Windows Defender FirewallでDROPされている可能性が高い

なお、netshのローカルIPアドレスを"\*"の設定することは問題ないようだ。複数のIPアドレスで動いている場合で、全てのIPからの接続を受け付けたい場合は"\*"が適切。

```
サーバーを立ち上げたPC上で実行
> telnet 172.xx.xx.xxx           # WSLのIPアドレス
> telnet 192.168.1.xxx port　　　# WindowsのIPアドレス
```

厄介なのが、Windows Defender FirewallでDROPしている場合。
当然、設定を確認することが基本だが、目的とした接続を適切に”許可”している場合でも接続できないことがある。このような場合は、Windows Defender Firewallのルールで”ブロック”側のルールを確認すると、思わぬ設定がある場合がある。
この記事を書いたきっかけは、WSL2で実行するperlに対する受信が全てブロックされていることが原因で、急にperlで書いたプログラムが動かないトラブルに遭遇したこと。証拠？はないが、Windows Updateの後で通信が出来なくなったので、この時のWindows Updateでルールが追加されたと推測している（セキュリティ的には、理解できないでもないが、これをやられるとつらい）。

perlのブロック設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529969/f9032964-e38b-aaaf-d123-4b8f0ed4f288.png)

”操作”でソートし、ブロックを抜き出したもの
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/529969/13e68e4f-ef81-64b7-572b-2e4e6fa69637.png)

## Windows Firewall のログの確認方法

Windows Denfender FirewallでDROPされているかを確認したいときがある。Defaultでは、ログを記録しないが、DROPログ、ALLOWログを記録することができる。
設定方法やログの見方は、こちらをご参照いただきたい。Windows Firewall Defenderのプロパティからログを設定などが紹介されている。

- [Windows ファイアウォールのログ取得の設定方法やリアルタイム閲覧について](https://www.haruru29.net/blog/how-to-get-windows-firewall-log/)

ログを調べる際には、grepやtail -fを使いたいところだが、Windows Defender Firewallのログは、パーミッションが厳しく、WSLから見ることが出来ない。
この問題についても、紹介されている。
その一つを紹介すると、管理者モードで立ち上げたPowerShellのコンソールから、以下のスクリプトを実行することで、grepやTail -Fと同様の事が可能。
なお、出力がバッファされているようで、表示されるまでタイムラグがあるので、おまじないに-ReadCount 0を追加した。

```
 Get-Content C:\windows\system32\LogFiles\Firewall\pfirewall.log -ReadCount 0　-Tail 0 -Wait"
 Get-Content C:\windows\system32\LogFiles\Firewall\pfirewall.log -ReadCount 0 | Select-String "SEARCH STR"
```

# tcpdump
WSL2ではtcpdumpが動くので、tcpdumpを使うことでパケットがWSLまで届いているのかを確認することが出来る。


# それでも謎は多い

それでも残った謎は少なくない
- Windows Defender Firewallのルールは、チェックの順番があるようで、通るときと通らないときがある
- 動作するセッティングのPCでWSL2のIPアドレスが変わっていることがある
不思議なことにportproxyはそのまま動いているように見える。
もしかすると、サービスは立ち上げた時のIPアドレスで動いていると考えるとあっているのだろうか

地道に確認するしかないな。