This script applies to VPS and Bare Metal servers. Make sure you have an SSH key pair configured before running this script — it will disable password authentication.
Before You Start
Generate an SSH Key Pair
If you don’t already have an SSH key, generate one on your local machine (not the server):
ssh-keygen -t ed25519 -C "your-email@example.com"
Copy Your Public Key to the Server
ssh-copy-id -i ~/.ssh/id_ed25519.pub user@your-server-ip
Verify you can log in with your key before proceeding:
ssh -i ~/.ssh/id_ed25519 user@your-server-ip
If you run this script without a working SSH key on the server, you will be locked out . Verify key-based login works before proceeding. If you do get locked out, use the VirtFusion console (VPS) or IPMI (Bare Metal) to recover access.
The Script
#!/bin/bash
set -euo pipefail
# --- Configuration ---
# Set to "yes" to change the SSH port, then set the desired port number.
CHANGE_PORT = "no"
NEW_SSH_PORT = 22
# Set to "yes" to install and configure Fail2Ban
INSTALL_FAIL2BAN = "yes"
# ---------------------
SSHD_CONFIG = "/etc/ssh/sshd_config"
echo "=== SSH Hardening ==="
# Detect distribution
if [ -f /etc/os-release ] ; then
. /etc/os-release
DISTRO = " $ID "
else
echo "Cannot detect distribution."
exit 1
fi
# Backup current sshd_config
cp " $SSHD_CONFIG " "$ { SSHD_CONFIG } .bak.$( date +%Y%m%d%H%M%S)"
echo "Backed up sshd_config"
# Function to set or add an sshd_config directive
set_sshd_option () {
local key = " $1 "
local value = " $2 "
if grep -qE "^#?\s*$ { key } \s" " $SSHD_CONFIG " ; then
sed -i "s/^#*\s*$ { key } \s.*/$ { key } $ { value } /" " $SSHD_CONFIG "
else
echo "$ { key } $ { value } " >> " $SSHD_CONFIG "
fi
echo " Set: $ { key } $ { value } "
}
# Disable root login
set_sshd_option "PermitRootLogin" "no"
# Disable password authentication (key-only)
set_sshd_option "PasswordAuthentication" "no"
# Disable empty passwords
set_sshd_option "PermitEmptyPasswords" "no"
# Disable challenge-response authentication
set_sshd_option "KbdInteractiveAuthentication" "no"
# Limit authentication attempts
set_sshd_option "MaxAuthTries" "3"
# Disable X11 forwarding (not needed on servers)
set_sshd_option "X11Forwarding" "no"
# Change SSH port if configured
if [ " $CHANGE_PORT " = "yes" ] ; then
set_sshd_option "Port" " $NEW_SSH_PORT "
echo ""
echo "*** SSH port changed to $NEW_SSH_PORT ***"
echo "*** Update your firewall rules before restarting SSH ***"
fi
# Validate sshd config before restarting
echo ""
echo "Validating sshd_config..."
sshd -t
# Restart SSH
echo "Restarting SSH..."
systemctl restart sshd
echo ""
# --- Fail2Ban ---
if [ " $INSTALL_FAIL2BAN " = "yes" ] ; then
echo "=== Installing Fail2Ban ==="
case " $DISTRO " in
debian | ubuntu )
apt update -qq
apt install -y fail2ban
;;
almalinux | rocky | centos | rhel | cloudlinux | fedora )
if command -v dnf & > /dev/null ; then
dnf install -y epel-release 2> /dev/null || true
dnf install -y fail2ban
else
yum install -y epel-release
yum install -y fail2ban
fi
;;
arch | manjaro )
pacman -Sy --noconfirm fail2ban
;;
opensuse * | sles )
zypper install -y fail2ban
;;
*)
echo "Manual Fail2Ban installation required for $DISTRO "
INSTALL_FAIL2BAN = "no"
;;
esac
if [ " $INSTALL_FAIL2BAN " = "yes" ] ; then
# Determine the SSH port for Fail2Ban
F2B_PORT = "ssh"
if [ " $CHANGE_PORT " = "yes" ] ; then
F2B_PORT = " $NEW_SSH_PORT "
fi
# Create Fail2Ban local jail config
cat > /etc/fail2ban/jail.local << EOF
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
banaction = iptables-multiport
[sshd]
enabled = true
port = $F2B_PORT
logpath = %(sshd_log)s
backend = %(sshd_backend)s
maxretry = 3
bantime = 24h
EOF
systemctl enable fail2ban
systemctl restart fail2ban
echo "Fail2Ban installed and configured."
echo " SSH jail: max 3 retries, 24-hour ban"
fi
fi
echo ""
echo "=== SSH Hardening Complete ==="
echo ""
echo "Summary:"
echo " Root login: disabled"
echo " Password auth: disabled (key-only)"
echo " Max auth tries: 3"
echo " X11 forwarding: disabled"
[ " $CHANGE_PORT " = "yes" ] && echo " SSH port: $NEW_SSH_PORT "
[ " $INSTALL_FAIL2BAN " = "yes" ] && echo " Fail2Ban: active"
echo ""
echo "A backup of your original sshd_config was saved."
See all 151 lines
Usage
Edit the configuration variables at the top of the script before running:
# To change the SSH port, set:
CHANGE_PORT = "yes"
NEW_SSH_PORT = 2222
# To skip Fail2Ban installation, set:
INSTALL_FAIL2BAN = "no"
Then run:
chmod +x ssh-harden.sh
./ssh-harden.sh
If You Change the SSH Port
If you change the port, update your firewall before disconnecting:
UFW (Debian / Ubuntu)
firewalld (RHEL family)
ufw allow 2222/tcp
ufw delete allow 22/tcp
ufw reload
firewall-cmd --permanent --add-port=2222/tcp
firewall-cmd --permanent --remove-service=ssh
firewall-cmd --reload
Then connect using the new port:
ssh -p 2222 user@your-server-ip
Verifying Fail2Ban
# Check Fail2Ban status
fail2ban-client status
# Check the SSH jail specifically
fail2ban-client status sshd
# Unban an IP if needed
fail2ban-client set sshd unbanip 203.0.113.50
What the Script Changes
Setting Before After Root login yes (default)noPassword authentication yes (default)no (key-only)Empty passwords yes (default)noMax auth tries 6 (default) 3 X11 forwarding yes (default)noFail2Ban Not installed 3 retries, 24-hour ban