ufw allow 443 对 VPN 后面的服务毫无作用
入站放行针对的是防火墙主机,而不是它后面的机器。先读 UFW 的拦截记录,再为被转发的服务写一条路由规则。
从 VPN 客户端发起 ping 能通,TCP 服务却超时,而 ufw allow 443/tcp 完全不起作用。当应用运行在网关后面的主机上、而不是运行在网关上时,这条放行就是写给错误的流量路径的。数据包并不是发给防火墙的,也不会交给任何本地进程,因此输入规则永远看不到它。修复办法是一条针对被转发流量的路由规则,而证据其实早就在日志里。
先判断目的是本机还是转发
按内核看到的样子读这个包。交给网关本机服务的流量属于本地输入。网关继续路由到另一台主机的流量属于转发,UFW 为这两条路径维护不同的规则集。决定区分的,是目的地址和路由查找,而不是数据包从哪个接口进来。
一条日志里 IN 和 OUT 两个字段显示同一个接口,仍然可能描述的是被路由的包,因为把客户端从同一台设备收进又送出的隧道看起来正是如此。目的地址比接口名字更快地给出答案。看到同一个名字两次,并不能证明是本地投递;凭空想象一个部署从未创建的隧道接口,同样没有帮助。
任何规则都要在读懂拦截之后再加
复现一次已获授权的连接尝试,按网关记录的方式捕获它的源、目的、协议和目的端口。把这四个值与日志条目以及服务的实际位置对照。之前的 ping 成功只证明它自己那次交换被放行了,而防火墙对发往应用的 TCP SYN 可能完全是另一种处理。
sudo dmesg | grep "UFW BLOCK" | tail -n 5
sudo ufw status numbered
第一条命令连同字段一起返回判定,第二条显示已经加载了什么、按什么顺序。改动任何东西之前先把两条都读一遍,因为排在你这条规则前面的旧拒绝,在没有任何缺失规则的情况下也能解释同样的症状。
写出最窄的合适路由规则
在这个示例里,客户端从 172.20.8.0/24 经 tun0 进来,服务监听 10.30.0.15 的 8443 端口,网关从 ens5 出去。这些值刻意选得完全不像云上 VPC 默认的 10.0.0.0/16 或家里的 192.168.0.0/16,而 8443 让示例避开了网关自己的 443 监听。
sudo ufw --dry-run route allow in on tun0 out on ens5 proto tcp from 172.20.8.0/24 to 10.30.0.15 port 8443
试运行会把将要生成的规则集打印出来,这是查看你的规则落在现有拒绝之前还是之后最便宜的办法。接口名和内部地址要对着实际部署核对,而不是对着示例,并且记住当目的地址转换先执行时,过滤器看到的是翻译后的目的地址。
让回程路径保持有状态
UFW 会跟踪已放行的连接,因此对被放行的转发流,其回包无需显式的反向规则就能通过。为了让它工作而在相反方向加一条不受限的规则,会把策略放宽到远远超出这一项服务,而正常的请求与应答交换并不需要这样做。让回程保持隐含,让规则集保持可读。
只有试运行符合你的意图之后才应用规则,然后确认有序列表把规则显示在预期位置。把 VPN 监听和管理规则当作独立的事情保留,因为改动它们与放行单个应用是不同的决定。把改动记在写拓扑的同一处,免得下一位工程师再从抓包里重新发现一遍。
sudo ufw route allow in on tun0 out on ens5 proto tcp from 172.20.8.0/24 to 10.30.0.15 port 8443
sudo ufw status numbered
检查 UFW 无法替你保证的部分
正确的规则仍然需要内核转发和一条能到达服务的路由。第二次怪罪防火墙之前,先把这两项确认清楚。
sysctl net.ipv4.ip_forward
ip route get 10.30.0.15
然后看看主机之外。在云实例上,客户机的 UFW 策略并不会取代安全组、子网 ACL 或云厂商自己的转发配置,所以本地拦截只是关于一层的证据,而不是所有其他层都允许该服务的证明。
最后在防火墙开启的状态下测试新连接,一次来自被允许的源,一次来自无关的源。停用 UFW 会在同一条命令里既消除症状又毁掉证据,所以它应该是最后一个实验,而不是第一个。