使用 SSH TUNNEL 打通公司和家里的網絡

前言


平時經常遇到下面的情況:

  1. 工作沒做完,想回家繼續做。帶電腦回家?
  2. 碰到以前做過的功能,想從家里的電腦撈一點代碼。沒獨立 IP 怎么訪問?
  3. 服務器只允許公司網絡訪問,在外面要登服務器。找領導設置安全組權限 ( 或者防火墻 ) ?
  4. 在外面想訪問公司局域網中的某個服務,比如 Gitlab
  5. 公司網絡不能逛淘寶?
  6. 想去墻外的世界看一看?

一般情況下的對應方法:

  1. 遠程連接 ( 比如 Windows 自帶的 )
  2. NAT 內網穿透 ( 比如花生殼,幾年前用過免費版的,不太穩定,Linux 下安裝也不太順暢 )
  3. 一般需要正當理由
  4. 公司有固定公網 IP 的話,路由器設置端口轉發,不過路由器權限呢?
  5. 代理服務器
  6. VPN ( 作用和代理服務器差不多,但原理不一樣 )

本文的目的就是使用 SSH TUNNEL 這項技術解決上面的問題,也就是打通所謂的 "公司內網" 和 "家庭內網"。

SSH TUNNEL 簡介


經常遠程連接 Linux 服務器的小伙伴,對于 ssh 這個命令應該不陌生吧。它還可以不執行遠程指令只建立一條和遠程服務器之間的連接,也就是形成了一條通道,在這條通道上我們就可以做一些文章了。
先看下 ssh 命令的參數介紹 ( 需要了解更多請通過命令 man ssh 查看 )

-1: 強制使用ssh協議版本1;
-2: 強制使用ssh協議版本2;
-4: 強制使用IPv4地址;
-6: 強制使用IPv6地址;
-A: 開啟認證代理連接轉發功能;
-a: 關閉認證代理連接轉發功能;
-b: 使用本機指定地址作為對應連接的源IP地址;
-C: 請求壓縮所有數據;
-F: 指定ssh指令的配置文件;
-f: 后臺執行ssh指令;
-g: 允許遠程主機連接主機的轉發端口;
-i: 指定身份文件;
-l: 指定連接遠程服務器登錄用戶名;
-N: 不執行遠程指令;
-o: 指定ssh客戶端配置選項來覆蓋配置文件中的配置,比如: -o "StrictHostKeyChecking no" 可以去除檢查主機的提示;
-p: 指定遠程服務器上的ssh端口(默認為22);
-q: 靜默模式;
-X: 開啟X11轉發功能;
-x: 關閉X11轉發功能;
-y: 開啟信任X11轉發功能。

-D: 監聽本地的指定端口,本機將作為 SOCKS5 服務器使用
-L: 本地監聽指定端口,該端口收到的請求會通過遠程服務器轉發到另一臺機器的指定端口上
-R: 遠程服務器上監聽指定端口,該端口收到的請求會通過本地轉發到另一臺機器的指定端口上

關鍵的就是最后的三個參數,對應 SSH 隧道 的三種端口轉發模式: 本地轉發 ( -L Local ),遠程轉發 ( -R Remote ) 和動態轉發 ( -D Dynamic )

本地轉發

本地監聽指定端口,該端口收到的請求會通過遠程服務器轉發到另一臺機器的指定端口上

  • 語法:

    ssh [-p ssh_port] -L [bind_address:]port:host:hostport user@remote_host
    
    解釋:
        port: 本地監聽的端口
        bind_address: 監聽端口使用的本地地址 ( 如: 192.168.1.2,127.0.0.1,0.0.0.0 ), 不設置時默認為回環地址 ( 127.0.0.1 )
        host: 轉發的目標 IP
        hostport: 轉發的目標端口
    注意:
        host:hostport 是遠程主機 user@remote_host:ssh_port 所能訪問到的地址 ( 包括 user@remote_host 自己 ),本地主機不一定能訪問該地址
    
  • 示例:

    ssh -p 23 -Nf -L 0.0.0.0:3000:123.123.123.124:3001 root@123.123.123.123
    
    說明:
        1. 通過 23 端口 ssh 遠程服務器 123.123.123.123,使用帳號 root 登錄
        2. 本地監聽端口 0.0.0.0:3000
        3. 123.123.123.124 和 123.123.123.123 在一個局域網下,而本地不能直接訪問 123.123.123.124
        4. 123.123.123.124 主機通過 3001 端口開放服務
        5. 調用鏈路: 本地訪問 --> 127.0.0.1:3000 -- ssh 隧道 --> 123.123.123.123 -- 轉發 --> 123.123.123.124:3001
    

    上面示例中相當于將遠程主機 123.123.123.1243001 端口映射為本地的 3000 端口,這樣就可以通過局域網訪問被限制的公網服務了。

