内网穿透的基础知识

内网穿透的基础知识

路由器和地址转换

网络是怎样链接的,户根勤

IP 地址(如 101.x.x.x)是用于在互联网中标识设备的唯一地址。按照设计初衷,每台接入互联网的设备都应该拥有一个全球唯一的 IP 地址。然而,随着接入互联网的设备数量越来越多,没有足够的 IP 地址分配给每一台设备。为了解决这个问题,产生了内网-外网的地址管理模式。如下图所示,公司 A 和公司 B 各自拥有独立的内部网络。由于不同公司的内网是相互隔离的,不需要相互访问,因此可以使用相同的 IP 地址范围不会产生冲突。而当两个网络中的设备需要通信时,所有的数据包都经过各自的路由器进行统一转发。只要两个路由器各自拥有一个公网 IP 地址,就能够实现网络间的通信。路由器收到数据包后,可以根据内部 IP 地址将数据转发给目标设备,于是就实现了不同内网之间的通信。为了规范内网地址的使用,互联网标准规定了专门的私有地址范围:

  • 10.0.0.0/8(即 10.x.x.x)
  • 172.16.0.0/12(即 172.16.x.x 到 172.31.x.x)
  • 192.168.0.0/16(即 192.168.x.x)

协议规定这些地址只能在内网使用,不能在互联网上直接使用。这里的 /8、/12、/16 表示子网掩码的位数。子网掩码用于确定 IP 地址中的网络号和主机号。以常见的 255.255.255.0 为例,每一个字段都可以用8个二进制来表示。将每一个字段转换为二进制后连在一起,前24位为1,后8位为0,于是这个子网掩码就将 IP 地址分为两部分:

  • 前 24 位作为网络地址(如 192.168.1.0),用于标识子网
  • 后 8 位作为主机地址(如 .1),用于标识该子网内的具体设备

上面提到过,内网设备只有私有地址,没有公网 IP ,需要经过路由器才能与互联网通信。路由器在这里起到的一个重要功能就是网络地址转换(NAT, Network Address Translation)来实现连接,工作流程如下: - 内网设备发起对外通信时,数据包首先到达路由器(NAT 设备) - 路由器识别数据包的目标地址是否为外网地址 - 如果是外网通信,路由器会将数据包的源 IP 地址和端口号替换为自己的公网 IP 地址和一个新的端口号 - 最后,路由器将修改后的数据包转发到互联网

举例说明:假设内网设备的私有 IP 地址为 10.10.1.1,使用端口 1025 发送数据包,而路由器的公网 IP 地址为 198.18.8.31。当路由器收到这个数据包时,会将源地址从 10.10.1.1:1025 改写为 198.18.8.31:5436(其中 5436 是路由器动态分配的空闲端口号)。这个地址映射关系会被记录在路由器的 NAT 表中,用于后续的双向通信。当外网服务器回复数据包时,目标地址为路由器的公网地址和端口(198.18.8.31:5436)。路由器接收到返回的数据包后,会查询转换表,将目标地址还原为内网设备的私有地址和端口(10.10.1.1:1025),然后将数据包转发给内网中的原始设备。这样就完成了一次完整的 NAT 通信过程。通过这样的机制,具有私有地址的设备就也可以访问互联网了。从互联网一端来看,实际的通信对象是路由器。比如在家里,我们的设备链接家用路由器时,家用路由器就会给连接的设备(电脑、手机、智能设备等)分配一个 192.168.x.x / 10.x.x.x / 172.16.x.x 这样的私有 IP。当设备用 私有 IP 访问互联网时,路由器会把它转换成公网 IP,让互联网服务器能识别请求,整个家庭的所有设备都可以共享一个公网 IP 访问外网。

内网设备之间的通信无需经过 NAT 转换,可以直接使用私有 IP 地址。内网通信可分为同一网段通信和跨网段通信两种情况:当两台设备位于同一网段时(例如 A:192.168.1.10 和 B:192.168.1.20,子网掩码均为 255.255.255.0)首先源设备通过子网掩码计算确定目标设备在同一网段;之后通过 ARP 协议获取目标 IP 对应的 MAC 地址;交换机根据 MAC 地址在二层网络直接转发数据包,无需路由器参与。由于无需经过路由器,此类通信具有较高效率。而当两台设备位于不同网段时(例如 A:192.168.1.10 向 B:192.168.2.20 发送数据),首先源设备通过子网掩码计算判断目标设备在不同网段;之后数据包被发送至默认网关(路由器);路由器根据路由表确定转发路径,将数据包转发至目标网段。此过程虽然需要路由器参与,但不涉及 NAT 转换,源 IP 地址保持不变。

