websocket
首先从clientless ssl vpn开始谈起,有客户端的sslvpn比较容易理解,还是通过虚拟网卡和路由去实现。没有客户端意味着没有虚拟ip,不能改路由,怎么实现vpn呢?
如果仅仅是web应用的话,可以使用https代理和url改写,实现一个网关对多个http网站的代理。
但如果是http以外的tcp应用呢,比如ssh和vnc,如何通过浏览器去访问呢?这个时候websocket就登场了。
浏览器支持websocket协议,websocket跟http处于同一层次,支持双向tcp通信,为浏览器与后端tcp服务之间的通信提供了可能。
服务器部署一个websocket<->tcp的的代理,就可以实现浏览器到后端tcp应用的访问了,后端的tcp应用可以是ssh,vnc等服务。
当然,这样只是可以把内容发到浏览器端,具体的应用层协议解析,还需要由浏览器或者websocket代理去转换成html。
通过浏览器完成ssl vpn访问的例子,可以参考websocketfiy,webssh和webvnc项目。
这种模式的好处,是通过浏览器的一条加密通道,把内网资源暴露出来。需要网关有公网ip,网关和被暴露的资源可以正常连接。
当然websocket还是有局限的,对于非tcp应用,还要再做封装。
NAT穿越
NAT穿越也分为TCP和UDP两种场景。
打洞需要借助第三方服务器,经过服务器协助建立起来的真实会话通道,又有经过第三方服务器代理和不经过第三方服务器两种情况。
经过第三方服务器的方式相对简单,通过ssh的端口转发功能就可以实现。
具体的,假设两台内网服务器A,C都可以访问公网服务器B,那么A先ssh连接B,并请求开启ssh端口代理转发。C连接B的代理端口,建立一条新的SSH通道。
B在两条SSH会话通道之间进行代理转发,就完成了A,C的通信。
类似的,两个nat后面的ssl vpn客户端都连接到B上的ssl vpn服务器,然后服务器上允许客户端主机之间互相转发,也可以通过服务器的转发达到nat穿越的目的。
不经过第三方服务器的方式,通常用于P2P打洞。
具体的,假设两台内网服务器A,C都可以访问公网服务器B。大致步骤如下:
-
A,C都先向B进行连接,由此通告B自己的外网地址。
-
B给A发通知“C的外网地址和端口是XXX:M”,通知C,“A的外网地址是YYY:N”。
-
A监听向B发起连接使用的源端口,并且使用该源端口连接C的外网地址XXX:M。(该连接报文会被B的NAT网关丢掉,但是会在A的NAT网关上留下半连接会话)
-
B使用向B发起连接的源端口,去连接A的外网地址YYY:N,A的NAT网关上会话建立成功,A成功收到报文。
-
A的会话建立成功并且回复B,B上的会话建立成功,打洞完成。
具体参考下图(源自网络):
对称和非对称
使用开源的frp试了一下NAT穿越,然而P2P模式失败了。于是进一步去调查原因,发现是NAT模式的问题。
上面讲的NAT穿越方式有一个很重要的前提:主机使用同一个端口往外进行连接的时候,NAT网关上的端口不会改变。
在实际场景中,往往不能满足这个条件,只有完全锥形NAT才是这样的。
对于对称型的NAT,目的主机变了,即使NAT发起者的源IP和端口没变,NAT网关的源端口也会改变。
这样,公网服务器记录的连接就起不到什么作用了,只能提供一些猜测依据。如果端口选择完全随机的话,就完全无法猜测了。
所以,基于端口预测去做两个对称型NAT的穿越是非常困难的。