wsl2のsshサーバーに他PCから接続できるよう自動設定する


## TL;DR
- @yabeenicoさんの記事 [wsl2でsshサーバを起動し、外部からそこに接続 (Qiita)](https://qiita.com/yabeenico/items/15532c703974dc40a7f5) をもとにssh接続のためポートフォワード設定をやった。
- 上の記事で省略されている、タスクスケジューラの設定をやってみた。
- これだけではsshできず！sshの自動再起動スクリプトを設定することでsshできるようになった。

## はじめに
日本時間5/28にWindows10 2004 がリリースされ、wslがwsl2にアップデートされました:tada:

このwsl2ではネットワークの仕様がwsl1と大きく変更されておりwsl1と同じ方法でsshサーバーを有効にすることができません。このあたりの事情と解決方法についてはInsider Previewで既にwsl2を試されていた方々が検証してくれています。

特に、@yabeenicoさんの記事[[1]](https://qiita.com/yabeenico/items/15532c703974dc40a7f5)ではwsl2のネットワーク仕様からssh設定の自動化までが大変分かりやすくまとめられて、本当に勉強になります。

ただ、[[1]の記事](https://qiita.com/yabeenico/items/15532c703974dc40a7f5)では作成したスクリプトの自動起動方法について以下のように触れられているだけです。(まあ本題じゃないですからね。)
> このスクリプトを、タスクスケジューラでログオン時に自動起動するように設定すれば完了です。

タスクスケジューラ is なに？？？:thinking: 
windows何もわからない自分はこの設定に数時間かかりました・・・

ということで、この記事では自分のような悲劇を再び生まないように、[[1]](https://qiita.com/yabeenico/items/15532c703974dc40a7f5)をベースとしたwsl2のssh起動自動化方法を紹介します。


## 検証環境
- Windows: Windows 10pro 2004
- WSL2 (WSL1からバージョン変更したもの)
- Linuxディストリビューション: Ubuntu (20.04および18.04)
- Windowsの管理者アカウントを使用

## wsl2側の設定
- とりあえずwslでsshが入っていることを確認しておきましょう。

```
$ sudo apt install -y openssh-server
```

- 以降の設定は[[1]](https://qiita.com/yabeenico/items/15532c703974dc40a7f5)と基本的に同様です。
     

- ポートフォワード用のシェルスクリプトをport-forward.shとして作成します
    - [[1]](https://qiita.com/yabeenico/items/15532c703974dc40a7f5)では/opt/binに配置していますが、自分は/opt/に配置しています。

- ファイルを作成します。(例はvimの場合)
 
```
$ sudo vim /opt/port-forward.sh
```

- 以下を作成したファイルに入力します。

```/opt/port-forward.sh
#!/bin/bash
IP=$(ifconfig eth0 | grep 'inet ' | awk '{print $2}')
netsh.exe interface portproxy delete v4tov4 listenport=22
netsh.exe interface portproxy add    v4tov4 listenport=22 connectaddress=$IP                                                                                 
```
- rootにのみ実行権限を付けておきましょう。

```
$ sudo chmod 700 /opt/port-forward.sh
```

## windows側のスクリプト作成
- 上のスクリプトをwslに叩かせるためのスクリプトを作成します。
- 自分はドキュメントフォルダ`%homepath%\Documents`に`port-forward.bat`として作成しました。
    - もし複数のディストリビューションをインストールされている場合は設定したいものを`-d <ディストリビューション名>`で指定する必要があります。
    - rootにのみ実行権限を付けているため、[[1]](https://qiita.com/yabeenico/items/15532c703974dc40a7f5)の例とは異なりwslのrootユーザーとして実行する必要があります。

```port-forward.bat
wsl -u root --exec /bin/bash /opt/port-forward.sh

rem Ubuntuというディストリビューションを指定するときは以下のコマンド。
rem wsl -d Ubuntu -u root --exec /bin/bash /opt/port-forward.sh
```

<img width="500" alt="win-shell.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646635/671047fc-d063-0b60-bfbf-c97076be75ed.png">

## タスクスケジューラの設定
- ここからが本題です。
- スタートメニューで`スケジューラ`と入力するとタスクスケジューラが表示されるのでクリックしてください。

<img width="500" alt="search-scheduler.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646635/55c2c829-349c-2ec8-27b9-e6682e8934c6.png">

- `タスクの作成`をクリック

 <img width="500" alt="Click-Newtask.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646635/30cf682a-ad6f-5971-2a55-8cb4a2183257.png">

- この画面でタスクの詳細な設定を行います。
    - 名前は`wsl-port-forward`としました。(適当でいいです。）
    - 今回はコンピューターがスタートアップ時点で（ログオン前に）タスクを実行させたいため`ユーザーがログオンしているかどうかにかかわらず実行する`にチェックをいれます。
        - なん
となく`パスワードを保存しない`にチェックをいれます。
    - ポートフォワーディングの実行には管理者権限が必要なため、`最上位の特権で実行する`にチェックを入れます。
    - 構成を`Windows 10`に変更します。

<img width="500" alt="create-task-port-forward.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646635/625a854e-40e0-b94a-b979-7aa5aef1ab1f.png">

- タスクの実行タイミングを指定します。トリガータブに移動し`新規`をクリックします。

<img width="500" alt="Scheduler-Triger.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646635/b62f2990-f8ad-075a-109f-f4e4299949d4.png">

- `タスクの開始`でプルダウンから`スタートアップ時`を選択、OKで閉じます。

<img width="500" alt="Shcheduler-Triger-New.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646635/5dcd30ea-f026-f78e-652c-14f6d9068bb6.png">


- 次に実行するスクリプトの指定です。操作タブに移動し、`新規`をクリックします。

<img width="500" alt="Scheduler-Job.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646635/6b12117b-036b-9602-7aca-c20e3733d3a4.png">

- 操作は`プログラムの開始`のまま、`参照`ボタンから`プログラム/スクリプト`の欄に先ほど作成した`port-forward.bat`のパスを入力します。OKで閉じます。


<img width="500" alt="Shcheduler-Job-NewJob.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646635/ac0e5da0-40b3-7b7e-a8a7-63c8ed76d0b2.png">


- これでタスクの登録は終了です。OKを押して`タスクの作成`ウィンドウを閉じてください。なお、作成したタスク一覧はタスクスケジューラの`タスクスケジューラ ライブラリ`から確認できます。
- では再起動してsshができるようになっているか確認してみましょう！

## 問題発生！
- これでwsl2にsshできるようになる・・・ハズですが自分の環境ではうまくいきませんでした。

```bash
$ ssh <user名>@<IPアドレス>
kex_exchange_identification: Connection closed by remote host
```

- どうやらwsl2にまでは到達できているものの何らかの問題がsshサーバーで発生しているようです。
- いろいろ試したところsshのサービスをWindows起動時に再起動することでssh接続ができるようになりました。
  - 実はwsl1の頃も同様の処理が必要でした。[2. Windows Subsystem for Linuxにssh接続する (Qiita)](https://qiita.com/ezmscrap/items/30eaf9531e240c992cf1) で詳しく説明されています。
    - `systemctl enable ssh`などでwsl側での自動起動を有効にしても問題は解消しませんでした。

## sshサーバー再起動自動化
- この操作も自動化したいので、sshのサービスをWinodws起動時に再起動するスクリプトを作成、タスクスケジューラに設定していきます。

- 先ほどと同様に、ドキュメントのフォルダ`%homepath%\Documents`に`wsl2-ssh-restart.bat`として以下のスクリプトを保存します。
    - ここでも複数のディストリビューションを使用されている場合はディストリビューション名の指定`-d <ディストリビューション名>`が必要です。
    -  [3. Windows10のWSL上にあるServiceを簡単に自動起動させる(Qiita)](https://qiita.com/nashi2603/items/d0645305b137017b91b0) の記事でwsl1向けに紹介されているものを流用しています。
 
```wsl2-ssh-restart.bat
@echo off
wsl -u root -- service ssh restart

rem ubuntuというディストリビューションを指定するときは下のコマンド。
rem wsl -d Ubuntu -u root -- service ssh restart
```

- 先ほどと同様にタスクスケジューラに設定します。
- この操作は管理者権限が必要ないので、`最上位の特権で実行する`のチェックはつけません。

<img width="500" alt="Scheduler-ssh.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646635/a8328120-2057-f3fd-e178-63efe62f9706.png">


- 操作 -> 新規 と進みます。実行するスクリプトとして`wsl2-ssh-restart.bat`のパスを入力します。 OKで閉じます。

<img width="500" alt="Scheduler-Job-ssh.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646635/d360f9df-3285-8f04-e396-9c316c4ed9b2.png">

- `トリガー` -> `新規`と進み、からスタートアップ時にタスクを起動するよう設定します。

<img width="500" alt="Scheduler-Triger-ssh.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646635/d9eb1550-d638-a203-ccc6-f8c0eb2c479d.png">

- これで設定は終了です！お疲れさまでした。
- 再起動してしばらく待ってからsshを実行してみましょう!
    - sshできるようになるまで1分ほど時間がかかります。


## うまくいかないときは
- コマンドプロンプトやpowershellを管理者権限で起動したうえで、作成した2つのスクリプト`port-foward.bat`と`wsl2-ssh-restart.bat`を手で実行してみましょう。スクリプトに問題がなければこれでssh接続ができるようになるはずです。
- ポートフォワードができているか不安な場合は[[1]](https://qiita.com/yabeenico/items/15532c703974dc40a7f5)で紹介されている以下のコマンドをPowerShellで実行することでポートフォワードの状態を確認できます。
  - 以下のようになっていればOKです。(アドレスは違ってOK）


```
> netsh.exe interface portproxy show v4tov4

ipv4 をリッスンする:         ipv4 に接続する:

Address         Port        Address         Port
--------------- ----------  --------------- ----------
*               22          172.29.240.72   22
```
- 設定したタスクが実行されているかはタスクスケジューラの`タスクスケジューラ ライブラリ`から確認できます。

- 低スペックなPCだとwslが起動する前にタスクが実行されてしまうことがあります。タスクスケジューラのトリガーの設定で、5分 ～ 15分程度の遅延時間を設定してもいいかもしれません。

<img width="500" alt="Scheduler-Triger-ssh-delay.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646635/466a7f4c-51ad-8124-6234-e232830410f6.png">


## まとめ
- [[1]](https://qiita.com/yabeenico/items/15532c703974dc40a7f5) の記事で紹介されているスクリプトをタスクスケジューラで自動実行する方法についてまとめました。
- 自分の環境(wsl1からwsl2へアップグレードした環境）によるものかもしれませんが、sshの再起動も必要でした。
- @yabeenicoをはじめ記事を引用させていただいた方々、素晴らしい記事を本当にありがとうございました！

それでは楽しいwsl2ライフを！

## 引用させていただいた記事
- [1. wsl2でsshサーバを起動し、外部からそこに接続(Qiita)](https://qiita.com/yabeenico/items/15532c703974dc40a7f5)@yabeenico
- [2. Windows Subsystem for Linuxにssh接続する (Qiita)](https://qiita.com/ezmscrap/items/30eaf9531e240c992cf1)@ezmscrap
- [3. Windows10のWSL上にあるServiceを簡単に自動起動させる(Qiita)](https://qiita.com/nashi2603/items/d0645305b137017b91b0)@nashi2603

## 変更履歴
- (6/6) port-foward用のバッチファイルを一部修正(bash -> /bin/bash)