ProxyCommandを使って踏み台(Bastion)経由で直接ssh/scpする
自分の作業環境の作り直しをしているのですが、
たまに理解が怪しくなるので、確認ついでにここに吐き出しておこうと思います。
背景
「リモート環境にあるホストAにsshあるいはscpするためには、
踏み台(Bastion)となるホストXに一度SSHログインしないとならない」
というケース/環境はそこそこ多いと思います。
これがsshなのであればまあ、ssh X
-> ssh A
とするだけなのでそこまででもありませんが、
scpだった場合は概ねこんな手順になりますよね。
- まず
ssh X
としてホストXにログイン - ホストX上で
scp -p A:/path/to/datafile ~/
(オプションはお好み) - コピーが終わるのを待ってから、Xからログアウト
scp -p X:~/datafile .
- コピーが終わるのを待って
ssh X rm ~/datafile
これで何が困るかというと、
- コピー(scp)が2回必要なので、時間が余計にかかる
- 1回目のコピーが終わったことに気づかず、あるいは
終電業務時間が終了するなどして、さらに時間がかかる - ホストXの空き容量がたりないときがある
- 分割したり、X以外に使えそうなホストを探し回ったり作ったりと余計な時間やコストがかかる
- ホストX上の一時ファイルを消し忘れる ←
- むしろ「もしもの時のためにとっておこう、用が済んだらあとで消そう」などと積極的に残してしまう
そして忘れる1- 結果ホストXのディスクが一杯になって他の利用者や管理者に怒られる
こうなるからです。2
これは手動のときだけの話ではなく、
定期的にscpを実行させたいときや、スクリプトやバッチ処理時にsshでコマンド実行しないとならないときにも問題になります。
本来ならば、データ受け渡しのための仕組みをちゃんと用意すべきところではありますが、逆に(様々な事情で)恒常的な仕組みを設けられない場合もありますし、 一時的なメンテナンスや調査等でしか必要にならない場合、「そこまでしなくても…」と思ってしまうのも人情というものではないでしょうか。
ProxyCommandに-Wオプションという救世主
定番中の定番という話ではあるのですが、
ProxyCommand
を使えば、ワンライナで実行できます。
$ ssh -oProxyCommand='ssh -W %h:%p X' A
$ scp -p -oProxyCommand='ssh -W %h:%p X' A:/path/to/datafile .
ProxyCommand
中の%h
%p
が、
本来の接続先である A とそのポート(無指定だとデフォルトの 22)に置換される、という動作になります。
実例としてはこんな感じになるかと。
$ ssh -oProxyCommand='ssh -W %h:%p ec2-user@ec2-13-112-xxx-xxx.ap-northeast-1.compute.amazonaws.com' ec2-user@ip-172-31-xxx-xxx.ap-northeast-1.compute.internal
- Host X ... ec2-13-112-xxx-xxx.ap-northeast-1.compute.amazonaws.com
- Host A ... ip-172-31-xxx-xxx.ap-northeast-1.compute.internal
鍵指定(-i
)やポート番号の指定(-P9022
、-oPort=9022
等)といったオプションも、
勿論上記コマンドラインの中に含めることが可能です。
その際は、
ホストXに対するオプションをProxyCommand
内に、
ホストAに関するものを外側(-oProxyCommand
と同列)に記述してください。
なお、実際にAにアクセスするのはXなので、
コマンドを実行するホストがAのホスト名を名前解決できなくても構いませんし、
勿論ホスト名ではなく、IPアドレスで書いても構いません。
むしろ注意すべきなのは、ワンライナで書こうともAに接続するのはあくまでXなので、
Xが名前解決・接続できるようにAを指定(ホスト名、IPアドレス)する必要があるというところです。
要は普段Xで実行している内容そのままを記述すればいいのですが、
一方でSSHとしてAにログインする、つまりホストAと鍵の交換を行うのは手元のホストになるので、
鍵の置き場やknown_hosts
の記述などは、ホストXではなく手元の環境になります。
関係する全インスタンスのユーザやSSH鍵が同じならそこまで問題にはならないでしょうけど、
インスタンス先によってユーザや鍵が変わる場合は若干ややこしいですね。。。
~/.ssh/configに記述して楽をする
このような操作をそこそこ繰り返し行うなら、~/.ssh/config
に記述してしまえば楽になります。
といいますか、そういった話はさすがに定番どころの設定なので、Developers.IOでも何回か記事になっていて参考になります。
- 外部から踏み台サーバ経由の多段SSH接続をWindowsクライアントから行う | Developers.IO
- Amazon VPC環境にメンテナンス用の踏み台サーバを構築する | Developers.IO
蛇足がてら付け加えるなら、ssh_config
はHost
にパターン指定ができるので、
Host *.compute.internal 172.31.* ProxyCommand ssh -W %h:%p X
のように、VPC内のホストはすべてホストX経由で接続する…という記述しておくとより楽です。
例えば上述の例で言えば、
Host *.compute.internal ProxyCommand ssh -W %h:%p ec2-13-112-xxx-xxx.ap-northeast-1.compute.amazonaws.com Host * User ec2-user
としておくことで、下記のように短くなります:
$ ssh ip-172-31-xxx-xxx.ap-northeast-1.compute.internal
ちなみに
この指定方法は、Ansibleやrsync等、バックエンドにssh/scpを使っているコマンドにも有効です。
参考:
また直接関係はありませんが、このような踏み台ホストは頻繁にアクセスすることになるので、
ControlMaster
なども設定するとより幸せになれたことを併せてご報告いたします。
参考: