Tools: Latest: Linux Server Hardening Basics: SSH Keys, Firewall, Fail2Ban, and Updates

Tools: Latest: Linux Server Hardening Basics: SSH Keys, Firewall, Fail2Ban, and Updates

Replace Password Login with SSH Keys

Lock Down Network Access with a Minimal Firewall

Throttle Brute‑Force Attempts with Fail2Ban

Automate Security Updates

Want to go deeper? A freshly installed Linux server is ready to run services, but it also presents a wide attack surface. Even a small VPS can become a target for credential stuffing, port scans, and automated brute‑force attacks. Hardening the base system does not require complex tools; a handful of well‑known utilities, properly configured, raise the security bar dramatically. This guide walks through the four core pillars: SSH key authentication, a lock‑down firewall, Fail2Ban for intrusion throttling, and reliable update automation. Follow the steps, test each change, and you will have a solid foundation for any production or home‑lab server. Public‑key authentication eliminates the need for passwords that can be guessed or leaked. Generate a key pair on your workstation, copy the public key to the server, and disable password authentication in sshd_config. After the key is in place, edit /etc/ssh/sshd_config: Restart the daemon and test a new login before closing the old session: If the login works, you can safely lock the account password with passwd -l user. For environments with multiple administrators, consider a shared authorized_keys directory or a separate Git‑managed key store. Even with SSH keys, exposing unnecessary ports invites scanning tools and automated exploits. A host‑based firewall such as ufw (Uncomplicated Firewall) provides a readable rule set and integrates with systemd. Install and enable it, then allow only the services you actually need. Check the rule list with sudo ufw status verbose. For more complex routing or VLAN segregation, see our comparison of OPNsense vs pfSense which shows how a dedicated perimeter firewall can complement host‑level rules. Fail2Ban monitors log files for repeated authentication failures and temporarily bans the offending IP address. Install the package, copy the default jail configuration, and enable the SSH jail. After editing, restart the service: You can add custom jails for services like nginx, vsftpd, or any daemon that writes to syslog. Adjust bantime and maxretry to match your risk tolerance. Keeping the OS and installed packages patched is the last line of defense. On Debian‑based systems the unattended-upgrades package can apply security updates automatically. On Red Hat‑based distributions use dnf-automatic. Both tools allow you to schedule nightly runs and receive email reports. For Fedora, CentOS Stream, or RHEL, enable the timer: Regularly audit the update logs (/var/log/unattended-upgrades/unattended-upgrades.log or /var/log/dnf.log) and confirm that critical services restart cleanly after a kernel upgrade. Pair automatic updates with a reliable backup strategy such as the 3‑2‑1 backup rule to protect against accidental breakage. Need to audit your server setup? Our Small Business IT Audit Checklist covers hardware, software, security posture, backups, and network documentation. $9, instant download. Get the IT Audit Checklist Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

