OpenWrt 版本为 23.05.4,本来以为直接 opkg install fail2ban
安装上再把之前的 sshd.conf 配置文件拷到 /etc/fail2ban/jail.d/ 下就行了,谁知道并没有那么简单,遇到不少问题。
一切都是日志问题
fail2ban 是通过分析 ssh 的登录日志来封禁IP的,然而OP为节省空间通常并不把日志文件写出来,并没有通常的 /var/log/auth.log 等类似的文件。由于日志文件无法读取,fail2ban无法正常工作,其实是无法正常启动。fail2ban-client status
会提示错误。虽然 /etc/init.d/fail2ban status
查看是在 running,但是 ps | grep fail2ban
根本没有相应进程!
1. dropbear => sshd
虽然我装了 openssh-server,但是OP默认的 ssh 服务器并不是 sshd 而是 dropbear,可以通过 netstat -nap |grep :22
查看。dropbear 是轻量级的 ssh 服务器,占用内存小功能也相对少,行为上和 sshd 也并不完全一样。一开始我以为不写日志是 /etc/ssh/sshd_config 中没有打开如下相应设置
SyslogFacility AUTH
LogLevel INFO
但事实上该文件对 dropbear 根本无效。于是关掉 dropbear 启用 sshd:
/etc/init.d/dropbear stop
/etc/init.d/dropbear disable
/etc/init.d/sshd start
/etc/init.d/sshd enbale
不过,刚进行完上述设置后 LUCI 登录总是密码错误,重新 ssh 登录OP也进不去,倒是之前没退出得 ssh 会话依然可用。原来是因为 sshd_config中没有启用 root 登录,加上如下设置并重启 sshd 后解决
PermitRootLogin yes
然而,启用 sshd 后登录OP仍然没有在 /var/log/ 中生成日志文件!!
2. logread
OP 默认的日志都是在内存中,要通过 logread
命令读取出来,于是想到通过 logread | grep sshd > /var/log/myauth.log
将日志保存在文件中,这样再重启 fail2ban 就能正常显示 fail2ban-client status sshd
的结果了
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:
其实,也可以通过 luci 图形界面设置将日志写到文件中,在 “系统 / 系统 / 系统属性 / 日志 / 将系统日志写入文件” 处设置并保存即可。不过这样的日志是所有系统日志,不只是 sshd 的。
3. 日志解析无果
有了日志后 fail2ban 可以正常运行了,为了测试,我有意错误登录多次,按规则应该被禁掉,然而 fail2ban-client status sshd
的结果和上面的一样都是 0,没有分析出登录失败也没有禁掉的 IP。fail2ban 是通过正则表达式匹配规则来分析日志的,sshd 的默认定义在 /etc/fail2ban/filter.d/sshd.conf
中,不过内容太多看不太明白。其实是可以通过 fail2ban-regex
命令分析日志查看匹配情况的,格式如下。
fail2ban-regex 日志文件 '...正则表达式匹配规则...'
显然,问题就在于配置文件中的默认规则并不能匹配出日志的条目,应该是格式不符。日志格式如下
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
通过猜测和尝试,发现把 sshd 前面的 auth.info 或 auth.err 去掉后就行了!
其实,logread
可以通过 -e
参数添加过滤内容,不需要用 grep 筛选,而且可以用 -f
参数保持一直运行监测最新日志,只要有一条就会输出一条。于是,最终比较好的生成日志的方式是在 /etc/rc.local
(该文件默认没有可执行权限,我加了,不知道不加是否行)中添加如下命令用于开机启动
logread -e sshd -f | sed 's/auth\.[^ ]* //' >> /var/log/myauth.log &
这样只要有新的 sshd 相关日志就会及时的追加到日志文件中,且已经把 auth.info 等内容去除。
4. 防火墙
日志正常分析了,fail2band status sshd
也能列出失败次数以及禁用的 IP 列表了。但是,如果没有把这些 IP 成功添加进防火墙的话依然是没有真正禁用。比如我有意多次登录失败的那个 IP 就在列表中,然而我仍然能登录。通过如下搜索防火墙规则
nft list ruleset | grep 相应IP
结果是什么都没有。这说明分析出要禁用的 IP 后并没有成功添加进防火墙!jail.d/sshd.conf 配置文件中我原来的 action 设置是
action = %(action_mwl)s
它应该对应 action.d 下 mail-whois-lines.conf 文件所定义的动作。由于我不清楚这个动作是使用 nftables 还是 iptables 作为防火墙,以为它用了 iptables 而实际系统用的却是 nftables。为此,问 chatgpt,说可以如下明确指定防火墙动作
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 # 后来干脆改成 30d 了
findtime = 300
maxretry = 5
enabled = true
filter = sshd
action = nftables[name=SSH, port=ssh, protocol=tcp]
logpath = /var/log/myauth.log
然而,这样之后仍不能成功把待封禁 IP 填进防火墙。记得之前用 /etc/init.d/firewall restart
重启防火墙是会有提示
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'
我猜,一个 config 段只要有问题就无法被防火墙成功加载,这个不被支持的 reload 使得 fail2ban 相关的这部分设置并没有生效。于是去掉 reload 这一行再重启防火墙,待禁IP被成功加入防火墙,一切正常了!
5. 或许就用 dropbear 也行
我发现 fail2ban 里有 /etc/fail2ban/filter.d/dropbear.conf
这个文件,这说明不用把 dropbear 换成 sshd 或许也行,只不过需要从日志中提取 dropbear 相关条目,然后在 fail.d 中创建 dropbear.conf 而不是 sshd.conf 应该就可以了。
6. 最后
发现日志中有好多如下的行并没有被处理,既没有识别其 IP 也没有封禁。
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]
原来是因为默认的 /etc/fail2ban/filer.d/sshd.conf
规则中使用的是 mode=normal
模式。试了改成 mode=extra
模式后是可以识别出失败的 IP 的,但是不封禁;如果改成 mode=aggressive
的激进模式则既可以识别也可以封禁 IP,就用它了! 可见,sshd.conf 还是定义的比较全面的,反观 dropbear.conf 就比较简单,内容也不多。
另外,如果某个 IP 被误封,可以手动解除封禁
fail2ban-client unban IP # 也可以指定多个 IP,以空格隔开
fail2ban-client unban --all # 手动解除所有封禁