Proxy下のwindows 10 proをサーバーにしてsshする

色々と苦労したので、メモ。誰かの参考になればと思う。

# 0. 環境
サーバー、クライアントともにwindows 10 pro。
VPSはconohaのcentosのものを使っている（が、他の無料VPSでも問題ないと思う）。

# 1. したいこと
グローバルipが割り当てられていない、かつProxy下に置かれているwindowsをサーバーにして、そこへローカルのwindowsクライアントからssh接続したい！

https://red-full-moon.com/no-nat-ssh/

このサイトに書いてあることをそのまますればよいが、Proxyがある場合少し厄介になる。大枠としては、VPSを踏み台にして、多段sshを組むことになる：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1384496/6b3861ee-2ee6-0318-ca73-78c800395bfa.png)

様々なサイトを参考にさせていただいたので、その都度参考先を紹介していく。

### 注意
おそらくこの手順に従えば研究室や職場のPCにリモートからアクセスなどできると思うが、機密保持の観点から禁止されているところも多い（らしい、まだ職場では働いたことはないので知らないが）。そもそも私自体がsshの初心者で、多分大丈夫だとは思うがこれがセキュアなやり方なのかもわからない。なので、ここからは自己責任で進めてほしい。

# 2. ssh接続できるまで
以降、接続したいwindowsマシンを「サーバー」、踏み台にするVPSを「VPS」、接続元となるwindowsマシンを「クライアント」と呼ぶことにする。

## 2.1 サーバからVPSにつなぐ
サーバーから設定していく。まずはサーバーからVPSにトンネルを掘る。後のことを考え、Configファイルに記述していく：

```config:（サーバ上）C\Users\{サーバのユーザ名}\.ssh\config
Host svr_to_conoha
    User {VPSのログインユーザー名}
    HostName {VPSのホスト名}
    Port 22
    IdentityFile "C:\Users\{サーバのユーザー名}\.ssh\id_rsa"
    ProxyCommand C:\Program Files\Git\mingw64\bin\connect.exe -H {プロキシサーバーのアドレス}:{ポート} %h %p
    ServerAliveInterval 60
    ExitOnForwardFailure yes
    TCPKeepAlive no
```

