autossh

sshのコネクションはどうしても切れてしまうことがあるので、それを阻止するためにautosshを使ってみました。
まず、sshのコマンドラインを簡素にするため、MQTTブローカへの接続設定を書いたconfigファイルを用意します。

sshのコンフィギュレーション

まずは、sshの接続のコマンドラインを簡略化するためにconfigファイルを設定します。
設定を隠蔽できる(シェルスクリプトなどにパラメタを書かなくてもいい)というてんで良いかなと思います。

~/.ssh/config
1
2
3
4
5
6
Host Broker
  HostName        MQTT_Broker
  IdentityFile    ~/.ssh/id_rsa_MQTTClient
  User            MQTT_connection
  LocalForward    22883 localhost:1883
  Port            22222

ポート番号などは適宜設定してください。

こうすることで、ssh -f -N Brokerのコマンドでトンネリング設定ができます。 ここで、公開鍵はパスフレーズ無しで作ったものを指定します。セキュリティのため、ブローカのssh設定にはポート制限やIP制限、コマンドを実行しないなどの処置を必ずとる必要があります。設定の具体例(「パスフレーズなしでの接続方法とセキュリティ設定」)

さらに、これが切れたとき自動的に再接続するため、autosshを導入します。

1
 $ sudo apt-get install autossh

autosshを使ってトンネリングを設定するには

1
 $ autossh -M0 -f -N Broker

とします。

-M0 : 接続が切れているかどうかを確認するためのポート指定です。0は切断確認をポートではしないようにする設定です。

-f : 実行をバックグラウンドに移行します。

-N : sshへのオプションです。

autosshはsshを起動してそのプロセスを監視するプロセスを起動するコマンドです。

-Mオプションについては以下のように解説があります。

specifies the base monitoring port to use. Without the echo port, this port and the port immediately above it ( port + 1) should be something nothing else is using. autossh will send test data on the base monitoring port, and receive it back on the port above. For example, if you specify “-M 20000”, autossh will set up forwards so that it can send data on port 20000 and receive it back on 20001.

Alternatively, a port for a remote echo service may be specified. This should be port 7 if you wish to use the standard inetd echo service. When an echo port is specified, only the specified monitor port is used, and it carries the monitor message in both directions.

Many people disable the echo service, or even disable inetd, so check that this service is available on the remote machine. Some operating systems allow one to specify that the service only listen on the localhost (loopback interface), which would suffice for this use.

The echo service may also be something more complicated: perhaps a daemon that monitors a group of ssh tunnels.

Setting the monitor port to 0 turns the monitoring function off, and autossh will only restart ssh upon ssh’s exit. For example, if you are using a recent version of OpenSSH, you may wish to explore using the ServerAliveInterval and ServerAliveCountMax options to have the SSH client exit if it finds itself no longer connected to the server. In many ways this may be a better solution than the monitoring port.

ということなので、0を指定するとsshがexitした時に再起動します。接続はServerAliveIntervalとServerAliveCountMaxだけ待って応答が来なければexitするので、その場合sshが再起動されるという事でしょう。「この方法がポートモニタするより良い方法だ」といっているので、そうすることにします。

sshのデフォルト設定ではServerAliveIntervalは0になっている(確認のメッセージを送らない)ようなので、適宜設定する必要があります。
これがまたちょっと厄介ですね。あまり頻繁にパケットを送ると電話回線などプレミアムな回線を使っているときにコストがかさみます。本来はきちんとTPOで設定する必要があると思いますが、とりあえず30秒ぐらいに設定しておきます。

configに追記
1
2
3
4
#  connection alive detecting. 30 x 3 [s]
#
ServerAliveInterval   30
ServerAliveCountMax   3

ServerAliveCountMaxは3がデフォルトのようですので、応答がなくなったあと1分30秒で切断されるようになります。

これで、ちゃんと接続できるか確認してみます。

1
2
3
4
5
6
7
8
9
$ ps ax | grep 'ssh'
 6543 ?        Ss     0:00 /usr/lib/autossh/autossh -M0 -N Broker
 6544 ?        S      0:00 /usr/bin/ssh -N Broker

$ mosquitto_sub -v -p 22883 -t '$SYS/#'
$SYS/broker/version mosquitto version 1.3.5
$SYS/broker/timestamp 2014-10-18 21:06:44+0100
 :
 :

Okですね。 さらに、keepalive確認のパケットがどのように出ているか確認してみます。

IPアドレスは下記のようになっています。
192.168.0.XXX クライアント
192.168.0.YYY サーバ

Keep Alive packets example
1
2
3
4
5
6
7
No.     Time        Source                Destination           Protocol Length Info
      1 0.000000    192.168.0.XXX         192.168.0.YYY           TCP      130    41387 > SSHPORT  [PSH, ACK]
      2 0.010903    192.168.0.YYY         192.168.0.XXX           TCP      98     SSHPORT > 41387 [PSH, ACK]
      3 0.010941    192.168.0.XXX         192.168.0.YYY           TCP      66     41387 > SSHPORT [ACK]
      4 30.039772   192.168.0.XXX         192.168.0.YYY           TCP      130    41387 > SSHPORT [PSH, ACK]
      5 30.043964   192.168.0.YYY         192.168.0.XXX           TCP      98     SSHPORT > 41387 [PSH, ACK]
      6 30.044004   192.168.0.XXX         192.168.0.YYY           TCP      66     41387 > SSHPORT [ACK]

30秒ごとに何かしらのパケットをやり取りしていることがわかりました。設定通りです。

さらに、途中で回線を切断してみます。

Packet example (in case of disconnection)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
No.     Time        Source                Destination           Protocol Length Info
# 正常なalive確認のパケットやり取り
      1 0.000000    192.168.0.XXX         192.168.0.YYY         TCP      130    41405 > SSHPORT [PSH, ACK]
      2 0.002137    192.168.0.YYY         192.168.0.XXX         TCP      66     SSHPORT > 41405 [ACK]
      3 0.002566    192.168.0.YYY         192.168.0.XXX         TCP      98     SSHPORT > 41405 [PSH, ACK]
      4 0.041204    192.168.0.XXX         192.168.0.YYY         TCP      66     41405 > SSHPORT [ACK]

# ここで回線を切断 sshがシャットダウンするまで130秒ぐらい待ち、回線を復旧させる
#
#  sshがautosshによって再起動されて通信を開始

# 切断前のコネクションをリセット
      5 138.686269  192.168.0.XXX         192.168.0.YYY         TCP      258    41405 > SSHPORT [FIN, PSH, ACK]
      6 138.687686  192.168.0.YYY         192.168.0.XXX         TCP      98     SSHPORT > 41405 [PSH, ACK]
      7 138.687721  192.168.0.XXX         192.168.0.YYY         TCP      54     41405 > SSHPORT [RST]
      8 138.689209  192.168.0.YYY         192.168.0.XXX         TCP      130    SSHPORT > 41405 [FIN, PSH, ACK]
      9 138.689233  192.168.0.XXX         192.168.0.YYY         TCP      54     41405 > SSHPORT [RST]

# 新たなコネクションのスタート
     10 148.955582  192.168.0.XXX         192.168.0.YYY         TCP      74     41407 > SSHPORT [SYN]
     11 148.958766  192.168.0.YYY         192.168.0.XXX         TCP      74     SSHPORT > 41407 [SYN, ACK]

という感じになりました。何回か試して見ましたが、切断前のコネクションをリセットするやり取りがない場合が多いかもしれません。さらに、alive確認のパケットは2往復でなく1往復半という事もありました。

無事sshの再起動も確認出来ました。

接続確認のためのパケットのサイズは、今回の実験では360byteぐらいでした。もしこの設定(30秒に1回)だとすると1日で1Mbyteぐらいの通信量です。 最近の安いデータ通信用のSIMにとってみれば大したデータ量ではないかもしれませんね。