本地端口转发和远程端口转发

最近在研究gost源码,看到端口转发这一块表示一头雾水,遂研究了一下。

为什么要端口转发

百度的解释是

端口转发(Port forwarding),有时被叫做隧道,是安全壳(SSH) 为网络安全通信使用的一种方法。端口转发是转发一个网络端口从一个网络节点到另一个网络节点的行为,其使一个外部用户从外部经过一个被激活的NAT路由器到达一个在私有内部IP地址(局域网内部)上的一个端口。

端口转发的意义在于,在客户端和目标服务器中间建立一条安全隧道,使得原本因某些原因而无法访问的服务,得以访问。最常用的端口转发模式分为本地端口转发以及远程端口转发两种。

本地端口转发(Local forwarding)

假设现在有三台主机,local_host是本机,proxy_host是代理主机,remote_host是我们要访问的服务器。因为某些原因,local_host无法直接访问remote_host,而proxy_host和local_host以及remote_host之间可以两两互相访问。以下是它们的访问关系

local_host <-> proxy_host <-> remote_host

注意,这三台主机访问关系要么全在内网,要么全在公网

使用SSH命令的话,我们就可以利用proxy_host创建一条代理链,通过指定-L参数,即可连上remote_host服务。在本机local_host上执行命令

1
ssh -L 2222:remote_host:22 username@proxy_host

解释一下,2222为本机端口,通过指定SSH绑定本机的2222端口,然后将proxy_host接收到的所有数据转发到目标主机remote_host的22端口。

1
ssh -p 2222 username@local_host

然后,SSH访问本机2222端口,就相当于访问remote_host的22端口了。这样就实现了本地端口的转发。

什么是 本地端口转发

所谓本地端口转发,就是将发送到本地端口的请求,转发到目标端口。这样,就可以通过访问本地端口,来访问目标端口的服务。使用-L属性(Local的意思),就可以指定需要转发的端口。

-L的语法如下

1
-L 本机地址:本地端口:目标地址:目标端口

其中本机地址可以省略。即localhost。

gost的本地端口转发

gost的本地端口转发功能类似于ssh中 -L参数的功能,而与ssh的区别在于:

  1. gost不仅支持TCP端口的转发(上面的例子就是),还支持UDP端口的转发。
  2. gost支持通过转发链进行端口转发。
TCP端口转发

仍然是上面的例子,假设proxy_host已经部署了http代理并监听在8080端口上

1
gost -L :8080

我们只需要下面一段命令,就能进行本地TCP端口转发了

1
gost -L=tcp://:2222/remote_host:22 -F=proxy_host:8080

然后ssh访问本地2222端口就能访问目标主机了。

1
ssh -p 2222 username@local_host
UDP端口转发

仍旧上面的例子,假设proxy_host已经部署了socks5代理并监听在8080端口上

1
gost -L socks5://:8080

现在想访问remote_host上的UDP 53端口,也就是DNS服务

1
gost -L udp://:5353/remote_host:53 -F proxy_host:8080

然后在local_host上执行

1
dig @localhost -p 5353 www.google.com

此时就相当于访问到remote_host的UDP 53端口。

注意: UDP的端口转发使用了UDP-over-TCP,所以转发链的末端(最后一个-F参数)一定要是gost socks5类型

本地端口转发的其它场景

上面的例子是本地端口转发的一种场景,为了加深理解,再举例一个可能的场景。

假设现在上面的场景换了换,只有两台主机local_host以及remote_host。local_host可以访问remote_host,但只能访问remote_host的22端口。因为remote_host主机的防火墙默认只打开了22端口,如果要访问remote_host的3000端口的话就要修改防火墙。为了保证安全,防火墙需要配置允许访问的IP地址。但是,本机公网IP通常是网络运营商动态分配的,是不断变化的。这样的话,防火墙配置需要经常修改,就会很麻烦。

通过本地端口转发,可以将发送到本机local_host端口2000的请求,转发到remote_host主机的3000端口。

1
ssh -L 2000:localhost:3000 username@remote_host

上面的localhost指的是remote_host,并不是本机local_host,要区分开来,因为目标主机是相对remote_host而言的。

这样,在本机local_host上可以通过访问http://localhost:2000来访问目标主机remote_host上的3000端口服务了。

远程端口转发(Remote forwarding)

借着本地端口转发的假设,local_host是本机,proxy_host是代理主机,remote_host是我们要访问的服务器。但这次情况有所不同,proxy_host可以访问local_host,proxy_host和remote_host之间可以互相访问,local_host无法访问proxy_host以及remote_host,以下是它们的访问关系

local_host <- proxy_host <-> remote_host

注意,proxy_host到local_host是单通的,换句话说,local_host在公网,proxy_host和remote_host在内网。

现在我们想通过proxy_host来访问remote_host上的服务该怎么做呢?

在proxy_host主机上执行命令

1
ssh -R 2222:remote_host:22 username@local_host

然后在local_host本机上就能ssh访问remote_host的22端口了

1
ssh -p 2222 username@localhost

解释一下,上面的命令让local_host主机监听自己的2222端口,然后所有数据经proxy_host主机转发到remote_host目标主机的22端口。对于proxy_host来说,要远程绑定local_host的2222端口,所以被称为远程端口转发。而本地端口转发绑定的是它本机自己的端口,所以被称为本机端口转发。

什么是远程端口转发

所谓远程端口转发,就是将发送到远程端口的请求,转发到目标端口。这样,就可以通过访问远程端口,来访问目标端口的服务。使用-R属性,就可以指定需要转发的端口,语法是这样的:

1
-R 远程地址:远程端口:目标地址:目标端口

其中远程地址可以省略。也就是上面例子中的local_host的公网地址。

如何区分本地端口转发还是远程端口转发
本地端口转发 远程端口转发
使用-L参数 使用-R参数
ssh -L 命令执行在本机上 ssh -R 命令执行在代理服务器上
绑定的是本机端口 绑定的是远程(公网)端口
gost的远程端口转发

gost远程端口转发的功能类似于ssh中-R参数的功能,与本地端口转发一样,也支持UDP和转发链。

TCP端口转发

假设在local_host上部署了gost socks5代理并监听在1080端口上

1
gost -L socks5://:1080

然后在proxy_host上执行命令

1
gost -L=rtcp://:2222/remote_host:22 -F=socks5://local_host:1080

这样gost就会(通过转发链)连接到local_host,并让local_host监听TCP 2222端口,当我们使用ssh连接local_host的2222端口时,就相当于连接到了remote_host的22端口了

1
ssh -p 2222 username@localhost
UDP端口转发

假设local_host上部署了gost socks5代理并监听在1080端口上

1
gost -L socks5://:8080

现在想在local_host上访问remote_host上的DNS服务,在proxy_host上执行

1
gost -L=rudp://:5353/remote_host:53 -F=socks5://local_host:1080

这样gost就会(通过转发链)连接到local_host,在local_host上监听UDP 5353端口,当我们向local_host:5353发送UDP数据

1
dig @localhost -p 5353 www.google.com

就相当于发送到了remote_host:53上了

注意: TCP远程端口转发使用了socks5 bind协议,所以转发链的末端(最后一个-F参数)一定要是socks5类型;
UDP远程端口转发使用了UDP-over-TCP,所以转发链的末端(最后一个-F参数)一定要是gost socks5类型

总结

简单地介绍了一下关于本地端口转发以及远程端口转发的基本概念以及应用场景。顺便也学习了gost下关于端口转发的使用。

avatar

chilihotpot

You Are The JavaScript In My HTML