WSL2の localhost:3000 にスマホからアクセスするための設定

# はじめに
WSL2で立ち上げたアプリケーションの`http://localhost:3000`へ，スマホでアクセスしたいことがあると思います．
しかし，MacやUbuntu，WSL2を使わずにWindowsで立ち上げた時と違い，単純にWSL2のホストへアクセスすることはできません．

WSL2で作成したアプリケーションへアクセスするための設定を，スクリプトで簡単に設定する方法紹介します．

# 環境
```
OS: Windows 10
WSL2: Ubuntu-20.04
```

# もくじ
- [手順](#手順)
    - [準備](#準備)
    - [設定用スクリプト](#設定用スクリプト)
    - [PowerShellから実行](#powershellから実行)
    - [スマホからアクセス](#スマホからアクセス)
- [問題点](#問題点)
- [対処](#対処)
    - [準備](#準備-1)
    - [設定消去スクリプト](#設定消去スクリプト)
    - [PowerShellから実行](#powershellから実行-1)
- [おわりに](#おわりに)
- [参考記事](#参考記事)

# 手順
PowerShellスクリプトを作成します．
コピペでOKです！
## 準備
適当な場所に`open_wsl_port.ps1`など適当な名前の`.ps1`ファイルを作成します．

メモ帳などで開きます．

## 設定用スクリプト
以下のスクリプトを`.ps1`ファイルに書き込みます．
ポートフォワーディングを設定しています．

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

$ip = bash.exe -c "ip r |tail -n1|cut -d ' ' -f9"
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,3001,8080);
$ports_a = $ports -join ",";

# Add envvironment variable is port number
[Environment]::SetEnvironmentVariable('PORT',$ports,"User");

# 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";
```
`$ports=@(22,3000,3001,8080);`の部分に，アクセスしたいポートを列挙します．

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

`[Environment]::SetEnvironmentVariable('PORT',$ports,"User");`でユーザ環境変数を設定していますが，[理由は後述](#設定消去スクリプト)します．

## PowerShellから実行
2. ファイルを保存しておきます．
1. WSL2を起動しておきます．
1. ファイルを右クリックし「PowerShell で実行」を選択します．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506931/7cbdae30-ab1a-9d72-b11e-1c165d95b011.png)
PowerShellが一瞬開きます．これで，設定は完了です！簡単でしょ？

## スマホからアクセス
### 同一LAN内に入る
PCと同じWifiにスマホを接続します．かならず同じものに接続し，同一LAN内にPC，スマホがある状態にします．

### WSLでローカルホストでアプリを起動
ReactでもVueでもRailsでも何でもいいので，スマホからアクセスしたいアプリケーションを起動します．

### WindowsのIPにアクセス
PCでコマンドプロンプトを開き，以下のコマンドを実行します．

```:cmd
ipconfig
```

その中に`Wireless LAN adapter Wi-Fi:`の`IPv4 アドレス: 192.168.x.xx`というものがあると思います．(x: 伏字)

これをスマホのブラウザで入力します．
たとえば`http://192.168.x.xx:3000`(ローカルホストが3000番ポートで開いた場合)

### 接続できない時
1. 先ほどのポートフォワーディング時にポートを指定できていない．
    1. 今回，WSLのポート3000にアクセスするので，スクリプトで`@(3000)`の記述は必須
1. もしくは，同一LAN内に接続できていない．
1. windowsのファイアウォールでポート3000が制限されてしまっている．
1. ポートフォワーディングの設定が古くなっている
    1. WSLのホストが新しくなったりしていませんか？
    1. [こちらを参考にホストの齟齬が起きていないかチェックしましょう](https://qiita.com/RoaaaA/items/b3a134a6320dfc8b3abc#%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95)

等を疑ってみてください．

# 問題点
一つ問題があります．
`WSL2を立ち上げなおすたびにホストが変わってしまう`ので，そのたびにこのスクリプトを走らせなければいけません．
何が問題かというと，古い設定値が残っているとポートフォワーディングが不適切に行われることで，そのポートへのアクセスができなくなります．

私がまさしくこの地雷を踏んでしまい四苦八苦しました．

https://qiita.com/RoaaaA/items/b3a134a6320dfc8b3abc

https://ja.stackoverflow.com/questions/88395/npm-start%E3%81%A7%E7%89%B9%E5%AE%9A%E3%81%AE%E3%83%9D%E3%83%BC%E3%83%88%E3%81%AB%E3%81%A0%E3%81%91%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84

https://ja.stackoverflow.com/questions/88367/npm-start-%E3%81%97%E3%81%A6%E3%82%82-react-%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AB-localhost-%E3%81%A7%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%81%A7%E3%81%8D%E3%81%AA%E3%81%84


# 対処
[こちらの記事で示す通り](https://qiita.com/RoaaaA/items/b3a134a6320dfc8b3abc#%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95) ポートフォワーディングの古い設定を更新する，もしくは消してしまえば，またアクセスできるようになります．

しかし，毎回手作業で１つ１つポートフォワーディングの古い設定を消すのもめんどくさいので，設定値を消すスクリプトも同時に用意しておきましょう！

## 準備
適当な場所に`close_wsl_port.ps1`など適当な名前の`.ps1`ファイルを作成します．

メモ帳などで開きます．

## 設定消去スクリプト
以下のスクリプトを`.ps1`ファイルに書き込みます．
ポートフォワーディングの設定を消去します．

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

# Get current portforwarding numbers from environment variable
$ports = @([Environment]::GetEnvironmentVariable("PORT","User") -Split " ");

for( $i = 0; $i -lt $ports.length; $i++ ){
  $port = $ports[$i];
  iex "netsh interface portproxy delete v4tov4 listenport=$port";
  echo "closed Port:$port"
}

# Remove environment values is port number
[Environment]::SetEnvironmentVariable('PORT',"","User")

# Show proxies
iex "netsh interface portproxy show v4tov4";
```
先ほどユーザ環境変数にポート番号を設定していたのは，このポートフォワーディングの設定を消すときに使いたかったからです．(もっといい方法あったら教えてください．)

ユーザ環境変数`PORT`には常にWSLとポートフォワーディングするために開放したポートが記されるようにしているので，この値をもとに設定の消去を行います．

また，`[Environment]::SetEnvironmentVariable('PORT',"","User")`で定義したユーザ環境変数を消しています．

参考：[PowerShellでシステム環境変数を取得・上書・追加・削除する](https://qiita.com/kishiro/items/6424ad9da39188cc2465)


## PowerShellから実行
先ほどと同様です。

2. ファイルを保存しておきます．
1. WSL2を起動しておきます．
1. ファイルを右クリックし「PowerShell で実行」を選択します．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/506931/ecfe0ad3-ecfb-0912-e395-807991e3eba8.png)

PowerShellが一瞬開きます．これで，設定を消去できます．

# おわりに
手作業で設定していくと意外とめんどくさいので，自動化していきましょう！
ポートを開放し，作業が終わったら忘れないように閉じることを心がければ，私のような地雷は踏まなくてよくなるはず．

良いWebアプリ制作ライフを！

それでは:wave:

# 参考記事
- [PowerShellでシステム環境変数を取得・上書・追加・削除する](https://qiita.com/kishiro/items/6424ad9da39188cc2465)
- [Windowsでnesh portproxyコマンドでポートフォワーディングする方法](https://ityorozuya.hatenablog.com/entry/2020/04/04/051022)
- [【PowerShell】環境変数を設定する](https://hosopro.blogspot.com/2017/01/powershell-set-environment-variable.html#:~:text=%E4%B8%80%E6%99%82%E7%9A%84%E3%81%AB%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0,%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B%E3%81%A0%E3%81%91%E3%81%A7%E3%81%99%E3%80%82&text=PS%20C:%5Cwork%3E%20$env,%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%97%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%80%82)
- [PowerShell/文字列をカンマなどで区切り文字を指定し配列に格納する・Split](https://win.just4fun.biz/?PowerShell/%E6%96%87%E5%AD%97%E5%88%97%E3%82%92%E3%82%AB%E3%83%B3%E3%83%9E%E3%81%AA%E3%81%A9%E3%81%A7%E5%8C%BA%E5%88%87%E3%82%8A%E6%96%87%E5%AD%97%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%97%E9%85%8D%E5%88%97%E3%81%AB%E6%A0%BC%E7%B4%8D%E3%81%99%E3%82%8B%E3%83%BBSplit)

# 関連記事
- [WSL2でReactの localhost:3000 にアクセスできなくなった時](https://qiita.com/RoaaaA/items/b3a134a6320dfc8b3abc#%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95)