SSH Tunnel介紹

由於最近碰到一些特殊網路情境必須使用到SSH Tunnel,因此花點時間複習了一下。SSH Tunnel是個非常好用的技術,可以在一些比較安全或封閉的網路將流量透過SSH Protocol傳輸出去。使用SSH Tunnel一定要準備一個與Client端不同網路的Remote Server當作跳板機,其原理類似Port Forwarding的概念,在Client以及Remote Server之間建立一個SSH Tunnel,然後將特定的服務全部透過這個Tunnel來傳送,就可以達到加密及穿透的效果。

SSH Tunnel又可分成兩種:

  • Local Tunnel (又稱SSH Local Forwarding)
  • Remote Tunnel (又稱SSH Remote Forwarding)

這兩種差別其實就是要從哪一端將流量透過SSH Tunnel傳送出去,一個是從local端傳送給Remote端,一個是從Remote傳回來給local。接下來會針對這兩種Tunnel做細部解釋以及適用的情境。

Local Tunnel

Local Tunnel主要是從Local端(Client端),將Client端特定服務的流量透過SSH傳給Remote Server,再透過Remote Server傳出去給Application Server。其使用的情境可以分成兩種:一種是想要連到特定的服務,但是卻不想被網管監測到流量,就可以使用SSH來傳送流量出去,這樣網管只能看到SSH加密的流量,沒辦法看到內容。另一種是公司防火牆有限制員工不能連到特定服務或網站,也可以將流量包到SSH裡面來繞過防火牆出去(前提是防火牆沒有檔SSH服務)。

指令(此指令需在Client端執行):

$ ssh -NfL <local listening IP>:<local listening port>:<application ip>:<application port> <Remote Server>

# Example
$ ssh -NfL localhost:1234:<web server ip>:80 user@<remote server ip>

-N 代表不要開起shell code模式
-f 代表在背景執行
-L 代表Local Tunnel
<local listening IP> 要在Client上監聽的IP。
<local listening port> 要在Client上監聽的Port,可自行定義。
<application ip> 要存取的服務IP
<application port> 要存取的服務port

這邊意思是假如Client上有任何Request要傳送到<local listening IP>:<local listening port>,Client就會將他轉送到SSH Tunnel來傳給Remote Server,Remote Server收到流量後,會把封包轉送到<application ip>:<application port>,因此從Client端到Remote Server端之間的路是透過SSH傳送的(加密連線)。

Client <--SSH Tunnel--> Remote Server <--Internet--> Application Server

請注意這邊的<local listening port><application port>與SSH服務使用到的Port完全沒有任何關係,預設SSH Server使用的Port還是22,然後Client端是亂數產生。如果Remote Server的SSH Port不是22的話,那就必須在指令後面加上 -p <port number>

Example:
ssh -NfL localhost:1234:<web server ip>:80 user@<remote server ip> -p 3000

這邊需要注意的是<local listening IP>可以打也可以不打,預設是在 Client 端監聽 localhost(127.0.0.1),如果要監聽在不同的IP,需要針對sshd_config做設定,在稍後會做說明。

情境1

想要在公司使用ptt(ssh bbsu@ptt.cc),但是不想被網管發現。
角色:

  1. Client: 公司個人電腦or筆電,需要可以使用SSH指令。
  2. Remote Server: 當作跳板的機器,需要有SSH Server服務,且必須位於公司網路之外。Client必須連得到它。
  3. ptt.cc: 要連線的Application Server。

Step

  1. 在Client上Console執行Local Tunnel。

    $ ssh -NfL 1234:ppt.cc:23 <Remote Server>

  2. 在Client端檢查是否有成功建立起Tunnel

    $ netstat -plnt 
    $ ps -ef | grep ssh
  3. 接下來就可以透過Client連到ptt

    $ ssh bbsu@localhost -p 1234

  4. 如果要關掉Tunnel,只需要在Client上面執行以下指令:

    $ ps -ef | grep ssh
    $ kill <ssh tunnel 那條服務的PID>

說明
在這個情境中,當Client收到要通往localhost:1234的Request時,就會將它丟到SSH Tunnel傳送給Remote Server,Remote Server收到後再把它丟到ptt.cc:22,當封包回來也是一樣,只是就反過來,Remote Sever將Response透過SSH Tunnel丟回給Client,Client再將封包從SSH Tunnel丟給1234 port。

情境2

