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
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."
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"
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
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) | no |
| Password authentication | yes (default) | no (key-only) |
| Empty passwords | yes (default) | no |
| Max auth tries | 6 (default) | 3 |
| X11 forwarding | yes (default) | no |
| Fail2Ban | Not installed | 3 retries, 24-hour ban |