|   | 
| 山口県角島大橋 | 
WSL2「Windows Subsystem for Linux
    2」への外部からのアクセスは,「netsh」コマンドによるポートフォワーディング手法が一般的です。
  
    しかし,この手法は,PCの電源再起動が起こるとHyper-V仮想環境のIPアドレスが変更される可能性があり,その都度再設定する必要があります。
  
  
    パソコンの電源再起動の度にWSL2のIPアドレスを調べて,「netsh」コマンドによりポートフォワーディング設定することは大変な手間です。
  
  
    そこで,このコマンド設定を自動化する方法について学習します。
  
   ※「netsh」コマンドは、ネットワーク全般の制御用コマンドです。
    
    【実証前提条件】
  
  
    
        ・パソコン : Windows11 pro 24H2 (IPアドレス:IPv4固定値)
      
  
  
    ・Linux ディストリビューション : Ubuntu-24.04 LTS
  
  
    ・WSL2  バーション   :   2.4.10.0 
  
  
  それでは学習を始めます。
WSL2へ外部からアクセスするための自動設定手順
  
      WSL2へ外部からアクセスする場合の設定手順について,あらためて考えると以下のようになります。
    
  
      手順①:WSL2が稼動するパソコンのファイアーウォールポートを許可する。
    
  
      手順②:WSL2が稼動するHyperーVのファイアーウォールポートを許可する。
    
  
      手順③:Windowsに来る通信メッセージをWSL2にポートフォワーディングする。
    
  
      手順④:リモート通信アプリのインストールと設定
    
  
      この中で,電源の再起動で設定を変更しなくてはならないのは,再起動でWSL2のIPアドレスが変化する恐れのある手順③です。
    
  
      しかも,この手順③のポートフォワーディングは,「netsh」コマンドを管理者権限で実行する必要があります。
    
  
      そのため,自動化に必要なスクリプトの仕様は次のようになります。
    
- スクリプトの起動が管理者権限を有してない場合は管理者権限で再起動する。
- 初期値(文字コード,ログファイル名,ポート番号テーブル)を設定する。
- 「netsh」コマンドで既に設定した定義を削除する。
- WSL2のIPアドレスを新規に取得する。
- 「netsh」コマンドでネットワークフォワーディングを新たに設定する。(この作業をポート番号ごとにポート番号テーブルが空になるまで繰り返す。)
- パソコンの電源オンに同期してこのスプリクトが実施されるように定義する。
  
      以下,各々についてスクリプトを作っていきます。なお,このスクリプトは今後主流になるであろう「Windows PowerShell」を中心に作成します。 
      1.管理者権限で起動するスクリプトの作成
  
  
      スクリプトが管理者権限で稼働していることをチェックし,稼働してない場合管理者権限で再起動するコード例を以下に示します。
    
