Iptables之recent模組小結

散盡浮華發表於2018-11-23

 

Iptables的recent模組用於限制一段時間內的連線數, 是謹防大量請求攻擊的必殺絕技! 善加利用該模組可充分保證伺服器安全。

recent常用引數
--name      設定列表名稱,即設定跟蹤資料庫的檔名. 預設DEFAULT;
--rsource   源地址,此為預設。 只進行資料庫中資訊的匹配,並不會對已存在的資料做任何變更操作;
--rdest       目的地址;
--seconds  指定時間內. 當事件發生時,只會匹配資料庫中前"幾秒"內的記錄,--seconds必須與--rcheck或--update引數共用;
--hitcount   命中次數. hits匹配重複發生次數,必須與--rcheck或--update引數共用;
--set           將地址新增進列表,並更新資訊,包含地址加入的時間戳。 即將符合條件的來源資料新增到資料庫中,但如果來源端資料已經存在,則更新資料庫中的記錄資訊;
--rcheck     檢查地址是否在列表,以第一個匹配開始計算時間;
--update    和rcheck類似,以最後一個匹配計算時間。 如果來源端的資料已存在,則將其更新;若不存在,則不做任何處理;
--remove   在列表裡刪除相應地址,後跟列表名稱及地址。如果來源端資料已存在,則將其刪除,若不存在,則不做任何處理;

recent模組需要注意的地方
a)  目錄/proc/net/下的xt_recent目錄是在啟用recent模組之後才有的,如果沒有在iptables中使用recent模組,/proc/net/目錄中是沒有xt_recent目錄的;
b)  因recent模組最多隻能記錄20條記錄,所以當源傳送的資料包超過20後,recent模組的計數器會立刻減掉20,這也就是為什麼old_packets的值就總是處於1-20之間;
c)  如果配合seconds引數使用的是--rcheck引數而不是--update,則recent模組會從收到第一個資料包開始計算阻斷時間,而--update是從收到的最後一個資料包開始計算阻斷時間,即如果伺服器在8點收到了源發出第一個icmp資料包,在8點15分收到源發出的第20個資料包,如果使用的是--rcheck引數,那麼8點半的時候,使用者就又可以傳送icmp資料包了,如果使用是--update引數,則使用者必須等到8點40才能傳送icmp資料包;
d)  當源傳送資料包的個數大於或等於recent模組的hitcount引數所指定的值時,相應的iptables規則才會被啟用;

recent命令大體有如下三個排列組合
--set句在前,--update(或--rcheck)句在後;
--update(或--rcheck)句在前,--set句在後;
--set句帶或不帶-j ACCEPT。基本是上面這三項的排列組合;

下面通過幾個案例進行說明
1)  利用iptables的recent模組來抵禦簡單的DOS攻擊, 下面以限制ssh遠端連線為例

iptables -I INPUT -p tcp --dport 22 -m connlimit --connlimit-above 3 -j DROP
iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds  300 --hitcount 3 --name SSH -j DROP

以上三條規則的解釋如下:
a) 利用connlimit模組將單IP的併發設定為3;會誤殺使用NAT上網的使用者,可以根據實際情況增大該值;
b) 利用recent和state模組限制單IP在300s內只能與本機建立3個新連線。被限制一分鐘後即可恢復訪問。
c) 第一句是記錄訪問tcp 22埠的新連線,記錄名稱為SSH, --set 記錄資料包的來源IP,如果IP已經存在將更新已經存在的條目
d) 第三句是指SSH記錄中的IP,300s內發起超過3次連線則拒絕此IP的連線。
     --update 是指每次建立連線都更新列表;
     --seconds必須與--update同時使用
     --hitcount必須與--update同時使用
e) 可以使用下面的這句記錄日誌:

iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --name SSH --second 300 --hitcount 3 -j LOG --log-prefix "SSH Attack"

recent模組可以看作iptables裡面維護了一個地址列表,這個地址列表可以通過"--set"、"--update"、"--rcheck"、"--remove"四種方法來修改列表,每次使用時只能選用一種
還可附帶"--name"引數來指 定列表的名字(預設為DEFAULT),"--rsource"、"--rdest"指示當前方法應用到資料包的源地址還是目的地址(預設是前者)。recent語句都帶有布林型返回值,每次執行若結果為真,則會執行後續的語句,比如"-j ACCEPT"之類的。"--seconds"參數列示限制包地址被記錄進列表的時間要小於等於後面的時間。

基於上面的說明,現在來看四個基本方法的作用:
--set       將地址新增進列表,並更新資訊,包含地址加入的時間戳。
--rcheck  檢查地址是否在列表。
--update  跟rcheck一樣,但會重新整理時間戳。
--remove 就是在列表裡刪除地址,如果要刪除的地址不存在就會返回假。

