I will show you how to install fail2ban on centos 6 and centos 7 to protect SSH brute force attacks. There are many ways to protect SSH server, the best way is to use ssh-keys authentication rather than regular password authentication. I have also written a long detailed article How to install, config and secure openssh server. In that article I have shown you several ways to secure ssh server:
- Use SSH Protocol 2
- Limit access to ssh server
- Config Idle Log Out Timeout Interval
- Disable .rhosts Files
- Disable Host-Based Authentication
- Disable root Login via SSH
- Having a Warning Banner
- Change SSH server port
- Disable Empty Passwords
- Use StrictModes
To make your ssh server more secure, you can try fail2ban. Fail2ban is a great software that let you monitor system services like SSH, Apache, proftpd, pure-ftpd, vsftpd, lighttpd-auth, sendmail, quid, cyrus-imap, … and block the ip(s) which are trying to brute force your system.
Install Fail2ban
Fail2ban is not available by default from CentOS, but you can always download and install fail2ban from Extra Packages for Enterprise Linux (EPEL) repository
For CentOS 6
# rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm # yum install fail2ban
For CentOS 7
# rpm -Uvh http://dl.fedoraproject.org/pub/epel/7/x86_64/e/epel-release-7-2.noarch.rpm # yum install fail2ban
Configure fail2ban
Fail2ban can be configured for various software, but for this article I will only show you how to configure fail2ban to protect and prevent brute force for SSH server. The default fail2ban jail file is located at /etc/fail2ban/jail.conf . You don’t want to modify this file, you want to create a new jail file named jail.local
# cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
After you created jail.local file, lets edit the file
# nano /etc/fail2ban/jail.local
The first section you can modify
ignoreip: which fail2ban will never block that IP, you should put your public IP here so fail2ban will never lock you out of your SSH server.
bantime: the time amount (in second) fail2ban will ban a hostname/IP (I suggest you set this high to decrease the chance bots/attackers brute force)
findtime: is the time window fail2ban keeps track of the failed login attempts.
maxretry: is the number failed login attempts before a host/IP get blocked.
[DEFAULT] # "ignoreip" can be an IP address, a CIDR mask or a DNS host. Fail2ban will not # ban a host which matches an address in this list. Several addresses can be # defined using space separator. ignoreip = 127.0.0.1/8 # External command that will take an tagged arguments to ignore, e.g. , # and return true if the IP is to be ignored. False otherwise. # # ignorecommand = /path/to/command ignorecommand = # "bantime" is the number of seconds that a host is banned. bantime = 600 # A host is banned if it has generated "maxretry" during the last "findtime" # seconds. findtime = 600 # "maxretry" is the number of failures before a host get banned. maxretry = 3 # "backend" specifies the backend used to get files modification. # Available options are "pyinotify", "gamin", "polling" and "auto". # This option can be overridden in each jail as well. # # pyinotify: requires pyinotify (a file alteration monitor) to be installed. # If pyinotify is not installed, Fail2ban will use auto. # gamin: requires Gamin (a file alteration monitor) to be installed. # If Gamin is not installed, Fail2ban will use auto. # polling: uses a polling algorithm which does not require external libraries. # auto: will try to use the following backends, in order: # pyinotify, gamin, polling. backend = auto # "usedns" specifies if jails should trust hostnames in logs, # warn when DNS lookups are performed, or ignore all hostnames in logs # # yes: if a hostname is encountered, a DNS lookup will be performed. # warn: if a hostname is encountered, a DNS lookup will be performed, # but it will be logged as a warning. # no: if a hostname is encountered, will not be used for banning, # but it will be logged as info. usedns = warn
The next section we should look at is:
[ssh-iptables] enabled = true filter = sshd action = iptables[name=SSH, port=ssh, protocol=tcp] sendmail-whois[name=SSH, [email protected], [email protected], sendername="Fail2Ban"] logpath = /var/log/secure maxretry = 5 [ssh-ddos] enabled = false filter = sshd-ddos action = iptables[name=SSHDDOS, port=ssh, protocol=tcp] logpath = /var/log/sshd.log maxretry = 2
As you can see, the default fail2ban configuration is pretty much well configured for you, you can tweak here and there a little bit but it’s ready for you to go.
Restart fail2ban
You have to restart fail2ban to let the new configuration can take effect.
CentOS 6:
# service fail2ban restart
CentOS 7:
# systemctl restart fail2ban.service
Start fail2ban at boot
To let fail2ban starts at boot if you reboot your system
CentOS 6:
# chkconfig fail2ban on
CentOS 7:
# systemctl enable fail2ban
Check fail2ban banned IP
After a while (few days) you can check your iptables rules to see what fail2ban has banned
# iptables -L
This is an example from one of my machine
Chain fail2ban-SSH (1 references) target prot opt source destination REJECT all -- 122.225.109.211 anywhere reject-with icmp-port-unreachable REJECT all -- 58.58.27.124 anywhere reject-with icmp-port-unreachable REJECT all -- 122.225.97.109 anywhere reject-with icmp-port-unreachable REJECT all -- 122.225.109.206 anywhere reject-with icmp-port-unreachable REJECT all -- abts-kk-dynamic-152.191.172.122.airtelbroadband.in anywhere reject-with icmp-port-unreachable REJECT all -- 219.230.50.51 anywhere reject-with icmp-port-unreachable REJECT all -- 113.240.234.21 anywhere reject-with icmp-port-unreachable REJECT all -- 122.226.131.132 anywhere reject-with icmp-port-unreachable REJECT all -- 60.213.190.98 anywhere reject-with icmp-port-unreachable REJECT all -- 58.221.66.24 anywhere reject-with icmp-port-unreachable REJECT all -- 42-64-231-201.fibertel.com.ar anywhere reject-with icmp-port-unreachable REJECT all -- 122.225.97.111 anywhere reject-with icmp-port-unreachable REJECT all -- 117.117.198.5 anywhere reject-with icmp-port-unreachable REJECT all -- 202.63.166.69 anywhere reject-with icmp-port-unreachable REJECT all -- host114-72-static.18-80-b.business.telecomitalia.it anywhere reject-with icmp-port-unreachable REJECT all -- abts-north-dynamic-147.33.163.122.airtelbroadband.in anywhere reject-with icmp-port-unreachable REJECT all -- 115.185.145.233 anywhere reject-with icmp-port-unreachable REJECT all -- 222.221.240.6 anywhere reject-with icmp-port-unreachable REJECT all -- abts-kk-dynamic-244.63.179.122.airtelbroadband.in anywhere reject-with icmp-port-unreachable REJECT all -- bfb47839.virtua.com.br anywhere reject-with icmp-port-unreachable REJECT all -- 210.98.148.172 anywhere reject-with icmp-port-unreachable REJECT all -- r190-64-79-146.su-static.adinet.com.uy anywhere reject-with icmp-port-unreachable REJECT all -- 58.62.239.210 anywhere reject-with icmp-port-unreachable REJECT all -- 189-20-94-73.customer.tdatabrasil.net.br anywhere reject-with icmp-port-unreachable REJECT all -- 218.2.0.127 anywhere reject-with icmp-port-unreachable REJECT all -- abts-north-dynamic-034.38.163.122.airtelbroadband.in anywhere reject-with icmp-port-unreachable REJECT all -- 202.197.127.218 anywhere reject-with icmp-port-unreachable REJECT all -- 58.20.53.8 anywhere reject-with icmp-port-unreachable REJECT all -- abts-north-dynamic-190.34.163.122.airtelbroadband.in anywhere reject-with icmp-port-unreachable REJECT all -- 58.58.27.122 anywhere reject-with icmp-port-unreachable REJECT all -- 59.99.36.166 anywhere reject-with icmp-port-unreachable REJECT all -- abts-north-dynamic-221.54.163.122.airtelbroadband.in anywhere reject-with icmp-port-unreachable REJECT all -- 213.201.48.94.static.user.ono.com anywhere reject-with icmp-port-unreachable REJECT all -- server109-228-30-49.live-servers.net anywhere reject-with icmp-port-unreachable REJECT all -- 59.60.176.197 anywhere reject-with icmp-port-unreachable REJECT all -- 219.142.20.182 anywhere reject-with icmp-port-unreachable REJECT all -- 219.224.52.90 anywhere reject-with icmp-port-unreachable REJECT all -- 112.124.50.24 anywhere reject-with icmp-port-unreachable REJECT all -- ip-50-62-30-150.ip.secureserver.net anywhere reject-with icmp-port-unreachable REJECT all -- ec2-54-94-160-237.sa-east-1.compute.amazonaws.com anywhere reject-with icmp-port-unreachable REJECT all -- 14.170.14.34 anywhere reject-with icmp-port-unreachable REJECT all -- 94.211.60.190.host.ifxnetworks.com anywhere reject-with icmp-port-unreachable REJECT all -- 60.160.97.123 anywhere reject-with icmp-port-unreachable REJECT all -- 183.224.97.88 anywhere reject-with icmp-port-unreachable REJECT all -- 58.11.28.94.enforta.com anywhere reject-with icmp-port-unreachable REJECT all -- 125-227-191-235.HINET-IP.hinet.net anywhere reject-with icmp-port-unreachable REJECT all -- 200.166.195.52 anywhere reject-with icmp-port-unreachable REJECT all -- static.200.219.209.134.datacenter1.com.br anywhere reject-with icmp-port-unreachable REJECT all -- 193.253.101.67 anywhere reject-with icmp-port-unreachable REJECT all -- 222.133.29.94 anywhere reject-with icmp-port-unreachable REJECT all -- mail.unimedsorocaba.com.br anywhere reject-with icmp-port-unreachable REJECT all -- 210.40.30.21 anywhere reject-with icmp-port-unreachable REJECT all -- 58.210.170.18 anywhere reject-with icmp-port-unreachable REJECT all -- b39bafce.virtua.com.br anywhere reject-with icmp-port-unreachable REJECT all -- customer-static-210-94-141.iplannetworks.net anywhere reject-with icmp-port-unreachable REJECT all -- 14.169.28.105 anywhere reject-with icmp-port-unreachable REJECT all -- 211.206.124.4 anywhere reject-with icmp-port-unreachable REJECT all -- 117.218.155.76 anywhere reject-with icmp-port-unreachable REJECT all -- 136.38.202.62.static.wline.lns.sme.cust.swisscom.ch anywhere reject-with icmp-port-unreachable REJECT all -- ec2-54-86-167-57.compute-1.amazonaws.com anywhere reject-with icmp-port-unreachable REJECT all -- 50.97.43.46-static.reverse.softlayer.com anywhere reject-with icmp-port-unreachable REJECT all -- 177.54.136.58 anywhere reject-with icmp-port-unreachable REJECT all -- 218.80.194.42 anywhere reject-with icmp-port-unreachable REJECT all -- 216.196.133.34 anywhere reject-with icmp-port-unreachable REJECT all -- 221.125.86.204 anywhere reject-with icmp-port-unreachable REJECT all -- 175.139.231.217 anywhere reject-with icmp-port-unreachable REJECT all -- 121.65.252.200 anywhere reject-with icmp-port-unreachable REJECT all -- customer-CLN-154-138.megared.net.mx anywhere reject-with icmp-port-unreachable REJECT all -- rasoff.ru anywhere reject-with icmp-port-unreachable REJECT all -- 26.0.95.219.kmr01-home.tm.net.my anywhere reject-with icmp-port-unreachable REJECT all -- 220.191.230.219 anywhere reject-with icmp-port-unreachable REJECT all -- wsip-98-172-21-240.dc.dc.cox.net anywhere reject-with icmp-port-unreachable REJECT all -- adsl.viettel.vn anywhere reject-with icmp-port-unreachable REJECT all -- 213.165.37.54 anywhere reject-with icmp-port-unreachable REJECT all -- 189-68-104-134.dsl.telesp.net.br anywhere reject-with icmp-port-unreachable REJECT all -- 210.72.245.5 anywhere reject-with icmp-port-unreachable REJECT all -- ip-194-206.sn1.eutelia.it anywhere reject-with icmp-port-unreachable REJECT all -- covial3.vinicolaaurora.com.br anywhere reject-with icmp-port-unreachable REJECT all -- 39.252.9.221.adsl-pool.jlccptt.net.cn anywhere reject-with icmp-port-unreachable REJECT all -- 117.27.158.91 anywhere reject-with icmp-port-unreachable REJECT all -- hn.kd.ny.adsl anywhere reject-with icmp-port-unreachable REJECT all -- 186-244-215-103.user.veloxzone.com.br anywhere reject-with icmp-port-unreachable REJECT all -- 202.194.131.143 anywhere reject-with icmp-port-unreachable REJECT all -- 186.215.144.88.static.gvt.net.br anywhere reject-with icmp-port-unreachable REJECT all -- 123.127.36.162 anywhere reject-with icmp-port-unreachable REJECT all -- 177.43.213.35.static.gvt.net.br anywhere reject-with icmp-port-unreachable REJECT all -- host26-217-static.33-88-b.business.telecomitalia.it anywhere reject-with icmp-port-unreachable REJECT all -- 144.0.0.51 anywhere reject-with icmp-port-unreachable REJECT all -- 122.225.109.220 anywhere reject-with icmp-port-unreachable REJECT all -- 122.225.109.112 anywhere reject-with icmp-port-unreachable REJECT all -- v103-18-4-214.myvps.vn anywhere reject-with icmp-port-unreachable REJECT all -- 218.2.0.125 anywhere reject-with icmp-port-unreachable REJECT all -- 222.186.130.242 anywhere reject-with icmp-port-unreachable REJECT all -- 220.51.174.61.dial.wz.zj.dynamic.163data.com.cn anywhere reject-with icmp-port-unreachable REJECT all -- 221.228.205.196 anywhere reject-with icmp-port-unreachable REJECT all -- 219.51.174.61.dial.wz.zj.dynamic.163data.com.cn anywhere reject-with icmp-port-unreachable REJECT all -- ppp14-208.tis-dialog.ru anywhere reject-with icmp-port-unreachable REJECT all -- ip72-213-191-22.ok.ok.cox.net anywhere reject-with icmp-port-unreachable REJECT all -- static-50-122-222-171.roch.ny.frontiernet.net anywhere reject-with icmp-port-unreachable REJECT all -- broadband-109-173-65-5.nationalcablenetworks.ru anywhere reject-with icmp-port-unreachable REJECT all -- mx6.fund123.cn anywhere reject-with icmp-port-unreachable REJECT all -- broadband-5-228-78-138.nationalcablenetworks.ru anywhere reject-with icmp-port-unreachable REJECT all -- ip68-227-118-117.ok.ok.cox.net anywhere reject-with icmp-port-unreachable REJECT all -- r75-110-184-14.unknwn.ab.dh.suddenlink.net anywhere reject-with icmp-port-unreachable REJECT all -- broadband-178-140-6-184.nationalcablenetworks.ru anywhere reject-with icmp-port-unreachable REJECT all -- 122.225.97.74 anywhere reject-with icmp-port-unreachable REJECT all -- 222.186.58.241 anywhere reject-with icmp-port-unreachable REJECT all -- 122.225.109.209 anywhere reject-with icmp-port-unreachable
Update 12/01/2014, it look like jail.local file for Centos 7 is not the same for CentOS 6 since Centos 7 uses firewalld as default rather than iptables. In jail.local file for Centos 7 you will have to uncomment a few lines to enable ssh banning ip in fail2ban.
This is the section at the very beginning of fail2ban jail.local file in CentOS 7. Uncomment the lines that I did below.
[DEFAULT] bantime = 3600 # [sshd] enabled = true # # See jail.conf(5) man page for more information
In Centos 7, to check if fail2ban is running and working, you can use this command
# journalctl -a -f -n1000 -u fail2ban
The output:
-- Logs begin at Sun 2014-11-30 02:12:42 CET. -- Dec 01 22:55:06 sd-21451 systemd[1]: Starting Fail2ban Service... Dec 01 22:55:11 sd-21451 fail2ban-client[12869]: 2014-12-01 22:55:11,437 fail2ban.server.server[12872]: INFO Starting Fail2ban v0.9.0 Dec 01 22:55:11 sd-21451 fail2ban-client[12869]: 2014-12-01 22:55:11,439 fail2ban.server.server[12872]: INFO Starting in daemon mode Dec 01 22:55:12 sd-21451 systemd[1]: Started Fail2ban Service. Dec 01 23:12:48 sd-21451 systemd[1]: Stopping Fail2ban Service... Dec 01 23:12:48 sd-21451 fail2ban-client[13159]: Shutdown successful Dec 01 23:12:48 sd-21451 systemd[1]: Starting Fail2ban Service... Dec 01 23:12:52 sd-21451 fail2ban-client[13163]: 2014-12-01 23:12:52,638 fail2ban.server.server[13164]: INFO Starting Fail2ban v0.9.0 Dec 01 23:12:52 sd-21451 fail2ban-client[13163]: 2014-12-01 23:12:52,640 fail2ban.server.server[13164]: INFO Starting in daemon mode Dec 01 23:12:53 sd-21451 systemd[1]: Started Fail2ban Service.
or
# systemctl status fail2ban -l
and the output
fail2ban.service - Fail2ban Service Loaded: loaded (/usr/lib/systemd/system/fail2ban.service; enabled) Active: active (running) since Mon 2014-12-01 23:12:53 CET; 9h ago Process: 13159 ExecStop=/usr/bin/fail2ban-client stop (code=exited, status=0/SUCCESS) Process: 13163 ExecStart=/usr/bin/fail2ban-client -x start (code=exited, status=0/SUCCESS) Main PID: 13166 (fail2ban-server) CGroup: /system.slice/fail2ban.service └─13166 /usr/bin/python /usr/bin/fail2ban-server -b -s /var/run/fail2ban/fail2ban.sock -p /var/run/fail2ban/fail2ban.pid -x Dec 01 23:12:52 sd-21451 fail2ban-client[13163]: 2014-12-01 23:12:52,638 fail2ban.server.server[13164]: INFO Starting Fail2ban v0.9.0 Dec 01 23:12:52 sd-21451 fail2ban-client[13163]: 2014-12-01 23:12:52,640 fail2ban.server.server[13164]: INFO Starting in daemon mode Dec 01 23:12:53 sd-21451 systemd[1]: Started Fail2ban Service.