最強のSSH踏み台設定

# 追記：openssh-7.3 以降なら `ProxyJump` や `-J` が使えます

ホスト名を `+` で繋げることで多段Proxy接続も簡単に、がコンセプトだった本エントリの設定ですが、OpenSSH 7.3 から `ProxyJump` という設定が使えるようになったので、使えるなら ProxyJump を使う方が健全だし柔軟で使い勝手も良いのでそちらを覚えて帰ることをオススメします。

使い方は簡単で以下のような感じです。多段も行けるし、踏み台ホスト毎にユーザ名やポート番号を変えることも出来ます。

```bash
# 1. bastion.example.jp -> internal.example.jp
ssh -J bastion.example.jp internal.example.jp

# 2. bastion.example.jp -> internal.example.jp -> super-deep.example.jp
ssh -J bastion.example.jp,internal.example.jp super-deep.example.jp

# 3. kawaz@bastion.example.jp:10022 -> ec2-user@internal.example.jp -> ec2-user@super-deep.example.jp
ssh -J kawaz@bastion.example.jp:10022,ec2-user@internal.example.jp ec2-user@super-deep.example.jp
```

- 1,2 のケースではローカルユーザが kawaz なら踏み台含めた全てのホストに kawaz が使われます。
- 2,3 のケースのようにカンマ区切りでで踏み台ホストを区切ることで多段もできます。
- 3 のケースのように踏み台ごとにポートやユーザ名を指定することも出来ます。
- 3 のケースで、以下のような `~/.ssh/config` を作っておけば `ssh super-deep.example.jp` だけでいけます。

```~/.ssh/config
Host super-deep.example.jp
  User ec2-user
  ProxyJump kawaz@bastion.example.jp:10022,ec2-user@internal.example.jp
```

または↓こんな感じに設定を整理しておくのも良いと思います。

```~/.ssh/config
Host bastion.example.jp
  Port 10022
  User kawaz
Host internal.example.jp
  User ec2-user
  ProxyJump bastion.example.jp
Host super-deep.example.jp
  User ec2-user
  ProxyJump internal.example.jp
```


---

以下は追記前の投稿全文です。

# 序文
Ansibleでホスト毎に踏み台サーバを切り替える設定を探したりして色々悩んでたら、最強のSSH設定を編み出してしまったｗｗ


# 最強のSSH踏み台設定

```text:~/.ssh/config
# 最強のSSH踏み台設定 https://qiita.com/kawaz/items/a0151d3aa2b6f9c4b3b8
Host */*
  ProxyCommand ssh -W "$(basename "%h")":%p "$(dirname "%h")"
Host *+*
  ProxyCommand ssh -W "$(sed -E 's/.*\+//'<<<"%h")":%p "$(sed -E 's/\+[^\+]*//'<<<"%h")"
```

※ `/` 区切りだとrsyncとかで都合悪い時があるので `+` 区切りも出来るようにしておく事にした。というかむしろ最近 `+` しか使ってないから `/` 区切り消そうかなと思ってる。


# 使用例

上記設定を1個書いておくだけでこんなふうに接続できます。

```bash:
ssh bastion.example.jp+internal.example.jp
```

## 多段もOK

いい感じに再帰しながらマッチしてくれるので多段SSHも出来ます。

```bash:
ssh bastion.example.jp+internal.example.jp+super-deep.example.jp
```

これは外から見ると↓こんなプロセスツリーになってます。

```text:
\_ ssh bastion.example.jp+internal.example.jp+super-deep.example.jp
    \_ ssh -W super-deep.example.jp:22 bastion.example.com+internal.example.jp
        \_ ssh -W internal.example.jp:22 bastion.example.com
```

各段階の接続ユーザ名やポートが異なる場合も、そこだけ切り出した設定をたとえばこんな風に書いておけばOK

```
# 個別設定は先に書く
Host bastion.example.jp
  Port 10022
Host bastion.example.jp+internal.example.jp
  User internaluser
Host bastion.example.jp+internal.example.jp+super-deep.example.jp
  User deepuser

# 最強のSSH踏み台設定は最後に書く
Host */*
  ProxyCommand ssh -W "$(basename "%h")":%p "$(dirname "%h")"
Host *+*
  ProxyCommand ssh -W "$(sed -E 's/.*\+//'<<<"%h")":%p "$(sed -E 's/\+[^\+]*//'<<<"%h")"
```

あ、別に個別設定の中にProxyCommandがない限りはどっちが先でも問題ないのか。まぁ共通系は下に書くのを基本にしとくのがベターってことで。


## 多段経路で rsync も実行したい
rsyncでホスト名部分に`/`を入れると引数のパースがうまく出来なくて駄目だけど、↓こんな風に`/`が無いエイリアスなホスト名を設定しておけば…

```text:~/.ssh/config
Host super-deep
  HostName bastion.example.jp/internal.example.jp/super-deep.example.jp
```

実行できます！

```bash:
rsync -a src/ super-deep:/path/to/remote/
```

もしくは追加設定などせずとも、↓こんな風に実行してやれば多段rsyncも余裕です。

```bash:
rsync -a -e "ssh -o 'HostName bastion.example.jp/internal.example.jp/super-deep.example.jp'" src/ dummy:/path/to/remote/
```

この場合、dummy ていうホスト名はsshの追加オプションのHostNameで上書きされるからこの値は意味を持ちません。

## 多段経路で rsync その２
上に書いたrsyncのやり方は簡単とはいえ、折角最強設定使ったのにあまりスマートじゃないので改善したい…。
で、ホスト名のセパレータが`/`じゃなきゃ良いんじゃね？ってことで、`+`でも繋げられるようにしてみたのがこの設定。

```text:~/.ssh/config
Host *+*
  ProxyCommand ssh -W "$(sed -E 's/.*\+//'<<<"%h")":%p "$(sed -E 's/\+[^\+]*//'<<<"%h")"
```

これだと何の工夫も無しにrsyncが出来る！

```bash:
rsync -a src/ bastion.example.jp+internal.example.jp+super-deep.example.jp:/path/to/remote/
```

どっちの文字で区切るのが良いかは用途別または趣味の問題かもなー。ていうか両方設定しといても害はないだろうから両方書いておけば良いのか。


# ControlPath は %C でキマリ！
今回作ったSSH踏み台設定はに実はこれを使うとホスト名がどんどん長くなってそれにより`ControlPath`の設定がどうあがいても使い物にならなくなるという問題があった。だけどそれはOpenSSH6.7以降なら`ControlPath`の設定で`%C`を使うようにすれば解決するよって話。

詳しくはこの記事の以下コメントに書いたので`ControlPath`で困ってる人は見ておくと多分ご利益あります。
http://qiita.com/kawaz/items/a0151d3aa2b6f9c4b3b8#comment-8932a2c6a4d83d519bd8