由於最近碰到一些特殊網路情境必須使用到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端執行):
1
2
3
4
$ 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傳送的(加密連線)。
1
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>
可以打也可以不打,預設是localhost(127.0.0.1),如果要監聽在不同的IP,需要針對sshd_config做設定,在稍後會做說明。
情境1
想要在公司使用ptt(ssh bbsu@ptt.cc
),但是不想被網管發現。
角色:
- Client: 公司個人電腦or筆電,需要可以使用SSH指令。
- Remote Server: 當作跳板的機器,需要有SSH Server服務,且必須位於公司網路之外。Client必須連得到它。
- ptt.cc: 要連線的Application Server。
Step
- 在Client上Console執行Local Tunnel。
1
$ ssh -NfL 1234:ppt.cc:23 <Remote Server>
- 在Client端檢查是否有成功建立起Tunnel
1 2
$ netstat -plnt $ ps -ef | grep ssh
- 接下來就可以透過Client連到ptt
1
$ ssh bbsu@localhost -p 1234
- 如果要關掉Tunnel,只需要在Client上面執行以下指令:
1 2
$ 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網站,要如何才能繞過防火牆連到網站
角色:
- Client: 公司個人電腦or筆電,需要可以使用SSH指令。
- Remote Server: 當作跳板的機器,需要有SSH Server服務,且必須位於公司網路之外,Client必須連得到它。
- Web Server: 要連線的Application Server。
Step
- 在Client上Console執行Local Tunnel。
1
$ ssh -NfL 80:<web server>:80 <Remote Server User>@<Remote Server IP>
- 在Client端檢查是否有成功建立起Tunnel
1 2
$ netstat -plnt $ ps -ef | grep ssh
- 接下來就可以透過Client上的瀏覽器連到網站(輸入
http://localhost
)1 2
#除了瀏覽器之外,也可以使用curl測試 $ curl http://localhost
- 如果要關掉Tunnel,只需要在Client上面執行以下指令:
1 2
$ 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上執行):
1
2
3
4
$ 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>
服務。
1
外部主機 --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>
可以打也可以不打,預設是localhost(127.0.0.1),如果要監聽在不同的IP,需要針對sshd_config做設定,在稍後會做說明。
一般情境
公司的PC是在內網裡面,且是虛擬IP,有沒有辦法不透過VPN,從公司外面SSH連回來自己的PC? or 公司的防火牆擋掉外對內的連線,因此沒辦法從外部透過SSH連回公司裡自己建置的SSH Server,是否有辦法不透過VPN,從公司外面SSH連回來SSH Server?
角色:
- Client: 公司內部的主機,也就是希望能從公司外部透過SSH連回的SSH Server,需要可以使用SSH指令。
- Remote Server: 當作跳板的機器,需要有SSH Server服務,且必須位於公司網路之外,Client要能連得到它。
- 外部電腦: 位於公司外部的電腦,必須能連得到Remote Server。
Step
- 在Client端執行Remote SSH Tunnel。
1
$ ssh -NfR 1234:localhost:22 <Remote Server User>@<Remote Server IP>
-
在Remote Server端以及Client端檢查是否有成功建立起Tunnel
Client
1
ps -ef | grep ssh
Remote Server
1
netstat -plnt
- 接下來先透過外部電腦連到Remote Server。
1 2
# 在外部電腦上執行 $ ssh <Remote Server User>@<Remote Server IP>
- 透過Remote Server反向連回Client。
1 2
# 在Remote Server上執行 $ ssh localhost -p 1234
- 如果要關掉Tunnel,只需要在Client上面執行以下指令:
1 2
$ 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。
- 到Remote Server上開啟
sshd_config
1
$ vim /etc/ssh/sshd_config
- 找到GatewayPorts選項並設成yes,如果沒有這個選項可以自己加上去。
1
GatewayPorts yes
- 重啟SSH服務
1
$ systemctl restart sshd.service
- 這樣就可以將SSH Tunnel監聽在任何IP上
1 2 3 4 5 6 7 8
# 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。
1
$ 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上的遠端桌面服務。
1
$ 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防範的方法有很多,這邊目前先列出幾種比較常聽到的,之後如果還有學習到別的方法再補充上來。
- SSH Server關閉SSH Tunnel服務,開啟
sshd_config
設定AllowTcpForwarding no
。這樣可以防止別人用你的SSH Server當SSH Tunnel跳板機。 - 在防火牆直接擋掉SSH服務,不管內對外,外對內都擋。(曾經真的有遇到類似的案例,只是是限制特定的主機,例如給guest使用的或是一些非資訊技術相關的End User用的,並不是真的全公司的主機都擋)。
- 使用一些端點安全防護的產品直接禁止客戶使用SSH Tunnel指令或是SIEM產品去trace哪個使用者使用了Tunnel。
參考資料
- 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
- How to make ssh tunnel open to public? - https://superuser.com/questions/588591/how-to-make-ssh-tunnel-open-to-public
- Securing Your SSH Server - http://download.asperasoft.com/download/docs/proxy/1.4.0/admin_linux/webhelp/dita/securing_ssh_server.html
文章內容的轉載、重製、發佈,請註明出處: https://blog.phshih.com/