自部署OpenBSD以来,经常发现有script kid使用暴力破解的方式试探SSH的密码,一直以来都不以为然,最近总部下发的通报让我痛下决心,非解决这个问题不可。

解决方式有两种:

  1. 将密码验证更改为证书认证;

  2. 实时监控`/var/log/authlog`,一旦发现非法IP,就列入黑名单,使用pf对黑名单进行过滤。

考虑到证书方式有些麻烦,所以我选择了第二种,经过一番研究,决定采用pf+fail2ban来实现。

1. 工作原理

OpenBSD使用/var/log/authlog来中记录每一次SSH访问,包括访问IP、用户名,以及认证结果(失败或成功),如下所示:

Mar  7 18:04:31 openbsd-2 sshd[21329]: Failed password for root from 114.251.14.194 port 36813 ssh2
Mar  7 18:04:31 openbsd-2 sshd[2578]: Received disconnect from 114.251.14.194: 11: Bye Bye
Mar  7 18:04:32 openbsd-2 sshd[17077]: Failed password for root from 114.251.14.194 port 38940 ssh2
Mar  7 18:04:33 openbsd-2 sshd[12636]: Received disconnect from 114.251.14.194: 11: Bye Bye
Mar  7 18:04:34 openbsd-2 sshd[5435]: Failed password for root from 114.251.14.194 port 40996 ssh2
Mar  7 18:04:34 openbsd-2 sshd[10562]: Received disconnect from 114.251.14.194: 11: Bye Bye
Mar  7 18:04:36 openbsd-2 sshd[13110]: Failed password for root from 114.251.14.194 port 43031 ssh2
Mar  7 18:04:36 openbsd-2 sshd[1946]: Received disconnect from 114.251.14.194: 11: Bye Bye
Mar  7 21:37:25 openbsd-2 sshd[3059]: Accepted password for root from 221.182.194.246 port 56502 ssh2
Mar  7 21:40:09 openbsd-2 sshd[32191]: Invalid user a from 211.137.70.139
Mar  7 21:40:09 openbsd-2 sshd[25000]: input_userauthj_request: invalid user a
Mar  7 21:40:09 openbsd-2 sshd[32191]: Failed password for invalid user a from 211.137.70.139 port 26429 ssh2
Mar  7 21:40:09 openbsd-2 sshd[25000]: Received disconnect from 211.137.70.139: 11: Bye Bye
Mar  7 21:40:10 openbsd-2 sshd[30727]: Invalid user aabdulka from 211.137.70.139
Mar  7 21:40:10 openbsd-2 sshd[7248]: input_userauth_request: invalid user aabdulka
Mar  7 21:40:10 openbsd-2 sshd[30727]: Failed password for invalid user aabdulka from 211.137.70.139 port 26572 ssh2
Mar  7 21:40:10 openbsd-2 sshd[7248]: Received disconnect from 211.137.70.139: 11: Bye Bye

从上面的记录可以看出,除了`221.182.194.246`这个用户输入了正确的帐号/密码外,211.137.70.139`则一直在尝试不同的用户名和密码,这个用户的异常行为是:短时间内重复尝试登录失败。从这可以判断该IP是一个非法IP,这时候我们可以使用一个定时执行的脚本,扫描/var/log/authlog`,统计IP的访问失败次数,当达到一定阀值的时候就将结果添加到pf的<blacklist>这个table中,再利用block语句就可以将恶意IP过滤掉。

fail2ban就是这个定时脚本。

2. 安装

fail2ban最新版本为0.9,OpenBSD 4.9的port中版本比较低,因此我采用了编译源码方式安装fail2ban最新版本。

# pkg_add -iv python
# pkg_add -iv gamin
# cd /usr/src
# wget http://www.fail2ban.org/nightly/fail2ban-trunk.tar.bz2
# tar xjf fail2ban-trunk.tar.bz2
# cd fail2ban-0.9-SVN
# python2.6 setup.py install

3. 配置

3.1 pf

pf的配置非常简单,只要在pf.conf配置文件靠前的位置添加anchor fail2ban这一条语句即可。这就在pf.conf中创建了一个临时锚,同时创建了一个同名的table,fail2ban将对该table进行增、删IP的操作。

3.2 fail2ban

在配置fail2ban之前,需说明fail2ban是如何工作的,先来看看它的配置文件目录:

