樹莓派autossh反向隧道

通常我們會將樹莓派放在家裡,做家庭影院之類的用途。而現在,各個運營商的寬帶都開始實施內網政策,也就是不會給民宅公網IP,所以像以前藉助花生殼軟件來實現DNS動態修改的方法是無法奏效了。

那麼該怎麼辦呢?

ssh反向隧道

現在,我們想要在外部訪問家庭網絡中的樹莓派,可以使用ssh反向隧道技術,它的原理比較簡單:

樹莓派主動向某公網服務器建立ssh連接,並請求公網服務器開啟1個額外的SSH的服務端口,充當樹莓派的反向代理服務。樹莓派與公網服務器之間的TCP(SSH)連接是樹莓派主動發起的,而公網服務器與外部用戶之間的TCP(SSH)連接是外部用戶主動發起的,公網服務器在中間充當代理角色,轉發兩側的數據。

從更具體的角度來講,外部用戶到公網服務器之間可以建立多條TCP連接,而公網服務器到樹莓派則只有一條共享的反向TCP連接,這是整個技術實現角度的原理。

配置autossh

樹莓派向公網服務器建立的ssh連接可能因為網絡問題斷開,所以一般我們不直接使用ssh命令而是使用一個監督程序叫做autossh,它負責拉起ssh命令,並且當ssh斷開後可以重新拉起ssh。

首先,因為autossh會幫我們建立到公網服務器的ssh連接,為了免去輸入密碼的問題,我們要讓公網服務器信任樹莓派。

為樹莓派生成ssh公鑰私鑰:

pi@raspberrypi:~ $ ssh-keygen -t rsa

然後通過ssh自帶的命令將樹莓派的私鑰拷貝到公網服務器,這樣公網服務器就完成了對樹莓派的信任配置:

pi@raspberrypi:~ $ ssh-copy-id -i ~/.ssh/id_rsa work@yuerblog.cc

上述命令將樹莓派pi用戶授信給公網服務器yuerblog.cc的work用戶,此後在pi用戶下ssh work@yuerblog.cc就不需要輸入密碼了,你可以自己驗證。

接下來,安裝autossh:

pi@raspberrypi:~ $ sudo apt-get install autossh

編寫啟停autossh的腳本:

pi@raspberrypi:~ $ vim /etc/init.d/autossh.sh

#Insert the following scripts

#!/bin/sh
### BEGIN INIT INFO
# Provides:          autossh
# Required-Start:    $local_fs $remote_fs $network $syslog
# Required-Stop:     $local_fs $remote_fs $network $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: starts the autossh
# Description:       starts the autossh
### END INIT INFO

case "$1" in
    start)
    echo "start autossh"
    killall -0 autossh
    if [ $? -ne 0 ];then
       sudo /usr/bin/autossh -M 888 -fN -o "PubkeyAuthentication=yes" -o "StrictHostKeyChecking=false" -o "PasswordAuthentication=no" -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" -R 2222:localhost:22 -i /home/pi/.ssh/id_rsa work@yuerblog.cc
    fi
    ;;
    stop)
    sudo killall autossh
    ;;
    restart)
    sudo killall autossh
    sudo /usr/bin/autossh -M 888 -fN -o "PubkeyAuthentication=yes" -o "StrictHostKeyChecking=false" -o "PasswordAuthentication=no" -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" -R 2222:localhost:22 -i /home/pi/.ssh/id_rsa work@yuerblog.cc
    ;;
    *)
    echo "Usage: $0 (start|stop|restart)"
    ;;
esac
exit 0

#Then save the file

這個腳本支持start/stop/restart三個命令,啟動autossh的命令參數作用如下:

  • -o是指定ssh命令的參數,不需要做修改。
  • -M是autossh啟動的ssh進程會監聽888這個端口,autossh進程會通過探測ssh進程的888端口來獲知ssh連接是否斷開。
  • -f是指autossh後臺運行,不會阻塞shell繼續向下執行。
  • -N是指建立的ssh連接只用於轉發數據,不解析命令。
  • -R是指建立反向隧道,一般我們ssh某個服務器是正向隧道。
  • 2222是公網服務器上的代理端口。
  • localhost:22是指公網2222端口代理到的樹莓派端口。(當你連接公網服務器的2222端口後,數據通過樹莓派之間的反向ssh連接到達樹莓派,由樹莓派端的ssh進程代理髮起到localhost:22的2次連接,從而實現ssh訪問樹莓派的目的)
  • -i指定的是之前授信給公網服務器的ssh私鑰
  • 最後是公網服務器的地址,autossh會調用ssh建立到它的ssh反向隧道。

本來打算將上述腳本做到開啟自動啟動中,但是試驗發現開機沒有成功啟動autossh,索性把它寫到crontab裡算了:

* * * * * /bin/bash /etc/init.d/autossh.sh start

接下來,你可以觀察是否有autossh進程被拉起:

pi@raspberrypi:~ $ ps aux|grep autossh
root       692  0.0  0.2   1812  1276 ?        Ss   Sep13   0:00 /usr/lib/autossh/autossh -M 888 -N  -o PubkeyAuthentication=yes -o StrictHostKeyChecking=false -o PasswordAuthentication=no -o ServerAliveInterval 60 -o ServerAliveCountMax 3 -R 2222:localhost:22 -i /home/pi/.ssh/id_rsa work@yuerblog.cc
pi       11277  0.0  0.4   4744  2008 pts/0    S+   09:44   0:00 grep --color=auto autossh

最後

現在,我登錄yuerblog.cc服務器,執行如下命令即可登錄到pi:

[work@vultr ~]$ ssh pi@localhost -p 2222