ssh端口轉發也叫ssh隧道,我們知道ssh客戶端與服務端之間的通訊會自動加密和解密,除此以外,ssh為其他tcp連接提供了一個安全的通道進行數據傳輸,並提供了一系列相應的加密和解密服務,這一過程被稱之為“隧道”。例如,Telnet,SMTP,LDAP這些tcp應用均能夠從中得益,避免了用戶名,密碼以及隱私信息的銘文傳輸。與此同時,如果工作環境的防火牆限制了一些網絡端口的使用,只要允許ssh連接,也能夠通過將TCP端口轉發,來使用ssh進行通訊。
通過以上的闡述,不難發現SSH端口轉發給我們提供了兩大功能,一是加密ssh客戶端至ssh服務端之間的通訊數據;二是突破防火牆的限制完成之前無法建立的TCP連接;
ssh端口轉發分三大類:
1、本地端口轉發:把本地主機端口通過待登錄主機端口轉發到遠程主機端口上去。本地端口轉發通過選項-L指定,其格式為
ssh -L <local port>:<remote host>:<remote port> <SSH hostname>
選項:
-f:后台啟用
-N:不打開遠程shell,處於等待狀態
-g:啟用網關功能(這個選項需要把sshd_config配置文件中的GatewayPorts 參數配置成yes )
實驗環境:
通過上述得知,我們需要三台主機,一台是本地主機(A),一台是中間待登錄主機(B),一台是遠程主機(C)。這三台主機分別是這樣的,本地主機可以通過ssh連接至中間主機,中間主機能夠通過ssh連接至遠程主機,但是本地主機不能直接通過ssh連接至遠程主機。

