非管理者ユーザー上のWSL2にLAN内からSSHで接続する

家のデスクトップにWSL2(Windows Subsystem for Linux 2)環境を作ったはいいものの、そこにSSH接続するのに苦闘した。
今回の苦闘の主要因は、一言でいうと**「管理者からWSL環境にアクセスできなかった」**こと。この条件でもうまく行った(であろう)方法をメモしておく。

# やりたいこと
**Winの起動さえすれば(ログインしなくても)、LAN内からUbuntuにSSH接続できるようにする。**
家族共用のPC(スペック強め)なので、自身のmacノートからアクセスできるならありがたい。[^1]

SSH用の鍵設定等はここでは省略する。

# 実行環境
 - Windows 10 Home ver.21H1(OSビルド: 19043.1348)
    - ローカルIPアドレスはDHCP固定割当

 - WSL2 ディストリビューション: Ubuntu

    20.04.3 LTS (GNU/Linux 5.10.16.3-microsoft-standard-WSL2 x86_64)

 - Winの**管理者でないユーザーに**、Ubuntuディストリビューションをインストール済み

    - 管理者ユーザー(Administratorsにあるユーザー)のパスワードは持っている


# 課題
#### ①SSHサーバーが自動起動してくれない
Ubuntu上のSSHサーバーが常に走ってくれていれば良いが、Ubuntuを起動してもsshプロセスは自動起動してくれない。どうも、普通のlinuxでの自動起動方法は使えないらしい。
手動であれば、Ubuntu上で`sudo service ssh restart`とすれば起動できる。

→タスクスケジューラを使う

#### ②Ubuntuが勝手に閉じる
SSHサーバーを起動しても、しばらくするとUbuntuがシャットダウンしてしまっていた。たぶん、wsl.exeの(Ubuntuでなにか行う)プロセスが何かしら起動したままでないといけない。
調べている限りでは、そんなはずは無いのだろうけど…
そして、いったん閉じられてしまうと、再度起動してもsshは停止状態のままになってしまう。

→wsl.exeを(コマンドをつけずに)実行して、そのままバックグラウンドで走らせておく。

#### ③UbuntuのポートにWin外からアクセスできない
UbuntuがWinの作った仮想ネットワーク上にあるため、別端末からアクセスできない。
Winからは、UbuntuのIPアドレスを使う以外に、localhostからでもアクセスできる(デフォルトでフォワーディングされる)。

→localhostへのポートフォワーディングを設定する。

<br>

基本的には以上の3つ。
なお、ファイアウォールに関してはLAN内からの接続を信頼しているため、特に弾かれることはなかった。

①③の解決策は検索すればいろいろ出てくるが、どれも、管理者ユーザーから、インストールしたディストリビューションにアクセスできることを暗黙の前提としていた。
しかし、管理者権限で`wsl -l -v`を実行したところ、「ディストリビューションがインストールされていません」と返ってきた。つまりアクセスできていないのだ。(これは仕様…？バグ…？)

