Skip to content
CitexNode
All guides

Secure your server

Updates, automatic security updates, a sudo user, key-only login and fail2ban.

6 min read

Applies to Debian 12/13, Ubuntu 22.04/24.04, AlmaLinux 9 and Rocky Linux 9. You need a working SSH login as root, see Connect via SSH.

Every server on the internet sees its first login attempts from bots within minutes. After the steps below, they go nowhere.

1. Install updates

Debian and Ubuntu:

bash
apt update && apt full-upgrade -y

AlmaLinux and Rocky Linux:

bash
dnf upgrade -y

If a new kernel came in, reboot (reboot) and reconnect.

2. Automatic security updates

Debian and Ubuntu: unattended-upgrades

Ubuntu usually has the package already; on Debian, install it:

bash
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

Answer the question with Yes. Security updates are now installed daily on their own. To check:

bash
systemctl status unattended-upgrades
unattended-upgrade --dry-run --debug

The log is in /var/log/unattended-upgrades/.

AlmaLinux and Rocky Linux: dnf-automatic

bash
dnf install -y dnf-automatic

Open /etc/dnf/automatic.conf and set in the [commands] section:

ini
[commands]
upgrade_type = security
apply_updates = yes

Then enable the timer:

bash
systemctl enable --now dnf-automatic.timer
systemctl list-timers dnf-automatic.timer

3. Create a user with sudo

Don't work as root day to day. Create your own user, here alex.

Debian and Ubuntu:

bash
apt install -y sudo
adduser alex
usermod -aG sudo alex

AlmaLinux and Rocky Linux:

bash
useradd -m -G wheel alex
passwd alex

You'll need that password for sudo. Give the user the same SSH key as root:

bash
mkdir -p /home/alex/.ssh
cp /root/.ssh/authorized_keys /home/alex/.ssh/
chown -R alex:alex /home/alex/.ssh
chmod 700 /home/alex/.ssh
chmod 600 /home/alex/.ssh/authorized_keys

Test in a second window:

bash
ssh alex@203.0.113.10
sudo whoami

sudo whoami must ask for your password and then print root.

4. Harden SSH

Instead of editing /etc/ssh/sshd_config, create your own file in /etc/ssh/sshd_config.d/. Package updates leave it alone. All the systems listed here read this folder.

The name must start with 00-: for each setting, SSH uses the first value it finds, and cloud images often ship their own files such as 50-cloud-init.conf that allow password logins.

bash
nano /etc/ssh/sshd_config.d/00-hardening.conf
text
# Keys only, no passwords
PasswordAuthentication no
KbdInteractiveAuthentication no
PubkeyAuthentication yes

# root only with a key (or "no" if you only use your sudo user from now on)
PermitRootLogin prohibit-password

MaxAuthTries 3
X11Forwarding no

Check the file and reload SSH:

bash
sshd -t

No output means no errors. Then:

bash
# Debian and Ubuntu
systemctl reload ssh

# AlmaLinux and Rocky Linux
systemctl reload sshd

Check that the values apply:

bash
sshd -T | grep -Ei 'passwordauthentication|permitrootlogin|kbdinteractive'

Expected: passwordauthentication no, permitrootlogin without-password (the internal name of prohibit-password) and kbdinteractiveauthentication no.

Now test in the second window:

bash
ssh alex@203.0.113.10
ssh -o PubkeyAuthentication=no alex@203.0.113.10

The first must work, the second must fail with Permission denied (publickey).

5. fail2ban

fail2ban blocks addresses for a while after too many failed logins. With key-only login it isn't a must, but it keeps the logs clean.

Debian and Ubuntu:

bash
apt install -y fail2ban python3-systemd

AlmaLinux and Rocky Linux (fail2ban comes from EPEL):

bash
dnf install -y epel-release
dnf install -y fail2ban fail2ban-firewalld

Create your own file instead of editing jail.conf:

bash
nano /etc/fail2ban/jail.d/sshd.local
ini
[sshd]
enabled = true
backend = systemd
maxretry = 5
findtime = 10m
bantime = 1h

backend = systemd matters: Debian 12 and 13 no longer write /var/log/auth.log, so fail2ban reads from the journal instead.

bash
systemctl enable --now fail2ban
fail2ban-client status sshd

The output shows Currently banned and Total banned. To unban an address:

bash
fail2ban-client set sshd unbanip 198.51.100.7

Check that everything is in place

  • Logging in as your user with a key works, sudo whoami prints root.
  • Logging in with a password fails.
  • systemctl status unattended-upgrades or systemctl list-timers dnf-automatic.timer shows it as active.
  • fail2ban-client status sshd responds.

Only then close the root session.

Common errors

ProblemFix
Password login still works despite PasswordAuthentication noAnother file in /etc/ssh/sshd_config.d/ comes first. sshd -T shows the effective value; your file must sort first alphabetically (00-…).
sudo: command not foundDebian without sudo: apt install sudo as root.
alex is not in the sudoers fileGroup missing: usermod -aG sudo alex (Debian/Ubuntu) or usermod -aG wheel alex (AlmaLinux/Rocky), then log in again.
fail2ban doesn't startjournalctl -u fail2ban -n 50. Usually backend = systemd or python3-systemd is missing.
Locked outOpen the Console in the dashboard, log in as root with the password from Credentials, fix 00-hardening.conf and run systemctl reload ssh or sshd. Reset root password in the System tab sets a new password.

What next