OpenWrt 版本為 23.05.4,本來(lái)以為直接 opkg install fail2ban 安裝上再把之前的 sshd.conf 配置文件拷到 /etc/fail2ban/jail.d/ 下就行了,誰(shuí)知道并沒(méi)有那么簡(jiǎn)單,遇到不少問(wèn)題。
一切都是日志問(wèn)題
fail2ban 是通過(guò)分析 ssh 的登錄日志來(lái)封禁IP的,然而OP為節(jié)省空間通常并不把日志文件寫出來(lái),并沒(méi)有通常的 /var/log/auth.log 等類似的文件。由于日志文件無(wú)法讀取,fail2ban無(wú)法正常工作,其實(shí)是無(wú)法正常啟動(dòng)。fail2ban-client status 會(huì)提示錯(cuò)誤。雖然 /etc/init.d/fail2ban status 查看是在 running,但是 ps | grep fail2ban 根本沒(méi)有相應(yīng)進(jìn)程!
1. dropbear => sshd
雖然我裝了 openssh-server,但是OP默認(rèn)的 ssh 服務(wù)器并不是 sshd 而是 dropbear,可以通過(guò) netstat -nap |grep :22 查看。dropbear 是輕量級(jí)的 ssh 服務(wù)器,占用內(nèi)存小功能也相對(duì)少,行為上和 sshd 也并不完全一樣。一開(kāi)始我以為不寫日志是 /etc/ssh/sshd_config 中沒(méi)有打開(kāi)如下相應(yīng)設(shè)置
SyslogFacility AUTH
LogLevel INFO
但事實(shí)上該文件對(duì) dropbear 根本無(wú)效。于是關(guān)掉 dropbear 啟用 sshd:
/etc/init.d/dropbear stop
/etc/init.d/dropbear disable
/etc/init.d/sshd start
/etc/init.d/sshd enbale
不過(guò),剛進(jìn)行完上述設(shè)置后 LUCI 登錄總是密碼錯(cuò)誤,重新 ssh 登錄OP也進(jìn)不去,倒是之前沒(méi)退出得 ssh 會(huì)話依然可用。原來(lái)是因?yàn)?sshd_config中沒(méi)有啟用 root 登錄,加上如下設(shè)置并重啟 sshd 后解決
PermitRootLogin yes
然而,啟用 sshd 后登錄OP仍然沒(méi)有在 /var/log/ 中生成日志文件?。?/p>
2. logread
OP 默認(rèn)的日志都是在內(nèi)存中,要通過(guò) logread 命令讀取出來(lái),于是想到通過(guò) logread | grep sshd > /var/log/myauth.log 將日志保存在文件中,這樣再重啟 fail2ban 就能正常顯示 fail2ban-client status sshd 的結(jié)果了
root@OpenWrt:~# fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 0
| `- File list: /var/log/myauth.log
`- Actions
|- Currently banned: 0
|- Total banned: 0
`- Banned IP list:
其實(shí),也可以通過(guò) luci 圖形界面設(shè)置將日志寫到文件中,在 “系統(tǒng) / 系統(tǒng) / 系統(tǒng)屬性 / 日志 / 將系統(tǒng)日志寫入文件” 處設(shè)置并保存即可。不過(guò)這樣的日志是所有系統(tǒng)日志,不只是 sshd 的。
3. 日志解析無(wú)果
有了日志后 fail2ban 可以正常運(yùn)行了,為了測(cè)試,我有意錯(cuò)誤登錄多次,按規(guī)則應(yīng)該被禁掉,然而 fail2ban-client status sshd 的結(jié)果和上面的一樣都是 0,沒(méi)有分析出登錄失敗也沒(méi)有禁掉的 IP。fail2ban 是通過(guò)正則表達(dá)式匹配規(guī)則來(lái)分析日志的,sshd 的默認(rèn)定義在 /etc/fail2ban/filter.d/sshd.conf 中,不過(guò)內(nèi)容太多看不太明白。其實(shí)是可以通過(guò) fail2ban-regex 命令分析日志查看匹配情況的,格式如下。
fail2ban-regex 日志文件 '...正則表達(dá)式匹配規(guī)則...'
顯然,問(wèn)題就在于配置文件中的默認(rèn)規(guī)則并不能匹配出日志的條目,應(yīng)該是格式不符。日志格式如下
Thu Aug 22 22:37:21 2024 auth.info sshd-session[4772]: Failed password for root from 183.81.169.238 port 56310 ssh2
Thu Aug 22 22:47:27 2024 auth.info sshd-session[5176]: Invalid user testuser from 221.181.127.106 port 25516
Thu Aug 22 22:47:27 2024 auth.info sshd-session[5176]: Failed password for invalid user testuser from 221.181.127.106 port 25516 ssh2
Thu Aug 22 22:47:27 2024 auth.info sshd-session[5176]: Connection closed by invalid user testuser 221.181.127.106 port 25516 [preauth]
Thu Aug 22 22:47:29 2024 auth.info sshd[4639]: drop connection #0 from [221.181.127.106]:26456 on [192.168.0.10]:22 penalty: failed authentication
Thu Aug 22 22:47:29 2024 auth.err sshd-session[5180]: error: Could not get shadow information for NOUSER
通過(guò)猜測(cè)和嘗試,發(fā)現(xiàn)把 sshd 前面的 auth.info 或 auth.err 去掉后就行了!
其實(shí),logread 可以通過(guò) -e 參數(shù)添加過(guò)濾內(nèi)容,不需要用 grep 篩選,而且可以用 -f 參數(shù)保持一直運(yùn)行監(jiān)測(cè)最新日志,只要有一條就會(huì)輸出一條。于是,最終比較好的生成日志的方式是在 /etc/rc.local (該文件默認(rèn)沒(méi)有可執(zhí)行權(quán)限,我加了,不知道不加是否行)中添加如下命令用于開(kāi)機(jī)啟動(dòng)
logread -e sshd -f | sed 's/auth\.[^ ]* //' >> /var/log/myauth.log &
這樣只要有新的 sshd 相關(guān)日志就會(huì)及時(shí)的追加到日志文件中,且已經(jīng)把 auth.info 等內(nèi)容去除。
4. 防火墻
日志正常分析了,fail2band status sshd 也能列出失敗次數(shù)以及禁用的 IP 列表了。但是,如果沒(méi)有把這些 IP 成功添加進(jìn)防火墻的話依然是沒(méi)有真正禁用。比如我有意多次登錄失敗的那個(gè) IP 就在列表中,然而我仍然能登錄。通過(guò)如下搜索防火墻規(guī)則
nft list ruleset | grep 相應(yīng)IP
結(jié)果是什么都沒(méi)有。這說(shuō)明分析出要禁用的 IP 后并沒(méi)有成功添加進(jìn)防火墻!jail.d/sshd.conf 配置文件中我原來(lái)的 action 設(shè)置是
action = %(action_mwl)s
它應(yīng)該對(duì)應(yīng) action.d 下 mail-whois-lines.conf 文件所定義的動(dòng)作。由于我不清楚這個(gè)動(dòng)作是使用 nftables 還是 iptables 作為防火墻,以為它用了 iptables 而實(shí)際系統(tǒng)用的卻是 nftables。為此,問(wèn) chatgpt,說(shuō)可以如下明確指定防火墻動(dòng)作
action = nftables[name=SSH, port=ssh, protocol=tcp]
最終的 /etc/fail2ban/jail.d/sshd.conf 配置文件如下:
[sshd]
ignoreip = 127.0.0.1/8 192.168.0.1/24 192.168.4.1/24
bantime = 1h # 后來(lái)干脆改成 30d 了
findtime = 300
maxretry = 5
enabled = true
filter = sshd
action = nftables[name=SSH, port=ssh, protocol=tcp]
logpath = /var/log/myauth.log
然而,這樣之后仍不能成功把待封禁 IP 填進(jìn)防火墻。記得之前用 /etc/init.d/firewall restart 重啟防火墻是會(huì)有提示
Section @include[0] option 'reload' is not supported by fw4
于是查看 /etc/config/firewall,最后部分果然有 reload
config include
option path '/etc/firewall.fail2ban'
option enabled '1'
option reload '1'
我猜,一個(gè) config 段只要有問(wèn)題就無(wú)法被防火墻成功加載,這個(gè)不被支持的 reload 使得 fail2ban 相關(guān)的這部分設(shè)置并沒(méi)有生效。于是去掉 reload 這一行再重啟防火墻,待禁IP被成功加入防火墻,一切正常了!
5. 或許就用 dropbear 也行
我發(fā)現(xiàn) fail2ban 里有 /etc/fail2ban/filter.d/dropbear.conf 這個(gè)文件,這說(shuō)明不用把 dropbear 換成 sshd 或許也行,只不過(guò)需要從日志中提取 dropbear 相關(guān)條目,然后在 fail.d 中創(chuàng)建 dropbear.conf 而不是 sshd.conf 應(yīng)該就可以了。
6. 最后
發(fā)現(xiàn)日志中有好多如下的行并沒(méi)有被處理,既沒(méi)有識(shí)別其 IP 也沒(méi)有封禁。
Fri Aug 23 03:11:29 2024 sshd-session[3896]: Unable to negotiate with 170.64.167.224 port 41802: no matching host key type found. Their offer: ecdsa-sha2-nistp256, ......,ssh-rsa,ssh-dss [preauth]
原來(lái)是因?yàn)槟J(rèn)的 /etc/fail2ban/filer.d/sshd.conf 規(guī)則中使用的是 mode=normal 模式。試了改成 mode=extra 模式后是可以識(shí)別出失敗的 IP 的,但是不封禁;如果改成 mode=aggressive 的激進(jìn)模式則既可以識(shí)別也可以封禁 IP,就用它了! 可見(jiàn),sshd.conf 還是定義的比較全面的,反觀 dropbear.conf 就比較簡(jiǎn)單,內(nèi)容也不多。
另外,如果某個(gè) IP 被誤封,可以手動(dòng)解除封禁
fail2ban-client unban IP # 也可以指定多個(gè) IP,以空格隔開(kāi)
fail2ban-client unban --all # 手動(dòng)解除所有封禁