跳转至

内网穿透技术#

为什么需要内网穿透技术?#

设想一个场景: 由于各种原因, 你没有公网IP, 你的设备在NAT之后, 你需要从外面访问这个设备, 这时候就需要内网穿透.

例如说你有一台NAS在家里, 你想在外面访问这个NAS, 这时候就需要内网穿透技术.

内网穿透技术的原理和类型#

NAT类型#

  1. 全锥形NAT: 所有从同一个内网IP:Port发送的数据包都会被映射到同一个外部IP:Port, 反之同理.
  2. 限制锥形NAT: 与全锥形NAT类似, 但是必须要有一个内部主机先向外部主机发送数据包, 才能建立映射
  3. 端口限制NAT: 与限制锥形NAT类似, 但是限制了外部主机只能使用一个端口与内部主机通信. 如果外部主机使用了不同的端口, 则会被拒绝
  4. 对称NAT: 与端口限制NAT类似, 但是每次内部主机向外部主机发送数据包时, NAT会为这次连接分配一个新的端口, 使得外部主机无法预测下一次连接的端口, 即为每一个连接都会分配一个新的端口, 相当随机化.

alt text

  1. p2p: 通过中继服务器和UDP打洞, 两个设备建立p2p连接, 之后就可以直接通信, 不需要中继服务器的参与. 好处是速度快, 延时低, 但UDP打洞的成功率不高.
sequenceDiagram
    participant A as A
    participant B as B
    participant S as S
    A ->> S: 发送请求和自己的IP和端口
    B ->> S: 发送请求和自己的IP和端口
    S ->> A: 发送B的IP和端口
    S ->> B: 发送A的IP和端口
    A --> B: 交换数据
  1. 中继服务器: 通过中继服务器转发数据, 两个设备之间不直接通信. 好处是稳定, 但引入了中继服务器的延时, 并且转发能力强依赖于中继服务器的带宽.
sequenceDiagram
    participant A as A
    participant S as S
    participant B as B
    A ->> S: 建立连接
    B ->> S: 建立连接
    A ->> S: 发送数据A
    S ->> B: 转发数据A
    A --> B: 虚假的连接

穿透实践#

frp/rathole/nps - 端口转发系列#

无论是frp, rathole还是nps, 都是通过中继服务器的方式实现内网穿透的. 他们的原理都是通过中继服务器转发数据, 从而实现内网穿透. 当然实际上frp也有udp打洞的功能, 但是成功率不高, 失败后会转为中继服务器转发数据.

rathole是Rust实现的高性能内网穿透工具, 性能高, 相比frp更加轻量级, 但是功能也更少且没有web管理界面.

nps是golang实现的内网穿透工具, 功能最丰富且web管理界面比较方便, 但性能最差.

nps和frp相比, nps可以完全通过服务端控制所有的客户端, 但是frp只能通过客户端控制连接, 当客户端数量非常多时, nps相当方便

具体配置方法可以参考Github官方文档.

WireGuard - VPN系列#

WireGuard是一种新型的VPN技术, 通过51280端口建立UDP隧道, 可以打洞也可以中继服务器实现, 而且因为通过Linux内核实现, 性能非常高.

配置方法#

假定需要搭建如图网络拓扑:

graph LR
A["A节点1<br>10.100.100.2"]<-->M((M: 中继节点<br>10.100.100.1))<-->B[B节点2<br>10.100.100.3]

​ 安装wg

sudo apt install wireguard

​ 生成密钥: pubkey和privatekey

wg genkey | tee privatekey | wg pubkey > publickey
配置文件: /etc/wireguard/wg0.conf

服务端

[Interface]
Address = 10.100.100.1/24
SaveConfig = true
PrivateKey = 服务端私钥
ListenPort = 51820

[Peer]
PublicKey = A的公钥
AllowedIPs = 10.100.100.2/32

[Peer]
PublicKey = B的公钥
AllowedIPs = 10.100.100.3/32
客户端配置
# Client A
[Interface]
Address = 10.100.100.2/32
PrivateKey = A的私钥
# DNS = 1.1.1.1

[Peer]
PublicKey = 服务端公钥
Endpoint = 服务端IP:Port
AllowedIPs = 10.100.100.0/24 # 只转发这个子网的ip
PersistentKeepalive = 25

# Client B
[Interface]
Address = 10.100.100.3/32
PrivateKey = B的私钥

[Peer]
PublicKey = 服务端公钥
Endpoint = 服务端IP:Port
AllowedIPs = 10.100.100.0/24
PersistentKeepalive = 25
有意思的是, 如果AllowedIPs设置为0.0.0.0/0, 则会转发所有的IP, 如果恰好服务端在海外的话, 所有的流量就会通过服务端转发, 从而实现科学上网.

此外WG推荐设置内网DNS, 以防止DNS泄露.

启动与停止

sudo wg-quick up wg0.conf
sudo wg-quick down wg0.conf

sudo wg show wg0 # 查看wg0情况

​ PS: 如果出现无法访问的情况, 可能是没有开IP转发, 在M中开启IP转发:

# 重启后失效
sudo sysctl net.ipv4.ip_forward=1

# 持久化配置, 加入
sudo nano /etc/sysctl.conf
net.ipv4.ip_forward = 1