遠程轉發

遠程服務器上監聽指定端口,該端口收到的請求會通過本地轉發到另一臺機器的指定端口上

  • 語法:

    ssh [-p ssh_port] -R [bind_address:]port:host:hostport user@remote_host
    
    解釋:
        語法和 -L 一樣,只不過監聽側和目標側的網絡對換而已
        port: 在遠程主機 user@remote_host 上監聽的端口
        bind_address: 遠程主機 user@remote_host  監聽端口使用的地址 ( 如: remote_host, 127.0.0.1, 0.0.0.0 )。和 -L 有點區別,指定 IP 需要在遠程主機上修改 /etc/ssh/sshd_config 中的配置: GatewayPorts。
          "yes" 表示 強制為 0.0.0.0;
          "no" 表示 強制為回環地址 ( 127.0.0.1 ); ( 默認 )
          "clientspecified" 表示 由客戶端決定;
        host: 轉發的目標 IP
        hostport: 轉發的目標端口
    注意:
        host:hostport 是本地主機所能訪問到的地址 ( 包括本機 ),遠程主機 user@remote_host:ssh_port 不一定能訪問該地址
    
  • 示例:

    ssh -p 23 -Nf -R 172.17.0.1:3000:192.168.1.100:3001 root@123.123.123.123
    
    說明:
        1. 通過 23 端口 ssh 遠程服務器 123.123.123.123,使用帳號 root 登錄
        2. 123.123.123.123 監聽端口 172.17.0.1:3000 ( 我的遠程服務器運行著 docker 所以有一個虛擬網絡 172.17.0.0 )
        3. 192.168.1.100 和 本地主機 在一個局域網下,而 123.123.123.123 ( 外網 ) 不能直接訪問 192.168.1.100 ( 內網 )
        4. 192.168.1.100 主機通過 3001 端口開放服務
        5. 調用鏈路: 遠程服務器中訪問 --> 172.17.0.1:3000 -- ssh 隧道 --> 本地 -- 轉發 --> 192.168.1.100:3001
    

    上面示例中相當于將局域網主機 192.168.1.1003001 端口映射為遠程服務器 123.123.123.1233000 端口,這樣就可以通過公網來訪問局域網中的服務了。

動態轉發

監聽本地的指定端口,本機將作為 SOCKS5 服務器使用

  • 語法:

    ssh [-p ssh_port] -D [bind_address:]port user@remote_host
    
    解釋:
        port: 本地監聽的端口
        bind_address: 監聽端口使用的本地地址 ( 如: 192.168.1.2, 127.0.0.1, 0.0.0.0 ), 不設置時默認為回環地址 ( 127.0.0.1 )
    注意:
        啟動 socks5 代理
    代理測試示例:
        curl --socks5 bind_address:port baidu.com
    
  • 示例:

    ssh -p 23 -Nf -D 127.0.0.1:3000 root@123.123.123.123
    
    說明:
        1. 通過 23 端口 ssh 遠程服務器 123.123.123.123,使用帳號 root 登錄
        2. 本地監聽端口 127.0.0.1:3000
        3. 調用鏈路: 本地局域網中訪問任意 http 服務 -- 使用代理 --> 172.17.0.1:3000 -- ssh 隧道 --> 123.123.123.123 -- 轉發 --> 目標 http 服務
    

    上面示例中相當于開啟了一個 代理服務器 ,比起 本地轉發 需要指定具體端口來說更加方便。

正文


上面已經介紹了 SSH TUNNEL 的使用方式,接下來就開始干正事了,先上兩張網絡拓撲圖。

SSH TUNNEL 最終效果
原先未配置的網絡