例1:限制無法ssh直接連線伺服器,需先用較大包ping一下,此時在15秒內才可以連線上

iptables -P INPUT DROP
iptables -A INPUT -s 127.0.0.1/32 -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p icmp --icmp-type 8 -m length --length 128 -m recent --set --name SSHOPEN --rsource -j ACCEPT
iptables -A INPUT -p icmp --icmp-type 8 -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --rcheck --seconds 15 --name SSHOPEN --rsource -j ACCEPT

以上命令解說
a) 將INPUT鏈預設策略置為DROP,當包走完INPUT鏈而沒被拿走時就會丟棄掉;
b) 本地localhost的包全部接受;
c)  對於已建立連線或是與已連線相關的包都接受,伺服器對外連線回來的包一般都走這條;基本環境已經配好了,現在開始要為連線到伺服器的ssh開啟通路。
d)  icmp型別8是ping包;指定包大小為128位元組;recent用的列表名稱為SSHOPEN,列表記錄源地址。符合上述條件的資料包都接收。如果ping包內容為100位元組,則加上IP      頭, ICMP頭的28位元組,總共128位元組。
e) 接受一般的ping包;
f)  對連線ssh 22埠的連線進行處理,來源於SSHOPEN源地址列表並且在列表時間小於等於15秒的才放行。

例2:  限制每ip在一分鐘內最多對伺服器只能有8個http連線

iptables -I INPUT -p tcp --dport 80 -d 192.168.10.10 -m state --state NEW -m recent --name httpuser --set
iptables -A INPUT -m recent --update --name httpuser --seconds 60 --hitcount 9 -j LOG --log-prefix 'HTTP attack: '
iptables -A INPUT -m recent --update --name httpuser --seconds 60 --hitcount 9 -j DROP

以上命令解說
a) 192.168.10.10是伺服器ip
b) 引數-I,將本規則插入到 INPUT 鏈裡頭的最上頭。只要是 TCP連線,目標埠是80,目標 IP是我們伺服器的IP,剛剛新被建立起來時,我們就將這個聯機列入 httpuser 這分  清單中;
c) 引數-A,將本規則附在 INPUT 鏈的最尾端。只要是60秒內,同一個來源連續產生多個聯機,到達第9個聯機時,我們對此聯機留下Log記錄。記錄行會以 HTTP attack 開頭。  每一次的本規則比對, –update 均會更新httpuser清單中的列表;
d)  引數-A,將本規則附在 INPUT 鏈的最尾端。同樣的比對條件,但是本次的動作則是將此連線丟掉;
e) 所以,這三行規則表示,我們允許一個客戶端,每一分鐘內可以接上伺服器8個。具體數值可以看運維者決定。這些規則另外也可以用在其它對外開放的網路服務上,例如port  22 (SSH), port 25 (smtp email)。

2) 對連線到伺服器B的SSH連線進行限制,每個IP每小時只限連線5次

-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name SSHPOOL --rcheck --seconds 3600 --hitcount 5 -j DROP
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name SSHPOOL --set -j ACCEPT

伺服器A連線伺服器B的SSH服務的資料包流程,假設以下資料包是在一小時(3600秒)內到達伺服器B(iptables配置如上)的:
a) 當這個伺服器A的第1個SSH包到達伺服器B,規則1檢查SSHPOOL列表中這個源IP是否有hitcount,因為是第一個包,顯而易見,列表是0,規則1判定這個資料包不必執行      DROP,並且也不處理這個資料包,將資料包轉給下條規則。
b) 規則2將這個資料包計入SSHPOOL列表,就是做+1,因為規則中有-j ACCEPT,規則2放行這個包。
c) 第1個資料包進入伺服器B,不用再在iptables裡轉了。
d) 當第2個SSH包到達伺服器B,規則1檢查SSHPOOL列表的hitcount,發現是1沒有超過5,於是判定不執行DROP並轉給下條規則處理。
e) 規則2在SSHPOOL中+1,並放行,第2個資料包進入伺服器B。
f) 第3、4、5個包同上。
g) 第6個包到達伺服器B,規則1檢查SSHPOOL列表中的hitcount,發現是5了已經連線5次了,於是規則2執行DROP,不必再轉給下條規則了丟棄該包。
h) 第7、8…個包同上。

實際上recent的處理更為複雜, 從上面的流程可以看出,--set的功能在於計錄資料包,將源IP加入列表。--rcheck(update)的功能在於判定資料包在seconds和hitcount條件下是否要DROP。

