内网穿透
内网穿透,是一项可以让 NAT 设备后的主机可以被互联网访问的技术。本文介绍笔者使用过的多种内网穿透技术,希望读者能找到适合自己的一种。
frp
frp 是一个使用 go 语言开发的内网穿透工具,适合多种应用场景。像 frp 这种需要自己搭建服务器,端口对端口的是常用的一种内网穿透形式。
使用方法可见 gofrp.org。
Cloudflare Tunnels
Cloudflare Tunnels 是 Cloudflare 的一个内网穿透工具。配置简单,不需要公网服务器,备案和 https 证书,速度也相当可观,所以 Cloudflare Tunnels 适合用来搭建网站。
- 首先注册登录 Cloudflare Zero Trust, 打开 Access Tunnels。
- 点击 Create a tunnel, 填入名字,选择环境。
- 按照操作指引安装 connector,等待下方列表自动刷新后,应该可以看到有新的一项。
- 点击 Next, 填写配置信息。需要注意的是,服务类型选择的是你本机部署的服务类型,而不是用户访问时候的数据类型,例如:本机部署 http 服务,选择 http 类型,但用 https 访问。
- 点击 Save,保存后,就可以在你所配置的域名访问到了。
例如,本机运行一个简单的测试服务。
Cloudflare 自动加上了 https 证书。
v2ray
v2ray 虽然被大量用于科学上网,但是实际上它也可以用来内网穿透,除了类似 frp 的内网穿透(反向代理) 的一对一,v2ray 还有一个一对多的模式,我把它叫做 vpn 内网穿透模式。
使用 v2ray 的此技术,可以使内网外的机器的部分网络环境和内网相同,像校外访问校园网这样的场景可以用到此技术(也就是 vpn 最初的用处)。
假设 A 为内网内的一台电脑, 它的出站流量为 freedom, 也就是自由访问内网, 另一个出站流量是外网服务器 B, A 主动连接 B, 然后转发来自 B 的请求。
而 B 有两个入站流量, 一个监听A的连接, 一个做代理服务器,这里可以用 v2ray 支持的任意协议,例如 http,vmess,Shadowsocks。
这样, 只要使用B的代理, 就相当于和A的网络环境相同, 也就能访问内网了。
内网服务器配置
{
"reverse":{
// 这是 A 的反向代理设置,必须有下面的 bridges 对象
"bridges":[
{
"tag":"bridge", // 关于 A 的反向代理标签,在路由中会用到
"domain":"private.cloud.com" // A 和 B 反向代理通信的域名,可以自己取一个,可以不是自己购买的域名,但必须跟下面 B 中的 reverse 配置的域名一致
}
]
},
"outbounds": [
{
//A连接B的outbound
"tag":"tunnel", // A 连接 B 的 outbound 的标签,在路由中会用到
"protocol":"vmess",
"settings":{
"vnext":[
{
"address":"serveraddr.com", // B 地址,IP 或 实际的域名
"port":16823,
"users":[
{
"id":"b831381d-6324-4d53-ad4f-8cda48b30811",
"alterId":64
}
]
}
]
}
},
// 另一个 outbound,最终连接内网服务
{
"protocol":"freedom",
"settings":{
},
"tag":"out"
}
],
"routing":{
"rules":[
{
// 配置 A 主动连接 B 的路由规则
"type":"field",
"inboundTag":[
"bridge"
],
"domain":[
"full:private.cloud.com"
],
"outboundTag":"tunnel"
},
{
// 反向连接访问内网服务的规则
"type":"field",
"inboundTag":[
"bridge"
],
"outboundTag":"out"
}
]
}
}
外网服务器配置
{
"reverse":{ //这是 B 的反向代理设置,必须有下面的 portals 对象
"portals":[
{
"tag":"portal",
"domain":"private.cloud.com" // 必须和上面 A 设定的域名一样
}
]
},
"inbounds": [
{
// 接受客户端的inbound
"tag":"external", // 标签,路由中用到
"port":10808,
"protocol":"http",
},
// 另一个 inbound,接受 A 主动发起的请求
{
"tag": "tunnel",// 标签,路由中用到
"port":16823,
"protocol":"vmess",
"settings":{
"clients":[
{
"id":"b831381d-6324-4d53-ad4f-8cda48b30811",
"alterId":64
}
]
}
}
],
"routing":{
"rules":[
{ //路由规则,接收客户端请求后发给 A
"type":"field",
"inboundTag":[
"external"
],
"outboundTag":"portal"
},
{ //路由规则,让 B 能够识别这是 A 主动发起的反向代理连接
"type":"field",
"inboundTag":[
"tunnel"
],
"domain":[
"full:private.cloud.com"
],
"outboundTag":"portal"
}
]
}
}
然后分别运行 v2ray,需要访问内网的机器通过代理(本配置文件是 http 代理,实际使用时建议使用安全性更高的协议,并加入鉴权)即可访问到内网。
还可以通过编辑 v2ray 的路由规则做到只有部分流量被转发,相当灵活。
ssh Tunnels
相信大家都很熟悉 ssh,但未必知道它还可以用来网络流量的转发,其中一项功能就可以用来做内网穿透。只不过,相对于以上三种技术,ssh 相对而言没有那么稳定,但它使用简单,只要支持 ssh,不需要进行配置,用于短时的开发测试还是方便的。
注:ssh tunnels 的三种模式中,仅 Remote Port Forwarding 可用于内网穿透。
Dynamic forwarding
在本地建立一个 Socks 代理服务器,复制远程主机的网络环境(把代理的请求转发给远程主机)。
例如:
ssh -D 7777 username@hostname
1
本地浏览器代理设置为 socks
, localhost:7777
,现在浏览器的请求均由远程主机转发。
Local Port Forwarding
将发往本地端口的流量由远程主机转发到某地址。
例如:
ssh -L 7777:localhost:9999 username@hostname
1
这样,访问本机 7777
端口相当于访问远程主机的 localhost:9999
,同理,这里的 localhost:9999
也可以换成其他地址,例如只有远程主机才能访问到的其他服务器等。
容易发现,Local Port Forwarding 是在 ssh 客户端监听端口,流量由 ssh 服务端转发。
该转发不仅仅支持 http, 其他协议例如 upd, ssh 协议也是可以转发的。
需要注意的是,想把该转发用作 http 代理是几乎行不通的,因为大部分网站使用了 https,即使没有强制使用的,大都会检查 http 中的 host
,而此种方法发出的 http 请求中的 host
是 localhost
, 会被认为不合法。
注:这里的 7777:localhost:9999
实际上是省略了 localhost
, 实际上是 localhost:7777:localhost:9999
,如果想监听非 localhost
, 可改为 "*:7777:localhost:9999"
(注意引号)。
Remote Port Forwarding
此技术可用于内网穿透。
和 Local Port Forwarding 不同的地方在于,Remote Port Forwarding 是在 ssh 服务端监听端口,流量由 ssh 客户端转发。
例如:
ssh -R "*:7777:localhost:8888" username@hostname
1
这样,访问远程主机的 7777
端口相当于访问本机的 8888
端口,起到了内网穿透的效果。
假设还想通过外网服务器访问内网中的 B 主机,我可以在内网中的 A 主机 运行
ssh -R "*:7777:B:8888" username@hostname
1
以 A 主机为控制端和转发桥梁。