跳到主要内容

内网穿透

阅读量

0

阅读人次

0

在解释 内网穿透 这件事之前,我先抛出一个问题:如果让你来设计一套框架去实现两台内网主机进行通信,你会怎么做?

中继(Relaying)

一个非常直观的设计就是先准备一台拥有公网IP的服务器命名为Server,然后两台处于同内网下的客户端主机,分别为ClientAClientB,然后两台客户端机器分别连接服务器。这时候服务器充当一个数据中转站,把ClientA发送的数据转发给ClientB,反向同理,这就实现了两台内网的客户端之间的数据通信。

比较常见的就是平常经常使用到的frp,它就是一个典型的通过中继(也叫转发)实现的内网穿透功能。

NAT(Network Address Translation)穿透

NAT穿透,简单地讲就是要让处于不同NAT网络下的两个节点(Peer)建立直接连接,只知道自己的内网地址是肯定不行的,他们需要知道对方的公网IP和端口,然后双方互相向对方发送数据包,从而建立起连接。整个流程可以看做两个关键步骤:

  1. 发现自己的公网IP和Port
  2. 将自己的IP和Port共享给对方

而NAT有几种不同的形态:

Full Cone NAT(完全圆锥型)

这种会将同一内网地址端口192.168.1.2:4567发送至公网的所有请求都映射成同一个公网地址端口10.2.6.4.1:6666192.168.1.2可以收到任意外部主机发送到10.2.6.4.1:6666的数据报文,也就是说,只要知道内网地址被映射成的公网IP,任何公网IP对其发送数据,这个内网下的所有端口都能接收到此数据。

这种类型的客户端局限性最低,也是最理想的情况,但是现实大部分情况都不是这种模型。

Address Restricted Cone NAT (地址限制圆锥型)

这种会将从同一内网地址端口192.168.1.2:4567发至公网的所有请求都映射成同一个公网地址端口10.2.6.4.1:6666,只有当内部主机192.168.1.2先给服务器Server 6.7.8.9发送一个数据报后,192.168.1.2才能收到服务器Server 6.7.8.9发送到公网地址端口10.2.6.4.1:6666的数据报文。

注意理清楚这里面的细节,地址限制圆锥型的意思就是说,内网地址接收到数据的这个公网IP必须是这个内网地址曾经发送过数据的IP地址(这就是一种映射规则),当前内网地址下的所有端口才能接收到此数据。

Port Restricted Cone NAT(端口限制圆锥型)

与受限制锥型基本一样,区别就是最后内网地址只有指定的端口号才能接收到数据。

这会将从同一内网地址端口192.168.1.2:4567发至公网的所有请求都映射成同一个公网地址端口10.2.6.4.1:6666,只有当内部主机192.168.1.2:4567先给服务器Server 6.7.8.9发送一个数据报后,192.168.1.2:4567才能收到服务器Server 6.7.8.9发送到公网地址端口10.2.6.4.1:6666的数据报。

对称NAT(Symmetric)

对不同的外网IP地址都会分配不同的端口号。把所有来自相同内部主机IP地址和端口号,到特定目的IP地址和端口号的请求映射到相同的外部IP地址和端口。如果同一主机使用不同的源地址和端口对,发送的目的地址不同,则使用不同的映射。只有收到了一个IP包的外部主机才能够向该内部主机发送回一个UDP包。对称的NAT不保证所有会话中的(私有地址,私有端口)和(公开IP,公开端口)之间绑定的一致性。相反,它为每个新的会话分配一个新的端口号。

如果是对称型的NAT,则不适合做内网穿透。

NAT穿透流程

  1. ClientA 和 ClientB分别通过NAT与服务器进行通信,服务器只存储两个客户端的公网IP地址信息。
  2. 服务器接收到数据时分别将对方的IP地址信息发送给双方。
  3. 此时,ClientA 拥有了 ClientB的公网地址,ClientA先发送一个探测数据给ClientB的公网IP,但是ClientB接收不到数据,因为根据协议,NatB中的映射没有这条规则,也就是说NatB并没有发送过数据给NatA,那就接收不到来自NatA的数据,但此时NatA添加了一条NatB信息的映射规则。
  4. NatB发送一条探测数据给NatA, NatA收到了。因为NatA在上一步骤中发送数据给NatB了,有了这条映射规则。
  5. NatA 在发送一条探测数据给NatB,NatB此时也收到了。
  6. 现在,UDP隧道建立成功,可以进行数据传输了。这个过程也叫做UDP打洞。

对称NAT(Symmetric)的内网穿透

根据这个流程我们会发现对称NAT(Symmetric)不适合做内网穿透。如果两个客户端都是对称NAT根据上述流程就没法穿透了,举个例子:

  1. ClientA 和 ClientB分别通过Nat与服务器进行通信,服务器只存储两个客户端的公网IP地址信息。
  2. 服务器接收到数据时分别将对方的IP地址信息发送给双方
  3. 此时,ClientA 拥有了 ClientB的公网地址,ClientA先发送一个探测数据给ClientB的公网IP,但是此时对于ClientB发送数据的IP已经和服务器所保存的IP不一样了,因为对称NAT对于不同的公网IP地址发送数据所对应的端口是不一样的。那后面流程就没法继续了。

那两个对成型如何穿透呢,除非你能够准确的猜出客户端每次映射的端口号是多少(这个理论上可以实现,但是稳定性很低)。

是否可穿透组合

Peer APeer B是否可以打洞
全锥型全锥型
全锥型受限锥型
全锥型端口受限锥型
全锥型对称型
受限锥型受限锥型
受限锥型端口受限锥型
受限锥型对称型
端口受限锥型端口受限锥型
端口受限锥型对称型
对称型对称型

