只要你所管理的 linux 伺服器掛在網路上, 就一定要學會使用 fail2ban 保護它。 fail2ban 讓你用最少的力氣同時對 ssh 跟 apache 施以最基本的防護、 抵擋暴力攻擊和收割機騷擾。 本教學文主要針對新手; 但後半所提供的一些小程式,對老手可能也有用處。 [2018/2/3] 保護 ssh 的其他更多招術包含: 禁止 root 登入、 允許登入白名單、 用非對稱加解密取代密碼 以及 連鑰匙孔都藏起來的 SPA。
先前介紹的 denyhosts 在 2008 年推出最後更新; fail2ban 則還持續在更新, 所以我就改用 fail2ban 了。 本文介紹的是 0.8.6 (跟 0.8.4) 版。
如果你先前已經手動設定 iptables 防火牆,
且網路速度因為規則太多而變慢,
可以考慮把舊的設定刪掉。 (不刪也沒關係啦。)
先把舊的設定備份起來:
iptables-save > ~/iptables.rules.v4
這樣萬一以後改變心意, 還可以用 iptables-restore 還原。
然後照著
這一段 把 iptables 清乾淨。
(那一整篇很值得從頭開始看!)
為了加強防護, 還可以 把 ssh 設定成滴水不漏。
[2017/5/14] 另一個很值得幫伺服器做的保護措施是 定期自動更新 ( 簡體中文)。
一、 基本設定
根據 手冊, 客製設定應該寫在 /etc/fail2ban/jail.local 裡面。 這個檔裡面沒提到的, 就用 /etc/fail2ban/jail.conf 的預設值。 所以 不必 把整個 jail.conf 複製到 jail.local。 貼入短短幾句即可:
[DEFAULT] ignoreip = 127.0.0.1/8 999.999.999.0/24 888.888.888.0/24 [sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log # backend = systemd maxretry = 5 findtime = 600 bantime = 1200
其中 ignoreip 那句是白名單 --
這些好朋友不論怎麼攻擊你的伺服器,
都還是一律放行 :-)
而 [sshd] 那一節是從 /etc/fail2ban/jail.conf
裡面抄過來改的。 也就是說,
即使沒有在 jail.local 裡面加上這一節,
sshd 本來預設就會受到保護。
上面表示要求 fail2ban 定時檢查 /var/log/auth.log ,
如果在 10 分鐘內 (findtime) 發現同一 IP
企圖連線 5 次 (maxRetry), 就把這個 IP
封鎖 20 分鐘 (banTime)。
改好之後重新啟動: service fail2ban restart
注意: 在採用 systemd 的系統上, 請把 logpath 註解掉,
改用 backend = systemd
。
重新啟動的指令則改成 systemctl restart fail2ban
詳見 這個問答。
二、 測試確認
用 fail2ban-client status
應該會看到 jail list 清單裡已經出現了 sshd,
表示目前已有 sshd 一個服務受到 fail2ban 的保護。
再來, 開一個新的命令列視窗專門查看記錄檔。
後面一直要持續觀察, 請不要用 ^C 打斷這個視窗;
資料太亂時可以按幾次 Enter 鍵。
採用 logpath 設定的用戶請下:
tail -f /var/log/fail2ban.log
採用 systemd 設定的用戶請下: journalctl -f -u ssh
。
回到原來的命令列視窗, 試著把自己 ban 掉:
- 如果你人就在伺服器 (叫它 Z 好了) 電源開關旁邊, 請從另一部電腦 (叫它 X 好了) 連續試著故意用錯誤密碼 ssh 連線登入 Z。 (攻擊!)
- 如果伺服器在遠方, 請先從手邊的電腦 X 用 ssh 登入另一部伺服器 Y, 再確認可以從 Y 登入 Z, 最後才另開一個分頁, 從 X 直接攻擊 Z -- 否則 Z 把 X 列入黑名單 (封鎖掉) 之後, 你就哭哭了! (還好啦, 等二十分鐘就自動解鎖了。)
注意剛剛觀察記錄檔的視窗。 它的反應可能會有點 lag, 因為它隔一段時間才會去檢查記錄檔, 然後才會封鎖 X。
一定要測試到自己被封鎖為止才算數。 我曾在 proxmox 上面架設 debian 8 的 OpenVZ container, 成功地啟動 fail2ban, log 檔也開始動起來了; 但就是無法阻擋入侵。 後來不知道什麼時候, container 的 fail2ban 突然就好了。 (因為 host 重開嗎?)
三、 查看及解鎖
用 fail2ban-client status ssh
查看一下目前有哪些 IP 被封鎖、 過去總共有多少 IP 被封鎖。
如果把自己的 X 鎖住了... 歐歐~~ 希望你還能夠從 Y 登入 Z。
可以這樣解鎖: fail2ban-client set ssh unbanip [X的IP位址]
四、 寄信通知
你可以叫 fail2ban 用 e-mail 向你報告它封鎖了哪些 IP。 不過, 這兩天實驗很久的心得是: 不要設定 e-mail 通知比較安靜啦, 還可以直接略過這一節。 如果你一定要設的話...
請先手動寄 e-mail, 確認至少 root 可以寄信給自己。
然後在設定檔裡加一句 action = %(action_mwl)s
就可以了。 但是建議先別這麼設!
我設定 apache2 防護 (下詳) 並且重新啟動 fail2ban 沒多久,
我自己就被 DNS 伺服器給封鎖掉了 :-(
因為 action_mwl 會用 whois 指令 (透過 DNS?)
去查看到底是哪些邪惡 IP 在攻擊你。
當時正好有很多邪惡的 IP 在攻擊我,
於是 fail2ban 一口氣問了 DNS 很多問題,
然後 DNS 以為我是來亂的,
就直接把苦主我當成邪惡 IP 封鎖掉了~~
首先在 jail.local 的 "[DEFAULT]" 那一節加上這兩句:
action_ml = %(banaction)s[name=%(__name__)s, port="%(port)s", protocol="%(protocol)s", chain="%(chain)s"] %(mta)s-lines[name=%(__name__)s, dest="%(destemail)s", logpath=%(logpath)s, chain="%(chain)s"] action = %(action_ml)s
(若是較舊的 0.8.4 版, 要把 ", chain="%(chain)s" 刪掉)
這是從 jail.conf 裡面抄過來修改過的。
意思是: 凡是觸發 fail2ban 時, 就採取 action_ml 行動;
所謂 action_ml 行動, 就是先採取 banaction 行動,
再採取 {mta}-lines 行動。
這裡的 {mta} 會代值進去:
像我的 jail.conf 裡面設定 mta = sendmail
所以 %(mta)s-lines 會代換成 sendmail-lines。
於是它會去找 action.d/sendmail-lines 這個檔案。
如果你的 mta 不是 sendmail, 以下需要新增的檔名請自行更改。
當然, 原先 fail2ban 套件裡面並沒有附 action.d/sendmail-lines 這個檔案。 這是我從 action.d/sendmail-whois-lines.conf 改來的。 基本上就是把查詢 whois 那句刪掉而已。
重新啟動: service fail2ban restart
如果出現很長的 python 錯誤訊息, 請看最後一句。
如果沒有錯誤訊息但也沒有成功,
那有可能是讀不到它要的檔案
(已把 action.d/sendmail-lines 剪貼回去了嗎?
檔名正確嗎?) 請先改試其他系統內建的 action 看看。
另外, 可以用 destemail=你@公司.com
設定收件人地址。
五、 自建 filter 阻擋深層路徑騷擾
去年所發現的 收割機騷擾 apache 事件, 後來又出現其他新的騷擾 IP, 於是我的手動防禦當然又逐漸失效了。 而且認真想想, 這些 IP 好像又不是單純的收割機。 因為它們沒在抓有意義的頁面, 而是一直在抓一些很深的、 不存在的頁面 -- 以下取自我的 /var/log/apache2/access.log:
40.77.167.21 - - [02/Apr/2016:05:35:03 +0800] "GET /~ckhung/index.php/a/a/b/al/b/al/b/al/b/svg/dl/b/tk/b/dg/c/s/b/pr/ HTTP/1.1" 200 4096 "-" "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)" 40.77.167.21 - - [02/Apr/2016:06:35:24 +0800] "GET /~ckhung/index.php/a/a/b/al/b/al/b/al/b/svg/p/algotutor/b/svg/b/al/mm/c/l/ HTTP/1.1" 200 4097 "-" "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)" 40.77.167.21 - - [02/Apr/2016:08:50:14 +0800] "GET /~ckhung/index.php/a/a/b/al/b/al/b/al/b/svg/p/algotutor/s/c/v/c/mentor/c/p/toy/ HTTP/1.1" 200 4100 "-" "Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)"
讀過 這篇 我才知道有一種攻擊叫做 apache overflow; 我開始懷疑這些攻擊者是想要找 apache 的漏洞? 可是 fail2ban 預設的 filter.d/apache-overflows.conf 看起來弱弱的, 沒什麼效果。 沒關係, 有了 fail2ban, 凡是可以 「用 regexp 從某個服務的 log 檔裡面觀察出規則」 這種類型的攻擊, 現在我都知道該如何對付了。 [2023/8/10 建議先做 用 iptables 對付惡意幫派網頁爬蟲, 剩下零散的才用 fail2ban 阻擋]
請先檢查你的伺服器跟我有沒有相同的遭遇:
cd ~/public_html ; find . -type f | sed 's#[^/]##g' | sort | uniq -c perl -ne 'print "$1\n" if m#GET ((/[^/\s]*)+) #' /var/log/apache2/access.log | sed 's#[^/]##g' | sort | uniq -c perl -ne 'print if m#GET ((/[^/\s]*){20,}) #' /var/log/apache2/access.log
第一句話會產生一個簡單的統計數據, 顯示你的個人網頁目錄底下深淺各種層次 (/abc/def/xyz) 的目錄各有多少。 第二句話會產生另一個簡單的統計數據, 看看你的 log 檔裡面有多少筆 「很深」 (GET 後面那串包含很多個 /) 的記錄。 第三句話則會撈出所有 20 層以上的記錄。
想要阻擋這類流量, 請先把
apache-deepurls.conf 貼到 /etc/fail2ban/filter.d 底下。
其中 failregex = <HOST>.*GET\s+(/[^/\s]*){9}
這一句最後面的數字, 決定超過幾層目錄的記錄將觸發封鎖。
再用 fail2ban-regex /var/log/apache2/access.log
/etc/fail2ban/filter.d/apache-deepurls.conf
如果是 systemd 的系統, 則改下:
fail2ban-regex systemd-journal
/etc/fail2ban/filter.d/apache-deepurls.conf
這個指令測試我寫的規則對你的記錄檔有沒有幫助。
改一下大括弧裡的數字, 甚至改一下 regexp,
直到測試滿意為止。 然後在 /etc/fail2ban/jail.local
裡面加上這幾句:
[apache-deepurls] enabled = true port = http filter = apache-deepurls logpath = /var/log/apache2/access.log maxretry = 2
如果沒有加上 findtime、 bantime、 maxretry 這幾句, 就會沿用 [DEFAULT] 那一節 (ssh、 apache-deepurls、 ... 等等所有封鎖規則共用) 的預設值。
請再次確認你的 action 並沒有設定要去查詢 whois
(乾脆暫時先設定成 action = %(action_)s
就好)
最後重新啟動 fail2ban, 馬上感受到你的流量在幾分鐘之內大幅下降 :-)
隔一陣子再查看 fail2ban-client status apache-deepurls
,
會看到已有很多騷擾 IP 被封鎖。
六、 延長封鎖累犯之一: cron
有些 IP 很白目, 被阻擋半小時一小時之後, 又不斷地回來騷擾。 雖然頻率已大幅降低, 還是很煩。 於是決定用 cron 掃描 fail2ban 的 log 檔, 把累犯寫入 /etc/hosts.deny。
請把
repeated-fail2ban.pl 放到 /etc/fail2ban (或隨便哪裡都可以)。
要先安裝一個 perl 模組才能使用我寫的這支小程式:
apt-get install libdatetime-perl
。
可以這樣執行: /etc/fail2ban/repeated-fail2ban.pl -d 8 -g 4 -i 6
它會印出過去 8 天之內被 fail2ban 封鎖的
(1) 可疑子網域 (包含 4 個或更多被封鎖 IP)
(2) 累犯 IP (被封鎖 6 次或更多), 類似這樣:
# repeated-fail2ban: [20160327-20160404 g=4 i=6] 125.212.232.[4] (18) 157.55.39.[4] (130) 221.203.142.[5] (22) 62.4.15.46 (13) 207.46.13.[4] (42) 40.77.167.[6] (237) ALL: 125.212.232. 157.55.39. 221.203.142. 62.4.15.46 207.46.13. 40.77.167.
IP 最後一碼的方括弧內是同一子網域的惡意 IP 數; 小括弧則是該 IP 或該子網域被封鎖的總次數。 已列入子網域群組的 IP 就不另外單獨列出。 第二列是適合貼到 /etc/hosts.deny 的內容。 第一列則是提供較詳細資訊的註解。 如果使用 -f apache2 這個選項, 那麼它就改產生適用於 apache2 設定檔的語法格式。 注意: 我的程式只查看 /var/log/fail2ban.log.1 跟 /var/log/fail2ban.log, 所以實際上最多只能看到過去 7-13 天的記錄。
再把
repeated-fail2ban.sh 抓回去並改名放到
/etc/cron.daily/repeated-fail2ban
還要 chmod a+x /etc/cron.daily/repeated-fail2ban
才會生效。 它每天呼叫上述 perl 程式檢查累犯及共犯,
然後用 /etc/hosts.deny 跟 /etc/apache2/conf.d/repeated-fail2ban
來列出較長時間 (數天) 的黑名單。
使用這招的好處是: 對 ssh 服務而言, 你可以一口氣封鎖整個網段, 讓 fail2ban 比較輕鬆。 缺點是: /etc/hosts.deny 只適用於 xinetd 所啟用的服務 (例如 ssh); 像 apache2 就不適用, 所以必須另外處理。 而且 apache2 的 deny 設定運作的層次不是低層的 iptables, 所以 fail2ban 還是會看到攻擊者 (還是很忙)、 攻擊者還是會看到 403 forbidden (而不是完全連不上)。
七、 延長封鎖累犯之二: 第二層 fail2ban
fail2ban 的規則, 一般檢查和封鎖的範圍大約是幾十分鐘到幾小時。 那如果用 fail2ban 檢查自己的 log 檔呢? 它就能夠比較宏觀地看到哪些惡意 IP 一直不死心, 就像上節所說的, 被封鎖一段時間之後, 又捲土重來, 一天重複個幾十次。 那就再寫一個 filter 把這些 IP 封鎖久一點囉。
請把
f2b-ad.conf 抓回去放在 /etc/fail2ban/filter.d
底下, 並且用 fail2ban-regex /var/log/fail2ban.log
/etc/fail2ban/filter.d/f2b-ad.conf
測試看看對你的資料有沒有效果。
(注意: 這個 filter
只查看 (第五節所談的) apache-deepurl 所抓到的惡意 IP。)
若有效果, 就可以在 jail.local 裡面加上這一段:
[f2b-ad] enabled = true port = http filter = f2b-ad logpath = /var/log/fail2ban.log findtime = 86400 bantime = 604800 maxretry = 4
重新啟動 fail2ban 之後, 凡是過去 24 小時之內曾被重複封鎖 4 次的 IP, 都會被自動封鎖一週。
如果也想對 ssh 做相同的事, 可以另外寫一個類似的 filter。
八、 proxmox
[2017/5/13] 請把 這個過濾規則檔 照抄放到 /etc/fail2ban/filter.d/ 底下, 並且參考 這個設定檔 修改你的 jail.local。 詳見 這一篇。
九、 結語
網管 (還有秘書、社群分析師、...) 一定要學 字串樣版 regexp 啦! 這是超有用、 C/P 值超高的學習投資。
「自建 filter 阻擋深層路徑騷擾」 這一節雖然成功地擋下大部分這類的攻擊, 但我還是不懂攻擊者的目的是什麼。 更奇怪的是, 那個 IP 竟然是微軟的。 微軟是很賤沒錯 (例如 挾持電腦廠商、 加速 「舊電腦垃圾化」) 但也應該不至於這麼低級吧? 而我也很難想像潰客竟然可以控制那麼多個微軟的 IP。 找不到合理的解釋, 只好到 到 Apache Lounge 發問, 若有新的心得再回來這裡補。
對付累犯的那兩節, 我也才剛開始採用。 長期效果如何, 有待觀察。
春假好幾天都在寫這篇, 比寫學術論文還認真! (握拳) 但我覺得網管們分享防禦知識/經驗, 是一件很有意義的事。 如果你也遇到深層路徑騷擾, 或是其他奇怪、 fail2ban 沒有 filter 可以處理的問題, 請留言分享一下 log 檔裡面的幾句, 讓大家核對一下是否有來自相同 IP 的類似攻擊。 甚至也可以寄略長一段的 log 內容 給我, 我可以幫你寫 filter。 讓我們一起透過知識經驗分享來讓攻擊者更常撞牆 :-)
十、更多 fail2ban 中文教學文
- Vixual: 用 Fail2Ban 防範暴力破解 (SSH、vsftp、dovecot、sendmail)
- 清華大學 計算機與通訊中心: fail2ban 教學
- 「就是資安 Simply Security」: [工具介紹] 利用 fail2ban 避免密碼遭受暴力破解法(錯誤嘗試)
- 「網管人 Netadmin」: fail2ban主機型入侵偵測 即時防護網路服務
如果使用密鑰登陸 還用擔心這種事情嗎?
回覆刪除太厲害了,整個跪著看
回覆刪除我照著您的步驟做,結果新增的proxmox設定一直沒有作用,status永遠只有sshd在執行,該如何解決呢?
回覆刪除你的 OS 是哪一版呢? 我在 lubuntu 18.04 底下可以用 systemctl status fail2ban 檢查 fail2ban 有沒有錯誤訊息, 候如設定檔寫錯之類的。 請多貼一些錯誤訊息, 才有機會幫你除錯。
刪除謝謝您的回覆,我使用proxmox VE 5.3-7,執行似乎無錯誤,訊息如下:
刪除root@pve:~# systemctl status fail2ban
● fail2ban.service - Fail2Ban Service
Loaded: loaded (/lib/systemd/system/fail2ban.service; enabled; vendor preset: enabled)
Active: active (running) since Wed 2019-01-23 15:07:19 CST; 1min 57s ago
Docs: man:fail2ban(1)
Process: 2026 ExecStop=/usr/bin/fail2ban-client stop (code=exited, status=0/SUCCESS)
Process: 2035 ExecStart=/usr/bin/fail2ban-client -x start (code=exited, status=0/SUCCESS)
Main PID: 2054 (fail2ban-server)
Tasks: 3 (limit: 4915)
Memory: 14.2M
CPU: 394ms
CGroup: /system.slice/fail2ban.service
└─2054 /usr/bin/python3 /usr/bin/fail2ban-server -s /var/run/fail2ban/fail2ban.sock -p /var/run/fail2ban/fail2ban.pid -x -b
Jan 23 15:07:19 pve systemd[1]: Starting Fail2Ban Service...
Jan 23 15:07:19 pve fail2ban-client[2035]: 2019-01-23 15:07:19,686 fail2ban.server [2047]: INFO Starting Fail2ban v0.9.6
Jan 23 15:07:19 pve fail2ban-client[2035]: 2019-01-23 15:07:19,687 fail2ban.server [2047]: INFO Starting in daemon mode
Jan 23 15:07:19 pve systemd[1]: Started Fail2Ban Service.
root@pve:~# fail2ban-client status
Status
|- Number of jail: 1
`- Jail list: sshd
systemctl restart fail2ban
刪除tail /var/log/fail2ban.log
有沒有錯誤訊息呢?
抱歉回覆晚了,沒有錯誤但proxmox設定似乎沒有啟動,訊息如下:
刪除root@pve:~# systemctl restart fail2ban
root@pve:~# tail /var/log/fail2ban.log
2019-01-26 00:26:04,320 fail2ban.jail [23544]: INFO Initiated 'pyinotify' backend
2019-01-26 00:26:04,321 fail2ban.actions [23544]: INFO Set banTime = 86400
2019-01-26 00:26:04,323 fail2ban.filter [23544]: INFO Added logfile = /var/log/auth.log
2019-01-26 00:26:04,324 fail2ban.filter [23544]: INFO Set maxRetry = 5
2019-01-26 00:26:04,324 fail2ban.filter [23544]: INFO Set jail log file encoding to UTF-8
2019-01-26 00:26:04,325 fail2ban.filter [23544]: INFO Set findtime = 600
2019-01-26 00:26:04,325 fail2ban.filter [23544]: INFO Set maxlines = 10
2019-01-26 00:26:04,395 fail2ban.server [23544]: INFO Jail sshd is not a JournalFilter instance
2019-01-26 00:26:04,403 fail2ban.jail [23544]: INFO Jail 'sshd' started
2019-01-26 00:26:04,507 fail2ban.actions [23544]: NOTICE [sshd] Ban 140.143.1.49
您好,我找到錯誤了,是我enabled少了最後的d,謝謝您熱心的回覆,占用您寶貴的時間
刪除