在本博客中,從理論到實踐,系統的介紹了iptables,如果你想要從頭開始了解iptables,可以查看iptables文章列表,直達鏈接如下
[iptables零基礎快速入門系列](http://www.zsythink.net/archives/tag/iptables/)
前文已經總結了iptables中的基本匹配條件,以及簡單的擴展匹配條件,此處,我們來認識一些新的擴展模塊。
?
?
## iprange擴展模塊
之前我們已經總結過,在不使用任何擴展模塊的情況下,使用-s選項或者-d選項即可匹配報文的源地址與目標地址,而且在指定IP地址時,可以同時指定多個IP地址,每個IP用"逗號"隔開,但是,-s選項與-d選項并不能一次性的指定一段連續的IP地址范圍,如果我們需要指定一段連續的IP地址范圍,可以使用iprange擴展模塊。
使用iprange擴展模塊可以指定"一段連續的IP地址范圍",用于匹配報文的源地址或者目標地址。
iprange擴展模塊中有兩個擴展匹配條件可以使用
\--src-range
\--dst-range
沒錯,見名知意,上述兩個選項分別用于匹配報文的源地址所在范圍與目標地址所在范圍。
示例如下:

上例表示如果報文的源IP地址如果在192.168.1.127到192.168.1.146之間,則丟棄報文,IP段的始末IP使用"橫杠"連接,--src-range與--dst-range和其他匹配條件一樣,能夠使用"!"取反,有了前文中的知識作為基礎,此處就不再贅述了。
?
?
## string擴展模塊
使用string擴展模塊,可以指定要匹配的字符串,如果報文中包含對應的字符串,則符合匹配條件。
比如,如果報文中包含字符"OOXX",我們就丟棄當前報文。
首先,我們在IP為146的主機上啟動http服務,然后在默認的頁面目錄中添加兩個頁面,頁面中的內容分別為"OOXX"和"Hello World",如下圖所示,在沒有配置任何規則時,126主機可以正常訪問146主機上的這兩個頁面。

那么,我們想要達到的目的是,如果報文中包含"OOXX"字符,我們就拒絕報文進入本機,所以,我們可以在126上進行如下配置。

上圖中,'-m string'表示使用string模塊,'--algo bm'表示使用bm算法去匹配指定的字符串,' --string "OOXX" '則表示我們想要匹配的字符串為"OOXX"
設置完上圖中的規則后,由于index.html中包含"OOXX"字符串,所以,146的回應報文無法通過126的INPUT鏈,所以無法獲取到頁面對應的內容。
那么,我們來總結一下string模塊的常用選項
\--algo:用于指定匹配算法,可選的算法有bm與kmp,此選項為必須選項,我們不用糾結于選擇哪個算法,但是我們必須指定一個。
\--string:用于指定需要匹配的字符串。
?
?
## time擴展模塊
我們可以通過time擴展模塊,根據時間段區匹配報文,如果報文到達的時間在指定的時間范圍以內,則符合匹配條件。
比如,"我想要自我約束,每天早上9點到下午6點不能看網頁",擦,多么殘忍的規定,如果你想要這樣定義,可以嘗試使用如下規則。

上圖中"-m time"表示使用time擴展模塊,--timestart選項用于指定起始時間,--timestop選項用于指定結束時間。
如果你想要換一種約束方法,只有周六日不能看網頁,那么可以使用如下規則。

沒錯,如你所見,使用--weekdays選項可以指定每個星期的具體哪一天,可以同時指定多個,用逗號隔開,除了能夠數字表示"星期幾",還能用縮寫表示,例如:Mon, Tue, Wed, Thu, Fri, Sat, Sun
當然,你也可以將上述幾個選項結合起來使用,比如指定只有周六日的早上9點到下午6點不能瀏覽網頁。

聰明如你一定想到了,既然有--weekdays選項了,那么有沒有--monthdays選項呢?必須有啊!
使用--monthdays選項可以具體指定的每個月的哪一天,比如,如下圖設置表示指明每月的22號,23號。

前文已經總結過,當一條規則中同時存在多個條件時,多個條件之間默認存在"與"的關系,所以,下圖中的設置表示匹配的時間必須為星期5,并且這個"星期5"同時還需要是每個月的22號到28號之間的一天,所以,下圖中的設置表示每個月的第4個星期5

除了使用--weekdays選項與--monthdays選項,還可以使用--datestart 選項與-datestop選項,指定具體的日期范圍,如下。

上圖中指定的日期范圍為2017年12月24日到2017年12月27日
上述選項中,--monthdays與--weekdays可以使用"!"取反,其他選項不能取反。
?
?
## connlimit擴展模塊
使用connlimit擴展模塊,可以限制每個IP地址同時鏈接到server端的鏈接數量,注意:我們不用指定IP,其默認就是針對"每個客戶端IP",即對單IP的并發連接數限制。
比如,我們想要限制,每個IP地址最多只能占用兩個ssh鏈接遠程到server端,我們則可以進行如下限制。

上例中,使用"-m connlimit"指定使用connlimit擴展,使用"--connlimit-above 2"表示限制每個IP的鏈接數量上限為2,再配合-p tcp --dport 22,即表示限制每個客戶端IP的ssh并發鏈接數量不能高于2。
centos6中,我們可以對--connlimit-above選項進行取反,沒錯,老規矩,使用"!"對此條件進行取反,示例如下

上例表示,每個客戶端IP的ssh鏈接數量只要不超過兩個,則允許鏈接。
但是聰明如你一定想到了,上例的規則并不能表示:每個客戶端IP的ssh鏈接數量超過兩個則拒絕鏈接(與前文中的舉例原理相同,此處不再贅述,如果你不明白,請參考之前的文章)。也就是說,即使我們配置了上例中的規則,也不能達到"限制"的目的,所以我們通常并不會對此選項取反,因為既然使用了此選項,我們的目的通常就是"限制"連接數量。
centos7中iptables為我們提供了一個新的選項,--connlimit-upto,這個選項的含義與"! --commlimit-above"的含義相同,即鏈接數量未達到指定的連接數量之意,所以綜上所述,--connlimit-upto選項也不常用。
剛才說過,--connlimit-above默認表示限制"每個IP"的鏈接數量,其實,我們還可以配合--connlimit-mask選項,去限制"某類網段"的鏈接數量,示例如下:
(注:下例需要一定的網絡知識基礎,如果你還不了解它們,可以選擇先跳過此選項或者先去學習部分的網絡知識)

上例中,"--connlimit-mask 24"表示某個C類網段,沒錯,mask為掩碼之意,所以將24轉換成點分十進制就表示255.255.255.0,所以,上圖示例的規則表示,一個最多包含254個IP的C類網絡中,同時最多只能有2個ssh客戶端連接到當前服務器,看來資源很緊俏啊!254個IP才有2個名額,如果一個IP同時把兩個連接名額都占用了,那么剩下的253個IP連一個連接名額都沒有了,那么,我們再看看下例,是不是就好多了。

上例中,"--connlimit-mask 27"表示某個C類網段,通過計算后可以得知,這個網段中最多只能有30臺機器(30個IP),這30個IP地址最多只能有10個ssh連接同時連接到服務器端,是不是比剛才的設置大方多了,當然,這樣并不能避免某個IP占用所有連接的情況發生,假設,報文來自192.168.1.40這個IP,按照掩碼為27進行計算,這個IP屬于192.168.1.32/27網段,如果192.168.1.40同時占用了10個ssh連接,那么當192.168.1.51這個IP向服務端發起ssh連接請求時,同樣會被拒絕,因為192.168.1.51這個IP按照掩碼為27進行計算,也是屬于192.168.1.32/27網段,所以他們共享這10個連接名額。
聰明如你一定明白了,在不使用--connlimit-mask的情況下,連接數量的限制是針對"每個IP"而言的,當使用了--connlimit-mask選項以后,則可以針對"某類IP段內的一定數量的IP"進行連接數量的限制,這樣就能夠靈活許多,不是嗎?
?
?
## limit擴展模塊
剛才認識了connlimit模塊,現在來認識一下limit模塊。
connlimit模塊是對連接數量進行限制的,limit模塊是對"報文到達速率"進行限制的。
用大白話說就是,如果我想要限制單位時間內流入的包的數量,就能用limit模塊。
我們可以以秒為單位進行限制,也可以以分鐘、小時、天作為單位進行限制。
比如,限制每秒中最多流入3個包,或者限制每分鐘最多流入30個包,都可以。
那么,我們來看一個最簡單的示例,假設,我們想要限制,外部主機對本機進行ping操作時,本機最多每6秒中放行一個ping包,那么,我們可以進行如下設置(注意,只進行如下設置有可能無法實現限制功能,請看完后面的內容)

上例中,"-p icmp"表示我們針對ping請求添加了一條規則(ping使用icmp協議),"-m limit"表示使用limit模塊, "--limit 10/minute -j ACCEPT"表示每分鐘最多放行10個包,就相當于每6秒鐘最多放行一個包,換句話說,就是每過6秒鐘放行一個包,那么配置完上述規則后,我們在另外一臺機器上對當前機器進行ping操作,看看是否能夠達到限制的目的,如下圖所示。

我們發現,剛才配置的規則并沒有如我們想象中的一樣,ping請求的響應速率完全沒有發生任何變化,為什么呢?我們一起來分析一下。
我們再來回顧一下剛才配置的規則。

其實,我們可以把上圖中的規則理解為如下含義。
每6秒放行一個包,那么iptables就會計時,每6秒一個輪次,到第6秒時,達到的報文就會匹配到對應的規則,執行對應的動作,而上圖中的動作是ACCEPT。
那么在第6秒之前到達的包,則無法被上述規則匹配到。
之前總結過,報文會匹配鏈中的每一條規則,如果沒有任何一條規則能夠匹配到,則匹配默認動作(鏈的默認策略)。
既然第6秒之前的包沒有被上述規則匹配到,而我們又沒有在INPUT鏈中配置其他規則,所以,第6秒之前的包肯定會被默認策略匹配到,那么我們看看默認策略是什么。

現在再想想,我想你應該明白為什么剛才的ping的響應速率沒有變化了。
因為,上例中,第六秒的報文的確被對應的規則匹配到了,于是執行了"放行"操作,第6秒之前的報文沒有被上圖中配置的規則匹配到,但是被默認策略匹配到了,而恰巧,默認動作也是ACCEPT,所以,相當于所有的ping報文都被放行了,怪不得與沒有配置規則時的速率一毛一樣了。
那么,知錯就改,聰明如你一定想到了,我們可以修改INPUT鏈的默認策略,或者在上例限制規則的后面再加入一條規則,將"漏網之魚"匹配到即可,示例如下。

如上圖所示,第一條規則表示每分鐘最多放行10個icmp包,也就是6秒放行一個,第6秒的icmp包會被上例中的第一條規則匹配到,第6秒之前的包則不會被第一條規則匹配到,于是被后面的拒絕規則匹配到了,那么,此刻,我們再來試試,看看ping的報文放行速率有沒有發生改變。
如下圖所示

剛開始還真嚇我一跳,難道配置的規則還是有問題?
結果發現,只有前5個ping包沒有受到限制,之后的ping包已經開始受到了規則的限制了。
從上圖可以看出,除了前5個ping包以外,之后的ping包差不多每6秒才能ping通一次,看來,之后的ping包已經受到了規則的控制,被限制了流入[防火墻](http://www.zsythink.net/archives/tag/%e9%98%b2%e7%81%ab%e5%a2%99/ "查看與 防火墻 相關的文章")的速率了,那么,前5個ping包是什么鬼?為什么它們不受規則限制呢?其實,這個現象正好引出另一個話題,出現上圖中的情況,是因為另一個選項:"--limit-burst"
limit-burst選項是干什么用的呢?我們先用不準確的大白話描述一遍,"--limit-burst"可以指定"空閑時可放行的包的數量",其實,這樣說并不準確,但是我們可以先這樣大概的理解,在不使用"--limit-burst"選項明確指定放行包的數量時,默認值為5,所以,才會出現上圖中的情況,前5個ping包并沒有受到任何速率限制,之后的包才受到了規則的限制。
如果想要徹底了解limit模塊的工作原理,我們需要先了解一下"令牌桶"算法,因為limit模塊使用了令牌桶算法。
我們可以這樣想象,有一個木桶,木桶里面放了5塊令牌,而且這個木桶最多也只能放下5塊令牌,所有報文如果想要出關入關,都必須要持有木桶中的令牌才行,這個木桶有一個神奇的功能,就是每隔6秒鐘會生成一塊新的令牌,如果此時,木桶中的令牌不足5塊,那么新生成的令牌就存放在木桶中,如果木桶中已經存在5塊令牌,新生成的令牌就無處安放了,只能溢出木桶(令牌被丟棄),如果此時有5個報文想要入關,那么這5個報文就去木桶里找令牌,正好一人一個,于是他們5個手持令牌,快樂的入關了,此時木桶空了,再有報文想要入關,已經沒有對應的令牌可以使用了,但是,過了6秒鐘,新的令牌生成了,此刻,正好來了一個報文想要入關,于是,這個報文拿起這個令牌,就入關了,在這個報文之后,如果很長一段時間內沒有新的報文想要入關,木桶中的令牌又會慢慢的積攢了起來,直到達到5個令牌,并且一直保持著5個令牌,直到有人需要使用這些令牌,這就是令牌桶算法的大致邏輯。
那么,就拿剛才的"令牌桶"理論類比我們的命令,"--limit"選項就是用于指定"多長時間生成一個新令牌的","--limit-burst"選項就是用于指定"木桶中最多存放幾個令牌的",現在,你明白了嗎??示例如下

上例表示,令牌桶中最多能存放3個令牌,每分鐘生成10個令牌(即6秒鐘生成一個令牌)。
之前說過,使用"--limit"選項時,可以選擇的時間單位有多種,如下
/second
/minute
/hour
/day
比如,3/second表示每秒生成3個"令牌",30/minute表示沒分鐘生成30個"令牌"。
我不知道我到底解釋清楚沒有,我感覺我解釋清楚了,哥們兒你趕緊動手試試吧。
?
?
## 小結
老規矩,為了方便以后回顧,我們將上文中提到的命令總結如下。
### iprange模塊
包含的擴展匹配條件如下
\--src-range:指定連續的源地址范圍
\--dst-range:指定連續的目標地址范圍
Shell
#示例 iptables?-t?filter?-I?INPUT?-m?iprange?--src-range?192.168.1.127-192.168.1.146?-j?DROP iptables?-t?filter?-I?OUTPUT?-m?iprange?--dst-range?192.168.1.127-192.168.1.146?-j?DROP iptables?-t?filter?-I?INPUT?-m?iprange?!?--src-range?192.168.1.127-192.168.1.146?-j?DROP
1234#示例iptables -t filter -I INPUT -m iprange --src-range 192.168.1.127-192.168.1.146 -j DROPiptables -t filter -I OUTPUT -m iprange --dst-range 192.168.1.127-192.168.1.146 -j DROPiptables -t filter -I INPUT -m iprange ! --src-range 192.168.1.127-192.168.1.146 -j DROP
### string模塊
常用擴展匹配條件如下
\--algo:指定對應的匹配算法,可用算法為bm、kmp,此選項為必需選項。
\--string:指定需要匹配的字符串
Shell
#示例 iptables?-t?filter?-I?INPUT?-p?tcp?--sport?80?-m?string?--algo?bm?--string?"OOXX"?-j?REJECT iptables?-t?filter?-I?INPUT?-p?tcp?--sport?80?-m?string?--algo?bm?--string?"OOXX"?-j?REJECT
123#示例iptables -t filter -I INPUT -p tcp --sport 80 -m string --algo bm --string "OOXX" -j REJECTiptables -t filter -I INPUT -p tcp --sport 80 -m string --algo bm --string "OOXX" -j REJECT
### time模塊
常用擴展匹配條件如下
\--timestart:用于指定時間范圍的開始時間,不可取反
\--timestop:用于指定時間范圍的結束時間,不可取反
\--weekdays:用于指定"星期幾",可取反
\--monthdays:用于指定"幾號",可取反
\--datestart:用于指定日期范圍的開始日期,不可取反
\--datestop:用于指定日期范圍的結束時間,不可取反
Shell
#示例 iptables?-t?filter?-I?OUTPUT?-p?tcp?--dport?80?-m?time?--timestart?09:00:00?--timestop?19:00:00?-j?REJECT iptables?-t?filter?-I?OUTPUT?-p?tcp?--dport?443?-m?time?--timestart?09:00:00?--timestop?19:00:00?-j?REJECT iptables?-t?filter?-I?OUTPUT?-p?tcp?--dport?80??-m?time?--weekdays?6,7?-j?REJECT iptables?-t?filter?-I?OUTPUT?-p?tcp?--dport?80??-m?time?--monthdays?22,23?-j?REJECT iptables?-t?filter?-I?OUTPUT?-p?tcp?--dport?80??-m?time?!?--monthdays?22,23?-j?REJECT iptables?-t?filter?-I?OUTPUT?-p?tcp?--dport?80??-m?time?--timestart?09:00:00?--timestop?18:00:00?--weekdays?6,7?-j?REJECT iptables?-t?filter?-I?OUTPUT?-p?tcp?--dport?80??-m?time?--weekdays?5?--monthdays?22,23,24,25,26,27,28?-j?REJECT iptables?-t?filter?-I?OUTPUT?-p?tcp?--dport?80??-m?time?--datestart?2017-12-24?--datestop?2017-12-27?-j?REJECT
123456789#示例iptables -t filter -I OUTPUT -p tcp --dport 80 -m time --timestart 09:00:00 --timestop 19:00:00 -j REJECTiptables -t filter -I OUTPUT -p tcp --dport 443 -m time --timestart 09:00:00 --timestop 19:00:00 -j REJECTiptables -t filter -I OUTPUT -p tcp --dport 80 -m time --weekdays 6,7 -j REJECTiptables -t filter -I OUTPUT -p tcp --dport 80 -m time --monthdays 22,23 -j REJECTiptables -t filter -I OUTPUT -p tcp --dport 80 -m time ! --monthdays 22,23 -j REJECTiptables -t filter -I OUTPUT -p tcp --dport 80 -m time --timestart 09:00:00 --timestop 18:00:00 --weekdays 6,7 -j REJECTiptables -t filter -I OUTPUT -p tcp --dport 80 -m time --weekdays 5 --monthdays 22,23,24,25,26,27,28 -j REJECTiptables -t filter -I OUTPUT -p tcp --dport 80 -m time --datestart 2017-12-24 --datestop 2017-12-27 -j REJECT
### connlimit 模塊
常用的擴展匹配條件如下
\--connlimit-above:單獨使用此選項時,表示限制每個IP的鏈接數量。
\--connlimit-mask:此選項不能單獨使用,在使用--connlimit-above選項時,配合此選項,則可以針對"某類IP段內的一定數量的IP"進行連接數量的限制,如果不明白可以參考上文的詳細解釋。
Shell
#示例 iptables?-I?INPUT?-p?tcp?--dport?22?-m?connlimit?--connlimit-above?2?-j?REJECT iptables?-I?INPUT?-p?tcp?--dport?22?-m?connlimit?--connlimit-above?20?--connlimit-mask?24?-j?REJECT iptables?-I?INPUT?-p?tcp?--dport?22?-m?connlimit?--connlimit-above?10?--connlimit-mask?27?-j?REJECT
1234#示例iptables -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 2 -j REJECTiptables -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 20 --connlimit-mask 24 -j REJECTiptables -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 10 --connlimit-mask 27 -j REJECT
### limit模塊
常用的擴展匹配條件如下
\--limit-burst:類比"令牌桶"算法,此選項用于指定令牌桶中令牌的最大數量,上文中已經詳細的描述了"令牌桶"的概念,方便回顧。
\--limit:類比"令牌桶"算法,此選項用于指定令牌桶中生成新令牌的頻率,可用時間單位有second、minute 、hour、day。
Shell
#示例?#注意,如下兩條規則需配合使用,具體原因上文已經解釋過,忘記了可以回顧。 iptables?-t?filter?-I?INPUT?-p?icmp?-m?limit?--limit-burst?3?--limit?10/minute?-j?ACCEPT iptables?-t?filter?-A?INPUT?-p?icmp?-j?REJECT
123#示例 #注意,如下兩條規則需配合使用,具體原因上文已經解釋過,忘記了可以回顧。iptables -t filter -I INPUT -p icmp -m limit --limit-burst 3 --limit 10/minute -j ACCEPTiptables -t filter -A INPUT -p icmp -j REJECT
?
?
希望這篇文章能夠對你有所幫助~~~

**我的微信公眾號**
關注"實用運維筆記"微信公眾號,當博客中有新文章時,可第一時間得知哦~