NAT 机制解决了内网设备访问互联网的问题,但是默认情况下,NAT 设备不会自动接受外部请求,也不会主动为公网到私网的流量建立映射关系。因此,互联网无法直接访问路由器的内网设备。要解决这个问题,有以下两种常用方案:

  1. NAT 端口映射(Port Forwarding) 这是一种最直接的方法,在路由器上手动配置规则,将公网 IP 的特定端口映射到内网设备。这样可以让外网用户通过访问公网 IP 的指定端口来访问内网服务。
  2. 内网穿透(NAT Traversal) 当我们无法直接配置 NAT 设备(比如没有权限),或者需要更灵活的访问控制时,可以使用内网穿透。这种方案通常需要一个具有公网 IP 的中转服务器,内网设备主动与该服务器建立连接,由服务器转发外部访问请求到内网设备。

从互联网访问内网

NAT 端口映射

假设内网有一台设备(私有 IP 192.168.1.100,监听 80 端口),所连接的 NAT 设备公网 IP 为 101.43.128.210,此时可以在 NAT 设备上配置:

1
101.43.128.210:8080 → 192.168.1.100:80
这样,外网用户访问:
1
http://101.43.128.210:8080
NAT 设备就会把流量转发到
1
http://192.168.1.100:80
要进行这样的设置,需要进入到路由器后台管理页面,找到端口映射的设置页面,然后进行配置。一般来说,家用路由器贴着的的某个标签上会记录路由器管理地址,以及管理员的用户名和密码等。将设备链接到路由器上,在浏览器中访问该路由器管理地址,就可以进行配置了。然而,NAT 端口映射并不是总是可以配置的,主要有以下限制:

  1. 需要真实的独立公网 IP。许多情况下,我们的路由器可能本身就处在更大的内网中。比如一些运营商(一些校园网和家用宽带)会使用运营商级 NAT(CGNAT, Carrier Grade NAT),多个用户共享同一个公网 IP。

  2. 需要路由器的管理权限。在企业或学校环境中,我们基本没有权限访问网络设备的管理界面。

要判断是否拥有独立公网 IP,可以: 1. 在路由器管理界面查看 WAN 口 IP 地址 2. 访问 https://www.whatismyip.com/ 查看实际的公网 IP 3. 对比这两个地址是否一致

如果发现自己处于 CGNAT 环境(两个地址不一致),或者没有路由器管理权限,这时就可以使用内网穿透方案。

内网穿透

内网穿透的核心思路是:内网设备主动与具有公网 IP 的中转服务器(如云服务器)建立连接,形成一个通信隧道。外网请求首先到达中转服务器,再通过这个隧道转发至内网设备。内网设备处理请求后,响应数据同样通过这个隧道返回,最终发送给外网客户端。这样就实现了外网对内网设备的间接访问。比如我们在本地启动一个简单的 Flask 应用:

1
2
3
4
5
6
7
8
9
10
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello():
return 'Hello, World'

if __name__ == '__main__':
app.run(host='0.0.0.0', port=9090)

这里的 host='0.0.0.0' 表示监听所有网络接口。网络接口是设备与网络通信的接入点,可以是物理接口(如以太网卡 eth0)或虚拟接口(如本地回环接口 lo)。每个接口都有其对应的 IP 地址: - 物理接口:对应实际网卡,如以太网接口 eth0、无线网卡接口 wlan0 - 虚拟接口:如本地回环接口(127.0.0.1,表示来自本机的请求)、Docker 网桥、VPN 隧道等

比如我们插了3根网线并接了一个无线网卡,那么我们就有4个物理网络接口。0.0.0.0 就代表接收从任何一个网络接口来的请求。

接下来需要建立内网设备和中专服务器之间的通信通道,有多种工具可以实现这类通道,比如 ngrok、frp 等。这里使用最简单的 SSH 反向隧道:

1
ssh -R 0.0.0.0:9090:127.0.0.1:9090 user@your-cloud-server

这条命令在本地设备和中转服务器之间建立了一条 SSH 隧道,将服务器 9090 端口的请求转发至本地 9090 端口。要使 SSH 反向隧道正常工作,需要进行以下配置:

  1. 修改 SSH 配置允许外部访问:

    1
    2
    3
    4
    5
    # 编辑 /etc/ssh/sshd_config
    GatewayPorts yes

    # 重启 SSH 服务
    sudo systemctl restart sshd

  2. 配置防火墙规则:云服务器通常有两层防火墙需要配置:云平台安全组运行在服务器外部,是服务器外层的统一基本保障。我们可以在云服务器的管理界面,放开安全组的 9090 端口。同时,系统内部也会有自己的防火墙,比如对于 CentOS系统,我们需要开启 firewalld 防火墙的 9090 端口。

1
2
sudo firewall-cmd --zone=public --add-port=9090/tcp --permanent
sudo firewall-cmd --reload

完成配置后,外网用户就可以通过访问中转服务器的 IP:9090 来访问内网的 Flask 服务了。