CentOS 5 上的基于源的策略路由和 NAT(DNAT/SNAT),又称多 WAN

CentOS 5 上的基于源的策略路由和 NAT(DNAT/SNAT),又称多 WAN

最初发布在 Unix 和 Linux 上,但没人能够回答,所以我将问题迁移到这里:

我的问题是关于在 CentOS 5 上基于源的策略路由,该路由有 2 个 WAN 和一个带负载平衡的 LAN(NAT)端口,但在开始描述问题之前,首先要说一些话...

我知道这个话题在 Stack Exchange 上已经被提出过很多次了,似乎排名前 5 位的答案是(从多到少排序):

  1. 禁用 rp_filter
  2. 基于Mark/Connmark的策略路由
  3. 基于 IP 的策略路由(添加更多 IP)
  4. 安装 pfSense、Shorewall、Ubuntu?等等...
  5. 购买昂贵的 Cisco/3com/Juniper/Etc... 路由器

大多数情况下,其中一些答案是正确的,但对我来说,解决方案 1 和 2 不起作用(我不会放弃至少第 2 点,因为我的设置可能存在一些问题),第 3 点基本上是隔离问题而不是解决问题(也增加了路由表的复杂性)并且解决方案 4 和 5 超出了范围,因为我没有资源购买专用硬件,也无法使服务器脱机,因为它正在生产中,所以总结一下,用“更好”的东西替换 CentOS 服务器是不可能的。

好的,现在回到问题上,让我们首先描述当前的设置......

接口

eth1: IP: 10.0.0.1, GW: 10.0.0.1, NM: 255.255.255.0 (LAN)
eth0: IP: 10.0.1.1, GW: 10.0.1.254, NM: 255.255.255.0 (ISP1 - ADSL Router)
eth2: IP: 10.0.2.1, GW: 10.0.2.254, NM: 255.255.255.0 (ISP2 - ADSL Router)

/etc/sysctl.conf

# Controls IP packet forwarding
net.ipv4.ip_forward = 1

# Controls source route verification
net.ipv4.conf.default.rp_filter = 0

# Do not accept source routing
net.ipv4.conf.default.accept_source_route = 0

# Controls the use of TCP syncookies
net.ipv4.tcp_syncookies = 1

# Ignoring broadcasts request
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_messages = 1

/etc/iproute2/rt_tables:

#
# reserved values
#
255     local
254     main
253     default
0       unspec
#
# local
#
#1      inr.ruhep
2 ISP1
3 ISP2

/etc/sysconfig/network-scripts/route-eth0

10.0.1.0/24 dev eth0 src 10.0.1.1 table ISP1
default via 10.0.1.254 dev eth0 table ISP1

/etc/sysconfig/network-scripts/route-eth2

10.0.2.0/24 dev eth2 src 10.0.2.1 table ISP2
default via 10.0.2.254 dev eth2 table ISP2

/etc/sysconfig/network-scripts/rule-eth0

fwmark 2 table ISP1
from 10.0.1.1 table ISP1

/etc/sysconfig/network-scripts/rule-eth2

fwmark 3 table ISP2
from 10.0.2.1 table ISP2

/etc/sysconfig/iptables

*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# Basic Rules
-A INPUT -i lo -j ACCEPT
-A INPUT -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i eth2 -m state --state RELATED,ESTABLISHED -j ACCEPT

# SSH
-A INPUT -i eth0 -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -i eth2 -m tcp -p tcp --dport 22 -j ACCEPT

# OpenVPN
-A INPUT -i eth0 -m udp -p udp --dport 1194 -j ACCEPT
-A INPUT -i eth2 -m udp -p udp --dport 1194 -j ACCEPT

# Allow everything from LAN
-A INPUT -i eth1 -j ACCEPT

# Allow everything from the VPN
-A INPUT -i tun0 -j ACCEPT

# Default Drop on everything else
-A INPUT -j DROP

# Allow forwarding from LAN and VPN
-A FORWARD -i eth1 -j ACCEPT
-A FORWARD -i tun0 -j ACCEPT