如果採用下面的配置, 則必須在INPUT鏈的預設策略為ACCEPT的情況下才能生效並使用, 否則(即INPUT鏈的預設策略為DROP)所有的SSH包都被丟棄了!!!!

-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT                            //必須新增這個前提條件才能生效!
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name SSHPOOL --set
-A INPUT -p tcp --dport 22 -m state --state NEW -m recent --name SSHPOOL --rcheck --seconds 3600 --hitcount 5 -j DROP

這個命令與上面命令的區別在於:set句在前,且set句不帶-j ACCEPT。這導致資料包的流程也不同。
a) 第1個資料包到達規則1後馬上計入SSHPOOL列表,並且規則1因為沒有-j ACCEPT,直接將資料包轉給下條規則。規則2拿到這個資料包後檢查SSHPOOL列表,發現是1,也  不處理這個包,轉給下條規則。如果後續的規則都沒有再處理這個資料包,則最後由INPUT鏈的預設策略ACCEPT處理。由於上面的策略是DROP,所以丟棄該包,於是這兩行命令在我的伺服器上不能用。
b) 這裡有個問題,由於set句在前,資料包進入是先計入列表,再判定是否合法。這導致第5個包到達後,先在列表中+1,結果是5,再由規則2判定,發現是5,結果丟棄該包,    最後真正ACCEPT的只有4個包。其實個人認為這樣寫的程式碼不符合正常的思維邏輯, 而且這樣寫只能正常工作於預設策略是ACCEPT的情況,所以不建議用這個版本的命令,我的版本ACCEPT、DROP策略都能用。

從上面可以看出,在使用recent模組的命令的時候,一定要先確認iptables的INPUT鏈的預設策略是什麼。

接著說下--rcheck 和 --update的區別
--rcheck從第1個包開始計算時間,--update是在rcheck的基礎上增加了從最近的DROP包開始計算阻斷時間,具有準許時間和阻斷時間,update會更新last-seen時間戳。

就拿上面那個配置案例來說, rcheck是接收到第1個資料包時開始計時,一個小時內僅限5次連線,後續的包丟棄,直到一小時過後又可以繼續連線。update則是接收到第1個資料包時計算準許時間,在一個小時的准許時間內僅限5次連線,當有包被丟棄時,從最近的丟棄包開始計算阻斷時間,在一個小時的阻斷時間內沒有接收到包,才可以繼續連線。所以rcheck類似令牌桶,一小時給你5次,用完了抱歉等下個小時吧。update類似網銀,連續輸錯5次密碼,停止一小時,只不過update更嚴格,阻斷時間是從最近的一次輸錯時間開始算,比如輸錯了5次,過了半個小時又輸錯一次,這時阻斷時間不是剩半小時,而是從第6次重新計算,剩一小時. 

可以拿下面這個命令進行測試, 自行替換rcheck、update,然後ping一下就明白了:

-A INPUT -p icmp -m recent --name PINGPOOL --rcheck --seconds 30 --hitcount 5 -j DROP
-A INPUT -p icmp -m recent --name PINGPOOL --set -j ACCEPT
 
-A INPUT -p icmp -m recent --name PINGPOOL --update --seconds 30 --hitcount 5 -j DROP
-A INPUT -p icmp -m recent --name PINGPOOL --set -j ACCEPT

溫馨提示:
ICMP包和UDP包在iptables中的state情況是一樣的,因為是無狀態的,不同於TCP,iptables可以靠SYN等flags確定state,而iptables是基於ICMP包/UDP包到達伺服器的間隔時間來確定state的。比如在做上面測試的時候,使用ping 192.168.10.10 -t時,除了第一個ICMP包state是NEW,後續的包state都是ESTABLISHED,結果因為前面有一句:

-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

結果測試ping一直是通的,這個一定要弄明白!

3) 限制80埠60秒內每個IP只能發起10個新連線,超過記錄日記及丟失資料包,可防CC及非偽造IP的syn flood

iptables -A INPUT -p tcp --dport 80 --syn -m recent --name webpool --rcheck --seconds 60 --hitcount 10 -j LOG --log-prefix 'DDOS:' --log-ip-options
iptables -A INPUT -p tcp --dport 80 --syn -m recent --name webpool --rcheck --seconds 60 --hitcount 10 -j DROP
iptables -A INPUT -p tcp --dport 80 --syn -m recent --name webpool --set -j ACCEPT

以上命令解說
第1行規則表示: 60秒10個新連線,超過記錄日誌。
第2行規則表示: 60秒10個新連線,超過記錄日誌。
第3行規則表示: 範圍內允許通過。
即每個IP目標埠為80的新連線會記錄在案,可在/proc/net/xt_recent/目錄內檢視,rcheck檢查此IP是否在案及請求次數,如果超過規則就丟棄資料包,否則進入下條規則並更新列表資訊。