root@openbsd-2/etc/fail2ban:# tree ./
./
|-- action.d
|   |-- hostsdeny.conf
|   |-- ipfw.conf
|   |-- iptables-allports.conf
|   |-- iptables-multiport-log.conf
|   |-- iptables-multiport.conf
|   |-- iptables-new.conf
|   |-- iptables.conf
|   |-- pf-drop-all.conf
.....
|   `-- shorewall.conf
|-- fail2ban.conf
|-- filter.d
|   |-- apache-auth.conf
|   |-- apache-badbots.conf
|   |-- apache-noscript.conf
|   |-- apache-overflows.conf
.....
|   |-- qmail.conf
|   |-- sasl.conf
|   |-- sshd-ddos.conf
|   |-- sshd.conf
|   `-- xinetd-fail.conf
|-- jail.conf
`-- pf-anchor.conf
  1. fail2ban是一个python脚本,将通过fixme启动,随后一直在后台运行,实时监控/var/log/authlog这个文件;

  2. 一旦/var/log/authlog发生变化,fail2ban将根据fail2ban.conffilter.d/的定义,使用sshd-pf.confsshd-ddos-pf.conf这两个过滤器对变化日志进行比对;

  3. 若变化日志符合规则,而且数量超出定义的阀值,fail2ban将触发action.d/中相对应的的指令,将非法IP添加到pf的fail2ban这个黑名单中;

  4. 该非法IP再次访问访问OpenBSD SSHd时候将被block语句拒绝掉;

  5. fail2ban脚本会定时对pf黑名单进行清理,也就是过一段时间后,该IP又可以重新访问OpenBSD的SSHd服务。之所以这么设计是有原因的,因为黑客往往通过僵尸网络对目标进行攻击,非法IP是动态变化的,所以不能永久把它列入黑名单。

下面将对配置文件进行分别说明:

  • fail2ban.conf该文件配置fail2ban的log级别、log文件路径和socket。

  • jail.conf,jail是监牢的意思,该文件相当于一个监狱,建了很多个单间,每个单间针对一个单独的服务,在本例中就启用了ssh-pfssd-ddos-pf这两个jail,相对应的,filter.d/目录里面也必须包含同名的配置文件。

  • filter.d/,该目录中包含了sshd-pf.confssh-ddos-pf.conf这两个文件,相当于过滤器,sshd-pf.conf文件的内容如下:

# Fail2Ban configuration file
#
# Author: Cyril Jaquier
#
# $Revision: 695 $
#

[Definition]

# Option:  failregex
# Notes.:  regex to match the password failures messages in the logfile. The
#          host must be matched by a group named "host". The tag "<HOST>" can
#          be used for standard IP/hostname matching and is only an alias for
#          (?:::f{4,6}:)?(?P<host>\S+)
# Values:  TEXT
#
failregex = <TIME> <PREFIX> (?:error: PAM: )?Authentication failure for .* from <HOST>\s*$
            <TIME> <PREFIX> Failed [-/\w]+ for .* from <HOST>(?: port \d*)?(?: ssh\d*)?$
            <TIME> <PREFIX> ROOT LOGIN REFUSED.* FROM <HOST>\s*$
            <TIME> <PREFIX> [iI](?:llegal|nvalid) user .* from <HOST>\s*$
            <TIME> <PREFIX> User \S+ from <HOST> not allowed because not listed in AllowUsers$
            <TIME> <PREFIX> authentication failure; logname=\S* uid=\S* euid=\S* tty=\S* ruser=\S* rhost=<HOST>(?:\s+user=.*)?\s
*$
            <TIME> <PREFIX> refused connect from \S+ \(<HOST>\)\s*$
            <TIME> <PREFIX> Address <HOST> .* POSSIBLE BREAK-IN ATTEMPT\s*$

# Option:  ignoreregex
# Notes.:  regex to ignore. If this regex matches, the line is ignored.
# Values:  TEXT
#
ignoreregex =

这些配置文件中最重要的是failregex,这个语句由一组正则表达式组成,将被用于与非法入侵日志进行比较。看起来像火星文,不过读者大可放心,fail2ban已经提供了丰富的过滤器,无需担心。

  • action.d/,fail2ban根据该目录的文件向防火墙发出指令。譬如iptables.conf, 由于fail2ban并不支持pf,所以需用户自行创建pf-drop-all.conf这个文件,内容如下:

[Definition]
actionstart = /sbin/pfctl -a fail2ban -t fail2ban -Ts || /sbin/pfctl -a fail2ban -f /etc/fail2ban/pf-anchor.conf
actionstop = /sbin/pfctl -a fail2ban -F rules
actioncheck = /sbin/pfctl -s info | grep Enabled
actionban = /sbin/pfctl -a fail2ban -t fail2ban -T add <ip> && /sbin/pfctl -k <ip>
actionunban = /sbin/pfctl -a fail2ban -t fail2ban -T delete <ip>
[Init]

这样就完成了整个fail2ban的配置,实际需要配置的内容很少,主要是要创建pf-drop-all.conf这个文件。

4. 排错

  • 查看状态

    # fail2ban-client status
    Status
    |- Number of jail:      2
    `- Jail list:           ssh-ddos-pf, ssh-pf
    # fail2ban-client status ssh-ddos-pf
    Status for the jail: ssh-ddos-pf
    |- filter
    |  |- File list:        /var/log/authlog
    |  |- Currently failed: 0
    |  `- Total failed:     7
    `- action
       |- Currently banned: 0
       |  `- IP list:
       `- Total banned:     0
  • 查看log

    tail -f /var/log/fail2ban.log

    如果发现异常,fail2ban会显示非法IP信息。

  • 查看pf黑名单

    /sbin/pfctl -a fail2ban -t fail2ban -T show