iptables 實戰 - DNAT、SNAT 和負載均衡

前言

大家好,這裏是浩道 linux,主要給大家分享 linuxpython網絡通信相關的 IT 知識平臺。

今天浩道跟大家分享關於 iptables 相關的硬核乾貨,DNAT、SNAT 和負載均衡相關的實戰

 文章來源:https://blog.csdn.net/syaziou/article/details/123443578

本文主要涉及了三個實驗:利用 iptabels 實現 SNAT、DNAT 和出向負載均衡。

一、路由轉發與 SNAT 實驗

環境說明:

debian 機器位於內網,有一個網卡 ens38,ip 地址 172.16.1.2/24,網關爲 172.16.1.1(router 的 eth2)

router 機器位於內網和外網的邊界,有 2 個網卡 eth1 和 eth2,eth1 地址 192.168.124.247 接外網,網關 192.168.124.1;eth2 地址 172.16.1.1,連接 debian

在網卡配置正確的情況下,此時使用 debian 機器是可以直接 ping 通 172.16.1.1,但是無法 ping 通任何外網地址。但是 debian 機器可以 ping 通 router 上 eth1 的 ip192.168.124.247。部分同學可能對這一現象不太理解,原因簡單陳述一下:在 linux 系統中,ip 地址是屬於內核而非網卡的。在這個案例中,debian ping 192.168.124.247 時,發現該 ip 和自己不是同一網段,因此將數據包發往 172.16.1.1, 此時數據包的 dst ip 爲 192.168.124.247,dst mac 爲 eth2 的 mac。router 解開 mac 發現是自己的 mac(eth2 的 mac),繼續解包,再解開 ip 發現也是自己的 (eth1 的 ip),所以直接回包,此時兩者可以通信。

debian 機器無法 ping 通任何外網地址的原因是,數據包到網關 router 後,router 沒有打開 ip_forword 參數,因此會直接丟棄目的地址不屬於自己的包。因此第一步需要打開該參數。在 router 上打開路由轉發功能:

echo 1 > /proc/sys/net/ipv4/ip_forward

此時 debian 的數據包經過 router 時會被路由轉發,但是仍然無法和外界通信。原因是外界沒有返回 172.16.1.2 的路由。此時的解決方案是在 router 上增加一條 snat 規則。這樣 debian 機器出網時被 snat 成 router 的 eth1 上的 ip,外網回包時先回給 router 機器,router 機器根據原來的 snat 映射修改回包的目的地址,再發送給 debian 機器。

iptables -t nat -A POSTROUTING -s 172.16.1.0/24 -o eth0 -j SNAT --to-source 192.168.124.247

此時 debian 即可和外界通信。

二、DNAT 映射實驗

環境說明

拓撲如前,現在有一個網絡僅能和 router(192.168.124.247) 通信,無法和 debian(172.16.1.2) 通信。但是該網絡下的用戶想要連接位於內網的 debian 機器, 處於安全考慮不能讓用戶 ssh 到 router 後再跳到 debian,想在 router 上開一個端口 32200,映射到 debian 機器的 22 端口

思路與實現:

數據包到達 router 機器之後,不能直接流向用戶空間。因爲用戶空間並沒有程序監聽 32200。因此映射的變化應該是在路由決策之前,所以應該配置在 PREROUTING 鏈上。在 PREROUTING 鏈上增加一條 dnat 規則,使得 ssh 192.168.124.247 的 32200 端口時,把目的地址改爲 172.16.1.2,端口改爲 22。

 iptables -t nat -A PREROUTING -d 192.168.124.247 -p tcp --destination-port 32200 -i eth0 -j DNAT --to-destination 172.16.1.2:22

三、出向負載均衡和 nat 實驗

環境說明

debian 機器上配置了 4 個 ip 地址,需要實現 4 個 ip 輪詢訪問外部網絡。

爲了方便驗證,我搭建了一個 nginx。nginx 幾行配置即可返回客戶端的 ip,配置如下:

      location / {
      return 200 <span data-raw-text="" "="" data-textnode-index-1671119076312="26" data-index-1671119076312="1621" data-textnode-notemoji-index-1671119076312="1621" class="character">"$remote_addr\n<span data-raw-text="" "="" data-textnode-index-1671119076312="28" data-index-1671119076312="1636" data-textnode-notemoji-index-1671119076312="1636" class="character">";
      }

我們只需要多次請求 nginx,只要 nginx 輪詢返回不同的 ip,就證明我們實現了這樣的效果。

