課題:実家に設置したRaspberyPiをリモートでメンテナンスする
実家に置いたLinuxサーバーを管理したいけど、固定IPじゃないしルーターの向こう側なので、外からアクセスするのはVPNでも使わないと無理。そこで、実家のLinuxサーバーから固定IPのレンタルサーバーにssh接続して reverse port forwarding でトンネルを掘ることにしました。
概略
実家に置いたRaspberyPi(リモート側)から借りているレンタルサーバー(ローカル側)にssh接続して、reverse port forwardingを行い、レンタルサーバーから実家のRaspberyPiに接続できるようにする。
レンタルサーバーのアドレスは仮にlocal.server.com、reverse port forwarding に使うポートは仮に 99922 とする。
ローカル側の設定
インターネットの切断などでsshが途切れてしまうとポートが占有されたままになってしまう事があり、そういう場合には残ったプロセスを殺す必要がある。そこでリバースポートフォワーディングを受け入れるローカル側に以下のようなスクリプトを仕込む。
#!/bin/bash
# reverse port forwarding に使いたいポートが使われていたらそのプロセスを殺してOKを返すプログラム
# インターネット回線が途中で途切れたりするとプロセスが残っていたりするのでこの工程は必須
pid=`sudo lsof -i:99922 | grep 99922 | sed "s/ */\t/g" | cut -f 2`
if [ "$pid" = "" ]
then
echo OK
else
kill $pid
if [ $? -eq 0 ]
then
echo OK
fi
fi
リモート側からこのプロセスをキックするために RAPPORT_KEY と紐づけられているauthorized_keys の中身に以下を追記する。
command="/home/user/program/killZombieConnection.sh" ssh-rsa AAAAB3............. user@remote.com
リモート側の設定
実家側のRaspberyPiには起動したらcrontabを使ってreverse_ssh_forwarding.sh を実行させる。
そうするとインターネット接続ができるようになるのを待ってから私が管理するサーバーへreverse port forwardingを行う。
実家に設置したRaspberyPiはLEDとスピーカーが付いているので、インターネット接続が確立出来たらLEDを点灯させ、ポートフォワーディングが張れたらLEDを消灯することにする。また、IPアドレスを実家のJSAYにしゃべらせる。
#!/bin/bash
LOG="/home/pi/log/sshlog.txt"
SSH_KEY="/path/to/ssh/key" # reverse port forwarding 用の秘密鍵
RAPPORT_KEY="/path/to/ssh/rapport/key" # 接続したら同じポートを使っているプロセスを殺してOKを返してすぐに接続を切るスクリプトと紐付けた秘密鍵
SERVER="local.server.com" # 母艦となるサーバーのアドレス
USER="user" # ユーザー名
PORT="99922:127.0.0.1:22" # 99922 は任意のポート番号
LED=16 # LED につながっているGPIOのピン番号
JSAY="/home/pi/programs/talking/jsay.sh" # jsay へのパス
gpio -g mode $LED out
rm -f $LOG
# 試しに母艦にssh接続してみる。つながるようになったら reverse port forwarding を張るステップに進む
rapport=0
while [ "$rapport" != "1" ]
do
sleep 2
# echo "checking"
gpio -g write $LED 0
rapport=`ssh -i $RAPPORT_KEY -l $USER $SERVER 2>&1 | grep -c OK`
gpio -g write $LED 1
done
ADDRESS=""
times=0
while [ "$ADDRESS" = "" ]
do
echo "Checking address"
ADDRESS=`/sbin/ifconfig | grep "inet addr" | tail -1 | sed "s/.*addr://" | sed "s/ .*//"`
times=`expr $times + 1`
if [ $times -gt 5 ]
then
break
fi
sleep 1
done
echo "IPアドレスは $ADDRESSです。IPアドレスは $ADDRESSです。" | $JSAY
message="Connecting"
while [ true ]
do
sleep 5
#echo "$message"
echo "接続します" | $JSAY
gpio -g write $LED 0
# start reverse port forwarding
ssh -v -o ExitOnForwardFailure=yes -o ServerAliveInterval=60 -o ConnectTimeout=5 \
-o TCPKeepAlive=no \
-N -i $SSH_KEY -l $USER -R $PORT $SERVER >> $LOG 2>&1
wait
# ssh 接続が切れたら少し待ってまた接続しに行く
gpio -g write $LED 1
message="Re-connecting"
done
追記
帰省した時に話を聞いてみると、ルーターを再起動したときにはこのスクリプトでも再接続できない場合があるようです。Reverse port forwarding をする直前にも
rapport=ssh -i $RAPPORT_KEY -l $USER $SERVER 2>&1 | grep -c OK
を呼び出して残ったプロセスを殺す必要があるようです。
スクリプトが完成したらまた更新します。