SSH Security Hardening Checklist for DomainIndia VPS
The attack surface
Scanners probe every public IP's port 22 continuously. Logs on a new VPS show 500-2000 failed SSH attempts per day within the first hour. Default configuration is a ticking bomb.
The eight layers of SSH defense:
- Disable password auth entirely
- Require SSH key authentication
- Disable root login
- Create a non-root sudo user
- Install fail2ban
- Firewall rules
- (Optional) Change SSH port
- (Optional) Two-factor auth + jump host
Layer 1 — Generate strong SSH key (on your laptop)
ssh-keygen -t ed25519 -C "[email protected]" -f ~/.ssh/domainindia_vps -N 'strong-passphrase'ed25519is faster and more secure than RSA- Passphrase protects the key if your laptop is stolen
Add to SSH agent:
ssh-add ~/.ssh/domainindia_vpsLayer 2 — Add public key to VPS
# From laptop
ssh-copy-id -i ~/.ssh/domainindia_vps.pub root@your-vps-ip
# Or manually — paste contents of .pub file into:
# /root/.ssh/authorized_keys on VPSTest before continuing:
ssh -i ~/.ssh/domainindia_vps root@your-vps-ip
# Should succeed without prompting for passwordCritical — don't proceed until key login works.
Layer 3 — Create non-root user
# On VPS as root
adduser admin # or your name
usermod -aG wheel admin # AlmaLinux (sudo group)
# or on Ubuntu:
usermod -aG sudo admin
# Copy authorized_keys
mkdir -p /home/admin/.ssh
cp /root/.ssh/authorized_keys /home/admin/.ssh/
chown -R admin:admin /home/admin/.ssh
chmod 700 /home/admin/.ssh
chmod 600 /home/admin/.ssh/authorized_keysTest login as new user:
ssh -i ~/.ssh/domainindia_vps admin@your-vps-ip
sudo whoami # should return "root" after password promptConfigure passwordless sudo (optional, for automation):
echo 'admin ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/adminLayer 4 — Harden sshd config
Edit /etc/ssh/sshd_config:
sudo cp /etc/ssh/sshd_config /etc/ssh/sshd_config.backup
sudo vi /etc/ssh/sshd_configSet these:
# Disable root login entirely
PermitRootLogin no
# Keys only
PasswordAuthentication no
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM yes
# Limit to specific users
AllowUsers admin deploy
# Disable empty passwords
PermitEmptyPasswords no
# Limit protocol
Protocol 2
# Disable X11 forwarding if not needed
X11Forwarding no
# Reduce login grace time
LoginGraceTime 30
# Max auth tries per connection
MaxAuthTries 3
# Session timeout (idle disconnect)
ClientAliveInterval 300
ClientAliveCountMax 2
# Modern ciphers only
Ciphers [email protected],[email protected],aes256-ctr
KexAlgorithms curve25519-sha256,[email protected],diffie-hellman-group16-sha512
MACs [email protected],[email protected]Validate + reload:
sudo sshd -t # validate syntax — must return no output
sudo systemctl reload sshdCritical: keep your current SSH session open. Open a NEW terminal to test. If locked out, use the old session to revert.
Layer 5 — fail2ban (auto-ban brute-forcers)
# AlmaLinux
sudo dnf install -y epel-release
sudo dnf install -y fail2ban
# Ubuntu
sudo apt install -y fail2banConfigure /etc/fail2ban/jail.local:
[DEFAULT]
bantime = 24h
findtime = 10m
maxretry = 3
ignoreip = 127.0.0.1/8 YOUR_HOME_IP/32
[sshd]
enabled = true
port = ssh
logpath = /var/log/secure
backend = systemdStart:
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshd
# Currently banned: 2
# IP list: 45.123.x.x 62.45.x.xLayer 6 — Firewall
firewalld (AlmaLinux / Rocky)
sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reloadUFW (Ubuntu)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enableRestrict SSH to specific IP
If you have a static IP at office:
# firewalld
sudo firewall-cmd --permanent --remove-service=ssh
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="YOUR.OFFICE.IP/32" service name="ssh" accept'
sudo firewall-cmd --reload
# ufw
sudo ufw delete allow 22/tcp
sudo ufw allow from YOUR.OFFICE.IP to any port 22Layer 7 — Change SSH port (security through obscurity)
Scanners mostly hit port 22. Moving to 2222 or 22022 cuts 95% of noise (not a real defense, just log hygiene).
/etc/ssh/sshd_config:
Port 2222
# or add alongside 22 during transition:
# Port 22
# Port 2222Open firewall for new port, reload sshd, test, then close port 22:
# AlmaLinux: SELinux may block new port
sudo semanage port -a -t ssh_port_t -p tcp 2222
sudo firewall-cmd --permanent --add-port=2222/tcp
sudo firewall-cmd --reload
sudo systemctl reload sshd
ssh -p 2222 admin@your-vpsTell team. Update ~/.ssh/config on laptops:
Host domainindia-vps
HostName your-vps-ip
User admin
Port 2222
IdentityFile ~/.ssh/domainindia_vpsNow ssh domainindia-vps just works.
Layer 8 — Two-factor auth (Google Authenticator)
For extra paranoia:
sudo dnf install -y google-authenticator
# Ubuntu: sudo apt install libpam-google-authenticator
# As user (admin), run:
google-authenticator
# Scan QR with Authy/Google Auth on phone
# Save backup codes!Edit /etc/pam.d/sshd — add at top:
auth required pam_google_authenticator.soEdit /etc/ssh/sshd_config:
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactiveReload sshd. Now login requires: SSH key AND 6-digit code.
Layer 9 — Jump host / bastion
For multiple VPS, expose SSH only on one bastion host. All others only accept SSH from bastion's internal IP.
~/.ssh/config:
Host bastion
HostName bastion.yourcompany.com
User admin
Host internal-*
User admin
ProxyJump bastionUsage:
ssh internal-db # routes via bastion automaticallyAttack surface: only bastion is public.
Monitoring
Watch SSH logs
sudo journalctl -u sshd --follow
# or
sudo tail -f /var/log/secureAlert on successful root-like sudo
Set up a simple cron that monitors and emails:
# /etc/cron.hourly/ssh-monitor
#!/bin/bash
LAST_HOUR=$(date -d '1 hour ago' +'%Y-%m-%d %H')
SUDO_COUNT=$(journalctl --since "$LAST_HOUR:00:00" | grep -c 'sudo.*COMMAND')
if [ "$SUDO_COUNT" -gt 20 ]; then
mail -s "High sudo activity on $(hostname)" [email protected] <<EOF
Sudo commands in past hour: $SUDO_COUNT
Review: journalctl --since "$LAST_HOUR:00:00" | grep sudo
EOF
fiAudit login sessions
# Last 20 logins
last -20
# Failed attempts
lastb -20
# Who is currently logged in
whoSSH config management
Keep your ~/.ssh/config organized:
# Default settings
Host *
AddKeysToAgent yes
UseKeychain yes
IdentityFile ~/.ssh/id_ed25519
ServerAliveInterval 60
# DomainIndia VPS
Host domainindia-prod
HostName 65.108.x.x
User admin
Port 2222
IdentityFile ~/.ssh/domainindia_vps
Host domainindia-staging
HostName 65.108.y.y
User admin
Port 2222Common pitfalls
FAQ
Yes — still useful. It blocks scanning bots wasting CPU on auth attempts even if they'd never succeed. Also catches misconfigured services exposing other ports.
Security benefit is marginal (scanners scan other ports too). Main benefit: cleaner logs. If you want it, fine; don't rely on it as defense.
1) Remove pub key from authorized_keys on every server. 2) Generate new key, add to authorized_keys. 3) Rotate any service account keys. 4) Review logs for unauthorized access since compromise.
Excellent option — no public SSH port, audit trail, MFA built in. See our Cloudflare Zero Trust article.
On a VPS — no, you lose access. On a cPanel/DirectAdmin shared server — yes, use the panel. VPS admins always need some recovery path.
Harden your DomainIndia VPS from day one. Get a VPS