debian 機器的網卡配置如下:

auto ens38
iface ens38 inet static
address 172.16.1.2
netmask 255.255.255.0
gateway 172.16.1.1
auto ens38:1
iface ens38:1 inet static
address 172.16.1.3
netmask 255.255.255.0
auto ens38:2
iface ens38:2 inet static
address 172.16.1.4
netmask 255.255.255.0
auto ens38:3
iface ens38:3 inet static
address 172.16.1.5
netmask 255.255.255.0

思路與實現

通常來說,機器和外界通信時,會默認使用第一個網卡主 ip,想要修改這一行爲,可以在 POSTROUTING 鏈中修改源 ip。iptables 有一個 statistic 擴展,可以實現負載均衡的效果。

statistic 擴展:

statistic
This module matches packets based on some statistic condition. It supports two distinct modes settable with the --mode option.
Supported options:
該模塊根據一些統計條件匹配報文。 它支持兩個不同的模式,可通過--mode選項設置。  

--mode mode
Set the matching mode of the matching rule, supported modes are random and nth.
設置匹配規則的匹配模式,支持的匹配模式爲random和nth。  
[!] --probability p
Set the probability for a packet to be randomly matched. It only works with the random mode. p must be within 0.0 and 1.0. The supported granularity is in 1/2147483648th increments.
設置報文被隨機匹配的概率。 它只適用於隨機模式。P必須在0.0和1.0之間。 支持的粒度增量爲1/2147483648。  
[!] --every n
Match one packet every nth packet. It works only with the nth mode (see also the --packet option).
每n個包匹配一個包。 它只適用於nth個模式(同時請參閱--packet選項)。  
--packet p
Set the initial counter value (0 <= p <= n-1, default 0) for the nth mode.
設置初始計數器。計數器=p的時候,命中規則。0 <= p <= n-1,p=n-1之後,下一次會被重置爲0。如果不設置則默認是0。只適用於nth模式

以上翻譯自官方文檔 https://ipset.netfilter.org/iptables-extensions.man.html

所以進行如下設置。

iptables -A POSTROUTING  -t nat -d 192.168.127.247 -m statistic --mode nth --every 4 --packet 0 -j SNAT --to-source 172.16.1.2
iptables -A POSTROUTING  -t nat -d 192.168.127.247 -m statistic --mode nth --every 3 --packet 0 -j SNAT --to-source 172.16.1.3
iptables -A POSTROUTING  -t nat -d 192.168.127.247 -m statistic --mode nth --every 2 --packet 0 -j SNAT --to-source 172.16.1.4
iptables -A POSTROUTING  -t nat -d 192.168.127.247 -m statistic --mode nth --every 1 --packet 0 -j SNAT --to-source 172.16.1.5
計數器變化:
第一個tcp會話:0 ,第一條命中,對應計數器+1,被snat成172.16.1.2
第二個tcp會話:10,第二條命中,對應計數器+1, 被snat成172.16.1.3
第三個tcp會話:210,第三條命中,對應計數器清+1, 被snat成172.16.1.4
第四個tcp會話:3210,第四條命中,對應計數器+1,同時所有計數器都打到every上限,所有計數器清0, 被snat成172.16.1.2
。。。。進入下一個循環,以此類推

nat 表規則:

root@debian:~# iptables -nL -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
SNAT       all  --  0.0.0.0/0            192.168.124.247      statistic mode nth every 4 to:172.16.1.2
SNAT       all  --  0.0.0.0/0            192.168.124.247      statistic mode nth every 3 to:172.16.1.3
SNAT       all  --  0.0.0.0/0            192.168.124.247      statistic mode nth every 2 to:172.16.1.4
SNAT       all  --  0.0.0.0/0            192.168.124.247      statistic mode nth every 1 to:172.16.1.5

如此即可實現出向負載均衡的效果:

root@debian:~# curl 192.168.124.247
172.16.1.2
root@debian:~# curl 192.168.124.247
172.16.1.3
root@debian:~# curl 192.168.124.247
172.16.1.4
root@debian:~# curl 192.168.124.247
172.16.1.5
root@debian:~# curl 192.168.124.247
172.16.1.2
root@debian:~# curl 192.168.124.247
172.16.1.3
root@debian:~# curl 192.168.124.247
172.16.1.4
root@debian:~# curl 192.168.124.247
172.16.1.5

更多精彩

關注公衆號 「浩道 linux」

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/VfqUWKpnJsBkDIlTdZmJTA