🔀

ミラーモード設定を使ってWSL2上のnpm run devにローカルネットワークからアクセスする

2025/01/13に公開

https://zenn.dev/maretol/scraps/154d056f838489
これを改めてまとめたものです


WindowsとWSL2の開発環境はLinuxライクな開発環境をWindows上で再現するうえでとても有効だと考えています

そんなWSL上でWeb開発をしていると、どうしてもスマートフォンやタブレットからのアクセス環境を実機で確認したい場面があります

WSLでもそんなことできるのでしょうか?答えは可能です。ただ、結構特殊な設定が必要となりました(ここで言う特殊とは、Windows以外ではほぼ使えない知識という意味です)

ネットワークをミラーモードにする

Windows11 24H2であれば「wsl settings」というアプリケーションがスタートメニューのアプリ一覧にあるのでそれを開く。それ以前のものなら .wslconfig または wsl.conf ファイルをホームディレクトリに作成し、同様に設定する(細かい設定項目は各自調べてほしい。そんなに難しくないはず

以下wsl settingsを使用している前提で進める

まずアプリケーションのサイドバーの「ネットワーク」の項目でネットワークモードをデフォルトのNATから Mirrored に変更する。続いて同じページの「ホストアドレスのループバック」をオンにする

https://learn.microsoft.com/ja-jp/windows/wsl/networking#mirrored-mode-networking

設定的にはこれで完了。ただしファイアウォールの設定を変更しないと外部からの通信が届かないので続いてそれの設定を変更する

軽く解説:何を変更したのか

文字通りこの項は解説なので、さっさと動かしたい人と理解している人は飛ばしてもらって結構

そもそもWSL2は、Hyper-Vを利用した仮想環境上で動作するLinuxである

Hyper-Vは、LinuxでいうXenと同様のアーキテクチャを採用しており、この仮想化モードをオンにした状態でのWindowsはXenでいうところのdom0に該当する環境となっている

通常(デフォルト、つまりネットワークモード:NAT)のWSL2は一旦通信をすべてWindowsに転送した後、Windows側で仮想スイッチ経由でWSL2側に転送する動きをしている。この転送はNATを利用している(故にネットワークモード:NAT)

知識のある人は察しているだろうが、このNATモードでも適切な設定を行えばローカルネットワークからWSL2上の環境へアクセスすることはできる(以下の記事参考)

https://qiita.com/smallriv/items/698fb1373887aa1f7784

ネットワークモードを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のファイアウォールの設定を変更する

https://learn.microsoft.com/ja-jp/windows/security/operating-system-security/network-security/windows-firewall/hyper-v-firewall

ちなみに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アドレスやポート番号が間違えている
      • どこかの設定、あるいはアクセス時の指定で間違えている可能性があります
    • 同一ネットワーク内での通信をルータが許可していない
    • セキュリティソフトが別途ファイアウォールを動作させている

こんなところでしょうか。具体的な原因は個別の判断になるので確証は持てませんが、少なくとも上記の設定で私は接続できたのを確認しています


どうしても解決しない場合、WSLをわざわざ使わず適当なミニPCにLinuxを入れて開発環境の土台をそちらに移行し、VSCodeの機能やSSH等を利用して接続する開発環境を構築したたほうが楽なケースもあるかもしれません

こういう設定ははっきり言って生のLinuxのほうがやりやすいので……

Discussion