解釋下上圖:A想通過網絡訪問C,但是在A和C有防火牆,明確拒絕A的所有請求。A可以通過ssh和B連接,B可以和C連接。數據走向是這樣的,data---->A本地端口2048(假設是通過2048轉發)--->A連接SSH服務器的某一端口---->B作為SSH服務端22號端口---->B作為Telnet客戶端連接C的某一端口---->C作為Telnet服務端端口23。
明白了以上的闡述,下面來試驗吧。
首先准備以上需要的主機3台,ip地址分別是:A是192.168.0.11,B是192.168.0.12,C是192.168.0.13
在C上部署一個Telnet服務,並且在防火牆里明確拒絕A的所有請求。
[root@host_C ~]# yum install telnet-server -y Loaded plugins: fastestmirror, security Setting up Install Process Determining fastest mirrors * base: mirrors.aliyun.com * extras: mirrors.cn99.com * updates: mirrors.163.com base | 3.7 kB 00:00 epel | 5.3 kB 00:00 epel/primary_db | 6.1 MB 00:00 extras | 3.4 kB 00:00 extras/primary_db | 29 kB 00:00 updates | 3.4 kB 00:00 updates/primary_db | 6.6 MB 00:00 Resolving Dependencies --> Running transaction check ---> Package telnet-server.x86_64 1:0.17-48.el6 will be installed --> Processing Dependency: xinetd for package: 1:telnet-server-0.17-48.el6.x86_64 --> Running transaction check ---> Package xinetd.x86_64 2:2.3.14-40.el6 will be installed --> Finished Dependency Resolution Dependencies Resolved ============================================================================================== Package Arch Version Repository Size ============================================================================================== Installing: telnet-server x86_64 1:0.17-48.el6 base 37 k Installing for dependencies: xinetd x86_64 2:2.3.14-40.el6 base 122 k Transaction Summary ============================================================================================== Install 2 Package(s) Total download size: 159 k Installed size: 313 k Downloading Packages: (1/2): telnet-server-0.17-48.el6.x86_64.rpm | 37 kB 00:00 (2/2): xinetd-2.3.14-40.el6.x86_64.rpm | 122 kB 00:00 ---------------------------------------------------------------------------------------------- Total 968 kB/s | 159 kB 00:00 Running rpm_check_debug Running Transaction Test Transaction Test Succeeded Running Transaction Installing : 2:xinetd-2.3.14-40.el6.x86_64 1/2 Installing : 1:telnet-server-0.17-48.el6.x86_64 2/2 Verifying : 1:telnet-server-0.17-48.el6.x86_64 1/2 Verifying : 2:xinetd-2.3.14-40.el6.x86_64 2/2 Installed: telnet-server.x86_64 1:0.17-48.el6 Dependency Installed: xinetd.x86_64 2:2.3.14-40.el6 Complete! [root@host_C ~]# chkconfig telnet on [root@host_C ~]# chkconfig xinetd on [root@host_C ~]# service xinetd start Starting xinetd: [ OK ] [root@host_C ~]# chkconfig --list telnet telnet on [root@host_C ~]# ss -ntl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 :::22 :::* LISTEN 0 128 *:22 *:* LISTEN 0 64 :::23 :::* [root@host_C ~]#
說明:Telnet服務器已經准備就緒,也看到了相應的端口是出於監聽的狀態,接下來添加防火牆規則,拒絕A的所有請求。
[root@host_C ~]# iptables -L -n Chain INPUT (policy ACCEPT) target prot opt source destination Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination [root@host_C ~]# iptables -A INPUT -s 192.168.0.11 -j REJECT [root@host_C ~]# iptables -L -n Chain INPUT (policy ACCEPT) target prot opt source destination REJECT all -- 192.168.0.11 0.0.0.0/0 reject-with icmp-port-unreachable Chain FORWARD (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination [root@host_C ~]#
在主機A上建立與B連通的ssh隧道,並指定轉發特定端口2048(這個端口可以任意指定一個,不一定是這個)
[root@host_A ~]# telnet 192.168.0.13 Trying 192.168.0.13... telnet: connect to address 192.168.0.13: Connection refused [root@host_A ~]# ssh -L 2048:root@192.168.0.13:23 -Nf 192.168.0.12 The authenticity of host '192.168.0.12 (192.168.0.12)' can't be established. RSA key fingerprint is 3d:c6:e6:3a:2d:76:34:ba:59:7a:1c:33:f2:0a:6b:95. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '192.168.0.12' (RSA) to the list of known hosts. root@192.168.0.12's password: [root@host_A ~]# ss -ntl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 127.0.0.1:2048 *:* LISTEN 0 128 ::1:2048 :::* LISTEN 0 128 :::22 :::* LISTEN 0 128 *:22 *:* [root@host_A ~]# ss -nt State Recv-Q Send-Q Local Address:Port Peer Address:Port ESTAB 0 0 192.168.0.11:13551 192.168.0.12:22 ESTAB 0 52 192.168.0.11:22 192.168.0.232:8351
說明:主機A第一次連接主機B,所以會提示我們否相信對方公鑰的提示。可以看到在主機A上我們指定的端口2048,已經處於監聽狀態了,並且主機A和主機B已經建立連接,說明ssh隧道已經建立好了,接下來我們在來用主機A來連接本地2048端口。
[root@host_A ~]# telnet 127.0.0.1 2048
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
CentOS release 6.7 (Final)
Kernel 2.6.32-573.el6.x86_64 on an x86_64
in: root
Password:
Login incorrect
login: test
Password:
Last login: Mon Oct 28 03:01:05 from 192.168.0.12
[test@host_C ~]$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:b5:b3:f7 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.13/24 brd 192.168.0.255 scope global eth0
inet6 fe80::20c:29ff:feb5:b3f7/64 scope link
valid_lft forever preferred_lft forever
[test@host_C ~]$
說明:可以看到我們主機A可以正常連接主機C,這里說一下Telnet默認不允許用root去連接,所以我們試着用root連接,它會拒絕我們,用普通用戶沒有問題。通過上面的本地端口轉發,我們發現一個問題,我們指定的端口監聽在127.0.0.1上,這樣一來,如果我們用其他主機來連接就是一個問題,怎么樣才能讓它監聽在任何地址都可以連接的地址上呢?有辦法,我們只需要在剛才的命令上加上-g選項,-g選項表示開啟網關功能。在指定-g選項前,我們一定要確保ssh服務器上把GatewayPorts 參數配置成yes。
[root@host_b ~]# grep -i gatewayports /etc/ssh/sshd_config #GatewayPorts no GatewayPorts yes [root@host_b ~]#
說明:在本實驗host_b作為ssh服務器的角色,所以我們要在B主機上更改其對應的參數。
[root@host_A ~]# ssh -L 2048:192.168.0.13:23 -fNg 192.168.0.12 root@192.168.0.12's password: [root@host_A ~]# ss -ntl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 :::2048 :::* LISTEN 0 128 *:2048 *:* LISTEN 0 128 :::22 :::* LISTEN 0 128 *:22 *:* [root@host_A ~]#
說明:我們加了-g 選項后,我們所指定的端口監聽在×上面了,×表示任何地址,這樣我們就可以用互聯網地址去連接這個端口,從而達到連接C主機的目的。
[root@host_A ~]# telnet 192.168.0.11 2048
Trying 192.168.0.11...
Connected to 192.168.0.11.
Escape character is '^]'.
CentOS release 6.7 (Final)
Kernel 2.6.32-573.el6.x86_64 on an x86_64
in: test
Password:
Last login: Mon Oct 28 03:06:55 from 192.168.0.12
[test@host_C ~]$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:b5:b3:f7 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.13/24 brd 192.168.0.255 scope global eth0
inet6 fe80::20c:29ff:feb5:b3f7/64 scope link
valid_lft forever preferred_lft forever
[test@host_C ~]$
2、遠程端口轉發:把登錄主機端口通過本地主機端口轉發到遠程主機。遠程端口轉發通過選項-R指定,其格式為
ssh -R <local port>:<remote host>:<remote port> <SSH hostname>
說明:和上面對本地端口轉發不同的是,遠程端口轉發在本實驗中,host_b就充當着ssh客戶端的角色,host_a就充當着ssh服務端的角色,我們要在host_b 上執行ssh -R 2048:192.168.0.13:23 -fN 192.168.0.11
[root@host_b ~]# ssh -R 2048:192.168.0.13:23 -fN 192.168.0.11 root@192.168.0.11's password: [root@host_b ~]# ss -nt State Recv-Q Send-Q Local Address:Port Peer Address:Port ESTAB 0 52 192.168.0.12:22 192.168.0.232:7960 ESTAB 0 0 192.168.0.12:33508 192.168.0.11:22 [root@host_b ~]# ss -ntl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:22 *:* LISTEN 0 100 127.0.0.1:25 *:* LISTEN 0 80 :::3306 :::* LISTEN 0 32 :::21 :::* LISTEN 0 128 :::22 :::* LISTEN 0 100 ::1:25 :::* [root@host_b ~]# [root@host_A ~]# ss -ntl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 127.0.0.1:2048 *:* LISTEN 0 128 ::1:2048 :::* LISTEN 0 128 :::22 :::* LISTEN 0 128 *:22 *:* [root@host_A ~]# ss -nt State Recv-Q Send-Q Local Address:Port Peer Address:Port ESTAB 0 0 192.168.0.11:22 192.168.0.12:33508 ESTAB 0 52 192.168.0.11:22 192.168.0.232:8351 [root@host_A ~]#
說明:我們在B上執行了相應的命令,在A上是可以看到對應的端口已經處於監聽狀態。在B上可以看到是一個任意端口連接着A上的22號端口。充分說明了A是ssh服務端,B是客戶端。
接下來我們在A端來連接剛才指定的端口
[root@host_A ~]# telnet 127.0.0.1 2048
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
CentOS release 6.7 (Final)
Kernel 2.6.32-573.el6.x86_64 on an x86_64
in: test
Password:
Last login: Mon Oct 28 03:24:30 from 192.168.0.12
[test@host_C ~]$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:b5:b3:f7 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.13/24 brd 192.168.0.255 scope global eth0
inet6 fe80::20c:29ff:feb5:b3f7/64 scope link
valid_lft forever preferred_lft forever
[test@host_C ~]$
說明:可以看到我們是可以正常連接的。當然我這次是監聽在127.0.0.1上,如果要監聽在任意地址上,我們也可以在上面的命令加上-g選項,在A主機上確保其GatewayPorts這個參數是被開啟運行的。這里就不在演示。
3、動態端口轉發:動態轉發不需要指定特定的目標主機和端口號,可以實現不加密的網絡連接,全部走SSH連接,從而提高安全性。動態轉發通過參數 -D 指定,其格式:
ssh -D <local port> <SSH Server>
我們在C上搭建一個http服務。其網絡環境不變,A還是無法訪問C,B可以訪問C ,A通過B的代理來訪問C
[root@host_C ~]# yum install -y httpd
Loaded plugins: fastestmirror, security
Setting up Install Process
Loading mirror speeds from cached hostfile
* base: mirrors.aliyun.com
* extras: mirrors.cn99.com
* updates: mirrors.163.com
Resolving Dependencies
--> Running transaction check
---> Package httpd.x86_64 0:2.2.15-69.el6.centos will be installed
--> Processing Dependency: httpd-tools = 2.2.15-69.el6.centos for package: httpd-2.2.15-69.el6.centos.x86_64
--> Processing Dependency: apr-util-ldap for package: httpd-2.2.15-69.el6.centos.x86_64
--> Running transaction check
---> Package apr-util-ldap.x86_64 0:1.3.9-3.el6_0.1 will be installed
---> Package httpd-tools.x86_64 0:2.2.15-69.el6.centos will be installed
--> Finished Dependency Resolution
Dependencies Resolved
================================================================================
Package Arch Version Repository Size
================================================================================
Installing:
httpd x86_64 2.2.15-69.el6.centos base 836 k
Installing for dependencies:
apr-util-ldap x86_64 1.3.9-3.el6_0.1 base 15 k
httpd-tools x86_64 2.2.15-69.el6.centos base 81 k
Transaction Summary
================================================================================
Install 3 Package(s)
Total download size: 932 k
Installed size: 3.2 M
Downloading Packages:
(1/3): apr-util-ldap-1.3.9-3.el6_0.1.x86_64.rpm | 15 kB 00:00
(2/3): httpd-2.2.15-69.el6.centos.x86_64.rpm | 836 kB 00:00
(3/3): httpd-tools-2.2.15-69.el6.centos.x86_64.rpm | 81 kB 00:00
--------------------------------------------------------------------------------
Total 1.9 MB/s | 932 kB 00:00
Running rpm_check_debug
Running Transaction Test
Transaction Test Succeeded
Running Transaction
Installing : apr-util-ldap-1.3.9-3.el6_0.1.x86_64 1/3
Installing : httpd-tools-2.2.15-69.el6.centos.x86_64 2/3
Installing : httpd-2.2.15-69.el6.centos.x86_64 3/3
Verifying : httpd-tools-2.2.15-69.el6.centos.x86_64 1/3
Verifying : httpd-2.2.15-69.el6.centos.x86_64 2/3
Verifying : apr-util-ldap-1.3.9-3.el6_0.1.x86_64 3/3
Installed:
httpd.x86_64 0:2.2.15-69.el6.centos
Dependency Installed:
apr-util-ldap.x86_64 0:1.3.9-3.el6_0.1
httpd-tools.x86_64 0:2.2.15-69.el6.centos
Complete!
[root@host_C ~]# echo "THIS IS HOST_C" > /var/www/html/index.html
[root@host_C ~]# /etc/init.d/httpd restart
Stopping httpd: [ OK ]
Starting httpd: httpd: apr_sockaddr_info_get() failed for host_C
httpd: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName
[ OK ]
[root@host_C ~]# curl 192.168.0.13
THIS IS HOST_C
[root@host_C ~]# ss -ntl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 511 :::80 :::*
LISTEN 0 128 :::22 :::*
LISTEN 0 128 *:22 *:*
LISTEN 0 64 :::23 :::*
[root@host_C ~]#
說明:我們在主頁上寫了THIS IS HOST_C,並開啟了http服務。可以看到80端口已經處於監聽狀態,主頁面也是可以正常訪問。
[root@host_A ~]# ssh -D 2048 192.168.0.12 -fN root@192.168.0.12's password: [root@host_A ~]# ss -nt State Recv-Q Send-Q Local Address:Port Peer Address:Port ESTAB 0 0 192.168.0.11:13558 192.168.0.12:22 ESTAB 0 52 192.168.0.11:22 192.168.0.232:8351 [root@host_A ~]# ss -ntl State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 127.0.0.1:2048 *:* LISTEN 0 128 ::1:2048 :::* LISTEN 0 128 :::22 :::* LISTEN 0 128 *:22 *:*
接下來我們啟動代理來訪問下C
[root@host_A ~]# curl 192.168.0.13 curl: (7) couldn't connect to host [root@host_A ~]# curl --socks5 127.0.0.1:2048 192.168.0.13 THIS IS HOST_C [root@host_A ~]#
說明:curl啟動代理需要加上 --socks5 來指定代理服務器和端口,可以看到在沒有指定代理服務器的時候,A是不能夠訪問C的,指定了代理服務器和端口就可以正常訪問了。
總結:本地端口轉發和遠程端口轉發都需要指定目標主機的端口,通常我們轉發特定端口下的網絡數據可以用這兩種。動態端口可以實現socks代理從而實現加密以及突破防火牆對web瀏覽器的限制。