傳送特定指定執行相應操作,按上面設定, 如果自己IP被阻止了,可設定解鎖。

iptables -A INPUT -p tcp --dport 5000 --syn -j LOG --log-prefix "WEBOPEN: "
#記錄日誌,字首WEBOPEN:
iptables -A INPUT -p tcp --dport 5000 --syn -m recent --remove --name webpool --rsource -j REJECT --reject-with tcp-reset
#符合規則即刪除webpool列表內的本IP記錄

4)  預設封閉SSH埠,為您的SSH伺服器設定開門暗語

iptables -A INPUT -p tcp --dport 50001 --syn -j LOG --log-prefix "SSHOPEN: "
#記錄日誌,字首SSHOPEN:
iptables -A INPUT -p tcp --dport 50001 --syn -m recent --set --name sshopen --rsource -j REJECT --reject-with tcp-reset
#目標埠tcp 50001的新資料設定列表為sshopen返回TCP重置,並記錄源地址。
iptables -A INPUT -p tcp --dport 22 --syn -m recent --rcheck --seconds 15 --name sshopen --rsource -j ACCEPT
#開啟SSH埠,15秒內允許記錄的源地址登入SSH。
 
#開門鑰匙
nc host 50001 
telnet host 50001
nmap -sS host 50001

5) 指定埠容易被破解金鑰,可以使用ping指定資料包大小為開門鑰匙

iptables -A INPUT -p icmp --icmp-type 8 -m length --length 78 -j LOG --log-prefix "SSHOPEN: "
#記錄日誌,字首SSHOPEN:
iptables -A INPUT -p icmp --icmp-type 8 -m length --length 78 -m recent --set --name sshopen --rsource -j ACCEPT
#指定資料包78位元組,包含IP頭部20位元組,ICMP頭部8位元組。
iptables -A INPUT -p tcp --dport 22 --syn -m recent --rcheck --seconds 15 --name sshopen --rsource -j ACCEPT

安裝上面配置後, 依然無法ssh登入到指定主機, 因為沒有新增"INPUT鏈的預設策略為ACCEPT"的前提, 即需要下面這個前提條件!

iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

整理後的配置規則

iptables -F
iptables -X
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p icmp --icmp-type 8 -m length --length 78 -j LOG --log-prefix 'SSH_OPEN_KEY'
iptables -A INPUT -p icmp --icmp-type 8 -m length --length 78 -m recent --name openssh --set --rsource -j ACCEPT
iptables -A INPUT -p tcp --dport 22 --syn -m state --name openssh --rcheck --seconds 60 --rsource -j ACCEPT
iptables -P INPUT DROP
針對上面整理後的配置規則說明
第1,2行規則表示: 清空原有的iptables規則
第3行規則表示: 已經建立成功的連線和與主機傳送出去的包相關的資料包都接受,如果沒有這一步,後面的tcp連線無法建立起來
第4行規則表示: 出現長度為78位元組icmp迴響包在/var/log/syslog生成log,log以SSH_OPEN_KEY開頭
第5行規則表示: 出現長度為78位元組icmp迴響包,將源地址資訊記錄在openssh檔案中,並接受
第6行規則表示: 對於openssh檔案中的源地址60s以內傳送的ssh連線SYN請求予以接受
第7行規則表示: 將INPUT鏈的預設策略設定為drop

除錯過程:
a) 如果沒有設定第3行規則,則無法建立ssh連線
b) 在沒有第3行規則的情況下,設定第6行如果不加--syn,則可以ssh連線一會兒,過一會兒又自動斷線,除非ping一下目的地址.
原理是:ping目的地址,則會更新openssh的時間,這樣ssh連線還在60s之內,所以可以通訊,過一會兒,60s超時,則就會斷開ssh連線。如果加了--syn,只能進行開始的syn,無法正常連線

在客戶機上,如果需要ssh到主機,需要先ping主機進行解鎖

ping -s 50 ip    #linux主機的ip
ping -l 50 ip     #windows主機的ip

並在一分鐘之內ssh到主機,這樣在/proc/net/xt_recent/目錄下生成openssh檔案, 內容如下:

src=192.168.10.10
ttl: 53
last_seen: 42963x6778
oldest_pkt: 1 4296376778, 4295806644, 4295806895, 4295807146, 4295826619, 4295826870, 4295827122, 4295827372, 4295833120, 4295833369, 4295834525, 4295834777, 4295872016, 4295872267, 4295872519, 4295872769, 4295889154, 4295889406, 4295889658, 4295889910

相關文章