zaki work log

作業ログやら生活ログやらなんやら

[Linux] ProxyJump設定でSSHの多段アクセスとscp/ポートフォワード

検証環境へアクセスするために踏み台サーバ―を経由しないとアクセスできないとか、踏み台サーバーを3つ経由しないと本番環境へアクセスできないとか、そんな場合でもsshで1コマンドでアクセスするためのオプション指定について、man sshを眺めていたらたまたまProxyJumpというオプションが目に留まったので確認してみた。

環境

ローカルホストから、

  1. ssh 192.168.0.16 (踏み台1)
  2. ssh 172.16.1.0 (踏み台2)
  3. ssh 172.29.0.89 (お目当てのターゲットホスト)

という順序でSSHアクセスしたい場合、以前は(というより今でも多くのところで)ProxyCommandを使って多段アクセスしていたが、(2016年リリースのOpenSSH 7.3で)ProxyJumpオプションが追加されており、このオプションを使うとより簡潔なオプション指定で多段アクセスを実現できる。

図にするとこんな構成。

ProxyJumpを使った多段アクセス

[zaki@cloud-dev ~]$ ssh 172.29.0.89 -J 192.168.0.16,172.16.1.0
zaki@192.168.0.16's password: 
zaki@172.16.1.0's password: 
zaki@172.29.0.89's password: 
Last login: Wed Feb  2 21:10:16 2022 from 172.29.0.10
[zaki@restricted-node ~]$
[zaki@restricted-node ~]$ ip a s ens192
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:77:f6:c4 brd ff:ff:ff:ff:ff:ff
    inet 172.29.0.89/24 brd 172.29.0.255 scope global noprefixroute ens192
       valid_lft forever preferred_lft forever

この通り、-Jオプションにアクセスしたい順番にカンマ区切りで踏み台ホストを羅列すれば、記述した順序でSSHアクセスして最終的にお目当てのターゲットホストへログインする動作になる。

ユーザー名やポートが異なるサーバーがある場合

踏み台の特定ホストだけユーザー名やポート番号を指定する場合は、username@host:portの書式で記述する。
途中の踏み台の172.16.1.025022でListenしていて、さらにsample-userユーザーでアクセスする場合は以下の通り。

[zaki@cloud-dev ~]$ ssh 172.29.0.89 -J 192.168.0.16,sample-user@172.16.1.0:25022
zaki@192.168.0.16's password: 
sample-user@172.16.1.0's password: 
zaki@172.29.0.89's password: 
Last login: Wed Feb  2 21:10:49 2022 from 172.29.0.10
[zaki@restricted-node ~]$ 

鍵認証

ここまでの例はコマンド実行例からも分かる通り、パスワード認証でログインしている。
公開鍵認証については、ドキュメントからはちょっと読み取れなかったけど、コマンドラインオプションで指定できる秘密鍵は、お目当てのターゲットホストに登録してある公開鍵のみに作用している。
踏み台サーバーの鍵認証用の秘密鍵コマンドラインでは指定できなさそう。

ターゲットホストとの公開鍵認証はオプション指定で従来通り特に問題なく実行できる。
ローカルにある秘密鍵~/.ssh/id_rsa_4096の公開鍵が、ターゲットホストの~/.ssh/authorized_keysに登録済みであれば、以下の通り。

[zaki@cloud-dev ~]$ ssh 172.29.0.89 -J 192.168.0.16,sample-user@172.16.1.0:25022 -i ~/.ssh/id_rsa_4096
zaki@192.168.0.16's password: 
sample-user@172.16.1.0's password: 
Last login: Wed Feb  2 21:18:53 2022 from 172.29.0.10
[zaki@restricted-node ~]$ 

172.29.0.89アクセス時のパスワード認証が行われず、鍵認証でログインできている。
踏み台サーバーも鍵認証を行いたい場合は、少なくともssh_configに記述すれば可能。

ssh_config基本

まずは鍵認証なしの場合の記述。
踏み台2つ目以降は、その前段の踏み台のホストが何かをproxyjumpの行に記述する。

Host proxy1
    hostname 192.168.0.16
Host proxy2
    hostname 172.16.1.0
    user sample-user
    port 25022
    proxyjump proxy1
Host target
    hostname 172.29.0.89
    proxyjump proxy2

踏み台サーバーへの秘密鍵の指定がある場合は、それぞれ指定すればOK