# 手順
### 1. Ubuntuの自動バックグラウンド起動を設定
先に②を解決していく。
タスクスケジューラを検索して管理者権限で起動。スタートメニューのWindows管理ツールの中から見つけることもできる。
「操作」→「タスクの作成」から作成していく。
![作成の様子](https://i.imgur.com/dXkbgjQ.jpg)

「全般」タブで、「名前」には適当なタスク名を入力。
「ユーザーまたはグループの変更」から、Ubuntuにアクセスできるアカウントを実行時ユーザーとして指定。このとき、「詳細設定」→「検索」で出てきたリストから選択すると確実。
「ログオンしているかどうかに関わらず実行する」を選択する。一応、「パスワードを保存しない」もオンに。

「トリガー」タブでトリガー「スタートアップ時」を追加。
「操作」タブでは、「プログラム/スクリプト: `wsl`」で引数なしのものを追加。(ディストリビューションを指定したい場合は、引数が`-d <ディストリビューション>`)

これで自動起動タスクを作成できたわけだが、作成後、実行時ユーザーには「バッチジョブとしてログオン」権限が必要だと言われた。ということで手順2へ。

###2. 「バッチジョブとしてログオン」権限の割り当て
この権限の割り当ては、グループポリシーエディタ(ローカルセキュリティポリシー)から行える。これはHOME Editionには用意されていないので、有効化する。
以下のバッチファイルを作成し、実行する。

```winbatch:enablegpe.bat
@echo off
pushd "%~dp0"

dir /b %SystemRoot%\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientExtensions-Package~3*.mum >MumList.txt
dir /b %SystemRoot%\servicing\Packages\Microsoft-Windows-GroupPolicy-ClientTools-Package~3*.mum >>MumList.txt

for /f %%i in ('findstr /i . MumList.txt 2^>nul') do dism /online /norestart /add-package:"%SystemRoot%\servicing\Packages\%%i"
del MumList.txt
pause
```

そしたら「ローカルセキュリティポリシー」が追加されるので、検索して管理者権限で起動。
「ローカルポリシー」→「ユーザー権利の割り当て」→
「バッチ ジョブとしてログオン」から、先ほどの実行時ユーザーを追加する。

以上で、②が解決。
実際に再起動したあと、`wsl -l -v`で確認すると実行状態になっている。

### 3. SSHの自動起動
手順1と同様にして、`wsl sudo service ssh restart`をタスク登録しても良いが、
Ubuntuの起動時に1回だけ行いたいことは他にも増えそうなので、それをまとめたスクリプトをUbuntu上に用意しておく。

```bash:/opt/bin/on-startup.sh
#!/bin/bash
service ssh start
# その他やりたいことあれば
```

このスクリプトを、パスワードなしでsudo実行できるようにする。
`sudo visudo`を実行、エディタが開くので、最後の行に

```
%sudo ALL=NOPASSWD: /opt/bin/on-startup.sh
```
を追加する。

そしたら、このスクリプトの実行をタスクスケジューラに登録していく。
「全般」と「トリガー」タブは手順1と同様。「操作」は、`wsl`を引数`sudo /opt/bin/on-startup.sh`で実行する。
ただし、念の為トリガーに30秒遅延をかけておいた。これは、このスクリプト実行後に一旦Ubuntuが閉じてから、手順1のタスクが起動する可能性を避けるためだ。実際にそうなりうるかは未検証。

「バッチジョブとしてログオン」権限はすでに手順2で割り当ててあるので、これで①も解決。

### 4.ポートフォワーディング
基本的には、管理者権限で`netsh.exe interface portproxy add v4tov4`を叩けばいい。WinのローカルIPは固定割当にしているので、1回そのIPの好きなポート(今回は51234)からlocalhost:22にフォワーディング設定をすればうまくいく…はずだった。

しかし、Winを再起動するとうまく動いてくれない。[^2]それなら再起動後に再設定してやればいい。

```bat:port-forward.bat
netsh.exe interface portproxy delete v4tov4 listenport=51234 listenaddress=<WinのIPアドレス>
netsh.exe interface portproxy add v4tov4 listenport=51234 listenaddress=<WinのIPアドレス> connectport=22 connectaddress=localhost
```
このようなbatファイルを適当な場所に置き、またタスクスケジューラでスタートアップ時に呼び出すが、今回は

 - 実行時ユーザーを管理者ユーザーにする
 - 最上位の特権で実行

にする点が異なる。トリガーの遅延も一応つけた。

***

以上で無事SSH接続できるようになった。快適。
近々来るWin10 21H2で、WSL2からGPUにアクセスできるようになるそうで。まだかな。


# 参考
 - [wsl2でsshサーバを起動し、外部からそこに接続](https://qiita.com/yabeenico/items/15532c703974dc40a7f5)
 - [Windows WSL2に外部から直接アクセスするための設定](https://rcmdnk.com/blog/2021/03/01/computer-windows-network/)
 - [タスクスケジューラ設定時に"バッチ ジョブ としてログオン"の権利が必要と言われる](https://www.projectgroup.info/tips/Windows/comm_0128.html)
 - [「Windows 10 Home」でローカルグループポリシーエディターを使用する方法](https://www.japan-secure.com/entry/how-to-use-local-group-policy-editor-in-windows-10-home.html)

[^1]: WinへのSSH接続は、追加できるオプション機能が空欄([実際の画面](https://twitter.com/keikun2000/status/1458783965133742088))なのでSSHサーバーをインストールできない問題に阻まれ中。
[^2]: 同様のバグは[前からある](https://social.technet.microsoft.com/Forums/en-US/a80c7228-cf2a-440c-b293-4d8a3f54f570/netsh-portproxy-not-working-after-reboot?forum=w7itpronetworking)ようだ。