リモートマシンのWSL2にSSH接続したい

# はじめに
## 概要
リモートのWindows PCで動かしているWSL2に、SSH接続して開発をおこなおうとしたが、
色々と詰まった点があったのでまとめる。

## 環境
クライアント
OS: Windows 10 Home 64Bit

リモートマシン
OS: Windows 10 Home 64Bit
WSL2: Ubuntu-20.04

## なぜやろうと思ったのか
新しいPCを買ったので、新PCをきれいに保つために、旧PCを開発専用機に新PCは開発用途では使用しないようにしようと考えた。
しかし、わざわざ移動したり2つのPCを操作するのが面倒だったため、新PCから開発できるようにしたくなった。
そのため、新PCをあまり汚さずに新PCから開発するという目的を達成するために、新PCから旧PCにリモート接続して開発をしようと考えた。
Windowsアプリケーションの開発経験がないかつ、サーバが専門なのでWSL2上で開発しようと考えた。

開発の際はVS Codeの`Remote Development`という拡張機能を利用して開発をしようと考えたため、直接リモートマシンのWSL2にSSH接続ができるようにしたかった。

# 手順と詰まったとこ
## ポートフォワーディングを利用する（失敗）
### 手順
環境構築の手順は以下の2つを参考にした。
- [VSCode Remote - SSH と WSL2 でリモートFlutter開発 - タケユー・ウェブ日報](https://blog.takeyuweb.co.jp/entry/2021/01/30/024323)
- [wsl2でsshサーバを起動し、外部からそこに接続](https://qiita.com/yabeenico/items/15532c703974dc40a7f5)

どちらもやっていることはWSL2上でSSHサーバが起動するようにし、リモートマシンの22番ポートへの接続をWSL2に転送するために、ポートフォワーディングを起動時に設定するスクリプトをタスクスケジューラで起動時に実行しているだけである。
しかし、私の場合はこれらの手順では直接WSL2にSSH接続することができなかった。

### 詰まった点
どのようにSSH接続できなかったかと言うと、
リモートマシン自体へはSSH接続できるのだが、そこからWSL2へ転送されていなかった。

リモートマシンから`wsl`コマンドではなく、`ssh`コマンドでアクセスしようとするとアクセスできなかった。
そのため、SSHサーバが起動していないのかと考え、`systemctl status ssh`を実行すると下記のようなエラー文が出た。


```
$ systemctl status sshd
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
```

システムがPID 1でブートしていないとのことだった。
調べてみると下記のようなWEBページが見つかった。
[Windows 10 Home（WSL2）のUbuntuでsystemctlを利用する方法（systemdをPID1で動作させる方法）](https://snowsystem.net/other/windows/wsl2-ubuntu-systemctl/)

この記事によると[genie](https://github.com/arkane-systems/genie)を導入すると解決できるとのことだったので試した。
ただし、この記事の手順ではうまくいかないため、genieのGitHubのREADME.mdを参考に導入した。

### genieのインストール
まず依存パッケージであるdotnet-runtime-5.0を入れた。
上述のサイトを参考に、バージョンの部分だけ変更し実行した。

```
sudo apt-get install -y gpg
wget -O - https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o microsoft.asc.gpg
sudo mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/
wget https://packages.microsoft.com/config/ubuntu/20.04/prod.list
sudo mv prod.list /etc/apt/sources.list.d/microsoft-prod.list
sudo chown root:root /etc/apt/trusted.gpg.d/microsoft.asc.gpg
sudo chown root:root /etc/apt/sources.list.d/microsoft-prod.list
sudo apt-get update; \
  sudo apt-get install -y apt-transport-https && \
  sudo apt-get update && \
  sudo apt-get install -y dotnet-sdk-5.0.4
```

続いて、GitHubのREADMEに書かれているhttps://arkane-systems.github.io/wsl-transdebian/ に従って`wsl-translinux repository`を追加した。
この際、`<distro>`を下記に従って変更する。
（適当に読んで脳死で`<distro`をそのままにしたり、`Ubuntu-20.04`に変えて地味に詰まってたので気をつけて）

> Debian 10 buster  
Debian 11 bullseye  
Debian 12 bookworm  
Debian (testing) sid  
Ubuntu focal  
Ubuntu bionic  

```
apt install apt-transport-https

wget -O /etc/apt/trusted.gpg.d/wsl-transdebian.gpg https://arkane-systems.github.io/wsl-transdebian/apt/wsl-transdebian.gpg

chmod a+r /etc/apt/trusted.gpg.d/wsl-transdebian.gpg

cat << EOF > /etc/apt/sources.list.d/wsl-transdebian.list
deb https://arkane-systems.github.io/wsl-transdebian/apt/ <distro> main
deb-src https://arkane-systems.github.io/wsl-transdebian/apt/ <distro> main
EOF

apt update
```

上述のサイトではpackagecloud.ioにあるスクリプトファイルを利用していたようだが、下記の通り利用できなくなっているため上記を実行する。
```
(The packagecloud.io repository is now deprecated. If you are still using it, please update your system to use the repository above.)
```


これでパッケージのリポジトリを追加できたので、下記を実行してgenieをインストールする。

```
sudo apt update
sudo apt install -y systemd-genie
```

genieのインストール後は[上述のサイト](https://snowsystem.net/other/windows/wsl2-ubuntu-systemctl/)に従って、
`.bashrc`に下記を追加して、起動時にsystemdがPID1でない場合`genie -s`を実行するようにする。

```:.bashrc
if [ "`ps -eo pid,cmd | grep systemd | grep -v grep | sort -n -k 1 | awk 'NR==1 { print $1 }'`" != "1" ]; then
  genie -s
fi
```

### 結果
再度`systemctl status sshd`を実行すると下記のように`systemctl`が実行できることが確認できた。

```
systemctl status sshd
● ssh.service - OpenBSD Secure Shell server
     Loaded: loaded (/lib/systemd/system/ssh.service; enabled; vendor preset: enabled)
     Active: failed (Result: exit-code) since Mon 2021-03-29 16:17:58 JST; 4min 5s ago
       Docs: man:sshd(8)
             man:sshd_config(5)
```

**しかし、systemctlは実行できたもののポートフォワーディングがうまくいかず、WSL2まで届かずリモートマシンにつながってしまう**


## 多段SSHで接続する（解決）
ここで、リモートサーバ上のDBをDBクライアントで確認するときのことを思い出し、多段SSHでの接続を試してみようと考えた。

いろいろ試しているうちにリモートマシン上で`ssh localhost`とすることでWSL2に接続することが確認できた。
そのため、これを利用して多段SHHによる接続を試みた。
これにより、新PCからリモートマシンのWSL2にSSH接続することができ、
VSCodeのRemote Developmentでも問題なく利用することができるようになった。

> 前述の手順で設定したポートフォワーディングが効いていたのかと思ったが、どうやらlocalhostでWSL2にアクセスできるようになってたらしい。  
今までの苦労はなんぞやと……  
それまでは、WSL2のIPは起動するごとに変わり、WSL2に付けられたホスト名もサポートされていなかったため、前述の手順のようなことが求められたのだと思われる。    
参考: [「WSL 2」最大の課題であった“localhost”問題が解決 ～「Windows 10」Build 18945 - 窓の杜](https://forest.watch.impress.co.jp/docs/news/1198651.html)

### 手順
下記のように`C:\Users\{UserName}\.ssh\config`に追記する。

```:\.ssh\config
Host            Remote
  HostName      {RemoteHostName}
  User          {RemoteUserName}
  IdentityFile  C:\Users\{UserName}\.ssh\id_rsa

Host            WSL2
  HostName      localhost
  User          {WSL2UserName}
  IdentityFile  C:\Users\{UserName}\.ssh\id_rsa
  ProxyCommand  ssh.exe -l %r -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null {RemoteHostName} -W %h:%p
```

# まとめ
- 以前はWSL2のIPは起動ごとに変わりホスト名解決もできず、IPをわざわざ調べるなりポートフォワーディングなど迂遠な方法をしないとSSH接続はできなかったが、現在はlocalhostでアクセスすることができる
- このlocalhostForwardingというオプションは、以前は.wslconfigで指定しないといけないようだったが、現在はデフォルトで使えるっぽい
- リモートのリソースにアクセスするときはやっぱ踏み台なんだなって思った