远程控制Android手机

下载 scrcpy 软件。

首先将手机通过USB连接电脑。进入 开发人员选项,打开USB调试,在弹出的对话框下勾选 始终允许。现在打开 scrcpy.exe 就可以通过scrcpy控制手机了。

为了能够一直顺利打开手机的ADB调试模式,我们还需要 取消勾选 连接USB时总是弹出提示, 勾选"仅充电"模式下允许ADB调试

为了能够通过TCP/IP进行ADB调试,我们需要开启手机的TCP/IP ADB调试功能,在 scrcpy 目录下提供了 adb.exe,我们可以直接在该目录下使用命令行运行:

.\adb.exe tcpip 5555 # 是能tcp/ip adb调试,监听端口为 5555

这时能看到命令行输出:

restarting in TCP mode port: 5555

这时我们可以拔掉USB连接转而使用 TCP/IP 连接了,如果PC和手机在同一局域网内(假设手机的局域网IP为:192.168.3.86),连接 ADB:

.\adb.exe connect 192.168.3.86:5555

这时能看到命令行输出:

connected to 192.168.3.86:5555

然后我们执行:

.\scrcpy.exe --serial=192.168.3.86:5555

即可通过 scrcpy 使用 TCP/IP 远程控制安卓手机。到这里,我们可以将手机的 5555 端口通过 frp 暴露到公网中,那我们就可以远程通过 scrcpy 控制安卓手机了。推荐 Android frpc,只要能够使其能够在后台一直不断网运行,那么可以达到不错的控制效果。

V2Ray

V2Ray 也提供了内网穿透的服务,本质上也是中继模式。但是它在数据的传输上,实现了多种传输方式。

V2Ray 通过

目前发现 VMess 协议 单纯的 使用 TCP 能够被检测到代理行为,而比较保险的方式是采用 WebSocket + TLS + Web 这种方式,简单的来说就是流量通过 WSS 传输,行为特征不容易被发现,数据能够被 TLS 进行加密。

同时配合 Nginx 可以将 V2Ray 服务隐藏于内网之中,使用 Nginx 再反向代理其他 Web 服务。使得服务器看起来就像一个正常的 Web 服务器(够提供 HTTP 和 WebSocket 服务)。

提示

除此之外,还发现有些服务商会检测进程名,例如 vray。所以稳妥起见,运行程序之前,先将文件改个名,也不要出现 vmess 这种字样。

服务器 V2Ray 配置

下载 v2ray-core 后,V2Ray 对于时间有比较严格的要求,要求服务器和客户端时间差绝对值不能超过90 秒,所以一定要保证时间足够准确。为了方便起见,我们统一时区:

timedatectl set-timezone Asia/Shanghai

然后修改配置文件 config.json,比较推荐的方式是基于 vpoint_vmess_freedom.json 这个文件进行修改,修改完后重命名为 config.json 文件:

config.json
{
"inbounds": [
{
"port": 8089,
"listen": "127.0.0.1", //只监听 127.0.0.1,避免除本机外的机器探测到开放了 8089 端口
"protocol": "vmess",
"settings": {
"clients": [
{
"id": "4ff91e1c-794e-17a4-acc2-60bfc9a445fb", // 这里相当于一个密码,不要泄漏出去
"alterId": 0 // 新版本要求这里为0,以启动新的安全加密特性
}
]
},
"streamSettings": {
"network": "ws",
"wsSettings": {
"path": "/freedom" // 这里我们自己定义一个 path 就好
}
}
}
],
"outbounds": [
{
"protocol": "freedom",
"settings": {}
}
]
}

紧接着配置 Nginx:

server {
listen 443 ssl;
listen [::]:443 ssl;

ssl_certificate cert/freedom.amass.fun.pem;
ssl_certificate_key cert/freedom.amass.fun.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

server_name freedom.amass.fun.key;
location /freedom { # 与 V2Ray 配置中的 path 保持一致
if ($http_upgrade != "websocket") { # WebSocket协商失败时返回404
return 404;
}
proxy_redirect off;
proxy_pass http://127.0.0.1:8089; # 代理至 v2ray vmess over websocket 端口
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
# Show real IP in v2ray access.log
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}

注册使能 systemd 服务:

/etc/systemd/system/v2ray.service
[Unit]
Description=V2Ray Service
Documentation=https://www.v2fly.org/
After = network.target syslog.target
Wants = network.target

[Service]
Type=simple
ExecStart=/root/v2ray/v2ray run -config /root/v2ray/config.json
Restart=always
RestartSec=2s
LimitNOFILE=infinity

[Install]
WantedBy=multi-user.target
systemctl enable v2ray	# 开机自启动
systemctl start v2ray

Shadowrocket 客户端配置

类型:Vmess

地址:freedom.amass.fun

端口:443

UUID:填入 v2ray 的配置文件 config.jsoninbounds.settings.clients.id注意保密

额外ID:0,如 v2ray 的配置文件 config.jsoninbounds.settings.clients.alterId 所述。

算法:auto

传输方式:

  • 名称:websocket
  • Host:freedom.amass.fun记住这里也要修改
  • 路径:/freedom ,与 V2Ray 配置中的 path 保持一致

TLS:开启

Clash 客户端配置

可以基于模板文件进行修改,然后导入即可。

- name: "v2ray"
type: vmess
server: freedom.amass.fun
port: 443
uuid: 4ff91e1c-794e-17a4-acc2-60bfc9a445fb
alterId: 0
cipher: auto
tls: true
skip-cert-verify: false
network: ws
ws-path: /freedom
ws-headers:
Host: freedom.amass.fun

其他工具

v2rayN

v2raya

v2raya-openwrt