【Linux複合技】 SSHポートフォワーディングしてセッションを維持しつつ多段SSHでファイル転送
systemd
- TOP
- Old Archives
- 【Linux複合技】 SSHポートフォワーディングしてセッションを維持しつつ多段SSHでファイル転送
SSHでリモートからポートフォワーディング
例えばNAT内に存在するコンピュータに対してサーバーに代理応答してもらう方法になる。
この方法を使えばNAT内のコンピュータに対してサーバーを経由してアクセスすることが可能になる。
このポートフォワーディングは以下のように行う。
ssh -R 5000:host:10000
-L
と比べ-R
はその意味を見失いやすい。
5000
はリモートホスト側のポートであり、リモートホストのこのポートにアクセスすることによりローカルホスト側にアクセスすることができるようになる。
hostはローカルホストからみたポートだ。
ssh
を実行しているコンピュータ自身に転送するのであればlocalhost
だし、LANの他のコンピュータ、例えばbob
にアクセスしたいのならbob
になる。
10000
はhostのポートである。
ここではSSHに対してログインさせたいので、変更していなければ22
になるだろう。
リモート側ポートはそのポートを開くため、1024より小さい値を指定するには特権がいる。 逆にホスト側ポートは単にそのポートに転送するだけなので転送したいポートを指定する。
このままだとログインしてしまうのでバックグラウンドで実行するオプション-f
と、何もコマンドを実行しない-N
を組み合わせるのが一般的だ。
ssh -f -N -R 5000:localhost:22
なお、-g
をつけない限りはリモートホストではループバックネットワークインターフェイスにのみバインドされるため、リモートホストに対して外部からアクセス可能になるわけではない。
セキュリティを考えれば、公開はせずにSSHでサーバーにログインし、そこから転送するようにしたほうがいいだろう。
接続を維持する
このままでは環境によっては入出力がないSSHセッションはすぐ切断されてしまう。 そこで、このSSHセッションは維持してもらいたい。
ピンポンのための双方向入出力
結局使わなかったアイディア。
通常のシェルスクリプトではあるプロセスに対して別のプロセスが読むことも書くこともする、ということはできない。 そういうことがしたい場合の方法は主にふたつ。
Procfs
/proc/<PID>/fd/0
に対して書き込めば標準入力に入力が与えられる。
このとき注意すべきは、標準入力がつながっているのが端末だと端末に書いてしまうので、標準入力はパイプにつながっている必要がある。
特にパイプから何も入れる予定がないのであればsleep
につなぐと良いだろう。
sleep 30 d | tr 'a-z' 'A-Z'
出力はパイプで受け取れば良い。
FIFO
こっちのほうが普通。 FIFOを使えばそこに書かれた出力を一括して受け取れる。
fifoname="$$.sshsession"
mkfifo /tmp/$fifoname
tr 'a-z' 'A-Z' < $fifoname
rm $fifoname
複数のプロセスが書く場合はちゃんと排他制御すること。 また、後処理を忘れないこと。
リモートがぽん
こんな感じでよかった。
while sleep 30 do; print PING; done | ssh somehost pong.sh | (
while read -t 45
do
:
done
)
pong.sh
while read
do
[[ "$REPLY" == PING ]] && print PONG
done
readの-t
オプションでタイムアウトしている。Zshスクリプト。
30秒ごとにPINGしていて、45秒間PONGが返ってこなかったら、たぶんコネクションは死んでいる。 まぁ、多分ぴんぽんする必要はないけど。(片方が送り続けていればいいはず)
ただ、死活チェックのために返してほしい。 どちらかといえば向こう側に送り続けてもらう必要がある。
コネクションが切れたら確実に死んでもらおう
こんな感じ。
export mainpid=$$
ssh somehost pong.sh | (
while read -t 45
do
:
done
kill -TERM $mainpid
)
もしくはこんな感じ。
ssh somehost pong.sh | {
while read -t 45
do
:
done
exit 1
}
Systemdでrespawn
systemdで起動させることにして、死んだら復活させてもらう。
[Unit]
Description=Connect for SSH port forwarding
[Service]
ExecStart=/home/jrh/bin/sshforward.zsh
ExecStop=/bin/kill -TERM $MAINPID
Restart=always
enableする予定はないので、Install
は省略。
プロセスが死んだらRestart=always
なので復活する。
停止するときはユニットをstop
すること。
ちなみに、KillMode
を省略しているので、停止時にはsshもkillされる。
SSHの設定
SSHログインできるようにする
ここらへんは基本手順。
まず鍵の生成
$ ssh-keygen -f ~/.ssh/server_rsa
これを何らかの方法でサーバーの~/.ssh/authorized_keys
に追記する。
ない場合は作成。パーミッションは600
であること。
続いて~/.ssh/config
に設定
Host server
Uesr jrh
Port 22
HostName server.example.com
IdentityFile ~/.ssh/server_rsa
これで簡単にログインできるようになった。
$ ssh server
こちらはログインする側の端末の設定である。
コマンド専用鍵を作る
まずは前項と同じように鍵を作って登録する。
転送は許可しないといけないので、こんな感じ。
authorized_keys
のコマンド用鍵の行の先頭に以下のようなフィールドを追加する。
command="/usr/local/bin/pong.sh",no-pty,no-X11-forwarding
今回はno-port-forwarding
してしまうと動作しなくなる。
ポートフォワーディングと、標準入出力を使ったやりとりを行うためだ。
なお、この時SSH鍵を使用してアクセスした場合 コマンドは入れなくて良い。 そのコマンドしか実行できないので、勝手にそのコマンドが実行される。
なお、configファイルにはRemoteForward
の項目を入れるようにすると良いだろう。
次のように。
Host proxy-server
User jrh
Port 22
HostName server.example.com
RemoteForward 10000 localhost:22
IdentityFile ~/.ssh/server-proxy_rsa
こちらはログインされる側の端末の設定。
多段ログイン
ログインする端末はサーバーにログインしたあと、SSHポートフォワーディングを利用してログインされる端末にログインする。 ログインする端末から見るとSSHを二度行うことになる。
もちろん、このようなことはできないわけではないのだが、これだとSCPやSFTPなどは利用しづらい。 また、できればコマンド一発で簡単にログインしたいところだ。
そこで、まずはログインする側の鍵をログインされる側に登録する。 これでまず、サーバーを経由せず直接に鍵認証可能な状態になる。
その上で設定ファイル(~/.ssh/config
)にProxyCommandとしてサーバーのSSHを経由して接続する設定を記述する。
Host target-proxy
user jrh
Port 10000
HostName localhost
IdentityFile ~/.ssh/proxylogin_rsa
ProxyCommand ssh -CW %h:%p server
Port
は-R
によってサーバーに開かれているポートHostName
はサーバーにログインしてから接続するものなので、localhost
IdentityFile
は直接のログインにも使用できるログインされる側に登録されているものProxyCommand
として先程の設定に記載したHost
の値を利用する
これで外出中でもサーバーを経由して端末にログイン可能になった。
複雑なので手順のまとめ
本文は知識順に記述しているが、ここではミニマムな達成順で記述する。
ここではログインする側の端末をlaptop
、ログインされる側の端末をdesktop
、サーバーをserver
と呼称する。
desktop
で鍵を生成し、server
のauthorized_keys
に登録するlaptop
で鍵を生成し、server
のauthorized_keys
に登録するlaptop
で鍵を生成し、desktop
のauthorized_keys
に登録するserver
で動作するPONGコマンドを作成するserver
上のdesktop
の鍵をPONGコマンドに結びつける。desktop
上でserver
に接続するためのコマンドを作成する。このコマンドは基本的に<PING> | <SSH> | <READ>
desktop
で作成したコマンドを反復起動するためのsystemdユーザーユニットを作成するdesktop
の~/.ssh/config
にコマンドに結びつけられたserver
にログインする設定を記述する。ポートフォワーディングも記述するlaptop
の~/.ssh/config
にserver
へのログイン、及びdesktop
に対してProxyCommand
でserver
を通じてログインする設定を記述する
これで準備は完了。あとはdesktop
でユニットを起動し、laptop
からserver
経由desktop
ログインのsshを実行するだけ。
おわりに
SSHの応用, systemdユーザーユニット, シェルスクリプト, 入出力とファイルデスクリプタ, procfs, FIFO, プロセスとシグナルなど一般デスクトップユーザーは触れずにいるような基礎知識が詰まったものになり、計らずもさながら中級Linuxer認定試験のような内容になった。
基礎に関する知識と理解があればこのように便利に利用することもできるので、 この記事がmagicalに見える方はぜひがんばって取り組んでいただきたいと思う。