Host proxy1
    hostname 192.168.0.16
    identityfile ~/.ssh/id_rsa_4096
Host proxy2
    hostname 172.16.1.0
    user sample-user
    port 25022
    identityfile ~/.ssh/id_rsa_nopass
    proxyjump proxy1
Host target
    hostname 172.29.0.89
    identityfile ~/.ssh/id_rsa_4096
    proxyjump proxy2

それぞれ対応した公開鍵をそれぞれのホストに登録してあれば、ターゲットホストへのSSHアクセスのコマンドで一気にログインできる。
(パスフレーズが設定されてればもちろん入力は必要だけど)

[zaki@cloud-dev ~]$ ssh target 
Last login: Wed Feb  2 21:36:41 2022 from 172.29.0.10
[zaki@restricted-node ~]$ 

ポートフォワード

ターゲットホスト上からlocalhost:8888でアクセスできるwebサーバーがあるとして、このwebサーバーにSSHを実行するローカルからアクセスしたい場合について。といっても、通常のローカルポートフォワードと同じく-Lを使えばOK。
ローカルホストの8889/TCPをターゲットのlocalhost:8888にフォワードするには、-L 8889:localhost:8888を指定する。

[zaki@cloud-dev ~]$ ssh 172.29.0.89 -J 192.168.0.16,sample-user@172.16.1.0 -L 8889:localhost:8888
zaki@192.168.0.16's password: 
sample-user@172.16.1.0's password: 
zaki@172.29.0.89's password: 
Last login: Wed Feb  2 23:08:47 2022 from 172.29.0.10
[zaki@restricted-node ~]$

この状態で確認用webサーバーを起動。

[zaki@restricted-node ~]$ ls -aF
./  ../  .bash_history  .bash_logout  .bash_profile  .bashrc  .ssh/
[zaki@restricted-node ~]$ python -m SimpleHTTPServer 8888
Serving HTTP on 0.0.0.0 port 8888 ...

これで、SSH接続元のブラウザでlocalhost:8889にアクセスすると、ターゲットホストのlocalhost:8888フォワードされる。

ターゲットホストに対するローカルポートフォワード込みのssh_configは以下の通り。
(踏み台の記述は変更無し)

Host target
    hostname 172.29.0.89
    identityfile ~/.ssh/id_rsa_4096
    proxyjump proxy2
    localforward 8889 localhost:8888

※ SimpleHTTPServerについては以下参照。

qiita.com

scpの多段転送

[zaki@cloud-dev ~]$ scp ./kubeconfig.yaml target:/var/tmp

ssh_configが設定されていれば、これで一気にターゲットホストへ転送できる。
ホップ数によってはこれがかなり便利。

ssh_config設定無しでコマンドラインオプションで指定するには、scpには-Jオプションがないため、-oProxyJumpを指定する。(※ 後述)

[zaki@cloud-dev ~]$ scp -o 'ProxyJump 192.168.0.16,sample-user@172.16.1.0' ./kubeconfig.yaml 172.29.0.89:/var/tmp
zaki@192.168.0.16's password: 
sample-user@172.16.1.0's password: 
zaki@172.29.0.89's password: 

2023.08.01追記:
scpにもOpenSSH 8.0で-Jオプションに対応していた模様。ssh-Jと同様に踏み台の指定を行う。
ただし現時点(OpenSSH 8.8)では、-Jオプションを転送元・転送先ファイル名の後に指定すると踏み台の指定がファイル名と認識されてしまうため、コマンドの直後に指定する。

### 後ろにオプション指定した場合
$ scp ./memo.md  172.16.1.3:~ -J 192.168.0.16
192.168.0.16: No such file or directory

### 先にオプション指定した場合
$ scp -J 192.168.0.16 ./memo.md  172.16.1.3:~
zaki@172.16.1.3's password: 

WindowsSSH

動作する。

PS C:\Users\zaki> ssh -V
OpenSSH_7.7p1, OpenSSL 1.0.2o  27 Mar 2018
PS C:\Users\zaki> ssh 172.29.0.89 -J 192.168.0.19,172.16.1.0
zaki@192.168.0.19's password:
zaki@172.29.0.89's password:
Last login: Sat Jan 29 20:13:16 2022 from 172.29.0.10
[zaki@restricted-node ~]$

5年以上前に機能追加されてたなんて知らなかった。。
ついでにいうとssh-copy-idコマンドもしばらく知らなかった←