對比兩張圖把少的線連上就好了,是不是很 easy ?

  • 原先環境:
    三個獨立的網絡: 公司內網、家里內網、阿里云專有網絡,其中可以通過 公司網絡 訪問 阿里云 上的服務器而 家里網絡 則不行。

  • 目標:

    1. 使 家里網絡 的主機能訪問 公司內局域網 中的主機
    2. 使 家里網絡 的主機能訪問 公司阿里云 上的服務器
  • 預備工作:

    1. 有一臺公網的服務器作為 跳板機 ,公司和家里都能訪問到它

      ( 沒有服務器?阿里云騰訊云學生機弄臺玩玩 )

    2. 因為 ssh 命令不能添加密碼作為參數,只能手動輸入,而且每次連接都要重新輸入也不是很方便,這里采用 公鑰方式 登錄就可以免輸密碼了

      # 創建 ssh 密鑰對
      ssh-keygen -t rsa -b 4096 -C "備注1"
        
      # 遠程服務器上的 ~/.ssh/authorized_keys 權限要是 600
      # 追加本機公鑰到遠程服務器
      cat ~/.ssh/id_rsa.pub | ssh -p 23 root@123.123.123.123 "cat - >> ~/.ssh/authorized_keys"
        
      # 或者直接復制本機公鑰到遠程服務器 ( 會覆蓋掉遠程服務器已有的其他公鑰 )
      scp -P 23 -p ~/.ssh/id_rsa.pub root@123.123.123.123:~/.ssh/authorized_keys
      
  • 步驟:

    1. 公司主機A 的端口 22 映射到 跳板機J 的端口 9999 上, 在 公司主機A 按照下面的命令配置 ( 跳板機J 需要按上文中的方式將 GatewayPorts 設為 true )

      ssh -p 23 -Nf -R 9999:127.0.0.1:22 root@123.123.123.123
      

      當然也可以通過 docker 運行 autossh 來使這個隧道可以自動重連

      # 暴露本地 ssh 端口到遠端服務器端口
      ssh-to-expose-ssh-server:
        image: jnovack/autossh
        container_name: autossh-ssh-to-expose-ssh-server
        # 使用宿主機的網絡
        network_mode: host
        environment:
          - SSH_HOSTUSER=root
          - SSH_HOSTNAME=123.123.123.123
          - SSH_HOSTPORT=23
          - SSH_TUNNEL_REMOTE=9999
          - SSH_TUNNEL_HOST=127.0.0.1
          - SSH_TUNNEL_LOCAL=22
        restart: always
        volumes:
          - ~/.ssh/id_rsa:/id_rsa  
      

      這樣在 跳板機J 上就可以通過 ssh -p 9999 root@127.0.0.1 遠程連接到 公司主機A

    2. 跳板機J 上設置動態轉發使其作為 代理服務器 , 將接收到的請求再轉發到 公司主機A

      跳板機J上執行下面的命令

      ssh -p 9999 -Nf -D 0.0.0.0:1080 root@127.0.0.1
      

      同樣的,要把 跳板機J公鑰 復制到 公司主機A 上實現免密登錄

      # 創建 ssh 密鑰對
      ssh-keygen -t rsa -b 4096 -C "備注2"
      # 追加本機公鑰到遠程服務器
      cat ~/.ssh/id_rsa.pub | ssh -p 9999 root@127.0.0.1 "cat - >> ~/.ssh/authorized_keys"
      

      當然也可以使用 docker 啟動 autossh

      # 使用 SSH TUNNEL 創建 SOCKS5 代理
      ssh-tunnel-socks5-server:
        image: jnovack/autossh
        container_name: ssh-tunnel-socks5-server
        entrypoint:
          autossh
           -M 0
           -N
           -o ServerAliveInterval=5
           -o ServerAliveCountMax=1
           -o "ExitOnForwardFailure yes"
           -o "StrictHostKeyChecking no"
           -t -t
           -i /id_rsa
           -D 0.0.0.0:1080
           -p 9999
           # 使用宿主機的IP
           root@172.17.0.1
        ports:
          - "1081:1080"
        restart: always
        volumes:
          - ~/.ssh/id_rsa:/id_rsa
      

    這樣 家里主機F 就可以通過設置代理 跳板機J 連接上 阿里云 上的服務器了。

    原理: 公司主機A跳板機J 聯合組成 代理層 。調用鏈路為: 家里主機F -- 使用代理 --> 跳板機J -- ssh 隧道 --> 公司主機A -- 轉發 --> 阿里云 服務器。

    : 組成 代理層 還有另一種方式: 公司主機A 開代理服務同時把端口遠程暴露到 跳板機J 上, 通過這種方式就可以配置代理服務的密碼了, 具體配置參照 這里

