使用 ssh -R 建立反向/远程TCP端口转发代理

转自:https://yq.aliyun.com/articles/8469

ssh是一个非常棒的工具, 不但能建立动态转发, 例如chrome的Switchy插件用到的就是这个技术.
http://blog.163.com/digoal@126/blog/static/163877040201141821810103/
还能建立TCP的转发隧道, 例如我以前写过的关于使用ssh 隧道加密和加速WAN传输的几个例子.
http://blog.163.com/digoal@126/blog/static/163877040201342383123592/
http://blog.163.com/digoal@126/blog/static/16387704020115294425540/
上面的例子可以理解为正向的TCP端口转发代理(如local$ ssh -L 7001:1.1.1.1:1000 remote_A), 即本地服务器local建立和远程服务器的SSH连接, 本地监听一个端口7001, 连接本地的监听端口7001相当于"通过远程服务器remote_A访问指定的IP 1.1.1.1和PORT 1000".
这种方法适用于本地网络中其他服务器想访问远程的服务器, 但是其他服务器没有直接访问远程服务器的权限, 通过本地网络中有权限的服务器做端口转发的场景.

还有一种应用场景是这样的, 本地网络环境中没有任何一台服务器可以直接访问远程服务器, 但是远程服务器可以反过来访问本地的某台服务器(或者本地可以访问到的某台服务器), 这样的话也能实现本地服务器去访问远程服务器的目的. 这就是反向代理.
实际的应用场景如,
或者说A和B无法相互访问,但是A能访问C,B也能访问C。怎么通过C让AB能相互访问?
.1. 家里的网络是动态拨号网络, 没有固定IP, 如果想在上班的时候控制家里的电脑怎么办呢? 如果公司有一台服务器是可以在家拨号过去访问的, 那么你可以在家里的电脑上建立和这台公司服务器的反向代理, 到公司后, 就可以连接这台服务器上的反向代理(监听)来连接家里的电脑了.
.2. 有的客户可能不希望提供VPN给你去访问他们的服务器, 但是又需要得到你的远程协助, 怎么办呢?
你可以提供一台服务器让客户主动来访问你的服务器, 客户建立这样的反向代理, 然后你通过这个反向代理来连接客户开放的端口如VNC桌面共享, 进行远程协助. (这种方法比QQ远程协助是更安全的, 因为客户和这台服务器之间的数据时加密的, QQ远程协助毕竟有一定的风险存在.)
以下是反向代理的图例, 如图1 :
箭头表示允许访问的方向, 也就是说Server C可以访问Server A和Server B以及Client A.
而Client B和Client C可以访问Client A.
我们的目的是要让Client 可以访问Server. 这里就要用到SSH的反向端口转发, 或远程端口转发.

使用 ssh -R 建立反向/远程TCP端口转发代理

反向代理应用场景.
例如, 以下虚线表示Client C最终要访问Server A的22端口.
实现的步骤
1. Client A需要修改一下sshd_config, 开启GatewayPorts, 这样才能监听回环地址以外的地址, 如0.0.0.0, 这样的话其他服务器才能访问到这个端口.
2. Server C建立和Client A的反向端口转发, 让Client A在0.0.0.0上监听7001端口, 访问这个端口的数据转发到IP_SA的22端口.

ssh -CqTfnN -R  0.0.0.0:7001:IP_SA:22   IP_CA

其实再深层次一点, 因为client c和server a已经可以建立ssh了, 那么通过client c和server a建立反向代理, Server A也可以访问Client c. 这样需要经过了多层ssh隧道( IP_CA->IP_SC, IP_SA->IP_CC )

使用 ssh -R 建立反向/远程TCP端口转发代理

这种反向代理的用法很容易应用到实际的场景中, 例如前面提到的 :
有的客户可能不希望提供VPN给你去访问他们的服务器, 但是又需要得到你的远程协助, 怎么办呢?
你可以提供一台服务器让客户主动来访问你的服务器, 客户建立这样的反向代理, 然后你通过这个反向代理来连接客户开放的端口如VNC桌面共享, 进行远程协助.
如图 :
Public A是一台公用主机, 可以被客户和服务提供商同时访问.
当客户需要远程协助时.
1. 客户在SERVER C上开启一个VNC server
2. 客户将需要远程协助的窗口先登录好, 例如某些内网服务器(SERVER A , SERVER B)的telnet或ssh终端. 以供服务提供商使用.
3. 客户在SERVER C主动建立和这台公用主机的反向代理, 将端口转发到server c的vnc监听端口.
4. 服务提供商使用VNC客户端连接这台公共服务器的SSH代理监听端口, 相当于连接到了server c的vnc server. 以提供远程协助.
5. 客户可以在server c上观看, 录像, 控制这个VNC会话等.

使用 ssh -R 建立反向/远程TCP端口转发代理

下面来举个例子 :
场景 :
家里有一台WINDOWS服务器, 通过VPN拨号到公司网络.
WINDOWS服务器上跑了两个VMWARE的虚拟机, 分别是FreeBSD和CentOS.
公司有1台主机172.16.3.150, 安装了CentOS,
拨号后, 家里的虚拟机FreeBSD可以访问172.16.3.150.
现在要让公司的其他服务器如172.16.3.167可以访问到家里的192.168.198.129虚拟机.

使用 ssh -R 建立反向/远程TCP端口转发代理
简单步骤.
.1. 拨号, 略
.2. 172.16.3.150上需要修改sshd_config.

[root@db-172-16-3-150 ~]# vi /etc/ssh/sshd_config
GatewayPorts yes
[root@db-172-16-3-150 ~]# service sshd reload
Reloading sshd: [ OK ]

.3. 通过虚拟机192.168.198.130连接到172.16.3.150开启反向端口代理. (如果130不是LINUX是WINDOWS, 也可以用securecrt类似的软件进行代理配置.)

root@digoal:~ # ssh -CqTfnN -R 0.0.0.0:7001:192.168.198.129:22 172.16.3.150
root@172.16.3.150's password:

.4. 在172.16.3.150上可以看到这个监听.

[root@db-172-16-3-150 ~]# netstat -anp|grep 7001
tcp 0 0 0.0.0.0:7001 0.0.0.0:* LISTEN 2392/sshd
tcp 0 0 :::7001 :::* LISTEN 2392/sshd

.5. 现在到172.16.3.167上连接172.16.3.150的7001端口.

[root@db5 ~]# ifconfig
bond0 Link encap:Ethernet HWaddr 00:23:7D:A3:F0:4E
inet addr:172.16.3.176 Bcast:172.16.3.255 Mask:255.255.255.0
inet6 addr: fe80::223:7dff:fea3:f04e/64 Scope:Link
UP BROADCAST RUNNING MASTER MULTICAST MTU:1500 Metric:1
RX packets:200422824 errors:0 dropped:0 overruns:0 frame:0
TX packets:16849618 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:23012276676 (21.4 GiB) TX bytes:3567071726 (3.3 GiB)
[root@db5 ~]# ping 172.16.3.150
PING 172.16.3.150 (172.16.3.150) 56(84) bytes of data.
64 bytes from 172.16.3.150: icmp_seq=1 ttl=64 time=0.337 ms --- 172.16.3.150 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.337/0.337/0.337/0.000 ms [root@db5 ~]# ssh -p 7001 172.16.3.150
Password for root@digoal.org:
root@digoal:~ # ifconfig
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
ether 00:0c:29:c0:4b:65
inet 192.168.198.129 netmask 0xffffff00 broadcast 192.168.198.255
nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
media: Ethernet autoselect (1000baseT <full-duplex>)
status: active

到这里反向代理的测试完成.

接下来我们看看如何在反向代理的基础上, 再做一层反向代理.
例如我要在172.16.3.167建立和192.168.198.129的反向代理, 让192.168.198.130通过192.168.198.129来访问172.16.3.150.
.1. 首先要修改192.168.198.129的sshd_config

root@digoal:~ # vi /etc/ssh/sshd_config
GatewayPorts yes
root@digoal:~ # service sshd reload
Performing sanity check on sshd configuration.
root@digoal:~ # exit
logout
Connection to 172.16.3.150 closed.

.2. 在172.16.3.167建立和192.168.198.129的反向代理

[root@db5 ~]# ssh -CqTfnN -p 7001 -R 0.0.0.0:7001:172.16.3.150:22 172.16.3.150
Password for root@digoal.org:

.3. 在192.168.198.129上查看监听.

root@digoal:~ # netstat -AaLnSTW
Current listen queue sizes (qlen/incqlen/maxqlen)
Tcpcb Proto Listen Local Address
fffff800076ce800 tcp4 0/0/128 *.7001

.4. 在192.168.198.130上通过192.168.198.129:7001来访问172.16.3.150:22.

[root@digoal ~]# ssh -p 7001 192.168.198.129
The authenticity of host '[192.168.198.129]:7001 ([192.168.198.129]:7001)' can't be established.
RSA key fingerprint is 01:0b:96:e1:a8:be:a3:a3:69:a4:0a:11:5d:2a:6f:c2.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[192.168.198.129]:7001' (RSA) to the list of known hosts.
root@192.168.198.129's password:
Last login: Sat Jun 14 18:20:23 2014 from 10.0.0.60
[root@db-172-16-3-150 ~]#

[注意]
.1. 当使用vncserver共享桌面时, 有几点需要注意, 需要使用或勾选Shared connection选项, 否则一个连上来, 其他的就会断掉.
.2. 使用vncserver共享桌面, 只要把5901端口共享出来就可以了.
例如
在172.16.3.150建立和172.16.3.221的反向隧道, 代理VNCSERVER端口的反向转发.

