やっぱりMacが好き。MacからWindowsPC(WSL2)にSSHで繋いでAI開発

# WindowsPC(RTX 5070 Ti付き)を買ったけど...

最近どうしてもモンハンワイルズがしたくて、家で画像生成とかKaggleとかもちゃんとやるからと自分に言い聞かせてデスクトップPCを買ってしまいました。届いてからPCの設定を色々試したのですが、普段Mac、Arcブラウザ使いの私にとっては、開発環境をいくらMacbookに設定を近づけてもわずかな不快感が残りました。(フォント変えたけどしっくりこない...)

# MacbookからGPU資源にアクセスして開発したい！

ということで、ゲーム以外の開発(Kaggle・趣味の画像生成勉強など)を普段使いのMacBookでできるように、WindowsPC(WSL2)にSSH接続する設定を行いました！
備忘録として、記事に残しておきます。

## (前提)環境
WindowsPCの方では、**WSL2**とよばれるWindows上でLinuxを動作させるための実行環境を導入しています。本記事は**WSL2のLinux環境**にSSH接続することを前提としています。AI開発だとなにかとLinux環境の方が都合いいと思うので、AI系開発したい人は導入をお勧めします。下記記事が非常に参考になります。

:::note warn
コメントにてご指摘いただきましたが、UbuntuなどLinux OSを起動できるLive USBを用意できるのであれば、デスクPCにUbuntu入れて、そのUbuntuにSSHした方が圧倒的に楽です。Ubuntu入れるのめんどくさい、WSL2でやりたいという人がいれば参考にしてください。
:::

https://qiita.com/zakoken/items/61141df6aeae9e3f8e36

## ステップ1: SSHサーバをインストール

WSL環境にSSHサーバをインストールします。以下のコマンドを**WSL環境**で実行してください。

```zsh
sudo apt update
sudo apt install openssh-server
```

## ステップ2: SSHサーバの設定ファイル編集

SSHサーバのポートを設定します。設定ファイルを編集しましょう。

```zsh
sudo nano /etc/ssh/sshd_config
```

DefaultではPort 22になっていると思います。このままでもいいですが、一応セキュリティ的にはバレないポートを使った方がいいということで今回は`2222`に設定しています。

```config:/etc/ssh/sshd_config
Port 2222
```

:::note warn
とはいえ自宅のローカルネットワークなら心配ないと思います。22のままで使う場合は、次のステップ3を無視して、次のステップ4以降の`2222`の箇所を全て`22`に変更してください。
:::

また、PC再起動後にWSL2環境内のSSHサーバが自動で立ち上がるようにしておきましょう。
```zsh
# wsl環境内で以下を実行
sudo systemctl enable ssh
sudo systemctl start ssh
```

## ステップ3: sshd.socketを無効化

WSLのSSHサーバーは、`systemd socket activation` を利用して起動されています。
この場合、sshd.socketユニットのストリーム設定(通常22)が優先されており、ステップ2の設定が無視されます。以下のように`socket activation`を無効にすれば、「Port 2222」が有効になります。

```zsh
sudo systemctl disable ssh.socket
sudo systemctl stop ssh.socket
```

ここまで終わったら、SSHサーバを起動して、ポート2022でリッスンしてるか確認しましょう。`*:2022` のような出力が出ればOKです。

```zsh
sudo systemctl start ssh
sudo ss -tuln | grep 2022
```

## ステップ4: Windows側のポートフォワーディング設定

MacからWSLにアクセスするためのポートフォワーディングを設定します。
ここでのイメージは以下の通りです：

```zsh
[Mac] --(SSH 2222)--> [Windows (0.0.0.0:2222 portproxy)] --(転送)--> [WSL2 (WSLのIPアドレス:2222)]
```
Macから `(WindowsPCのIPアドレス):2222`にSSH接続すると、Windowsがそれを受け取って `(WSLのIPアドレス):2222（WSL内のSSH）` に転送する、というイメージです。

設定はPowerShell（管理者）で以下のようにするのですが、ここが少し鬼門です。