就這么兩步配置已經打通"公司網絡"和"家里網絡"了。不過,貌似實現的效果是單向的誒 ( 只能家里連公司,公司還不能連家里, 如下圖所示 )

SSH TUNNEL 單向配置效果

解決辦法: 家里主機F公司主機A 的步驟再配一遍就好啦。

拓展


  • 使用代理訪問受限制的網站 ( 比如公司內網的項目 ) :

    1. 由于代理服務器使用的是 SOCKS5 協議,Chrome 上安裝 SwitchyOmega 插件配置代理服務器地址,即可通過代理服務器訪問受限制的服務了。

    2. SOCKS5 協議轉為 HTTP 協議,然后通過系統自帶的方式 ( Win 下設置 IEInternet 屬性 ,移動設備設置 WIFI 的 HTTP 代理 ) 來設置全局代理,docker-compose 配置如下

      # 使用 gost 代理 SOCKS5 端口
      gost-server:
        image: ginuerzh/gost
        command: -L=:8080 -F=socks5://ssh-to-local-proxy:1080
        ports:
          - "1082:8080"
      restart: always
      
      # 使用 privoxy 代理 SOCKS5 端口
      privoxy:
        image: rdsubhas/tor-privoxy-alpine
        entrypoint: sh -c 'echo "listen-address 0.0.0.0:8118" > /etc/service/privoxy/config && echo "forward-socks5 / ssh-to-local-proxy:1080 ." >> /etc/service/privoxy/config && privoxy --no-daemon /etc/service/privoxy/config'
        ports:
        - "1083:8118"
        restart: always
      

      這里給出兩種轉換方式 : gostprivoxy , 選擇其中一種方式即可。privoxy 找不到純凈的鏡像, 都是捆綁 tor 的, 就挑一個最精簡的來做修改吧。

    3. 其實還可以在 公司主機A 搭建 SS 服務器 , 然后就可以通過 SS 客戶端 來連接代理。具體的本文就不細說了,搭建示例可以看 這里 對應的 compose 文件。

  • 終端工具中使用代理來連接云服務器
    這里的例子使用 WinSCP , 其他工具應該也差不多: 新建站點 -> 編輯 -> 高級 -> 連接

WinSCP
  1. ssh 隧道方式 : 使用 跳板機J9999 端口加 公司主機A 的帳號、密碼、端口。
WinSCP-ssh隧道方式
  1. 代理方式 : 可以選擇 SOCKS5 協議或者 HTTP 協議,和上面的配置一致即可。
WinSCP-代理方式

其他問題


  1. SSH 連接可能會中斷 ( 臨時的網絡擁塞、SSH 超時、中繼主機重啟等等 ),所以可以借助 autossh 實現自動重連來保證可靠的服務。
  2. SSH 連接有 超時斷開 的機制,WinSCP 有時會不斷提示 斷開重連 ,有兩種方式來維持連接:
    1. 設置 -> 面板 -> 遠程 -> 修改刷新面板間隔為 30s 或者更短
    2. session 設置 -> 高級 -> 連接 -> keepalives -> 執行啞命令 ( 空 SSH 包是沒效果的 )
  3. Win10 1803 已經默認集成了 SSH 工具 ,可以直接使用上面的命令。當然也可以借助終端工具來建立 SSH 隧道 ,比如在 MobaXterm 上可以使用圖形界面進行設置,比較直觀。
  4. 文中的方法使用 兩級代理三次轉發 ( 如果在 docker 中運行那就是四次轉發 ) 實現不同網絡的訪問,中間代理層的 帶寬 就限制了整體的訪問速度,需要注意下。
  5. 跳板機 提供的 SOCKS5 代理服務沒有密碼,如果被別人知道了 IP 和端口容易被惡意攻擊,所以一定要做好安全措施 ( 比如: 安全組防火墻 限制 跳板機 的入網 IP 為自己指定的 IP ) 。
  6. 你公司其他同事也想連你家里的電腦?總不能把 跳板機 暴露給他吧,太不安全了,你本地再開一層代理,讓他用你的電腦作為代理好了。

倉庫


基于 docker-compose 運行的完整配置已經上傳至 GitHub

參考文章



轉載請注明出處:http://www.lxweimin.com/p/388a93b1e7f7

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評論 6 534
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,634評論 3 419
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,835評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,235評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,459評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,000評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,819評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,004評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,257評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,717評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,003評論 2 374

推薦閱讀更多精彩內容