如果你有用 ssh 連線在管理某些主機, 請一定要不時查看一下 /var/log/auth.log。 粗心散漫的貴哥管理系上教學主機已經十幾年, 竟然直到最近才注意到有人持續用暴力嘗試所有密碼的方式, 企圖入侵我的主機的 root 帳號, 有時每小時的攻擊嘗試高達兩千多次! 還好我的 root 密碼夠亂, 不然早就毀了。 不論你的主機有沒有遭到攻擊、 沒空研究的讀者請至少照著第一節做。
[2016/4/4: 改推薦 fail2ban]
一、 ssh 的基本防護
防護 ssh 的基本方式有好幾招。
強力推薦第一招: 安裝 denyhosts 套件。
光是下 apt-get install denyhosts
這個指令, 就算完全沒有後續設定,
你的伺服器風險馬上就已降低百倍!
它會定時檢查 /var/log/auth.log 檔,
只要來自某 IP 的登入次數符合以下任一條件 (以下為預設值)
- 1 次 root 登入失敗 (
DENY_THRESHOLD_ROOT=1
) - 5 次企圖使用不存在的帳戶登入 (
DENY_THRESHOLD_INVALID=5
) - (5 天之內
AGE_RESET_VALID=5d
) 發生 10 次一般正常用戶登入失敗 (DENY_THRESHOLD_VALID=10
)
就會把那個 IP 加到 /etc/hosts.deny 去,
也就是封鎖了那個 IP。
更多選項詳見設定檔 /etc/denyhosts.conf 的註解。
另外, 我還偏好設定
PURGE_DENY = 5w
每五週清除舊的 IP。
這有兩個效果: (1) /etc/hosts.deny 不會無限長大;
它也會把太舊的 IP 清掉。 (2) 在 /etc/hosts.deny
裡面會出現註解, 標示每個 IP 被阻擋的日期時間。
更多設定詳見
正體中文、 簡體中文、 官網。
還可以考慮打開同步功能, 跟全球的
denyhosts 用戶一起交換
「惡意入侵 IP」 清單。
要記得重新啟動服務才會生效:
service denyhosts restart
第二招是禁止 root 登入。
先以 「非 root 帳號」 登入
(重要! 否則等一下可能會被自己鎖在外面),
再用 su 或 sudo bash 變身成 root。
編輯 /etc/ssh/sshd_config 找到
PermitRootLogin yes
那一句, 改成 PermitRootLogin no
最後重新啟動 ssh 服務: service
ssh restart
。 詳見
這一篇。
二、 封鎖整個網段
這一節的措施是我一開始的驚嚇反應, 需要手動設黑名單; 其實它的實用價值稍微低一些。 因為已發生的攻擊量很大, 當時又還不知道要採用哪種簡單有效的防護措施, 手邊最熟的工具就只有 regexp, 所以那時第一反應就是拿它來分析 auth.log。
先從原始資料當中只抓出登入失敗記錄的時間及來源 IP,
存在 fail.log 裡:
perl -ne 'print "$1/$2:$3 $4\n" if
m#^(\w+)\s+(\d+)\s+(\d+):.*failed.*from\s+(\S+)\s#i'
auth.log.4 auth.log.3 auth.log.2 auth.log.1 auth.log
> fail.log
(檔案的長像請參考: 從原始 auth.log 裡面抓出來的
一小部分 auth.log 以及它所產生的
一小部分 fail.log)
再從 fail.log 產生統計報表:
以 256 個 IP 為單位, 每個網段各有多少個 IP 參與攻擊?
列出至少 5 個 IP 以上參與攻擊的網段:
perl -pe 's#.* ##' fail.log | sort | uniq
| perl -pe 's/\.\d+$/.0/' | uniq -c
| perl -ne 'print unless /^\s*[1-4]\s/'
| sort -nr > evil-domains.txt
(這是我的主機遭受攻擊, 從原始 log 檔產生的真實
evil-domains.txt。)
看來某些網段似乎幾近完全被暗黑勢力控制,
有不只兩三個 IP 都參與 ssh 暴力攻擊。
於是用 iptables 封殺這些網段:
for d in $(perl -pe 's/.* //' evil-domains.txt) ;
do iptables -A INPUT -s $d/24 -j DROP ; done
最後, 記得要用
iptables-persistent 把新的 iptables 存起來,
以免重開機就消失。 詳見
「iptables 過濾封包」。
跟 denyhosts 搭配使用, 這個措施有助於避免 /etc/hosts.deny 長太大。
三、 動態應變的 iptables 規則
其實 iptables 有一個 recent 模組也可以做到有點類似 denyhosts 的功能。 它跟 denyhosts 搭配使用, 有點雙重防護的效果。
iptables -N SSH_BRUTE iptables -A SSH_BRUTE -j LOG --log-prefix "ssh brute force: " iptables -A SSH_BRUTE -j DROP iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 300 --hitcount 6 -j SSH_BRUTE iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --set
以上建立一個新的 chain 命名為 SSH_BRUTE, 凡是進入這條 chain 的封包都會被記錄, 然後丟掉。 (據說是寫在 /var/log/kern.log 裡面; 但可能因為我的 log level 沒設好, 所以 log 檔裡面找不到 「ssh brute force: 」 識別字串。) 什麼樣的封包會進入這條 chain 呢? 凡是在五分鐘 (300 秒) 之內企圖連到 port 22 (也就是 ssh) 高達 6 次的 IP, 未來它的封包都會被導向 SSH_BRUTE。 上面的 -I 跟先前的 -A 都是新增規則, 但 -I 新增到規則清單的最前面去。
用 iptables 的 「recent」 功能阻擋 ssh 暴力攻擊的優缺點包含:
- 在很早就把封包擋住, 所以耗費系統資源較少。
- 無法分辨成功與失敗的登入, 所以如果短時間內連續成功登入也會被封鎖。
- 一個 IP 如果超過一定時間沒試著登入 (例如五分鐘) 就不被封鎖了。 當然, 如果又連續登入, 就又被封鎖。
- 可以設定 多層次封鎖, 例如 「每 20 秒達 3 次, 或每 200 秒達 15 次, 或每 2000 秒達 80 次, 或每 20000 秒達 400 次, 一律封鎖。」
- 在 container 內無法使用。
目前有哪些 IP 被列入觀察名單?
這些資訊放在 /proc/net/xt_recent/ 目錄下的某個檔案。
以本節而言, 規則都加到 DEFAULT 這個 table 裡面,
所以你可以用 less /proc/net/xt_recent/DEFAULT
查看 kernel 記錄的原始資料, 或用
./pr_xt_recent /proc/net/xt_recent/DEFAULT
查看每個 IP 最近一次登入的時間。
那個 pr_xt_recent 是我根據
這一頁的問答 改以 perl 重寫的程式。
如果不小心把自己鎖住, 可以從另一個 IP 登入
(希望你還有另一個 IP 可用...) 然後下:
echo -12.34.56.78 > /proc/net/xt_recent/DEFAULT
就可把 12.34.56.78 從封鎖清單當中移除, 或是下:
echo / > /proc/net/xt_recent/DEFAULT
就可暫時把最近的封鎖名單清空。 詳見
這一頁問答 或是查手冊: man iptables
並在手冊內搜尋 「xt_recent」。
如果需要放行某些 IP, 那就要用 I (insert)
而不是 A (append) 因為這些必須放在 iptables 的最前面:
iptables -I INPUT -s 987.654.321.0/24 -j ACCEPT
關於 iptables 的 recent 模組, 請參考 1、 2、 3 更多文章。
四、 補充
更多關於如何抵擋 ssh 暴力攻擊的文章和工具:
附錄、 繪圖指令
從 fail.log 可以產生 「時間 次數」 攻擊流量統計文字檔:
perl -pe 's/ .*//' fail.log |
uniq -c > traffic.txt
。
這是我的主機遭到攻擊的
實際流量檔 traffic.txt。 然後跟
「遭收割機騷擾」 這篇一樣, 把
traffic.gpt 裡面的 set xrange ... 那一句改成你的實際資料範圍,
就可以進
gnuplot 以 load traffic.gpt
指令繪出本文插圖。
其實有很簡單的fail2ban就可以有效擋住大部分的嘗試
回覆刪除其實有很簡單的fail2ban就可以有效擋住大部分的嘗試
回覆刪除感謝分享~~~
回覆刪除