Ubuntu服务器在多网卡或复杂网络架构下,静态路由配置不当会直接导致流量路径不可控、管理网段暴露或策略冲突。很多人只关注iptables或ufw,却忽略了路由表本身就是一个基础安全边界。正确的做法不是简单地在/etc/netplan里堆几条routes,而是结合策略路由、内核参数和接口绑定,让流量只走你允许的路径。

先检查当前路由表和潜在风险

登录服务器后执行ip route show和ip rule show。默认情况下,Ubuntu会为每个已配置的网卡自动生成直连路由,但当你有多个上游网关时,可能出现非对称路由。例如eth0接内网、eth1接管理网,结果默认路由指向了管理网网关,导致业务流量从管理网出去,既暴露了管理网段,又可能被对端防火墙丢弃。另一个常见问题是netplan中同时存在多个默认网关,内核只会选metric最低的那个,其余全部失效,造成配置假象。

使用netplan做静态路由的硬性隔离

Ubuntu 18.04以后主推netplan,配置文件位于/etc/netplan/。假设eth0是业务网段192.168.10.0/24,网关192.168.10.1;eth1是存储网段10.10.10.0/24,网关10.10.10.1。你希望默认流量走业务网,存储流量只走存储网,并且存储网不能访问外部。配置如下:

network:
  version: 2
  ethernets:
    eth0:
      addresses:
        - 192.168.10.20/24
      routes:
        - to: default
          via: 192.168.10.1
          metric: 100
    eth1:
      addresses:
        - 10.10.10.20/24
      routes:
        - to: 10.10.10.0/24
          via: 10.10.10.1
          metric: 200

这里的关键不是把default写两遍,而是明确每个接口只负责自己的网段。存储网段的metric设高,避免它参与默认路由竞争。如果存储网还有更细的子网,比如10.10.20.0/24需要通过10.10.10.254中转,就再加一条:

        - to: 10.10.20.0/24
          via: 10.10.10.254
          metric: 200

配置完成后执行sudo netplan apply。注意netplan对缩进极其敏感,任何多余空格都会导致解析失败。建议先用sudo netplan try测试,确认无误再正式应用。

用策略路由防止源地址欺骗和路径回绕

仅靠静态路由无法解决所有问题。比如服务器上有多个IP,某些服务绑定在eth1的IP上,但回包时内核可能根据目标地址选择eth0发出,导致源地址与出口不匹配。这不仅是连通性问题,更可能被利用进行源地址欺骗。解决方法是添加策略路由规则,让来自特定源地址的流量强制从对应接口出去。

创建自定义路由表:

echo "100 storage" >> /etc/iproute2/rt_tables

然后添加规则:

ip rule add from 10.10.10.20 table storage
ip route add default via 10.10.10.1 dev eth1 table storage
ip route add 10.10.10.0/24 dev eth1 src 10.10.10.20 table storage

这样所有源地址为10.10.10.20的流量都会查storage表,从eth1出去,不会绕到eth0。要让规则重启后依然生效,不要把这些命令直接塞进rc.local,而是写成systemd服务或使用netplan的routing-policy字段。netplan从0.100版本开始支持routing-policy,配置如下:

    eth1:
      addresses:
        - 10.10.10.20/24
      routing-policy:
        - from: 10.10.10.20
          table: 100
      routes:
        - to: 10.10.10.0/24
          via: 10.10.10.1
          table: 100

这种方式比手动ip rule更可靠,且netplan apply后自动生效。

内核参数加固:关闭源路由和ICMP重定向

Ubuntu默认开启的某些内核参数会削弱静态路由的安全性。源路由(source routing)允许数据包指定自己的路径,攻击者可以利用它绕过你精心配置的路由表。ICMP重定向则允许对端设备动态修改你的路由表,这在受控网络中是多余的,在不受控网络中就是风险。执行以下命令立即生效:

sysctl -w net.ipv4.conf.all.accept_source_route=0
sysctl -w net.ipv4.conf.default.accept_source_route=0
sysctl -w net.ipv4.conf.all.accept_redirects=0
sysctl -w net.ipv4.conf.default.accept_redirects=0
sysctl -w net.ipv4.conf.all.secure_redirects=0
sysctl -w net.ipv4.conf.default.secure_redirects=0

