ミラーモード設定を使ってWSL2上のnpm run devにローカルネットワークからアクセスする
これを改めてまとめたものです
WindowsとWSL2の開発環境はLinuxライクな開発環境をWindows上で再現するうえでとても有効だと考えています
そんなWSL上でWeb開発をしていると、どうしてもスマートフォンやタブレットからのアクセス環境を実機で確認したい場面があります
WSLでもそんなことできるのでしょうか?答えは可能です。ただ、結構特殊な設定が必要となりました(ここで言う特殊とは、Windows以外ではほぼ使えない知識という意味です)
ネットワークをミラーモードにする
Windows11 24H2であれば「wsl settings」というアプリケーションがスタートメニューのアプリ一覧にあるのでそれを開く。それ以前のものなら .wslconfig
または wsl.conf
ファイルをホームディレクトリに作成し、同様に設定する(細かい設定項目は各自調べてほしい。そんなに難しくないはず
以下wsl settingsを使用している前提で進める
まずアプリケーションのサイドバーの「ネットワーク」の項目でネットワークモードをデフォルトのNAT
から Mirrored
に変更する。続いて同じページの「ホストアドレスのループバック」をオンにする
設定的にはこれで完了。ただしファイアウォールの設定を変更しないと外部からの通信が届かないので続いてそれの設定を変更する
軽く解説:何を変更したのか
文字通りこの項は解説なので、さっさと動かしたい人と理解している人は飛ばしてもらって結構
そもそもWSL2は、Hyper-Vを利用した仮想環境上で動作するLinuxである
Hyper-Vは、LinuxでいうXenと同様のアーキテクチャを採用しており、この仮想化モードをオンにした状態でのWindowsはXenでいうところのdom0に該当する環境となっている
通常(デフォルト、つまりネットワークモード:NAT)のWSL2は一旦通信をすべてWindowsに転送した後、Windows側で仮想スイッチ経由でWSL2側に転送する動きをしている。この転送はNATを利用している(故にネットワークモード:NAT)
知識のある人は察しているだろうが、このNATモードでも適切な設定を行えばローカルネットワークからWSL2上の環境へアクセスすることはできる(以下の記事参考)
ネットワークモードをMirroredにすると、Windows側のネットワーク設定がそのままWSL側にも適用される。つまり共通のIPアドレス、共通のポート環境で両方に通信が届くようになる。例えばWindowsのIPアドレスが192.168.0.10/24だった場合、それがWSL側にも適用される
ただし、この状態ではローカルループバック(つまりlocalhost, 127.0.0.1/8, ::1/128)だけはお互いに通信されない(WindowsはWindowsだけ、WSLはWSLだけで閉じる)。これを有効にするのが「ホストアドレスのループバック」設定で、これを有効にすると相互にローカルループバックアドレスでの通信が可能になる
当然だがこの状態では共通のポートは利用できない。たとえばWindows側で8080を利用していた場合、WSL側では8080のポートは利用できない。上記の注意欄で一旦動くかどうか確認しろと言ったのはそのためである(場合によってはポートの競合などで動作しなくなっている可能性がある
ポートの競合を解決するにはポートを変更するか「無視されたポート(日本語項目名)」を設定する必要がある。項目名の直訳感が強いので直感的ではないが、これを設定すると設定したポートはWSL環境で閉じることができる(逆にWindows側でも閉じた状態になる)。例えば3306(MySQLのデフォルトポート番号)を無視されたポート設定に追加してWSL上でMySQLサーバを立ち上げると、このデータベースにはWSL上からしかアクセスできなくなる。これはWindowsを含めた外部からもアクセスできなくなる(と思われる。これは確認してないのでもしかしたら違うかもしれない)
本音を言えば、可能であればWSL2の環境にも仮想スイッチ経由でIPアドレスを振りたい気持ちはあるが、まあ残念ながらそれはできないようである。あくまでWSLは仮想環境であり仮想マシンではないのだろう
Hyper-Vファイアウォールで特定のポートでの通信を許可する
続いてHypver-Vのファイアウォールの設定を変更する
ちなみにWSL settingsにはHyper-Vファイアウォールを無効化する設定もある。無効化した場合この項目はスキップしてもらって構わない(あまり勧めないが、下記の通りWindows本体のファイアウォールもあるので無効化しても大丈夫といえば大丈夫 と思う
以下のPowershellのコマンドはすべて管理者権限で叩いている。管理者権限でPowershellを開いて始めるといい
まずWSLのVMCreatorIdを取得する
PS C:\Users\maretol> Get-NetFirewallHyperVVMCreator
VMCreatorId : {40E0AC32-46A5-438A-A0B2-2B479E8F2E90}
FriendlyName : WSL
ちなみにこのVMCreatorIdは共通らしいので、ぶっちゃけコマンドは不要
続いてデフォルトの設定を確認する
PS C:\Users\maretol> Get-NetFirewallHyperVVMSetting -PolicyStore ActiveStore -Name '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}'
Name : {40E0AC32-46A5-438A-A0B2-2B479E8F2E90}
Enabled : True
DefaultInboundAction : Block
DefaultOutboundAction : Allow
LoopbackEnabled : True
AllowHostPolicyMerge : True
DefaultInboundAction
が Block になっているのが確認できる。なお、もしこれがすでにAllowになっているのであればこの項目はスキップして構わない
DefaultInboundActionをAllowにする
あまり勧めないが、すべての通信を許可する場合(したくない場合は次の「指定の通信を許可する」に進む)
PS C:\Users\maretol> Set-NetFirewallHyperVVMSetting -Name '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}' -DefaultInboundAction Allow
私はこれをやっていないのでここからどうなるかわからないが、ドキュメントいわくこれでいいはずである。完了したらWindowsファイアウォールの設定に進む
指定の通信を許可する
ファイアウォールの設定を行う。一般的なファイアウォールと同様に「基本全てブロック→特定の通信を許可」という感じの設定になるため、デフォルトのBlock設定の一部に穴を開けるイメージで問題ない
New-NetFirewallHypverVRule
でその特定の通信を許可するルール作成ができる。以下はポート3000で起動するNext.jsの通信の設定(実際に使ったやつ)
PS C:\Users\maretol> New-NetFirewallHyperVRule`
-Name "NextDevServer"`
-DisplayName "Next.js dev server"`
-Direction Inbound`
-VMCreatorId '{40E0AC32-46A5-438A-A0B2-2B479E8F2E90}'`
-Protocol any`
-LocalPorts 3000`
-RemotePorts 3000
Name : NextDevServer
DisplayName : Next.js dev server
Direction : Inbound
VMCreatorId : {40E0AC32-46A5-438A-A0B2-2B479E8F2E90}
Protocol : Any
LocalAddresses : Any
LocalPorts : 3000
RemoteAddresses : Any
RemotePorts : 3000
Action : Allow
Enabled : True
EnforcementStatus : OK
PolicyStoreSourceType : Local
Profiles : Any
PortStatuses : {
SwitchName: 30BE601B-A2AB-4EDC-9AD5-9D2600CF7CF0
PortName: DA2FDCB8-493A-45A5-A884-9BD0B80DCC6B
Profile: Public
NetworkType: FSE
InterfaceGuid: {7CBD9F53-4593-4865-AEED-0ECFB4135EB3}
PartitionGuid: {75D3A8AD-3ED5-41BF-9EC6-1788394712FD}
VMCreatorId: {40E0AC32-46A5-438A-A0B2-2B479E8F2E90}
EnforcementStatus: OK
}
{
SwitchName: 30BE601B-A2AB-4EDC-9AD5-9D2600CF7CF0
PortName: D11CD891-3B31-4058-88C0-5135D10D05B2
Profile: Public
NetworkType: FSE
InterfaceGuid: {B124778B-4F2F-11EC-AFF8-806E6F6E6963}
PartitionGuid: {75D3A8AD-3ED5-41BF-9EC6-1788394712FD}
VMCreatorId: {40E0AC32-46A5-438A-A0B2-2B479E8F2E90}
EnforcementStatus: OK
}
{
SwitchName: 30BE601B-A2AB-4EDC-9AD5-9D2600CF7CF0
PortName: C54243C2-FE87-4615-89B7-EEB852084428
Profile: Public
NetworkType: FSE
InterfaceGuid: {3DA627A7-986D-4836-AB7F-2B3677172DF4}
PartitionGuid: {75D3A8AD-3ED5-41BF-9EC6-1788394712FD}
VMCreatorId: {40E0AC32-46A5-438A-A0B2-2B479E8F2E90}
EnforcementStatus: OK
}
設定のオプションは特に難しいものもなく何となく分かるのではなかろうか。例えばIPアドレスの範囲も指定したい場合は -RemoteAddresses
や -LocalAddresses
をつけてあげれば設定できるのは予想できる(私はやらなかったけどやったほうが良かったかもしれない)
Windowsファイアウォールの特定のポートでの通信を許可する
最後にWindows自体のファイアウォール設定を変更する
さっきの解説だとHyper-Vのファイアウォールだけで通信できそうだが、あくまで仮想環境であるため親マシンのWindowsの設定の都合を受けるようである
「設定アプリ」→「プライバシーとセキュリティ」→「Windowsセキュリティ」→「Windowsセキュリティを開く」でセキュリティアプリが開く
さらにそこから「ファイアウォールとネットワーク保護」→「詳細設定」をクリックすると「セキュリティが強化されたWindows Defender ファイアウォール」のアプリケーションが開く(下のスクショの画面が開くはず)
ちなみに面倒だったら Win+R でファイル名を指定して実行を開いて wf.msc で一発起動するよ(先に書け
このアプリケーションの左サイドバーの「受信の規則」をクリックしたあと、右のサイドバーの「新しい規則…」を押すと「新規の受信の規則ウィザード」のウィンドウが開く
あとはここで規則の種類をポートにして、(今回の場合は)ポート3000の通信を許可する設定を追加する。TCPとUDPはそれぞれ設定する必要があるので1つずつ作成する(もちろん片方だけでいい場合は片方設定すれば完了)。設定する必要があるのはプロトコルとポートだけで、認証等無しで接続を許可するせっていでいい
ただし、この時点で作成されたルールにはIPアドレスの設定がないので、一旦作成後にプロパティを開き、スコープのタブでIPアドレスをローカルのアドレスに指定してあげたほうがいい。CIDR表記を用いてローカルアドレスを指定する(例:192.168.0.0/23)
最後に
開発環境を起動し、まずはlocalhost等で同一端末からアクセスします
それができたらhostname -I
なりip addr
なりなんでもいいので自分のローカルIPアドレスを把握したうえで、今度は同一端末から上記のIPアドレスでアクセスします
それも完了したら、今度は同一ネットワークの端末からIPアドレスとポート番号を指定してアクセスしてみます
おそらく上記の設定がすべて完了してれば外からでも確認できるでしょう。もしできなかった場合
- 同一端末からアクセスできたが、同一ネットワークからアクセスできなかった場合
- ネットワークが同一ではない
- たとえばスマートフォンが接続された無線LANの機器の下に別のルータを設定してそこにPCがつながっていたり
- 実はスマートフォンがWi-Fiに接続されてなかったり
- ネットワークが同一ではない
- localhostではアクセスできたが、IPアドレスを用いると同一端末でもアクセスできなかった場合
- IPアドレスやポート番号が間違えている
- どこかの設定、あるいはアクセス時の指定で間違えている可能性があります
- 同一ネットワーク内での通信をルータが許可していない
- セキュリティソフトが別途ファイアウォールを動作させている
- IPアドレスやポート番号が間違えている
こんなところでしょうか。具体的な原因は個別の判断になるので確証は持てませんが、少なくとも上記の設定で私は接続できたのを確認しています
どうしても解決しない場合、WSLをわざわざ使わず適当なミニPCにLinuxを入れて開発環境の土台をそちらに移行し、VSCodeの機能やSSH等を利用して接続する開発環境を構築したたほうが楽なケースもあるかもしれません
こういう設定ははっきり言って生のLinuxのほうがやりやすいので……
Discussion