セイテクエンジニアのブログ かつて山市良と呼ばれたおじさんのブログ メモ. WSL2へのポートプロキシ設定に“ ”で苦労した話
2024年04月24日配信
2024年06月18日更新
執筆者:山内 和朗
このブログは、月曜、木曜更新を基本としてスタートしました。定例スケジュールは1つのテーマがしばらく続く形になっています。この定例スケジュールとは別に、重大なトラブル情報や、ちょっとした思い付きやアイデア、最近気になることなど、タイムリーに共有できるように、今回のように「メモ」として、不定期に差し込んでいくことにします。
今回は、公式ドキュメントからのコピペが期待通りに動かずに、数時間も苦労してしまった話です。Windows Subsystem for Linux(WSL 2)上のディストリビューションへのLANからのアクセスを可能にするために、ポートプロキシ(ポート転送)を構成したのですが...
現在、自分専用のラボ環境をAzure IaaSおよび物理サーバーに構築しようとしている最中です(詳しいことはこのブログの定例スケジュールにて来月に)。ラボ環境の基盤はWindows Server 2022のHyper-Vで用意し、Hyper-Vの仮想ネットワークでクローズドな評価環境を準備する予定です。評価や検証時に、本番環境に影響してはいけないので、電子メール送受信環境など、クローズドな環境内に予め用意しようと考えています。
1つの実現案として思いついたのは、Windows Subsystem for Linux(WSL 2)の活用です。WSL 2は本物のLinuxカーネルが動くシェル環境(一部GUIに対応)であり、Windows 10/11の機能だと思われがちですが、Windows Server 2022でも利用可能です(ただし、デスクトップエクスペリエンスが必要、Server Coreではうまくいかないと思います)。
WSL 2のLinux環境で、必要な環境(例えば、postfixとdovecotの電子メール環境《smtp、pop3、imap》)を用意しておくのです。WSL 2にはエクスポート/インポート機能があるので、スタンドアロンサーバーにWSL 2の利用環境を用意すれば、構成済みの環境をインポートするだけですばやく導入できると考えました(画面1)。WSL 2の既定のネットワーク構成では、ディストリビューションはWindowsで管理されるNATネットワークに接続され、localhost(127.0.0.1)によるホストからのアクセスが可能です。
画面1 Ubuntu.tarはpostfixとdovecotを構成済みのものをエクスポートしたもの。スタンドアロンサーバーにインポートするだけで、クローズドなメール環境が利用可能になる予定(メール環境の設定や動作は検証中)
NATネットワークは外部から遮断されているため、そのままでは外部から接続することはできません(画面2)。外部からの接続を可能にする方法の1つに、Windowsのポートプロキシを使用したポート転送があります。
画面2 WSL 2側のポートがWindows側のIPアドレスでLISTENされることはない
NATネットワークに接続されたWSL 2のディストリビューションには、Windows(WSL 2)によってIPアドレスが動的に割り当てられます。このディストリビューションの特定のTCPポートへのアクセス(例えば、今回の場合はSMTPポート「25」)を許可するには、netshコマンドで構成可能なポートプロキシを使用できます。その方法は以下の公式ドキュメントで説明されています。
WSL を使用したネットワーク アプリケーションへのアクセス|ローカル エリア ネットワーク (LAN) からの WSL 2 ディストリビューションへのアクセス
https://learn.microsoft.com/ja-jp/windows/wsl/networking#accessing-a-wsl-2-distribution-from-your-local-area-network-lan
最低限必要なことは、ポート番号とディストリビューションに動的に割り当てられたIPアドレスです。そのIPアドレスはディストリビューション側でhostname -Iを実行すれば分かりますし、Windows側からもwsl hostname -I(またはwsl -e hostname -Iまたはwsl -d <Distribution> -e hostname -I)を実行して取得することもできます。
これらの情報を取得したら、次のようにnetshコマンドを実行することでポートプロキシを構成できます(画面3)。
しかし、ここから数時間の苦悩が始まりました。
WSL 2側のIPアドレスを直接入力した場合は(この他にWindowsファイアウォールでの受信の規則の許可設定も必要)、期待通りにWSL 2側へポート転送されるのですが、=(wsl hostname -I)の方のコマンドラインはポートプロキシの設定はされるものの、ポート転送される気配がありません。しかも、netsh interface portproxy show v4tov4で両者の設定を比べてみても、全く同じプロキシ設定のように見るのです(画面4)。
画面4 2つの設定はまったく同じように見えるが、下のコマンドラインの設定のほうは全くポート転送してくれない(別のPCで再現)
数時間試行錯誤しましたが、うまくいきません。パラメーターの順番を変えたりして、実行されるコマンドラインを変数に入れて確認してみたところ、ようやく不自然な空白を見つけました。(wsl hostname -I)が返すIPアドレスの最後に、空白が1つ付いていたのです。おそらく、複数のIPアドレスを返すことがあるのを想定しての空白なのでしょう。Trim()メソッドで末尾(および先頭)の空白を取り除くことで期待通りに動くようになりました(画面5)。ちなみに、最初は日本語環境の問題かとも思いましたが、英語環境でも同様に空白が付いていました。つまり、公式ドキュメントのコマンドラインでは期待どおりに設定できないのです。
画面5 wsl -e hostname -Iが返すIPアドレスには最後に余計な空白が付いていた
というわけで、次のようなPowerShellスクリプトコードを実行すれば、WSL 2側のポート25へのポート転送を開始し、Enterの入力で終了し、設定のクリーンアップができるだろうと思います。なお、WSL 2は現在、IPv6に正式には対応していませんが、WSL 2がIPv6に正式対応したときには、返り値から前方にあるIPv4アドレスだけを取り出すコードが追加で必要になると思います。