PowerShell
if(!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Administrators")) {
  Start-Process powershell.exe "-ExecutionPolicy RemoteSigned -File `"$PSCommandPath`"" -Verb RunAs -WindowStyle Minimized
  exit
}
  若干,説明します。
  
    管理者権限で起動している場合は,そのままで後続を実行しますが,そうでなければ当スクリプトを管理者権限で再実行して,「exit」で終わるようにしています。
  
    PowerShellを起動する時に,プロセスの起動オプションとして「-ExecutionPolicy  RemoteSigned」を加えて実行権を与えています。
  
  
    PowerShellはいろいろな事ができるので,セキュリティ上での制約が多く,「Process」,「LocalMachine」,「CurrentUser」などの実行権を意識しないと上手く動きません。
  
      この辺りのことは,後でもう少し詳しく述べます。
    
  「RunAs」は管理者権限を意味します。「`"$PSCommandPath`"」は当スクリプト自身を意味します。
  
  「-WindowStyle Minimized」を加えているのは,「powerShell」の画面が開くと邪魔なので,最小サイズで再起動するようにしているだけです。
    2.初期値(文字コード,ログファイル名,ポート番号テーブル)の設定
  
    スクリプトを作成するにあたり,文字コードを設定します。「PowerShell」は最初はShiftJISになっていると思います。
  
    従って,「netsh」コマンドなどの出力メッセージは「PowerShell」コンソール上に日本語で出力されると思います。
  
    この日本語出力メッセージをファイルに出力すると「文字化け」することが多く,文字コード変換など手間がかかりますので,今回は英語表記で全て対応することにします。
  
    文字コードをUTF8で統一するため,以下のコマンドを記述します。
PowerShell
chcp 65001
  
    続いて,ログファイル名を定義しておきます。スクリプトを稼働させると,上手く動作しないことが多く,どうしても何処で終了したかを知る必要があり,そのため,動作内容をログに記録することが望ましいです。
  
  
    そのためのログファイル名を定義します。なお,このログファイル名はフルパスで記述します。相対アドレス等を使うとコーディングを短く出来るのですが,管理者権限を与えたりすると実行フォルダが変わったり,思わぬエラーを招くことがあるからです。
  
    以下のようにログファイル名を定義します。格納フォルダは適宜指定してください。今回,ログファイル名は「netsh_log.txt」としています。
PowerShell
$netsh_log = "C:<格納フォルダパスを記述>\netsh_log.txt"
  
      次に設定したいポート番号は,複数あることを考慮して以下のようにテーブル化します。
  今回はリモートアクセスツールのための22番だけです。ほかにもある場合は「,」で繋ぎます。
    
PowerShell
$ports= @(22)
    3.既に設定したネットワーク定義を削除
  
      既にポート番号に設定したネットワーク定義がある場合は,以下のコードで削除します。
    
PowerShell
for( $i = 0; $i -lt $ports.length; $i++ ){
  $port = $ports[$i];
  iex "netsh interface portproxy delete v4tov4 listenport= $port Listenaddress= *"
}
  
      今回は,「Listenaddress」を指定しない場合を想定しました。WindowsのIPアドレスを指定する場合には,「Listenaddress」の「*」にIPアドレスを記述します。
    
ポートがテーブルにある限り繰り返しています。
4.WSL2のIPアドレスを新規に取得
  
      WSL2のIPアドレスを取得する方法はいろいろありますが,最近,コマンドプロンプトやPowerShell上で「Bash.exe」が使えるようになりました。
    
  
      このため,LinuxコマンドでIPアドレスを取得できる代表的なものとして「hostname」コマンドと「ip」コマンドがありますが,ここでは前者を利用してみます。
    
PowerShell
$virtual_ip = bash.exe -c "hostname -I"
if( ! $virtual_ip ){
  Get-Date -format "yyyy/MM/dd HH:mm" > $netsh_log
  echo "wsl2 vertual_ip address not exist" >> $netsh_log
  exit
}
  
      このスクリプトでは,IPアドレスが取得できなかった場合は,エラーメッセージをログ出力して終了しています。
    
  
      参考までにipコマンドによるIPアドレス取得スクリプトも記述します。
    
PowerShell
$virtual_ip = bash.exe -c "ip r | tail -n1 | cut -d ' ' -f9"
if( ! $virtual_ip ){
  Get-Date -format "yyyy/MM/dd HH:mm" > $netsh_log
  echo "wsl2 vertual_ip address not exist" >> $netsh_log
  exit
}
  
      「ip」コマンドの場合は,「|」パイプで繋いで加工しているのが特徴です。
    
    5.ネットワークフォワーディングの新規設定
  
      IPアドレスが取得できたら,以下のようにネットワークファワーディングの設定スクリプトを作成します。
    
PowerShell
for( $i = 0; $i -lt $ports.length; $i++ ){
  $port = $ports[$i];
  iex "netsh interface portproxy add v4tov4 listenport= $port listenaddress= * connectport= $port connectaddress= $virtual_ip"
}
    
        「
        netsh」コマンドを使用して、ポートフォワーディングの設定を行います。
      
  
  
    
        「iex」コマンドは,「””」内のコマンドを実行します。ポート番号がテーブルにある限り繰り返しています。
      
  
  
    6.スクリプトの自動起動設定
  
      これらのスクリプトを全て繋ぐと以下のようなコードになります。仮に,このスクリプトのファイル名を「wsl2_portforward_auto.ps1」とします。 
wsl2_portforward_auto.ps1
### netshによるPortForwarding Code ###
#管理者権限でスクリプトを起動する
if(!([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole("Administrators")) {
  Start-Process powershell.exe "-ExecutionPolicy RemoteSigned -File `"$PSCommandPath`"" -Verb RunAs  -WindowStyle Minimized
  exit
}
#文字コードを定義する
chcp 65001
#ログ取得ファイルを定義する
$netsh_log = "C:<格納フォルダパスを記述>\netsh_log.txt"
#ポートテーブルを作成する。
$ports= @(22)
#既存のネットワーク設定を削除する(IPv4対応)
for( $i = 0; $i -lt $ports.length; $i++ ){
  $port = $ports[$i];
  iex "netsh interface portproxy delete v4tov4 listenport= $port Listenaddress= *"
}
#wsl2のIPアドレスを取得する
$virtual_ip = bash.exe -c "hostname -I"
if( ! $virtual_ip ){
  Get-Date -format "yyyy/MM/dd HH:mm" > $netsh_log
  echo "wsl2 vertual_ip address not exist" >> $netsh_log
  exit
}
#IpアドレスのPortForwarding設定をポートTBL数繰り返す
for( $i = 0; $i -lt $ports.length; $i++ ){
  $port = $ports[$i];
  iex "netsh interface portproxy add v4tov4 listenport= $port listenaddress= * connectport= $port connectaddress= $virtual_ip"
}
  
      このスクリプトをPowerShell上で動かしてポートフォワーディングの設定が出来れば,完成ということになります。
    
  
      しかし,このスクリプトをPowerShell上で動かすと,「このシステムではスクリプトの実行が無効になっている・・・」という赤字のエラーが出ます。
    
  
      これは,PowerShellがいろいろな事ができるので,セキュリティが掛けられているためです。
    
  以下,「PoweShellの実行ポリシー」について述べますので,よくご存じの方は【自動化のためのバッチファイルの作成】まで飛んでください。
  
      PowerShellを起動して,次のコマンドを入力してみてください。
    
PowerShell
Get-ExecutionPolicy -list
  次のようなメッセージが出ると思います。
メッセージ
        Scope ExecutionPolicy
        ----- ---------------
MachinePolicy       Undefined
   UserPolicy       Undefined
      Process       Undefined
  CurrentUser       Undefined
 LocalMachine       Undefinedここで実行スコープは、 
- MachinePolicy(グループポリシーでコンピュータに係るもの)
- UserPolicy(グループポリシーで現在のユーザに係るもの)
- Process(powerShell下のプロセスに係るもの)
- CurrentUser(現在のユーザに適用するもの)
- LocalMachine(コンピュータの全ユーザに係るもの)
  の5種類に分類されます。
  そして,各々のスコープに対して
- 「Restricted(制限)」
- 「AllSigned(署名あり実行可能)」
- 「RemoteSigned(ローカル上と非ローカル上【署名あり】実行可能)」
- 「Unrestricted(全てのスクリプト実行可能【非ローカル上のスクリプト許可要】)」
- 「Bypass(すべてのスクリプト実行可能)」
  の実行ポリシーを適用できます。
  
      例えば,スコープ「LocalMachine」に「RemoteSigned」の実行ポリシーを与える場合は,次のように管理者権限でPowerShellを起動してに次のコマンドを入力します。
    
管理者: PowerShell
Set-ExecutionPolicy RemoteSigned -scope Localmachine
   「[Y] はい(Y)  [A] すべて続行(A)  [N] いいえ(N)・・・」と問われますので,「Y」と入力します。ここでは「-scope」オプションを記述しましたが,無くても「Localmachine」がデフォルトです。 
   ここで,先ほどの「Get-ExecutionPolicy -list」コマンドを入力すると,次のように出力されます。
    
メッセージ
        Scope ExecutionPolicy
        ----- ---------------
MachinePolicy       Undefined
   UserPolicy       Undefined
      Process       Undefined
  CurrentUser       Undefined
 LocalMachine    RemoteSigned
  
      この状態で,先ほどのスクリプトを実行すると動くと思います。しかし,これですと誰でも実行できてしまい セキュリティ上好ましくないので,「Set-ExecutionPolicy Undefined」で元に戻しておいてください。
    
  
      セキュリティ上,最も良いスコープは「Process」になります。このプロセスへの実行ポリシーはプロセスが終了すると解除されます。
    
  
        プロセスへの実行ポリシーのかけ方ですが,PowerShellの起動オプションで指定するのがもっとも簡単です。
      
  【自動化のためのバッチファイルの作成】
  
      自動化の場合は,コマンドプロンプトを利用して仮に「wsl2_portforward.bat」というバッチファイルから「wsl2_portforward_auto.ps1」というPowerShellスクリプトを起動します。
    
   この起動オプションで「-ExecutionPolicy RemoteSigned」の実行ポリシーを指定します。
    
wsl2_portforward.bat
@echo off
cd c:<格納フォルダパスを記述>
start /MIN powershell -ExecutionPolicy RemoteSigned -File .\wsl2_portforward.ps1
exitあとは,この起動バッチファイル「wsl2_portforward.bat」をタスクスケジューラに登録してパソコンの電源再起動時に動作させればいいのですが,長くなりましたので次の「まとめ」で記述します。
      まとめ
   先ほどのPowerShell「wsl2_portforward_auto.ps1」を起動した場合,どのようにネットワーク設定されたかわかりませんので,次のスクリプトを先程のPowerShellスクリプト
   「wsl2_portforward_auto.ps1」の最後に付け加えて下さい。
  
    これにより,ネットワーク設定の内容がログファイル内に出力されます。
  
PowerShell
#ネットワーク設定内容をログ出力する
Get-Date -format "yyyy/MM/dd HH:mm" > $netsh_log
netsh interface portproxy show all >> $netsh_log
exit
  最後に先ほどの起動バッチファイル「wsl2_portforward.bat」をユーザログオン時に自動的に動かすようにします。
    
  
      このバッチファイルをタスクスケジューラに設定します。以下,タスクスケジューラの設定手順を簡単に述べます。
    
  【タスクスケジューラの設定手順】
  Windows検索ボックスに「タスクスケジューラ」と入力→「タスクスケジューラ システム」と出たらクリックし,タスクスケジューラを起動します。
  左欄の「タスクスケジューラライブラリ」をクリック後,右欄の「タスクの作成」をクリックします。
  
  「タスクの作成」画面が開いたら,
  「全般」タグ画面で,「名前」(英数字表記が良い)と「説明」を記述し「最上位の特権で実行する」にチェックを入れます。
  
    「トリガー」タグ画面で「新規(N)」をクリックし,「新しいトリガー」画面を開きます。
- 「タスクの開始」項目で「ログオン時」を選択後,「特定のユーザ」を選択します。最後に「OK」を押します。
  「操作」タグ画面で「新規(N)」をクリックし,「新しい操作」画面を開きます。
  
- 「プログラム/スクリプト」に先程の「起動バッチファイル」(wsl2_portforward.bat)をフルパスで定義します。
  
    「条件」タグ画面で「電源」のチェックを外しバッテリー時も実行します。
  
  
    最後に「設定」タグ画面で「タスクを停止するまでの時間」が3日間になっていますので,パソコン稼働時間がそれ以上であればチェックを外します。(今回は関係ありません。)
   以上で,パソコンのログオン時に「netsh」コマンドによるポートフォワーディング設定が自動化されました。
  
  
    パソコンを起動し直して,ユーザログイン時にバッチファイルが起動されているか,ログファイルを確認してください。
ログファイル「netsh_log.txt」
2025/04/11 03:41
Listen on ipv4:             Connect to ipv4:
Address         Port        Address         Port
--------------- ----------  --------------- ----------
*               22          17.214.6.XX     22
  このように出ればOKです。
  WSL2のための外部アクセスポートフォワーディングの自動化設定は以上です。
  一通り,実証して稼働を確認していますが,最後まで辿り着くにはいろいろと試行錯誤が必要であろうと思います。
  また,最後のタスクスケジューラの設定は画像を入れていませんので,分かりにくいところがあろうかと思いますが,著作権上,画像を極力入れていませんのでご容赦下さい。実際やってみれば直ぐにわかると思います。
  以上,参考になれば幸いです。特に,今回は取り上げませんでしたが,「PowerShell
  」における日本語表記の問題は難しいです。
  「PowerShell」上で日本語表記されているメッセージをファイルに出力すると,文字化けします。文字コードの指定を上手く設定しないといけないのですが難しいです。
  一度,このテーマに絞って学習したいと思います。
  それでは,楽しいITリテラシーライフをお過ごしください。
  (ご注意)情報の正確性を期していますが,実施される場合には自己責任でお願いします。
0 件のコメント:
コメントを投稿