https://qiita.com/M11/items/8f3caf19a3911c4449fe を参考にした。**これを動かすために、3つほど注意点がある。**
1. まずはサーバPCにgitを各自インストールしてほしい。すると、connect.exeが入るはずなので、それをproxyをまたぐのに使う。
2. 私のVPSはSSH認証方式を公開鍵認証にしているため、予めサーバPCでssh-keygenしてid_rsaという鍵を生成し(https://qiita.com/overflowfl/items/14a2486df85fd7efac85 が非常に参考になる)、それをVPSのauthorized_keysに書き込んでおいた。この辺のやり方は検索すればいくらでも出てくると思う。
3. VPSの13389番ポートは開放しておこう。https://eng-entrance.com/linux-centos-port あたりを参考に、VPSのターミナル上で`firewall-cmd --zone=public --add-port=13389/tcp --permanent`からの`firewall-cmd --reload`とすればよい。

そして、以下のコマンドを実行する：

```powershell:サーバのpowershell
> Start-Job {& ssh -fNR 13389:localhost:22 svr_to_conoha}
```

各オプションについては https://www.kmc.gr.jp/advent-calendar/ssh/2013/12/09/tunnel2.html 参考。簡単に説明すると、-f,-Nはバッググラウンドで実行するのに必要なオプションで、-Rはポート転送のために必要なオプション。これでVPSの13389番ポートがサーバの22番ポートにフォワードされる。バックグラウンドで実行するために、Start-Jobでsshを起動した( https://www.nullpo.io/2020/04/01/windows-openssh-portforward/ )、、が、私の環境だとpowershellを消すとすぐにsshが途切れてしまう。誰か助けてくれ
まあ、不本意だがサーバ側でpowershellをフォアグラウンドで起動し続けることにする。ここは目を瞑ろう。

## 2.2 クライアントからVPSにつなぐ
こちらはProxyがない分さっきより簡単に済む。Configファイルにvpsの情報を記載する：

```config:（クライアント上）C\Users\{クライアントのユーザ名}\.ssh\config
Host cli_to_conoha
  User {VPSのログインユーザー名}
  HostName {VPSのホスト名}
  IdentityFile "C:\Users\{クライアントのユーザー名}\.ssh\id_rsa2"
```

もちろん、公開鍵は事前にVPSの方に登録しておく。それから

```powershell:クライアントのpowershell
> Start-Job {& ssh -fNL 50000:localhost:13389 cli_to_conoha}
```

でクライアントの50000番ポートがVPSの13389番ポートにフォワードされるだろう。ここでも例によってポートは開放しておこう。ここまででほとんど準備はできた。

## 2.3 クライアントからサーバにつなぐ
あとはクライアントが自分の50000番ポートにsshすれば、VPSの13389番ポートを通じてサーバの22番ポートに転送される。つまり、次のコードを実行することが目標である（しかしここが一番大変だった）：

```powershell:クライアントのpowershell
ssh -v -i C:\Users\{クライアントのユーザ名}\.ssh\id_rsa3 {クライアントのユーザ名}@localhost -p 50000
```

-vオプションを付けることでクライアント側のエラーログを出力するようにしている。問題なく接続できるようになれば必要ないと思う。ここで、更に新しい鍵が出てきているが、これも予めクライアントでssh-keygenしておき、公開鍵の方をサーバの~/.ssh/authorized_keyにいれておく。そして、次のあたりを設定することで、サーバで公開鍵認証をうけつける：

```config:（サーバ上）C\ProgramData\ssh\sshd_config
...
#Logging
SyslogFacility LOCAL0
LogLevel DEBUG
...
PubkeyAuthentication yes
...
AllowAgentForwarding yes
AllowTcpForwarding yes #ここの２ついらないかも、要検証
...
# Match Group administrators
#        AuthorizedKeysFile __PROGRAMDATA__/ssh/administrators_authorized_keys
```

編集する際は管理者権限で。また、はじめにsshをつなぐ際は、ログでどのへんがうまく行っているのか/いないのかをチェックするためにLogLevelをDEBUGにしている(参考：　https://qiita.com/pekepek/items/f38004b14e986691f30d )。ずっとこのままだとログが肥大化して逆にわかりにくくなってしまうので、うまく接続できることが確認できたらINFOに戻しておこう。

次に、サーバのsshdをリスタートする

```powershell:サーバのpowershell
> Restart-Service sshd
```

**が、どうやらwindowsの場合sshプロセスが残っているときちんとrestartできない**よう( https://takuya-1st.hatenablog.jp/entry/2021/03/04/065504 )なので、一旦2.1でstartjobしたプロセスは`kill -name ssh`などとしてからrestartしよう。
ここで一番はじめのコードを実行したがうまく行かなかった。そこで、LogLevelをDEBUGにしていたので次のようなログファイルが作られるはず：

```log:C\ProgramData\ssh\logs\sshd.log
...
debug1: Could not open authorized keys 'C:/Users/{サーバ上のユーザ名}/.ssh/authorized_keys': Permission denied
...
```
これを見るとauthorized_keysのパーミッションがおかしいことに気づく。 https://qiita.com/hatobeam75/items/825ee10a5261835e814b のとおりに、authorized_keysファイルから他のユーザーの権限削除、SYSTEMユーザーにフルアクセス権限を付与すればよい(これもやり方はググれば出てくる)。そして、killしたJobである2.1の`Start-Job {& ssh -fNR 13389:localhost:22 svr_to_conoha}`をしてから、クライアントの方で冒頭の`ssh -v -i C:\Users\{クライアントのユーザ名}\.ssh\id_rsa3 {クライアントのユーザ名}@localhost -p 50000`をすればつながる。もしつながらない場合、クライアント側とサーバ側のエラーログを見て調べるとよい。必要があればVPSのログもLogLevelをDEBUGにして見ると良いと思う。

# 3. configファイルによる接続の自動化
https://nxmnpg.lemoda.net/ja/5/ssh_config をみると、頑張れば今までのコードをそれなりに自動化できるような気がした。
## 3.1 サーバ側をConfigファイルによって自動化する
-Rオプションについては自動化できそう：

```config:（サーバ上）C\Users\{サーバのユーザ名}\.ssh\config
Host svr_to_conoha
    User {VPSのログインユーザー名}
    HostName {VPSのホスト名}
    IdentityFile "C:\Users\{サーバのユーザー名}\.ssh\id_rsa"
    ProxyCommand C:\Program Files\Git\mingw64\bin\connect.exe -H {プロキシサーバーのアドレス}:{ポート} %h %p
    ServerAliveInterval 60
    ExitOnForwardFailure yes
    TCPKeepAlive no
    RemoteForward 13389 localhost:22
```

これでマジックナンバーをすべてconfigに記述することができた。サーバのpowershellでは

```powershell:サーバのpowershell
Start-Job {& ssh -fN svr_to_conoha}
```

のみでトンネルを立ち上げられる。これをシェルスクリプトにしてスタートアップフォルダに設定すればOS起動で自動的に立ち上がる（後述）。

## 3.2 クライアント側をConfigファイルによって自動化する
まずは2.2の部分をもう少しconfigに入れていく。-Lオプションについても同様に自動化できる：

```config:（クライアント上）C\Users\{クライアントのユーザ名}\.ssh\config
Host cli_to_conoha
  User {VPSのログインユーザー名}
  HostName {VPSのホスト名}
  IdentityFile "C:\Users\{クライアントのユーザー名}\.ssh\id_rsa2"
  ServerAliveInterval 60
  ExitOnForwardFailure yes
  TCPKeepAlive no
  LocalForward 50000 localhost:13389
```

あとは、powershellで

```powershell:クライアント上のpowershell
Start-Job {& ssh -fN cli_to_conoha}
```

とすればバッググラウンドで動いてくれる。

最後に、トンネルを使ってクライアントとサーバを繋げば良い。ほんとうはProxyCommandなどを使って上げて一発で起動できるようにしたいのだが、普通ProxyCommandが使われる状況というのは目的のサーバにグローバルipが割り当てられている場合が多く、今回の状況でProxyCommandを使おうとすると頭がおかしくなって死ぬので、二段階に分けようと思う。

```（クライアント上）C\Users\{クライアントのユーザ名}\.ssh\config
Host cli_to_svr
  User {サーバのユーザ名}
  HostName localhost
  port 50000
  IdentityFile ~/.ssh/id_rsa3
  ForwardAgent yes
  ServerAliveInterval 60
  ExitOnForwardFailure yes
  TCPKeepAlive no
```

これであとは

```powershell:クライアント上のpowershell
ssh -v cli_to_svr
```

で起動できる(起動できれば-vオプションはもう必要ない)。


`Start-Job {& ssh -fN cli_to_conoha}`したあとにcli_to_svrのconfigを使えばvscodeでRemote-sshもできるだろう。

## 3.3 サーバの起動時、自動でトンネルを掘るようにする

```powershell:start_tunneling.sh1
Start-Job {& ssh -fN svr_to_conoha}
```

start_tunneling.sh1などとしてデスクトップなり適当なところにスクリプトを保存する。それからショートカットを作成し、 https://atmarkit.itmedia.co.jp/fwin2k/win2ktips/1024psshrtct/psshrtct.html を参考に
プロパティ＞ショートカットタブ＞"リンク先"の欄の先頭にpowershellを付け加える
とすればそのスクリプトをpowershellが実行できるようになる。あとはこのショートカットをwin+Rでshell:startupを実行したときにでてくるフォルダに入れれば良い。

# 4. 感想
初めて多段sshをしたのでいろいろ勉強になった。トリッキーな感じがしておもしろかった。