永久生效需要写入/etc/sysctl.d/99-network-security.conf:

net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0

然后执行sudo sysctl -p /etc/sysctl.d/99-network-security.conf。另外建议开启rp_filter(反向路径过滤),它能丢弃源地址与路由表不匹配的数据包,有效防止IP欺骗。但注意在多网卡且存在非对称路由的环境下,rp_filter=1可能导致正常流量被丢弃。如果确实需要非对称路由,至少保持rp_filter=2(宽松模式),但更推荐通过策略路由消除非对称,然后启用严格模式。

接口绑定与ARP安全

静态路由的下一跳必须是可达的,而可达性依赖ARP解析。如果攻击者在同一二层网络伪造ARP响应,就能把流量引向错误的主机。Ubuntu可以通过arp_ignore和arp_announce参数限制ARP行为。对于多网卡服务器,建议设置:

net.ipv4.conf.all.arp_ignore = 1
net.ipv4.conf.all.arp_announce = 2
net.ipv4.conf.default.arp_ignore = 1
net.ipv4.conf.default.arp_announce = 2

arp_ignore=1表示只回答目标IP落在接收接口上的ARP请求,避免跨接口响应。arp_announce=2表示始终使用与目标IP同网段的源地址进行ARP通告,防止泄露其他接口的IP信息。这两个参数对多网卡环境的安全提升非常明显,而且几乎不影响正常通信。

用ip route的blackhole和unreachable做主动防御

静态路由不仅能转发流量,还能主动丢弃流量。如果某些网段明确不应该被访问,或者你发现来自特定网段的扫描行为,可以添加黑洞路由:

ip route add blackhole 172.16.0.0/12
ip route add unreachable 192.168.100.0/24

blackhole会静默丢弃数据包,unreachable会返回ICMP不可达。对于内部保留网段或已知恶意网段,用blackhole可以防止流量误入默认路由。在netplan中配置黑洞路由:

      routes:
        - to: 172.16.0.0/12
          type: blackhole

这种主动防御手段比防火墙规则更底层,不消耗连接跟踪资源,适合处理大范围网段的阻断。

验证配置是否真正生效

很多人配置完就认为安全了,但路由表可能被其他服务覆盖。比如NetworkManager或cloud-init在重启后重新生成netplan配置,导致你的手动修改丢失。Ubuntu Server通常使用systemd-networkd作为netplan的后端,但某些云镜像可能同时存在NetworkManager。检查方式:

systemctl is-active systemd-networkd
systemctl is-active NetworkManager

如果两者同时运行,必须禁用其中一个。对于服务器,建议只保留systemd-networkd。另外cloud-init可能在每次启动时重写网络配置,如果不需要,可以禁用cloud-init的网络模块:

echo "network: {config: disabled}" > /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg

验证路由是否按预期工作,用ip route get命令模拟实际数据包:

ip route get 10.10.10.50 from 10.10.10.20
ip route get 8.8.8.8 from 192.168.10.20

第一条应该显示走eth1,第二条走eth0。如果结果不符合预期,说明策略路由或静态路由配置有误。

日志与监控:让路由异常可发现

静态路由配置完成后,还需要监控路由表变化。可以写一个简单的systemd timer脚本,定期比对当前路由表与基线文件,发现差异就告警。例如:

#!/bin/bash
ip route show > /tmp/current_routes
diff /etc/network/route_baseline /tmp/current_routes || logger -t route-monitor "Route table changed"

将基线文件设置为只读,防止被篡改。同时开启martian日志,记录那些源地址或目标地址不合法的数据包:

sysctl -w net.ipv4.conf.all.log_martians=1
sysctl -w net.ipv4.conf.default.log_martians=1

这些日志会出现在/var/log/syslog中,配合logwatch或自定义脚本可以及时发现异常路由行为。

Ubuntu服务器的静态路由安全不是一次性配置,而是一个持续维护的过程。核心原则是:明确每个接口的职责、用策略路由绑定源地址与出口、关闭可能被利用的内核特性、主动阻断不应到达的网段,并建立监控机制。这样即使服务器暴露在复杂网络环境中,路由层也能成为第一道可靠的防线。