实验环境
我使用的是 Ubuntu 24.04.4,基本环境配置见ARP攻击原理学习
网络结构如下:
| 节点 | IP 地址 | 作用 |
|---|---|---|
| A 区普通主机 1 | 10.8.0.5 |
用来测试从 A 区访问 B 区服务 |
| A 区普通主机 2 | 10.8.0.6 |
用来进行辅助连通性测试 |
| A 区网关 | 10.8.0.99 |
A 区出口,也是 SSH 隧道和 VPN 隧道的一端 |
| B 区服务器 1 | 192.168.20.5 |
B 区目标服务器,提供 Telnet 等被保护服务 |
| B 区服务器 2 | 192.168.20.6 |
B 区目标服务器,用来进行辅助连通性测试 |
| B 区网关 | 192.168.20.99 |
B 区出口,也是 SSH 隧道、SOCKS 代理和 VPN 隧道的一端 |
| 中间路由器 | 10.8.0.11 / 192.168.20.11 |
负责转发 A 区和 B 区之间的流量,并执行防火墙规则 |
启动实验前先清理容器环境,防止被之前的网络配置干扰
|
|
进入实验目录并启动
|
|
验证容器状态
|
|
基础网络测试
实验开始前,首先确认从A1到B1,和从A1到B2容器的网络连通性
|
|
查看中间路由器的路由表,这里可以看到它有两个网卡,eth0连接 A 区网段10.8.0.0/24,自己的地址是 10.8.0.11,eth1连接 B 区网段192.168.20.0/24,自己的地址是 192.168.20.11。说明这个路由器就是两个区域通信的中转
|
|
进入防火墙容器,查看默认的 iptables 规则
|
|
这部分规则模拟的就是一个简易的公司内网防火墙环境,类比到真实情况,A 就是防火墙外侧网络,可以作为外部访问者或者代理服务器,B 就是受保护的内网。这里重点是看FORWARD链,也就是路由器转发包的规则,默认策略是 ACCEPT
从 A 区方向进来的 TCP 流量中,已经建立的连接和SSH流量,也就是目标端口是22的流量可以继续通过,其他普通 TCP 会被丢弃,这样 A 区主机如果直接访问 B 区的 Telnet、HTTP 等普通 TCP 服务,就会被防火墙阻断
从 B 区方向进来的所有流量中,目标是 93.184.216.0/24的流量会被丢弃,这个 IP 在之后的实验中对应的是www.example.com,这就体现了 B 区主机访问某个网站被防火墙阻断的场景
静态端口转发
通过建立 SSH 静态隧道,将对本地端口的访问转发到远程内网的特定服务上,这里把 A 区网关的
8000端口映射到192.168.20.5:23,从而让区域 A 的主机穿透防火墙,访问区域 B 被保护的 Telnet 服务
新开一个终端窗口,进入 A 区网关容器
|
|
在 A 区网关上建立 SSH 隧道,目的是将10.8.0.99:8000映射到192.168.20.5:23。执行命令后终端会持续运行来维持 SSH 隧道
|
|
-
-L:启用本地端口转发 -
0.0.0.0:8000:让 A 区网关上的 SSH 客户端监听所有网卡上的8000端口 -
192.168.20.5:23:如果监听到8000端口的连接,就通过 SSH 隧道转发给 B 区网关,B 区网关再连接 B1 的 Telnet 服务 -
seed@192.168.20.99:SSH 连接到 B 区网关 -
-N:不执行远程命令,只做端口转发 -
-T:不分配伪终端
新开一个终端进入 A1 容器,通过 A 区网关的 8000 端口连接 B1 的 Telnet 服务
|
|
连接成功后会弹出 Ubuntu 的登录提示,输入用户和密码后,可以输入下面的命令看到当前处于 192.168.20.5主机上,说明连接已经成功建立
|
|
为了观察防火墙视角下的数据包,可以在路由器容器中抓包
|
|
可以看到防火墙主要能看到 10.8.0.99 到 192.168.20.99:22 的 SSH 流量,真正的 Telnet 数据被包在 SSH 加密连接内部,防火墙看不到里面访问的是 192.168.20.5:23,这就是静态端口转发可以绕过防火墙限制的原因
动态 SOCKS 代理
静态端口转发每次只能绑定一个固定目标,而动态端口转发会在本地启动一个 SOCKS 代理,仅指定代理出入口,但访问目标不写死在 SSH 命令里。因此能让 B 区主机通过 A 区网关作为出口,来访问原本被防火墙拦截的网站
新开一个终端窗口,进入 B 区网关容器
|
|
在 B 区网关上建立动态代理隧道,目的是通过 SSH 隧道把请求发到 A 区网关,再让 A 区网关去访问目标。执行命令后终端会持续运行来维持 SSH 隧道
|
|
-
-D:启用动态端口转发,也就是创建 SOCKS 代理。 -
0.0.0.0:9000:让 B 区网关上的 SSH 客户端监听所有网卡上的9000端口 -
seed@10.8.0.99:SSH 连接到 A 区网关
进入 B1 容器,修改本地映射,将域名与被防火墙封锁的网段绑定
|
|
直接访问目标网站,可以发现没有反应,说明无法访问
|
|
用 SOCKS5 代理访问网站,这次可以成功拿到网页 HTML 内容。这是因为 B1 会把请求交给 B 区网关的 SOCKS 代理,也就是192.168.20.99:9000,代理再通过 SSH 隧道把请求发给 A 区网关来访问外部网站。其中socks5h 中的 h 就表示域名解析也交给代理端处理
|
|
浏览器也是类似的流程,首先在 Firefox 浏览器中如图配置代理
访问www.example.com,可以发现页面成功加载了
关闭动态代理隧道,再次访问就发现代理服务器拒绝连接,这和真实网络中的代理是类似的
Layer 3 VPN 隧道
这部分更接近真正 VPN 的工作方式,它会创建一个虚拟网卡
tun0,然后把数据包放进 SSH 隧道中传输
开始这个部分前,先重启实验容器,清理之前的隧道和路由配置来防止干扰
|
|
A 区访问 B 区
这里先在防火墙上添加规则,禁止 A 区网段直接访问 B 区网段
|
|
此时从 A 区网关 ping B1 会失败
|
|
为了实现访问A 网关不再把包直接发向 B 区,而是先把包送进
tun0,再用 SSH 封装起来发给 B 网关。防火墙看到的外层还是 SSH,不是普通的 A 到 B 访问
进入 A 区网关容器,使用 SSH 的 -w 参数建立三层隧道。这个终端需要保持挂起来维持 VPN 隧道
|
|
-w 0:0 会在 SSH 两端创建 tun0 虚拟网卡。A 侧的 tun0 地址是 192.168.53.88/24,B 侧的 tun0 地址是 192.168.53.99/24。这相当于在 A 区网关和 B 区网关之间创建了一条虚拟三层链路。
新开一个终端进入 A 区网关,配置路由。为了避免 SSH 隧道自己的连接被错误引进 tun0,可以先给 B 区网关加一条单独的主机路由。再把访问 B 区网段的流量引入 tun0。
|
|
接着给 B1 和 B2 配置回程路由,让它们知道返回 192.168.53.0/24 这个隧道网段时应该交给 B 区网关
|
|
回到 A 区网关再次ping B1。此时虽然防火墙已经阻断了 10.8.0.0/24 到 192.168.20.0/24 的直接访问,但是 A 区网关访问 B 区网段时,数据包先进入 tun0,再被封装进 SSH 连接中。防火墙看到的外层流量仍然是 A 网关到 B 网关的 SSH 连接,看不到内部的三层访问。
|
|
B 区访问外部
这个场景用于模拟 B 区主机访问某个外部网段被防火墙阻断,然后通过带 NAT 的 VPN 隧道从 A 区出口访问外部网络。
测试前需要断开上一个场景的隧道,并重启容器清理路由。
|
|
在防火墙上添加出站阻断规则。
|
|
进入 B 区网关,建立带 NAT 的 VPN 隧道。
|
|
这里多了两处 NAT 配置。
B 区网关上的 NAT 用来处理进入隧道前的源地址转换。
|
|
A 区网关上的 NAT 用来处理从 A 区出口访问外部网络时的源地址转换。
|
|
接着给 B1 和 B2 添加到目标网段的路由,让它们访问 93.184.216.0/24 时先交给 B 区网关。
|
|
在 B1 中访问 www.example.com。
|
|
这里的流量路径可以理解为:
|
|
直接访问时,B1 到 93.184.216.0/24 的流量会命中防火墙的 DROP 规则。建立 VPN 隧道后,B1 的流量先进入 B 区网关,再通过 tun0 被封装到 SSH 连接中,最后从 A 区网关访问外部目标。
NAT 的作用是让返回包能正确回来。如果没有 NAT,外部目标或者中间节点可能不知道怎么返回到 B1 的私有地址。使用 MASQUERADE 后,出口侧会把源地址改成合适的地址,并记录连接关系,回包回来时再反向转换。
实验理解
这次实验最重要的点是理解防火墙的视角。防火墙不是天然知道用户真正想访问什么,它只能根据自己能看到的数据包字段做判断。
直接访问时,防火墙可以看到:
|
|
所以规则可以按照源网段和目的网段进行拦截。
建立 SSH 隧道后,防火墙看到的外层流量变成:
|
|
内部真正访问的 Telnet、HTTP 或者某个 IP 网段,都被封装在 SSH 连接里面。除非防火墙能解密或识别隧道行为,否则它只能把这部分流量当成普通 SSH 流量处理。
静态端口转发适合访问一个固定服务。
动态 SOCKS 代理适合让应用程序通过代理访问多个目标。
Layer 3 VPN 隧道更接近真正的 VPN,可以通过虚拟网卡和路由把一整段 IP 流量引入隧道。
这也解释了 VPN 的基本工作方式。VPN 的本质不是单纯“换 IP”,而是创建一条新的虚拟网络路径。系统把部分流量送进虚拟网卡,VPN 程序再把这些流量封装进另一条连接,发送到远端节点解封装,最后由远端节点继续访问目标网络。
这个实验对我来说比较应景。之前经常听到代理、隧道、VPN、NAT、路由这些词,但它们之间的关系一直比较散。这次通过 Docker 网络把它们连起来之后,能明显看到一条完整路径:防火墙负责拦截,路由决定流量走向,SSH 负责隧道封装,tun0 负责承载三层数据包,NAT 负责处理回包路径。
学完以后再看常见的 VPN 或代理软件,就不会只停留在“开了以后能访问”这个层面,而是能理解它大概在系统网络栈里动了哪些东西。