2016年4月4日 星期一

fail2ban: 新手老手 root 網管都要練的金鐘罩

fail2ban 標誌 只要你所管理的 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 中文教學文

  1. Vixual: 用 Fail2Ban 防範暴力破解 (SSH、vsftp、dovecot、sendmail)
  2. 清華大學 計算機與通訊中心: fail2ban 教學
  3. 「就是資安 Simply Security」: [工具介紹] 利用 fail2ban 避免密碼遭受暴力破解法(錯誤嘗試)
  4. 「網管人 Netadmin」: fail2ban主機型入侵偵測 即時防護網路服務

8 則留言:

  1. 如果使用密鑰登陸 還用擔心這種事情嗎?

    回覆刪除
  2. 我照著您的步驟做,結果新增的proxmox設定一直沒有作用,status永遠只有sshd在執行,該如何解決呢?

    回覆刪除
    回覆
    1. 你的 OS 是哪一版呢? 我在 lubuntu 18.04 底下可以用 systemctl status fail2ban 檢查 fail2ban 有沒有錯誤訊息, 候如設定檔寫錯之類的。 請多貼一些錯誤訊息, 才有機會幫你除錯。

      刪除
    2. 謝謝您的回覆,我使用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

      刪除
    3. systemctl restart fail2ban
      tail /var/log/fail2ban.log
      有沒有錯誤訊息呢?

      刪除
    4. 抱歉回覆晚了,沒有錯誤但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

      刪除
    5. 您好,我找到錯誤了,是我enabled少了最後的d,謝謝您熱心的回覆,占用您寶貴的時間

      刪除

因為垃圾留言太多,現在改為審核後才發佈,請耐心等候一兩天。