# ssh -CqTfnN -p 22 -R 0.0.0.0:5901:172.16.3.150:5901 172.16.3.221

这个使用的是172.16.3.221:5901来代理172.16.3.150:5901
在172.16.3.150开启一个vncserver, 让A主机可以连到这个vncserver.
在A主机开启一个vncviewer, 并打开共享连接, 连接到172.16.3.150:5901
在B主机开启一个vncviewer, 并打开共享连接, 连接到172.16.3.221:5901
最终, A变成了监控机, B是操作方.
B的操作, A可以在VNCVIEWER观看到.
使用完成后, 断开172.16.3.150和172.16.3.221的连接即可, 或者关闭172.16.3.150的vncserver服务即可. vncserver -kill :?.
.3. 如果是跨广域网的隧道, 中间经过的某些网络设备可能会有会话空闲自动断开机制, 为了确保隧道不会被这种机制自动干掉, 我们可以设置一下SSH连接的心跳.

# sysctl -w net.ipv4.tcp_keepalive_time=30
net.ipv4.tcp_keepalive_time = 30
# ssh -CqTfnN -o TCPKeepAlive=yes -o ServerAliveInterval=10 -o ServerAliveCountMax=10 -p 22 -R 0.0.0.0:5901:172.16.3.150:5901 目标IP

现在TCP的心跳时间缩短了, 空闲会自动发送TCP心跳包.

[root@150 ~]# netstat -anpo|grep ssh
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 2699/sshd off (0.00/0/0)
tcp 0 0 172.16.3.150:62230 目标IP:22 ESTABLISHED 21721/ssh keepalive (21.48/0/0)

.4. 如果目标机的5901端口监听被占用了, 可以换个端口, 那么使用vncviewer或者其他客户端连接时, 需要使用::port指定端口.
例如172.16.3.150::6666
.5. 考虑到跨广域网的带宽有限问题, vncserver启动时, 最好设置一下位宽, 因为大多数操作不需要那么好的色彩, 例如 :

# vncserver :1 -geometry 1200x700 -depth 8 -cc 3

初次配置时, 输入密码

# vi ~/.vnc/xstartup
#twm &
gnome-session & # vncserver -kill :1
# vncserver :1 -geometry 1200x700 -depth 8 -cc 3

参考
man vncserver

[参考]
.1. http://blog.163.com/digoal@126/blog/static/163877040201141821810103/
.2. http://blog.163.com/digoal@126/blog/static/16387704020115294425540/
.3. http://blog.163.com/digoal@126/blog/static/163877040201342383123592/
.4. man ssh

     -R [bind_address:]port:host:hostport
Specifies that the given port on the remote (server) host is to be forwarded to the given host and port
on the local side. This works by allocating a socket to listen to port on the remote side, and whenever
a connection is made to this port, the connection is forwarded over the secure channel, and a connection
is made to host port hostport from the local machine. Port forwardings can also be specified in the configuration file. Privileged ports can be forwarded only
when logging in as root on the remote machine. IPv6 addresses can be specified by enclosing the address
in square braces or using an alternative syntax: [bind_address/]host/port/hostport. By default, the listening socket on the server will be bound to the loopback interface only. This may be
overridden by specifying a bind_address. An empty bind_address, or the address ‘*’, indicates that the
remote socket should listen on all interfaces. Specifying a remote bind_address will only succeed if the
server’s GatewayPorts option is enabled (see sshd_config(5)). If the port argument is ‘0’, the listen port will be dynamically allocated on the server and reported to
the client at run time.

.5. 
linux 通过ssh代理上网
本网段上网需计费,想到通过另一网段的linux服务器做代理免费上网
前提是你有那台linux主机的帐户
然后打开终端输入

ssh -qTfnN -D 7070 user@host

说明: user是你的帐号,host指对方linux主机的ip

-q Quiet mode. 安静模式,忽略一切对话和错误提示。
-T Disable pseudo-tty allocation. 不占用 shell 了。
-f Requests ssh to go to background just before command execution. 后台运行,并推荐加上 -n 参数。
-n Redirects stdin from /dev/null (actually, prevents reading from stdin). -f 推荐的,不加这条参数应该也行。
-N Do not execute a remote command. 不执行远程命令,专为端口转发度身打造。

然后在浏览器中
如果是firefox:安装autoproxy插件,在代理中选择SSH -D即可。默认情况下的配置就是7070端口,所以可以不用修改配置,否则在首选项中更改服务器设置。
如果是Chrome:安装proxy switchy扩展,添加Proxy Profile,在右侧的socks host中输入127.0.0.1和7070,下方选中socks v5,保存,然后切换至该profile即可。

上一篇:Flask-信号(blinker)


下一篇:CSS中选择器优先级顺序实战讲解