Script 15 · SSH Security

15. Change SSH Port

Changes the SSH listening port, updates nftables and Fail2ban, and offers rollback if new login testing fails.

Category: SSH Security Risk: Destructive Lines: calculating Language: Bash / Linux
Back to index

What this script does

  • Move SSH away from port 22.
  • Open the new port in local firewall rules.
  • Preserve a recovery path before committing changes.

Prerequisites

  • Root access
  • Console or existing SSH session kept open
  • Provider firewall allows new port
  • nftables available

Execution flow

  1. Backs up sshd/fail2ban/nftables
  2. Opens new port
  3. Edits sshd_config
  4. Restarts SSH and Fail2ban
  5. Prompts to commit or rollback

Validation checklist

  • ss -tulpn | grep <port>
  • ssh root@IP -p <port>
  • nft list ruleset

Operational cautions

  • Can lock you out if provider firewall blocks the new port.
  • Always test a new SSH session before closing the old one.

Original script notes

ℹ️ Script Info: Safely moves the default SSH service from port 22 to a custom port of your choice, updating the firewall (NFTables) and Fail2ban configurations simultaneously to prevent unauthorized access.

⚠️ Destructive Action: Changing the SSH port may lock you out of the server if firewalls like NFTables, UFW, or your Cloud VPS provider have not allowed the new port. Always test the login in a new terminal tab before closing your current session.

Script source
cat << 'EOF' > change_ssh_port.sh
#!/bin/bash

# Output Colors
GREEN='\033[0;32m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m'

echo -e "${BLUE}=== SSH PORT & FAIL2BAN CHANGE WIZARD ===${NC}"

# 1. Ask for New Port
read -p "Enter the New SSH Port you want to use (example: 2022): " NEW_PORT

if ! [[ "$NEW_PORT" =~ ^[0-9]+$ ]] || [ "$NEW_PORT" -le 0 ] || [ "$NEW_PORT" -gt 65535 ]; then
    echo -e "${RED}Error: Invalid Port.${NC}"
    exit 1
fi

echo -e "\n${BLUE}[PROCESS 1/5] Backing up Configuration...${NC}"
cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak_auto
if [ -f /etc/fail2ban/jail.local ]; then
    cp /etc/fail2ban/jail.local /etc/fail2ban/jail.local.bak_auto
fi
# Backup current nftables rules
sudo nft list ruleset > /tmp/nftables.bak.rules

echo -e "${BLUE}[PROCESS 2/5] Opening Port $NEW_PORT in Firewall (NFTables)...${NC}"
# Add allow rule at the top to prevent blocking by other rules
sudo nft insert rule inet filter input tcp dport $NEW_PORT accept
echo "Port $NEW_PORT opened."

echo -e "${BLUE}[PROCESS 3/5] Updating SSH Configuration...${NC}"
# Change Port in sshd_config (handling #Port 22 or old Port line)
sudo sed -i "s/^#Port 22/Port $NEW_PORT/" /etc/ssh/sshd_config
sudo sed -i "s/^Port [0-9]*/Port $NEW_PORT/" /etc/ssh/sshd_config
sudo systemctl restart ssh
echo "SSH Service restarted on port $NEW_PORT."

echo -e "${BLUE}[PROCESS 4/5] Updating Fail2Ban...${NC}"
if [ -f /etc/fail2ban/jail.local ]; then
    # Update port in jail.local. Assuming [sshd] block exists.
    # We search for [sshd] block then replace port = ssh or port = number with new port
    # Safe sed approach for standard fail2ban config
    sudo sed -i "/^\[sshd\]/,/^\[/ s/^port\s*=.*/port = $NEW_PORT/" /etc/fail2ban/jail.local
    sudo systemctl restart fail2ban
    echo "Fail2Ban restarted with target port $NEW_PORT."
else
    echo "Fail2Ban configuration file not found, skipping this stage."
fi

echo -e "${BLUE}[PROCESS 5/5] Verifying...${NC}"
sleep 2
if ss -tulpn | grep -q ":$NEW_PORT"; then
    echo -e "${GREEN}SUCCESS: SSH detected running ('listening') on port $NEW_PORT.${NC}"
else
    echo -e "${RED}WARNING: SSH seems to have failed running on port $NEW_PORT.${NC}"
fi

echo -e "\n-----------------------------------------------------------"
echo -e "${RED}IMPORTANT:${NC} DO NOT CLOSE THIS TERMINAL!"
echo "Open a new terminal/PuTTY, and try logging in: ssh root@IP_VPS -p $NEW_PORT"
echo "-----------------------------------------------------------"

read -p "Was the login successful and do you want to save these changes? (y/n) 
[y] = Save Permanently (Apply)
[n] = Cancel & Return to Port 22 (Restore)
Your Choice: " CONFIRM

if [[ "$CONFIRM" =~ ^[Yy]$ ]]; then
    echo -e "\n${GREEN}[SAVING CHANGES]${NC}"
    # Save firewall permanently
    sudo nft list ruleset | sudo tee /etc/nftables.conf > /dev/null
    # Remove backup files
    rm /etc/ssh/sshd_config.bak_auto
    rm /etc/fail2ban/jail.local.bak_auto 2>/dev/null
    echo "Firewall, SSH, and Fail2Ban configurations have been saved permanently."
    echo -e "${GREEN}Done. Your SSH Port is now: $NEW_PORT${NC}"

else
    echo -e "\n${RED}[CANCELLING CHANGES - RESTORE]${NC}"
    
    # 1. Restore Config SSH
    echo "Restoring SSH config..."
    cp /etc/ssh/sshd_config.bak_auto /etc/ssh/sshd_config
    
    # 2. Restore Config Fail2Ban
    echo "Restoring Fail2Ban config..."
    if [ -f /etc/fail2ban/jail.local.bak_auto ]; then
        cp /etc/fail2ban/jail.local.bak_auto /etc/fail2ban/jail.local
    fi
    
    # 3. Restore Firewall (Remove new port rule)
    echo "Closing port $NEW_PORT again..."
    # Safest way to restore nftables to state right before script ran
    sudo nft -f /tmp/nftables.bak.rules
    
    # 4. Restart Service
    echo "Restarting services to initial state..."
    sudo systemctl restart ssh
    sudo systemctl restart fail2ban
    
    echo -e "${GREEN}Done. Server returned to using Port 22 (Default).${NC}"
fi
EOF
chmod +x change_ssh_port.sh && sudo ./change_ssh_port.sh
Done