DB-유

Hardening SSH: Ports, Fail2ban, and AllowUsers

· linux, ssh, security, fail2ban

I got tired of scrolling through /var/log/auth.log full of Failed password for invalid user admin from 185.x.x.x just to find a real event. Key-only auth already makes those attempts pointless, but they still fill logs and burn CPU on handshakes. So I add three layers on top: a different port, Fail2ban, and AllowUsers.

Examples are CentOS 7, with Ubuntu 16.04 differences noted. I'm assuming key login already works and PasswordAuthentication no is set. Keep a second SSH session open the whole time.

Move sshd off port 22

Changing the port isn't security by itself, but it removes nearly all the automated noise. Pick something above 1024 that nothing else uses, like 2222.

Edit /etc/ssh/sshd_config:

Port 2222

On CentOS 7 with SELinux enforcing, you must tell SELinux that sshd is allowed to bind the new port, or it will fail to start:

yum -y install policycoreutils-python
semanage port -a -t ssh_port_t -p tcp 2222

Then open the port in the firewall and close the old one:

firewall-cmd --permanent --add-port=2222/tcp
firewall-cmd --permanent --remove-service=ssh
firewall-cmd --reload

On Ubuntu with ufw: ufw allow 2222/tcp and ufw delete allow OpenSSH.

Test the configuration and restart:

sshd -t
systemctl restart sshd

Connect with ssh -p 2222 user@host from a new terminal before closing the old session. Put a Port 2222 entry in ~/.ssh/config so you don't have to type it.

AllowUsers

Even with keys only, I want sshd to reject any account that isn't explicitly listed. It covers service accounts that accidentally end up with a shell and an authorized_keys file. Append to sshd_config:

AllowUsers deploy backup@10.0.0.0/24
PermitRootLogin no
MaxAuthTries 3
LoginGraceTime 30

AllowUsers accepts user or user@host patterns, so here backup can only log in from the internal network. Anyone else gets Permission denied before the key is even looked at. AllowGroups works the same way if you'd rather manage a group like sshusers.

Run sshd -t and systemctl reload sshd again.

Fail2ban

Fail2ban watches logs and firewalls IPs that fail too often. On CentOS 7 it comes from EPEL:

yum -y install epel-release
yum -y install fail2ban fail2ban-firewalld
systemctl enable fail2ban

On Ubuntu 16.04 it is apt-get install fail2ban from the main repository.

Never edit /etc/fail2ban/jail.conf directly, since upgrades overwrite it. Put your settings in /etc/fail2ban/jail.local:

[DEFAULT]
bantime  = 3600
findtime = 600
maxretry = 5
ignoreip = 127.0.0.1/8 10.0.0.0/24
banaction = firewallcmd-ipset

[sshd]
enabled = true
port    = 2222
logpath = %(sshd_log)s

The port line matters once you've moved sshd; otherwise Fail2ban bans port 22 and the attacker keeps hammering 2222. I forgot it the first time. On Ubuntu, use iptables-multiport for banaction (or leave the default) since there's no firewalld.

systemctl start fail2ban
fail2ban-client status sshd

Check that it bans

Fail a few logins on purpose from another machine (wrong key or a nonexistent user), then:

fail2ban-client status sshd

You should see the IP under Banned IP list. On CentOS, firewall-cmd --ipset=f2b-sshd --get-entries lists the same addresses. To unban yourself:

fail2ban-client set sshd unbanip 203.0.113.55

Also confirm ss -tlnp | grep sshd shows only port 2222.

When it goes wrong

  • sshd won't start after the port change on CentOS: error: Bind to port 2222 on 0.0.0.0 failed: Permission denied. That's SELinux. Run the semanage port command above; semanage port -l | grep ssh shows what's allowed.
  • fail2ban-client status says ERROR Failed to access socket path: /var/run/fail2ban/fail2ban.sock when the service isn't running. journalctl -u fail2ban usually points at a typo in jail.local.
  • After adding AllowUsers, a valid user gets Permission denied (publickey) with nothing useful client-side. On the server, /var/log/secure (CentOS) or /var/log/auth.log (Ubuntu) says User deploy from 1.2.3.4 not allowed because not listed in AllowUsers. Look for a typo or missing host pattern.
  • Fail2ban bans your own IP while you test. Put it in ignoreip instead of trusting yourself to unban it.

None of this replaces key-only auth. It just makes the log quiet enough that I actually read it.