在學(xué)習(xí)docker和k8s時(shí)大量的用到了iptables况脆,那么iptables到底怎么玩饭宾,這篇文章告訴你答案。提起iptables/netfilter格了,我們應(yīng)該聽說過但在日常的工作中用到的機(jī)會(huì)并不多看铆,那平時(shí)用不到是不是就不需要了解了呢,那肯定不是的盛末,做技術(shù)嘛肯定是要有所追求的弹惦,當(dāng)你越過每座山峰之后,你就能看到遠(yuǎn)處美好的風(fēng)景[圖片上傳失敗...(image-fa5084-1591493797514)]
那么iptables到底是個(gè)什么東西呢悄但?說白了iptables就是一個(gè)用戶態(tài)的命令行工具來操作內(nèi)核態(tài)的netfilter棠隐,對(duì)進(jìn)入本機(jī)的網(wǎng)絡(luò)包進(jìn)行一系列操作,比如 accept檐嚣、reject助泽、drop等等。下圖說明了iptables的數(shù)據(jù)流向嚎京。
傳說中的iptables由四表五鏈構(gòu)成嗡贺,那就看看這是個(gè)什么東西吧~
四表
raw表:主要用來決定是否對(duì)數(shù)據(jù)包進(jìn)行狀態(tài)跟蹤(用的不多)。
mangle表:修改數(shù)據(jù)包的服務(wù)類型鞍帝、TTL诫睬、并且可以配置路由實(shí)現(xiàn)QOS內(nèi)核模塊(用的不多)。
nat表:主要用來做網(wǎng)絡(luò)地址轉(zhuǎn)換SNAT帕涌、DNAT摄凡。
filter表:主要對(duì)數(shù)據(jù)包進(jìn)行過濾,比如禁止某個(gè)ip數(shù)據(jù)包訪問蚓曼。
五鏈
PREROUTING鏈:在對(duì)數(shù)據(jù)包進(jìn)行路由之前經(jīng)過此鏈的處理亲澡,比如常見的做DNAT轉(zhuǎn)換。
INPUT鏈:收到訪問本機(jī)ip地址時(shí)經(jīng)過此鏈處理辟躏,比如常見的在filter表上設(shè)置input鏈谷扣,拒絕或只允許某個(gè)ip訪問。
FORWARD鏈:轉(zhuǎn)發(fā)數(shù)據(jù)包時(shí)應(yīng)用此鏈上的規(guī)則捎琐。
OUTPUT鏈:數(shù)據(jù)流出方向應(yīng)用此鏈上的規(guī)則会涎。
POSTROUTING鏈:對(duì)數(shù)據(jù)路由后應(yīng)用此鏈上的規(guī)則,比如常見的做SNAT轉(zhuǎn)換瑞凑。
由上圖可知末秃,當(dāng)一個(gè)數(shù)據(jù)包進(jìn)入宿主機(jī)的某張網(wǎng)卡時(shí),在四張表中的順序如下:raw -> manager -> nat -> filter 籽御。
數(shù)據(jù)如何在五條鏈中流轉(zhuǎn)练慕,根據(jù)目標(biāo)ip是否是本機(jī)某張網(wǎng)卡的ip來判斷惰匙。①:是本機(jī)數(shù)據(jù)包:prerouting -> input -> 本機(jī)進(jìn)程處理數(shù)據(jù) -> output -> postrouting 發(fā)送到網(wǎng)絡(luò)中。
②:不是本機(jī)數(shù)據(jù)包:prerouting -> forward -> postrouting 發(fā)送到網(wǎng)絡(luò)中铃将。每條鏈中可以添加多條規(guī)則项鬼,執(zhí)行時(shí)按照順序執(zhí)行,匹配到相應(yīng)的規(guī)則后執(zhí)行相應(yīng)的動(dòng)作劲阎,由于是順序執(zhí)行當(dāng)鏈中的規(guī)則數(shù)量過多時(shí)绘盟,性能會(huì)有明顯的損耗。
簡單介紹了iptables的四表五鏈后悯仙,是時(shí)候具體來操作一波iptables看看效果了龄毡。iptables基本語法
iptables [-t 表名] 命令選項(xiàng) [鏈名] [條件匹配] [-j 目標(biāo)動(dòng)作或跳轉(zhuǎn)]-t 表名 不寫的話默認(rèn)是filter表 命令選項(xiàng)
-A 在指定鏈的末尾添加一條規(guī)則
-D 刪除指定鏈的第幾條規(guī)則,后面加上具體數(shù)字
-I 默認(rèn)在鏈的頭部插入一條規(guī)則锡垄,也可以指定插入第幾條前面(-I chain num)
-F 刪除鏈中所有的規(guī)則
-L 列出現(xiàn)有鏈中的全部規(guī)則
通用參數(shù)
-p 協(xié)議沦零,tcp、udp货岭、icmp等
-s 源地址
-d 目的地址
--sport 源地址端口
--dport 目的地址端口
-i 指定入口網(wǎng)卡
-o 指定出口網(wǎng)卡
處理動(dòng)作
ACCEPT 允許數(shù)據(jù)包通過
DROP 丟棄數(shù)據(jù)包路操,數(shù)據(jù)發(fā)送方無法接受響應(yīng),直到過了超時(shí)時(shí)間
REJECT 拒絕此數(shù)據(jù)包茴她,數(shù)據(jù)發(fā)送方能感知到被拒絕
SNAT 做源地址轉(zhuǎn)換寻拂,如內(nèi)網(wǎng)下訪問外網(wǎng),把源地址ip修改成路由器的公網(wǎng)ip
DNAT 做目標(biāo)地址轉(zhuǎn)換
MASQUERADE SNAT的另一種模式丈牢,適用于ip地址不固定的情況下
iptables 的語法大致就是以上內(nèi)容,接下來搞幾個(gè)簡單的例子來測(cè)試一下瞄沙,我在本地搭了3臺(tái)虛擬機(jī)己沛,ip 地址分別為 192.168.113.99、192.168.113.100距境、192.168.113.101申尼。所有的iptables規(guī)則都在192.168.113.101這臺(tái)機(jī)器上設(shè)置。
只允許192.168.113.100ping操作
iptables -t filter -A INPUT -p icmp ! 192.168.113.100 -j REJECT
這里在filter 表的INPUT鏈上添加了一條記錄垫桂, 非 192.168.113.100 ip的ping 包直接拒絕掉师幕,只有 192.168.113.100 可以ping通。
發(fā)往192.168.113.100 的imcp包全部丟棄
iptables -t filter -A OUTPUT -p icmp -d 192.168.113.100 -j DROP
這里在filter 表的OUTPUT鏈上添加了一條記錄诬滩,丟棄了192.168.113.100 ping的響應(yīng)包霹粥。只允許特定的端口放開
iptables -t filter -A INPUT -p tcp -m multiport --dport 22,80,8080 -j ACCEPT
查看filter 表上的具體規(guī)則
iptables -t filter -nvL
如圖所示,操作filter 表的INPUT鏈疼鸟,開放了9000:10000之間的tcp端口后控,同時(shí)也開放了22、80空镜、8080端口浩淘,過input鏈時(shí)會(huì)從上往下順序執(zhí)行捌朴,當(dāng)匹配到在這個(gè)范圍內(nèi)的端口時(shí),把數(shù)據(jù)包傳給用戶態(tài)進(jìn)程张抄。最終沒有匹配砂蔽,會(huì)執(zhí)行最后一條DROP掉。注意:如果第三條規(guī)則放到最前面署惯,那就跪了察皇,那ssh客戶端直接斷開連接了,就表示禁止訪問所有tcp端口了泽台。
接下來操作nat表什荣,做nat地址的相關(guān)轉(zhuǎn)換。當(dāng)然怀酷,想要開啟forward數(shù)據(jù)轉(zhuǎn)發(fā)的話稻爬,需要如下操作,linux內(nèi)核默認(rèn)是關(guān)閉forward轉(zhuǎn)發(fā)的蜕依。
echo 1 > /proc/sys/net/ipv4/ip_forward
把192.168.113.101:12000轉(zhuǎn)發(fā)到192.168.113.100:8080
iptables -t nat -I PREROUTING -p tcp --dport 12000 -j DNAT --to-destination 192.168.113.100:8080
首先-t 操作nat表桅锄,在PREROUTING鏈上針對(duì)tcp協(xié)議,當(dāng)發(fā)現(xiàn)訪問本機(jī)是12000端口時(shí)样眠,做DNAT協(xié)議轉(zhuǎn)換友瘤,--to-destination 也可以簡寫成--to ,修改dst ip地址為 192.168.113.100檐束。最后在POSTROUTING做SNAT轉(zhuǎn)換辫秧,判斷發(fā)往目標(biāo)ip : port 為192.168.113.100 : 8080,修改源 ip 為192.168.113.101被丧。
// SNAT 也可以這樣寫盟戏,適用于ip不固定的情況下
iptables -t nat -I POSTROUTING -p tcp -d 192.168.113.100 --dport 8080 -j MASQUERADE
iptables lb負(fù)載均衡
k8s里面service的實(shí)現(xiàn)有3種方式,當(dāng)閱讀官方文檔發(fā)現(xiàn)有一種是根據(jù)iptables實(shí)現(xiàn)的甥桂,當(dāng)集群中的pod數(shù)量比較多時(shí)柿究,基于iptables做流量轉(zhuǎn)發(fā)和lb策略性能會(huì)有所損耗(鏈表形式)
)。所以后續(xù)k8s默認(rèn)使用ipvs實(shí)現(xiàn)了黄选。之后再寫k8sservice的實(shí)現(xiàn)時(shí)會(huì)說明ipvs的實(shí)現(xiàn)蝇摸。接下來看看iptables如何做lb
分別在192.168.113.99、192.168.113.100 使用docker 啟動(dòng)兩個(gè)nginx容器办陷,占用虛擬機(jī)的8080端口貌夕,在192.168.101上設(shè)置iptables規(guī)則,如下圖所示:
docker run -it --rm -p 8080:80 nginx
iptables兩種負(fù)載均衡策略
隨機(jī):random
// DNAT 轉(zhuǎn)換
iptables -A PREROUTING -t nat -p tcp --dport 12000 -m statistic --mode random --probability 0.5 -j DNAT --to-destination 192.168.113.100:8080
iptables -A PREROUTING -t nat -p tcp --dport 12000 -j DNAT --to-destination 192.168.113.99:8080
// SNAT 轉(zhuǎn)換
iptables -t nat -I POSTROUTING -p tcp -d 192.168.113.100 --dport 8080 -j SNAT --to-source 192.168.113.101
iptables -t nat -I POSTROUTING -p tcp -d 192.168.113.99 --dport 8080 -j SNAT --to-source 192.168.113.101
由于有兩個(gè)后端服務(wù)懂诗,做DNAT時(shí)第一個(gè)ip --probability 0.5 指定了50%的命中概率蜂嗽,沒有命中第一條的概率也是50%,當(dāng)沒有命中第一條時(shí)就走到了第二條殃恒,所以兩個(gè)服務(wù)被訪問的概率都是50%植旧。
輪訓(xùn):nth
// DNAT 轉(zhuǎn)換
iptables -A PREROUTING -t nat -p tcp --dport 12000 -m statistic --mode nth --every 2 --packet 0 -j DNAT --to-destination 192.168.113.100:8080
iptables -A PREROUTING -t nat -p tcp --dport 12000 -j DNAT --to-destination 192.168.113.99:8080
// SNAT 轉(zhuǎn)換
iptables -t nat -I POSTROUTING -p tcp -d 192.168.113.100 --dport 8080 -j SNAT --to-source 192.168.113.101
iptables -t nat -I POSTROUTING -p tcp -d 192.168.113.99 --dport 8080 -j SNAT --to-source 192.168.113.101
輪訓(xùn)算法中有兩個(gè)參數(shù) :n 指每n個(gè)包辱揭,p 指第p個(gè)包。第一條規(guī)則指每2個(gè)包病附,第一個(gè)包走第一條規(guī)則问窃,那第二個(gè)包就走第二條規(guī)則了。
總結(jié):基于iptables的這兩種方式完沪,很容易實(shí)現(xiàn)四層網(wǎng)絡(luò)代理轉(zhuǎn)發(fā)域庇,但是基于iptables做四層轉(zhuǎn)發(fā)是單點(diǎn)的,掛了就GG了覆积,所以業(yè)界最主流的玩法是lvs+keepalived做四層代理轉(zhuǎn)發(fā)听皿。而且lvs的性能比iptables更高。
前面我們對(duì)iptables做的操作都是直接在默認(rèn)的5個(gè)鏈上操作的宽档,一臺(tái)服務(wù)器上可能有很多個(gè)iptables規(guī)則尉姨,如果全部放一塊修改一處可能會(huì)對(duì)其他的有影響,而iptables支持自定義鏈吗冤,這時(shí)候我們把相同類型的規(guī)則全部放到同一個(gè)鏈中又厉,便于維護(hù)。k8s和docker中就定義了好幾個(gè)鏈椎瘟。
// 創(chuàng)建一個(gè)mysql的自定義鏈
iptables -t filter -N MYSQL
// mysql 的鏈中只允許 192.168.113.99 的ip訪問覆致,其余全部reject掉
iptables -t filter -I MYSQL -s 192.168.113.99 -j ACCEPT
iptables -t filter -A MYSQL -j REJECT
// 將mysql的鏈添加到filter表的INPUT鏈中
iptables -t filter -I INPUT -p tcp --dport 3306 -j MYSQL
自定義鏈規(guī)則如下:
參考文檔: