ssh reverse port forwarding + RaspberryPi おすすめのsystemd設定


sshのリバースポートフォワーディングを使うと、

  • NATの内側(家庭や企業のLAN内など)に置いたRaspberryPi
  • CGNのSIMでインターネットにつながっているRaspberryPi
  • グローバルアドレスを持っているが固定IPではないしDynamicDNSなども使いたくないRaspberryPi

などにも任意の場所からリモートログインできるようになり非常に便利ですが、sshのオプションとsystemdのオプションをそれぞれ適切に設定しないとコネクションが切れたときにうまく復帰してくれない。
いまのところうまくいっている設定をメモしておく。

RaspberryPiの設定

  • sshのコマンドラインオプションはReverse ssh tunnel を安定運用するを参考にした
  • Restart=alwaysでsshのプロセスがどのような終了ステータスの場合も再起動を試みる
  • RestartSecはサービス再起動までの待ち時間(秒)
  • StartLimitBurstは、サービスの再起動を停止するまでの試行回数。0に設定すると無限にサービスの起動を試みるようになる
/etc/systemd/system/ssh-rpfw.service
[Unit]
Description=ssh reverse port forwarding service
After=network.target auditd.service

[Service]
User=pi
Group=pi
WorkingDirectory=/home/pi
ExecStart=/usr/bin/ssh -oServerAliveInterval=10 -oExitOnForwardFailure=yes -oTCPKeepAlive=no -N -R 3022:127.0.0.1:22 [email protected]
Restart=always
RestartSec=1
StartLimitBurst=0

[Install]
WantedBy=multi-user.target

sshサーバの設定

  • ここで言うsshサーバとは、リバースポートフォワーディングの踏み台(中継)に使うサーバのこと
  • このサーバは当然RaspberryPi(前述)とsshクライアント(後述)の両方から接続ができる
  • 自分はさくらVPSを使っている

アカウントの作成と公開鍵の配置

  • RaspberryPiからは自動でsshが実行されるので、鍵認証が必須

sshdの設定

  • なくてもいいけど、ないとTCPのタイムアウトまで次の接続ができなくなる
  • 以下の設定だとRaspberryPiを停止してから2分弱くらいでTCPソケットが消えて再度接続できるようになる
/etc/ssh/sshd_config
~~~~ 前略
ClientAliveInterval 30
ClientAliveCountMax 3
~~~~ 後略

TCPの設定

前述の例では3022でLISTENしているが、3022は Registered Port なので、別の(本来このポートをLISTENするはずの)サービスがLISTENする可能性がある。
そのため、 Ephemeral Port の範囲を絞って、あいた範囲をリバースポートフォワーディングの待ち受け用ポートにしようかと思ったが、だるかったのでやめた。
(というのも、FreeBSD handbookの当該の箇所が日本語訳されていなかったので、先にドキュメントの修正をやらないといけない気持ちになってしまった)

sshクライアントの設定

  • ここでいうsshクライアントとは、上記の設定をしたRaspberryPiに対してリモートログインしたいワークステーションなどのこと
  • リバースポートフォワーディングのソケットはlocalhostにバインドされているので、多段sshを実行する
~/.ssh/config
Host exmaple.com
  User user
  Host example.com

Host raspberrypi
  HostName  localhost
  Port 2201
  User pi
  ProxyCommand ssh -W %h:%p example.com

上記のraspberrypiの設定があれば、

$ ssh raspberrypi

のみで、RaspberryPiにリモートログインできる。