# Generate a 4096‑bit RSA key (adjust algorithm if you prefer Ed25519) ssh-keygen -t rsa -b 4096 -C "admin@myserver" # Copy the public key to the remote host (replace user and host as needed) ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected] # Generate a 4096‑bit RSA key (adjust algorithm if you prefer Ed25519) ssh-keygen -t rsa -b 4096 -C "admin@myserver" # Copy the public key to the remote host (replace user and host as needed) ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected] # Generate a 4096‑bit RSA key (adjust algorithm if you prefer Ed25519) ssh-keygen -t rsa -b 4096 -C "admin@myserver" # Copy the public key to the remote host (replace user and host as needed) ssh-copy-id -i ~/.ssh/id_rsa.pub [email protected] PubkeyAuthentication yes PasswordAuthentication no ChallengeResponseAuthentication no UsePAM no PubkeyAuthentication yes PasswordAuthentication no ChallengeResponseAuthentication no UsePAM no PubkeyAuthentication yes PasswordAuthentication no ChallengeResponseAuthentication no UsePAM no -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart sshd ssh [email protected] -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart sshd ssh [email protected] -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart sshd ssh [email protected] # Install ufw (Debian/Ubuntu) or use the built‑in package on most distros -weight: 600;">sudo -weight: 500;">apt-get -weight: 500;">install ufw -y # Default deny inbound, allow all outbound -weight: 600;">sudo ufw default deny incoming -weight: 600;">sudo ufw default allow outgoing # Allow SSH from a trusted subnet (replace 203.0.113.0/24 with your network) -weight: 600;">sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp # If the server hosts a web site, open HTTP/HTTPS -weight: 600;">sudo ufw allow 80/tcp -weight: 600;">sudo ufw allow 443/tcp # Enable the firewall -weight: 600;">sudo ufw -weight: 500;">enable # Install ufw (Debian/Ubuntu) or use the built‑in package on most distros -weight: 600;">sudo -weight: 500;">apt-get -weight: 500;">install ufw -y # Default deny inbound, allow all outbound -weight: 600;">sudo ufw default deny incoming -weight: 600;">sudo ufw default allow outgoing # Allow SSH from a trusted subnet (replace 203.0.113.0/24 with your network) -weight: 600;">sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp # If the server hosts a web site, open HTTP/HTTPS -weight: 600;">sudo ufw allow 80/tcp -weight: 600;">sudo ufw allow 443/tcp # Enable the firewall -weight: 600;">sudo ufw -weight: 500;">enable # Install ufw (Debian/Ubuntu) or use the built‑in package on most distros -weight: 600;">sudo -weight: 500;">apt-get -weight: 500;">install ufw -y # Default deny inbound, allow all outbound -weight: 600;">sudo ufw default deny incoming -weight: 600;">sudo ufw default allow outgoing # Allow SSH from a trusted subnet (replace 203.0.113.0/24 with your network) -weight: 600;">sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp # If the server hosts a web site, open HTTP/HTTPS -weight: 600;">sudo ufw allow 80/tcp -weight: 600;">sudo ufw allow 443/tcp # Enable the firewall -weight: 600;">sudo ufw -weight: 500;">enable # Install Fail2Ban (most distributions) -weight: 600;">sudo -weight: 500;">apt-get -weight: 500;">install fail2ban -y # Create a local jail file to avoid overwriting defaults on -weight: 500;">upgrade -weight: 600;">sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local # Edit /etc/fail2ban/jail.local and -weight: 500;">enable the sshd section [sshd] enabled = true port = ssh logpath = %(sshd_log)s maxretry = 5 bantime = 3600 findtime = 600 # Install Fail2Ban (most distributions) -weight: 600;">sudo -weight: 500;">apt-get -weight: 500;">install fail2ban -y # Create a local jail file to avoid overwriting defaults on -weight: 500;">upgrade -weight: 600;">sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local # Edit /etc/fail2ban/jail.local and -weight: 500;">enable the sshd section [sshd] enabled = true port = ssh logpath = %(sshd_log)s maxretry = 5 bantime = 3600 findtime = 600 # Install Fail2Ban (most distributions) -weight: 600;">sudo -weight: 500;">apt-get -weight: 500;">install fail2ban -y # Create a local jail file to avoid overwriting defaults on -weight: 500;">upgrade -weight: 600;">sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local # Edit /etc/fail2ban/jail.local and -weight: 500;">enable the sshd section [sshd] enabled = true port = ssh logpath = %(sshd_log)s maxretry = 5 bantime = 3600 findtime = 600 -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart fail2ban # Verify the jail is active -weight: 600;">sudo fail2ban-client -weight: 500;">status sshd -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart fail2ban # Verify the jail is active -weight: 600;">sudo fail2ban-client -weight: 500;">status sshd -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">restart fail2ban # Verify the jail is active -weight: 600;">sudo fail2ban-client -weight: 500;">status sshd # Debian/Ubuntu: -weight: 500;">install the package -weight: 600;">sudo -weight: 500;">apt-get -weight: 500;">install unattended-upgrades -weight: 500;">apt-listchanges -y # Enable automatic installation of security updates -weight: 600;">sudo dpkg-reconfigure --priority=low unattended-upgrades # Optional: edit /etc/-weight: 500;">apt/-weight: 500;">apt.conf.d/50unattended-upgrades to fine‑tune # Example snippet – -weight: 500;">enable only security repos Unattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}-security"; }; # Set a daily reboot if a kernel -weight: 500;">update occurs (optional) Unattended-Upgrade::Automatic-Reboot "true"; Unattended-Upgrade::Automatic-Reboot-Time "02:00"; # Debian/Ubuntu: -weight: 500;">install the package -weight: 600;">sudo -weight: 500;">apt-get -weight: 500;">install unattended-upgrades -weight: 500;">apt-listchanges -y # Enable automatic installation of security updates -weight: 600;">sudo dpkg-reconfigure --priority=low unattended-upgrades # Optional: edit /etc/-weight: 500;">apt/-weight: 500;">apt.conf.d/50unattended-upgrades to fine‑tune # Example snippet – -weight: 500;">enable only security repos Unattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}-security"; }; # Set a daily reboot if a kernel -weight: 500;">update occurs (optional) Unattended-Upgrade::Automatic-Reboot "true"; Unattended-Upgrade::Automatic-Reboot-Time "02:00"; # Debian/Ubuntu: -weight: 500;">install the package -weight: 600;">sudo -weight: 500;">apt-get -weight: 500;">install unattended-upgrades -weight: 500;">apt-listchanges -y # Enable automatic installation of security updates -weight: 600;">sudo dpkg-reconfigure --priority=low unattended-upgrades # Optional: edit /etc/-weight: 500;">apt/-weight: 500;">apt.conf.d/50unattended-upgrades to fine‑tune # Example snippet – -weight: 500;">enable only security repos Unattended-Upgrade::Allowed-Origins { "${distro_id}:${distro_codename}-security"; }; # Set a daily reboot if a kernel -weight: 500;">update occurs (optional) Unattended-Upgrade::Automatic-Reboot "true"; Unattended-Upgrade::Automatic-Reboot-Time "02:00"; -weight: 600;">sudo -weight: 500;">dnf -weight: 500;">install -weight: 500;">dnf-automatic -y -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">enable --now -weight: 500;">dnf-automatic.timer # Review /etc/-weight: 500;">dnf/automatic.conf to restrict to security updates only -weight: 600;">sudo -weight: 500;">dnf -weight: 500;">install -weight: 500;">dnf-automatic -y -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">enable --now -weight: 500;">dnf-automatic.timer # Review /etc/-weight: 500;">dnf/automatic.conf to restrict to security updates only -weight: 600;">sudo -weight: 500;">dnf -weight: 500;">install -weight: 500;">dnf-automatic -y -weight: 600;">sudo -weight: 500;">systemctl -weight: 500;">enable --now -weight: 500;">dnf-automatic.timer # Review /etc/-weight: 500;">dnf/automatic.conf to restrict to security updates only