fail2ban
可以监视你的系统日志,然后匹配日志的错误信息(正则式匹配)执行相应的屏蔽动作,一般情况下是调用防火墙iptables
屏蔽,CentOS 7
改由firewalld
控制。
如:当有人在试探你的SSH
、SMTP
、FTP
密码,只要达到你预设的次数,fail2ban
就会调用防火墙屏蔽这个IP,而且可以发送e-mail通知系统管理员,是一款很实用、很强大的软件!
fail2ban
由python
语言开发,基于logwatch
、gamin
、iptables
、tcp-wrapper
、shorewall
等。如果想要发送邮件通知道,那还需要安装postfix
或sendmail
。
安装fail2ban
添加源
1 | wget http://epel.mirror.nucleus.be/7/x86_64/e/epel-release-7-5.noarch.rpm |
安装fail2ban
CentOS 7
已经将防火墙从iptables
升级到firewalld
,所以我们需要安装firewalld
版本的fail2ban
。1
yum install fail2ban-firewalld fail2ban-systemd
配置fail2ban
创建jail.local
配置文件1
2
3
4
5
6
7
8# /etc/fail2ban/jail.local
[DEFAULT]
findtime = 5000 # 检查周期。
[sshd]
enabled = true # 启用规则
bantime = 86400 # 阻止时间,一天。单位秒。
maxretry = 3 # 重试次数
上面的代码创建了一个简单的监狱配置,包含了一个针对sshd
的保护规则。
jail
配置文件参数说明
名称 | 默认值 | 描述 |
---|---|---|
enabled | 是否启用规则 | |
filter | 过滤规则文件名字。更多过滤规则存放在/etc/fail2ban/filter.d/ 目录中 | |
logpath | /var/log/messages | 监控的日志路径 |
maxretry | 5 | 匹配次数,达到次数的IP会被阻止访问 |
findtime | 600秒 | 检测周期,阻止在检测周期内达到失败次数的IP访问服务 |
bantime | 600秒 | 阻止时间,填负数会被永久阻止访问 |
banaction | 阻止IP时执行的动作。更多动作存放在/etc/fail2ban/action.d/ |
测试规则
执行1
fail2ban-client -d
输出:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55['set', 'syslogsocket', 'auto']
['set', 'loglevel', 'INFO']
['set', 'logtarget', '/var/log/fail2ban.log']
['set', 'dbfile', '/var/lib/fail2ban/fail2ban.sqlite3']
['set', 'dbpurgeage', 86400]
['add', 'sshd', 'systemd']
['set', 'sshd', 'usedns', 'warn']
['set', 'sshd', 'maxretry', 3]
['set', 'sshd', 'addignoreip', '127.0.0.1/8']
['set', 'sshd', 'logencoding', 'auto']
['set', 'sshd', 'bantime', 86400]
['set', 'sshd', 'ignorecommand', '']
['set', 'sshd', 'findtime', 5000]
['set', 'sshd', 'maxlines', '10']
['set', 'sshd', 'addfailregex', '^\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*(?:error: PAM: )?[aA]uthentication (?:failure|error) for .* from <HOST>( via \\S+)?\\s*$']
['set', 'sshd', 'addfailregex', '^\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*(?:error: PAM: )?User not known to the underlying authentication module for .* from <HOST>\\s*$']
['set', 'sshd', 'addfailregex', '^\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*Failed \\S+ for .*? from <HOST>(?: port \\d*)?(?: ssh\\d*)?(: (ruser .*|(\\S+ ID \\S+ \\(serial \\d+\\) CA )?\\S+ (?:[\\da-f]{2}:){15}[\\da-f]{2}(, client user ".*", client host ".*")?))?\\s*$']
['set', 'sshd', 'addfailregex', '^\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*ROOT LOGIN REFUSED.* FROM <HOST>\\s*$']
['set', 'sshd', 'addfailregex', '^\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*[iI](?:llegal|nvalid) user .* from <HOST>\\s*$']
['set', 'sshd', 'addfailregex', '^\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*User .+ from <HOST> not allowed because not listed in AllowUsers\\s*$']
['set', 'sshd', 'addfailregex', '^\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*User .+ from <HOST> not allowed because listed in DenyUsers\\s*$']
['set', 'sshd', 'addfailregex', '^\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*User .+ from <HOST> not allowed because not in any group\\s*$']
['set', 'sshd', 'addfailregex', '^\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*refused connect from \\S+ \\(<HOST>\\)\\s*$']
['set', 'sshd', 'addfailregex', '^\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*Received disconnect from <HOST>: 3: \\S+: Auth fail$']
['set', 'sshd', 'addfailregex', '^\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*User .+ from <HOST> not allowed because a group is listed in DenyGroups\\s*$']
['set', 'sshd', 'addfailregex', "^\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*User .+ from <HOST> not allowed because none of user's groups are listed in AllowGroups\\s*$"]
['set', 'sshd', 'addfailregex', '^(?P<__prefix>\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*)User .+ not allowed because account is locked<SKIPLINES>(?P=__prefix)(?:error: )?Received disconnect from <HOST>: 11: .+ \\[preauth\\]$']
['set', 'sshd', 'addfailregex', '^(?P<__prefix>\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*)Disconnecting: Too many authentication failures for .+? \\[preauth\\]<SKIPLINES>(?P=__prefix)(?:error: )?Connection closed by <HOST> \\[preauth\\]$']
['set', 'sshd', 'addfailregex', '^(?P<__prefix>\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*)Connection from <HOST> port \\d+(?: on \\S+ port \\d+)?<SKIPLINES>(?P=__prefix)Disconnecting: Too many authentication failures for .+? \\[preauth\\]$']
['set', 'sshd', 'addfailregex', '^\\s*(<[^.]+\\.[^.]+>)?\\s*(?:\\S+ )?(?:kernel: \\[ *\\d+\\.\\d+\\] )?(?:@vserver_\\S+ )?(?:(?:\\[\\d+\\])?:\\s+[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?|[\\[\\(]?sshd(?:\\(\\S+\\))?[\\]\\)]?:?(?:\\[\\d+\\])?:?)?\\s(?:\\[ID \\d+ \\S+\\])?\\s*pam_unix\\(sshd:auth\\):\\s+authentication failure;\\s*logname=\\S*\\s*uid=\\d*\\s*euid=\\d*\\s*tty=\\S*\\s*ruser=\\S*\\s*rhost=<HOST>\\s.*$']
['set', 'sshd', 'addjournalmatch', '_SYSTEMD_UNIT=sshd.service', '+', '_COMM=sshd']
['set', 'sshd', 'addaction', 'firewallcmd-ipset']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'actionban', 'ipset add fail2ban-<name> <ip> timeout <bantime> -exist']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'actionstop', 'firewall-cmd --direct --remove-rule ipv4 filter <chain> 0 -p <protocol> -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>\nipset flush fail2ban-<name>\nipset destroy fail2ban-<name>']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'actionstart', 'ipset create fail2ban-<name> hash:ip timeout <bantime>\nfirewall-cmd --direct --add-rule ipv4 filter <chain> 0 -p <protocol> -m multiport --dports <port> -m set --match-set fail2ban-<name> src -j <blocktype>']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'actionunban', 'ipset del fail2ban-<name> <ip> -exist']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'iptables', 'iptables <lockingopt>']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'known/chain', 'INPUT']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'known/lockingopt', '-w']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'protocol', 'tcp']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'name', 'sshd']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'chain', 'INPUT']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'known/__name__', 'Init']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'known/protocol', 'tcp']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'known/port', 'ssh']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'known/returntype', 'RETURN']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'known/iptables', 'iptables <lockingopt>']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'lockingopt', '-w']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'known/name', 'default']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'known/blocktype', 'REJECT --reject-with icmp-port-unreachable']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'returntype', 'RETURN']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'blocktype', 'REJECT --reject-with icmp-port-unreachable']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'port', 'ssh']
['set', 'sshd', 'action', 'firewallcmd-ipset', 'bantime', '86400']
['start', 'sshd']
可以看到我们前面的设置已经生效。
检测规则
执行1
fail2ban-client status
输出:1
2
3Status
|- Number of jail: 1
`- Jail list: sshd
sshd
规则已经启用。
添加fail2ban
并启动
1 | systemctl enable fail2ban |
检查被阻止的IP
执行:1
tail -f /var/log/fail2ban.log
输出:1
2
3
4
5
6
7
8
9
102015-12-10 11:54:40,693 fail2ban.actions [12313]: NOTICE [sshd] Ban 194.63.140.69
2015-12-10 11:54:40,702 fail2ban.actions [12313]: NOTICE [sshd] Ban 213.199.193.27
2015-12-10 11:54:40,712 fail2ban.actions [12313]: NOTICE [sshd] Ban 43.229.53.63
2015-12-10 11:54:40,745 fail2ban.actions [12313]: NOTICE [sshd] 113.195.134.231 already banned
2015-12-10 11:54:40,834 fail2ban.filter [12313]: INFO [sshd] Found 213.199.193.27
2015-12-10 11:54:40,847 fail2ban.filter [12313]: WARNING Determined IP using DNS Lookup: 213-199-193-27.tktelekom.pl = ['213.199.193.27']
2015-12-10 11:54:40,848 fail2ban.filter [12313]: INFO [sshd] Found 213.199.193.27
2015-12-10 11:54:41,746 fail2ban.actions [12313]: NOTICE [sshd] 213.199.193.27 already banned
2015-12-10 11:54:42,748 fail2ban.actions [12313]: NOTICE [sshd] 194.63.140.69 already banned
2015-12-10 11:54:42,778 fail2ban.filter [12313]: INFO [sshd] Found 213.199.193.27
坏蛋们都被关到监狱里面了~