公司防火牆擋掉xxx網站,要如何才能繞過防火牆連到網站
角色:

  1. Client: 公司個人電腦or筆電,需要可以使用SSH指令。
  2. Remote Server: 當作跳板的機器,需要有SSH Server服務,且必須位於公司網路之外,Client必須連得到它。
  3. Web Server: 要連線的Application Server。

Step

  1. 在Client上Console執行Local Tunnel。

    $ ssh -NfL 80:<web server>:80  <Remote Server User>@<Remote Server IP>
  2. 在Client端檢查是否有成功建立起Tunnel

    $ netstat -plnt 
    $ ps -ef | grep ssh
  3. 接下來就可以透過Client上的瀏覽器連到網站(輸入http://localhost)

    #除了瀏覽器之外,也可以使用curl測試
    $ curl http://localhost
  4. 如果要關掉Tunnel,只需要在Client上面執行以下指令:

    $ ps -ef | grep ssh
    $ kill <ssh tunnel 那條服務的PID>

說明
在這個情境中,當Client收到要通往localhost:80的Request時,就會將它丟到SSH Tunnel傳送給Remote Server,Remote Server收到後再把它丟到<web server>:80,封包回來得時候也是一樣,只是順序反過來。

這邊需要注意的是,如果是要Redirect 443 port,可能會因為憑證被換掉而導致憑證錯誤,因此這種情境不太建議使用在有TLS加密的Web Server,純粹拿來測試就好。另外如果Web Server是在Load Balancer後面,那這樣可能也會導致Local Tunnel無法成功,這邊不太確定為什麼,有可能是因為導到後面後,IP就不是原本設定Tunnel的那個Web Server IP,因此流量就不會正常的走SSH Tunnel。

Remote Tunnel

Remote Tunnel是我比較常用的Tunnel方式,因為可以達到類似於VPN的功能,來存取內網的服務,可以應用在很多種情境上。Remote Tunnel是指從Remote端(Remote Server端),將特定服務的流量透過SSH傳回給Client端上的服務。

指令(此指令需在Client上執行):

$ ssh -NfR <remote listening IP>:<remote listening port>:<client listening ip>:<client listening port> <Remote Server>

# Example
$ ssh -NfR localhost:1234:localhost:80 user@<remote server ip>

-N 代表不要開起shell code模式
-f 代表在背景執行
-R 代表Remote Tunnel
<remote listening IP> 要在Remote Server上監聽的IP
<remote listening port> 要在Remote Server上監聽的Port,可自行定義。
<client listening ip> Client上面服務監聽的IP
<client listening port> Client上面服務port

這段指令意思是任何送到Remote Server上<remote listening IP>:<remote listening port>的Request,Remote Server都會將他透過SSH Tunnel傳送到給Client,Client再將Request丟給自己的<client listening ip>:<client listening port>服務。

外部主機 --SSH--> Remote Server <--SSH Tunnel--> Client

請注意這邊的<remote listening port><client listening port>與SSH服務使用到的Port完全沒有任何關係,預設SSH Server使用的Port還是22,然後Client端是亂數產生。如果Remote Server的SSH Port不是22的話,那就必須在指令後面加上 -p <port number>

Example:
ssh -NfR localhost:1234:localhost:80 user@<remote server ip> -p 3000

這邊需要注意的是<remote listening IP>可以打也可以不打,預設是代表在 Remote Server 上監聽 localhost(127.0.0.1),如果要監聽在不同的IP,需要針對sshd_config做設定,在稍後會做說明。

一般情境

公司的PC是在內網裡面,且是虛擬IP,有沒有辦法不透過VPN,從公司外面SSH連回來自己的PC?
or
公司的防火牆擋掉外對內的連線,因此沒辦法從外部透過SSH連回公司裡自己建置的SSH Server,是否有辦法不透過VPN,從公司外面SSH連回來SSH Server?

角色:

  1. Client: 公司內部的主機,也就是希望能從公司外部透過SSH連回的SSH Server,需要可以使用SSH指令。
  2. Remote Server: 當作跳板的機器,需要有SSH Server服務,且必須位於公司網路之外,Client要能連得到它。
  3. 外部電腦: 位於公司外部的電腦,必須能連得到Remote Server。

Step

  1. 在Client端執行Remote SSH Tunnel。

    $ ssh -NfR 1234:localhost:22  <Remote Server User>@<Remote Server IP>
  2. 在Remote Server端以及Client端檢查是否有成功建立起Tunnel

    Client

    ps -ef | grep ssh

    Remote Server

    netstat -plnt
  3. 接下來先透過外部電腦連到Remote Server。

    # 在外部電腦上執行
    $ ssh <Remote Server User>@<Remote Server IP>
  4. 透過Remote Server反向連回Client。

    # 在Remote Server上執行
    $ ssh localhost -p 1234
  5. 如果要關掉Tunnel,只需要在Client上面執行以下指令:

    $ ps -ef | grep ssh
    $ kill <ssh tunnel 那條服務的PID>

說明
在這個情境中,主要是先在Client開啟Remote Tunnel,然後Remote Tunnel會在Remote Server上監聽1234 port,當有Request從1234 port來的話,Remote Server就會透過SSH轉送回Client,Client收到後再將這個Request轉送給自己的22 Port。

設定讓外部能存取SSH Tunnel

預設SSH Tunnel是不能監聽在localhost以外的IP,因此每次使用Remote Tunnel就一定要先連到Remote Server才能反連回去,這樣不太方便,因此我們可以在SSH Server上面允許SSH Tunnel可以監聽在其他Port。

  1. 到Remote Server上開啟sshd_config

    $ vim /etc/ssh/sshd_config
  2. 找到GatewayPorts選項並設成yes,如果沒有這個選項可以自己加上去。

    GatewayPorts yes
  3. 重啟SSH服務

    $ systemctl restart sshd.service
  4. 這樣就可以將SSH Tunnel監聽在任何IP上

    # Local Tunnel
    $ ssh -NfL 0.0.0.0:1234:<web server ip>:80 user@<remote server ip>
    $ ssh -NfL xx.xx.xx.xx:1234:<web server ip>:80 user@<remote server ip>
    ...

    # Remote Tunnel
    $ ssh -NfR 0.0.0.0:1234:localhost:80 user@<remote server ip>
    $ ssh -NfR xx.xx.xx.xx:1234:localhost:80 user@<remote server ip>
    ...

設定外部能存取後,就可以結合各式各樣的應用,例如:

Reverse Proxy
透過Remote Server反向連回公司PC上的Web Server。

$ ssh -NfR 0.0.0.0:80:localhost:80 user@<remote server ip>

如此一來不需要連到Remote Server,只要在任何能存取到Remote Server的主機使用瀏覽器連http://<remote server ip>或是執行curl http://<remote server ip>就可以連到公司的web server。

RDP
透過Remote Server反向連回公司PC上的遠端桌面服務。

$ ssh -NfR 0.0.0.0:3389:localhost:3389 user@<remote server ip>

如此一來不需要連到Remote Server,只要在任何能存取到Remote Server的主機上使用遠端桌面連到<remote server ip>:3389,就可反向連到公司PC上的遠端桌面服務。

這邊都只有使用Remote Tunnel來當作範例,但是其實Local Tunnel也可以把<local listening IP>設定成監聽任何IP,只是目前比較少碰到需要把Local Tunnel開放外部存取這種情況,因此這裡就不特別闡述,有興趣可以自行測試看看。

如何防止SSH Tunnel

SSH Tunnel防範的方法有很多,這邊目前先列出幾種比較常聽到的,之後如果還有學習到別的方法再補充上來。

  1. SSH Server關閉SSH Tunnel服務,開啟sshd_config設定AllowTcpForwarding no。這樣可以防止別人用你的SSH Server當SSH Tunnel跳板機。
  2. 在防火牆直接擋掉SSH服務,不管內對外,外對內都擋。(曾經真的有遇到類似的案例,只是是限制特定的主機,例如給guest使用的或是一些非資訊技術相關的End User用的,並不是真的全公司的主機都擋)。
  3. 使用一些端點安全防護的產品直接禁止客戶使用SSH Tunnel指令或是SIEM產品去trace哪個使用者使用了Tunnel。

參考資料

  1. SSH Tunnel - Local and Remote Port Forwarding Explained With Examples - https://blog.trackets.com/2014/05/17/ssh-tunnel-local-and-remote-port-forwarding-explained-with-examples.html
  2. How to make ssh tunnel open to public? - https://superuser.com/questions/588591/how-to-make-ssh-tunnel-open-to-public
  3. Securing Your SSH Server - http://download.asperasoft.com/download/docs/proxy/1.4.0/admin_linux/webhelp/dita/securing_ssh_server.html

文章內容的轉載、重製、發佈,請註明出處: https://pohsienshih.github.io