# Allow all outbound traffic
-A OUTPUT -o lo -j ACCEPT
-A OUTPUT -o eth1 -j ACCEPT
COMMIT

*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# DNAT to Developer Box (SSH Server)
-A PREROUTING -i eth0 -p tcp -m tcp --dport 2222 -j DNAT --to-destination 10.0.0.200:2222
-A PREROUTING -i eth2 -p tcp -m tcp --dport 2222 -j DNAT --to-destination 10.0.0.200:2222

# SNAT
-A POSTROUTING -o eth0 -j SNAT --to-source 10.0.1.1
-A POSTROUTING -o eth2 -j SNAT --to-source 10.0.2.1
COMMIT

*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# Mark Based Routing? (based on NerdBoys site)
-A PREROUTING -j CONNMARK --restore-mark
-A PREROUTING --match mark --mark 2 -j ACCEPT
-A PREROUTING -i eth0 -j MARK --set-mark 2
-A PREROUTING --match mark --mark 3 -j ACCEPT
-A PREROUTING -i eth2 -j MARK --set-mark 3
-A PREROUTING -j CONNMARK --save-mark
COMMIT

使用 gwping bash 脚本可以实现负载平衡,该脚本主要监控 2 个 wan(eth0 和 eth2)并在服务器中设置默认路由和权重,如下所示(在负载平衡或 2 个 wan 启动并运行时):

ip route replace default scope global nexthop via 10.0.1.1 dev eth0 weight 1 nexthop via 10.0.2.1 dev eth1 weight 1

我遇到的问题是,即使许多人都认为这个设置是正确的,我仍然无法从外部访问网络内部的服务(特别是 ssh 开发人员框和 OpenVPN),即使数据包被“标记”并相应地路由,来自开发框的答案总是会转到错误的路径。我不知道我是否遗漏了 mangle 或 nat 区域中的某些内容,或者我是否完全误解了基于源的路由,无论如何,如果有人知道如何使其正常工作,我将不胜感激。

此设置的来源如下:

lartc.org/lartc.html#LARTC.RPDB.MULTIPLE-LINKS
fatalsite.net/?p=90
nerdboys.com/2006/05/05/conning-the-mark-multiwan-connections-using-iptables-mark-connmark-and-iproute2/
policyrouting.org/PolicyRoutingBook/ONLINE/CH08.web.html
unix.stackexchange.com/questions/58635/iptables-set-mark-route-diferent-ports-through-different-interfaces
unix.stackexchange.com/questions/22770/two-interfaces-two-addresses-two-gateways
bulma.net/body.phtml?nIdNoticia=2145

最亲切的问候

PS1:我发现一个网站,上面说路由表中的标记应该与 iptables 标记相差 +1 (kim.attr.ee/2010/08/source-based-policy-routing-on-centos.html) 这是真的吗?或者这个网站非常不正确。


更新 15/08/2013 22:15

经过进一步的研究和调试后,我发现一个网站说我忘记在后路由表中添加 SNAT 部分,因此我将以下规则添加到 iptables 配置中:

-A POSTROUTING --match mark --mark 2 -j SNAT --to-source 10.0.1.1
-A POSTROUTING --match mark --mark 3 -j SNAT --to-source 10.0.2.1

但我仍然无法从网络外部连接到 devbox。好的一面是iptables -t nat -nvL POSTROUTING给出了关于基于 connmark 的策略路由的工作原理的提示,因此也许它与 ISP1 和 ISP2 路由器边缘有关:

Chain POSTROUTING (policy ACCEPT 520 packets, 56738 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 SNAT       all  --  *      *       0.0.0.0/0            0.0.0.0/0           MARK match 0x2 to:10.0.1.1
    6   312 SNAT       all  --  *      *       0.0.0.0/0            0.0.0.0/0           MARK match 0x3 to:10.0.2.1
  903 70490 SNAT       all  --  *      eth0    0.0.0.0/0            0.0.0.0/0           to:10.0.1.1
  931 78070 SNAT       all  --  *      eth2    0.0.0.0/0            0.0.0.0/0           to:10.0.2.1

另外,我从我的设置中添加了更多信息,请有人给我一个救生圈,因为我开始没有主意了……>.<

ip 路由显示

10.8.0.2 dev tun0  proto kernel  scope link  src 10.8.0.1
10.0.2.0/24 dev eth2  proto kernel  scope link  src 10.0.2.1
10.0.0.0/24 dev eth1  proto kernel  scope link  src 10.0.0.1
10.8.0.0/24 via 10.8.0.2 dev tun0
10.0.1.0/24 dev eth0  proto kernel  scope link  src 10.0.1.1
169.254.0.0/16 dev eth2  scope link
default
        nexthop via 10.0.1.254  dev eth0 weight 1
        nexthop via 10.0.2.254  dev eth2 weight 1

ip 规则显示:

0:      from all lookup 255
1024:   from all fwmark 0x2 lookup ISP1
1025:   from all fwmark 0x3 lookup ISP2
2024:   from 10.0.1.1 lookup ISP1
2025:   from 10.0.2.1 lookup ISP2
32766:  from all lookup main
32767:  from all lookup default

新来源:

sarcasmasaservice.com/2013/04/linux-routing-capabilities-my-abuse-thereof/

最亲切的问候

答案1

出色地...

经过数千小时的调试、尝试不同的设置以及在生产中 72 小时的大量测试,我终于找到了正确的解决方案/设置,问题出在 iptables 规则(mangle 部分)中,数据包在进入时显然被正确标记,但是当它们出来时,没有任何 dnat 数据包,无论如何,这是我的问题的最终解决方案:

/etc/sysconfig/iptables

*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# Basic Rules
-A INPUT -i lo -j ACCEPT
-A INPUT -i eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i eth2 -m state --state RELATED,ESTABLISHED -j ACCEPT

# SSH
-A INPUT -i eth0 -m tcp -p tcp --dport 22 -j ACCEPT
-A INPUT -i eth2 -m tcp -p tcp --dport 22 -j ACCEPT

# OpenVPN
-A INPUT -i eth0 -m udp -p udp --dport 1194 -j ACCEPT
-A INPUT -i eth2 -m udp -p udp --dport 1194 -j ACCEPT

# Allow everything from LAN
-A INPUT -i eth1 -j ACCEPT

# Allow everything from the VPN
-A INPUT -i tun0 -j ACCEPT

# Default Drop on everything else
-A INPUT -j DROP

# Allow forwarding from LAN and VPN
-A FORWARD -i eth1 -j ACCEPT
-A FORWARD -i tun0 -j ACCEPT

# Allow all outbound traffic
-A OUTPUT -o lo -j ACCEPT
-A OUTPUT -o eth1 -j ACCEPT
COMMIT

*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# DNAT to Developer Box (SSH Server)
-A PREROUTING -i eth0 -p tcp -m tcp --dport 2222 -j DNAT --to-destination 10.0.0.200:2222
-A PREROUTING -i eth2 -p tcp -m tcp --dport 2222 -j DNAT --to-destination 10.0.0.200:2222

# SNAT
-A POSTROUTING -o eth0 -j SNAT --to-source 10.0.1.1
-A POSTROUTING -o eth2 -j SNAT --to-source 10.0.2.1
COMMIT

*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# CONNMARK Source Based Routing
-A PREROUTING -i eth0 -m state --state NEW,RELATED,ESTABLISHED -d 10.0.1.1 -j CONNMARK --set-mark 0x2
-A PREROUTING -i eth2 -m state --state NEW,RELATED,ESTABLISHED -d 10.0.2.1 -j CONNMARK --set-mark 0x3
-A PREROUTING -i eth1 -m connmark --mark 0x2 -j CONNMARK --restore-mark
-A PREROUTING -i eth1 -m connmark --mark 0x3 -j CONNMARK --restore-mark
-A OUTPUT -m state --state ESTABLISHED,RELATED -j CONNMARK --restore-mark
COMMIT

显然,加上之前与 iproute 和 gwping(用于链路负载平衡和故障转移)相关的所有设置,解决方案之所以能够实现,要感谢来源 [1] 和 [2],这两个来源都为我指明了解决方案的不同部分(Luca Gibelli 为 PREROUTING 部分,Karl Bowden 为 OUTPUT 部分),此外,我还在这里找到了其他网站的更多来源,它们为我指明了正确的查找方向。希望这能在未来帮助其他系统管理员。

最亲切的问候

资料来源:

[1]www.nervous.it/2010/09/dnat-and-ip-source-routing-woes/
[2]blog.khax.net/2009/12/01/multi-gateway-balancing-with-iptables/
[3]home.regit.org/netfilter-en/links-load-balancing/
[4]mailman.ds9a.nl/pipermail/lartc/2006q2/018964.html
[5]web.archive.org/web/20120320115329/http://versa.net.au/index.php?option=com_content&task=view&id=21&Itemid=34

更新日期:2013 年 10 月 10 日

OpenVPN 需要额外的配置指令才能与多个 wan 设置一起使用(例如前一个),因此只需在您的 server.conf 中添加多宿主选项(OpenVPN >= 2.1,对于较低版本,只需将本地指令更改为仅在特定 ip 中监听)就可以了。

答案2

给你一个提示,我做了同样的事情,只是没有负载平衡和 3 个 wan 链接,我甚至没有使用 iptables。我发现使用简单的基于策略的路由和 socat 更有效

我的rt_表:

100     lan
102     wireless
103     wan1
104     wan2
105     wan3

在每个 WAN 链路的接口中:

/bin/ip route add <wan network id> dev eth0 src <wan host ip> table wan1
/bin/ip route add default via <wan gateway address> table wan1
/bin/ip rule add from <wan host ip> table wan1

在每个 WAN 链路的接口中:

/bin/ip route del default via <wan gateway address> table wan1
/bin/ip rule del from <wan host ip> table wan1

强制局域网客户端断开特定链接:将其放入您的接口启动脚本中:

/bin/ip rule add from <lan client ip> table <wan table number of link to force it through>

下面是下面的脚本:

/bin/ip rule del from <lan client ip> table <wan table number of link to force it through>

要将传入连接发送到特定计算机(例如 Web 服务器),请将类似这样的内容添加到 rc.local(无论链接是否打开都可以运行)

exec socat -T15 tcp4-listen:80,reuseaddr,fork tcp:<lan host ip to send it to>:80 >> /var/log/socat-web.log 2>&1

然后打开 IP 转发和伪装以及您需要的任何其他防火墙规则,一切就绪

在 ubuntu 上你甚至可以为 socat 创建一个 upstart 作业这是我的 /etc/init/socat-web.conf:

description "socat web port tunnel"
author      "jacqueline"

start on started mountall
stop on shutdown
respawn
respawn limit 99 5

script
    export HOME="/root"
    exec socat -T15 tcp4-listen:80,reuseaddr,fork tcp:192.168.0.97:80 >> /var/log/socat-web.log 2>&1
end script

post-start script
   # Optionally put a script here that will notifiy you socat has (re)started
end script

答案3

@CentOS_noob

首先,干得好,非常感谢你的分享,非常感谢!我为此奋斗了好几年。:)

如果您想要通过选定的链接直接访问指定的服务,您可以在当前标记规则前面这样做。

iptables -t mangle -A PREROUTING -i br1 -s 10.1.1.2 -p tcp --dport 80 -m state --state NEW,RELATED,ESTABLISHED -j CONNMARK --set-mark 0x3
iptables -t mangle -A PREROUTING -i br1 -s 10.1.1.2 -p icmp  -j CONNMARK --set-mark 0x4

在哪里:

br1 - is LAN interface
10.1.1.2 - is LAN's IP of some host.

相关内容