```powershell
netsh interface portproxy add v4tov4 listenport=2222 listenaddress=0.0.0.0 connectport=2222 connectaddress=(WSL2のIPアドレス)
```
上のコードに**WSL2のIPアドレス**と記載していますが、このIPアドレスはPCを再起動するたびに変わります。そのため、起動するたびにWSL2のIPアドレスを調べて、ポートフォワーディング設定をする必要があります。
WSLがWindowsの中でどのような立ち位置なのか興味がある人は、[こちらの記事](https://atmarkit.itmedia.co.jp/ait/articles/1909/09/news020.html)がお勧めです。


### ポートフォワーディング設定の自動化

面倒なことは自動化しましょう。C:配下に適当なフォルダ(scripts)を作って、その中に以下のPowerShellスクリプトを作成して下さい。

```powershell:wsl_portproxy.ps1
# WSLのIPを取得
$wsl_ip = (wsl hostname -I).Trim()

# ポート転送の再設定
netsh interface portproxy reset
netsh interface portproxy add v4tov4 listenport=2222 listenaddress=0.0.0.0 connectport=2222 connectaddress=$wsl_ip
```

:::note warn
`netsh interface portproxy reset` は既存のすべてのポート転送設定を削除します。他の転送設定を併用している場合は、必要なものを再登録してください。
:::

以下を管理者権限で実行し、動作確認しましょう。エラーが出なければOKです。
```powershell
powershell -ExecutionPolicy Bypass -File "C:\scripts\wsl_portproxy.ps1"
```

念のため、設定ができているか確認もしておきましょう。
```powershell
netsh interface portproxy show all
```
以下のような出力ならOKです。

```powershell:Output

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

Address         Port        Address         Port
--------------- ----------  --------------- ----------
0.0.0.0         2222        (各々のWSLのIPアドレス)      2222
```


続いて、Windows起動時に自動でこのスクリプトを実行するよう設定します。

#### ①タスクスケジューラを開く
スタートメニュー」をクリック
→「タスクスケジューラ」と検索
→「管理者として実行」で開く（右クリック→管理者権限で実行）

#### ②タスクの新規作成
タスクスケジューラで「基本タスクの作成...」または「タスクの作成」をクリック。

一般タブの設定例：

名前： WSL PortProxy自動設定（自由な名前でOK）

説明： WSL2のSSH接続用ポート転送設定をPC起動時に自動設定

#### ③トリガータブの設定
- 「新規」をクリック
- 「タスクの開始」を「コンピューターの起動時」に設定
- 「OK」をクリック

#### ④アクションタブの設定
- 「新規」をクリック
- 以下のように設定します

| 項目 | 入力内容 |
|:-:|:-:|
| プログラム/スクリプト  |  `powershell.exe` |
| 引数の追加  | `-ExecutionPolicy Bypass -File "C:\scripts\wsl_portproxy.ps1"` |

ここまで設定したら保存しましょう！これで、PC起動時に毎回WSL2のIPアドレスに正しくポートフォワーディングできるようになります！


## ステップ5: Windowsファイアウォール設定(2022番解放)
最後に、Windowsファイアウォールでポート2222の通信を許可しておきます。
これをしないと、外部からのSSH接続がブロックされます。
Powershellで以下を実行してください。

```powershell
New-NetFirewallRule -DisplayName "Allow SSH (WSL)" -Direction Inbound -LocalPort 2222 -Protocol TCP -Action Allow
```

以下をPowershellで実行し、次のような出力が出ればOKです。

```powershell
netsh advfirewall firewall show rule name="Allow SSH (WSL)" verbose
```

```powershell:Output
規則名:                               Allow SSH (WSL)
----------------------------------------------------------------------
有効:                                 はい
方向:                                 入力
プロファイル:                         ドメイン,プライベート,パブリック
グループ:
ローカル IP:                          任意
リモート IP:                          任意
プロトコル:                           TCP
ローカル ポート:                      2222
リモート ポート:                      任意
エッジ トラバーサル:                  いいえ
インターフェイスの種類:               任意
セキュリティ:                         NotRequired
規則のソース:                          ローカル設定
操作:                                 許可
OK
```

ここまでで完了です！

# Mac側の設定

```config:~/.ssh/config
Host my-desktop
    HostName WinPCのIPアドレス
    User WinPCのUserName
    Port 2222
    IdentityFile ~/.ssh/id_ed25519 # 鍵認証する場合は適宜設定してください
```

:::note warn
WinPCのIPアドレスも当然再起動すれば変わる可能性があります。毎回「WinPCのIPアドレス確認⇨HostName書き換え」は面倒なので、DHCP固定割当設定をしておくことをおすすめします。設定方法は家のルータの説明書に書いてあるはずです。
:::

```zsh
ssh my-desktop
```

接続できたら以上で本記事の内容は完了です！
もちろんVSCodeから直接アタッチすることも可能です。以下はMacからWSL環境に入り、Docker環境にアタッチしている例です。

![スクリーンショット 2025-03-31 19.18.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2629034/24877de8-ab1b-4097-8093-98352b2e2fb5.png)