Complete Guide to Linux Server (VPS) Hardening from Start to Finish
Server security is one of the most critical aspects of digital infrastructure management. The SSH (Secure Shell) protocol, as the primary gateway for server management, is constantly targeted by cyber attacks. In this article, you will learn about SSH hardening and receive a practical checklist for securing your server.
🔐 Complete Guide to Linux Server (VPS) Hardening from Start to Finish
Server security is one of the most critical aspects of digital infrastructure management. The SSH (Secure Shell) protocol, as the primary gateway for server management, is constantly targeted by cyber attacks. In this article, you will learn about SSH hardening and receive a practical checklist for securing your server.
📌 What Is SSH Hardening?
SSH hardening refers to a set of settings and changes in the configuration file /etc/ssh/sshd_config aimed at reducing the attack surface and closing security gaps. This includes disabling weak authentication methods, restricting access, and using strong encryption.
🛡️ Golden Checklist for SSH Hardening
| Setting | Ideal State | Description |
|---|---|---|
PermitRootLogin |
❌ no |
Disables direct login for the root user. This is the most important security step. |
PasswordAuthentication |
❌ no |
Disables password-based login. Makes brute-force attacks ineffective. |
PubkeyAuthentication |
✅ yes |
Enables public key authentication. The most secure method for server access. |
KbdInteractiveAuthentication |
❌ no |
Disables interactive authentication (typically used for passwords). |
| Non-root user with sudo | ✅ Enabled | Create a regular user (e.g., admin) and grant sudo access. |
⚙️ Implementation Steps for Hardening
-
Create a new user: Create a non-root user with
sudoaccess.sudo adduser admin sudo usermod -aG sudo admin
-
Set up SSH keys: Generate an SSH key for the new user and place the public key in
~/.ssh/authorized_keys.# On your local system ssh-keygen -t ed25519 -a 100 ssh-copy-id admin@server_ip
-
Edit
sshd_config: Open the SSH configuration file with a text editor.sudo nano /etc/ssh/sshd_config
-
Apply settings: Set the following values in the file:
PermitRootLogin no PasswordAuthentication no PubkeyAuthentication yes KbdInteractiveAuthentication no ChallengeResponseAuthentication no UsePAM no
-
Reload SSH: After saving changes, reload the SSH service.
sudo systemctl reload sshd
-
Test connection: Before closing the current session, open a new terminal and connect with the new user and SSH key to make sure everything works correctly.
ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no admin@server_ip # You should get "Permission denied (publickey)"
⚠️ Critical Note
Never close the current SSH session before testing the new connection! If an error occurs, you may lose access to your server. Always keep an active connection for troubleshooting.
🔥 Next Step: Firewall Configuration (UFW)
A firewall acts like a doorman that only allows authorized services to enter.
sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow ssh sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enable sudo ufw status numbered
Expected output:
[ 1] 22/tcp ALLOW IN Anywhere [ 2] 80/tcp ALLOW IN Anywhere [ 3] 443/tcp ALLOW IN Anywhere
🛡️ HTTPS with Let's Encrypt
-
Install Certbot:
sudo apt update sudo apt install certbot python3-certbot-apache # For Apache # Or sudo apt install certbot python3-certbot-nginx # For Nginx
-
Obtain certificate:
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
-
Verify auto-renewal:
sudo certbot renew --dry-run
-
Check TLS settings:
# In the web server configuration file SSLProtocol -all +TLSv1.2 +TLSv1.3 SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
✅ Security Tip
After enabling HTTPS, be sure to add the X-Content-Type-Options: nosniff header to the web server to prevent MIME-type sniffing attacks.
🗑️ Managing Unnecessary Services
Reviewing and disabling services that don't need to run reduces the attack surface.
Services that can be disabled:
sudo systemctl disable --now ModemManager sudo systemctl disable --now multipathd sudo systemctl disable --now multipathd.socket sudo systemctl disable --now udisks2 sudo systemctl disable --now upower
Before disabling any service, check its dependencies to make sure other services won't be affected. For example, for ModemManager:
systemctl status ModemManager.service # From here you can see: # Whether it's active/disabled # When it ran # Latest logs # PID # Sometimes why it's active # For more detailed check: # systemctl cat udisks2.service # Check the service's own dependencies systemctl list-dependencies ModemManager.service # This shows what ModemManager depends on to run. systemctl list-dependencies --reverse ModemManager.service # If I turn off this service, which units depend on it? # For services that likely have specific purposes # For example, before disabling multipathd sudo multipath -ll # And for ModemManager sudo mmcli -L
📌 Important Note
list-dependencies
alone does not prove that disabling a service is safe. We must examine both the dependency graph and the actual role of the service on the server.
For this reason, for example,
qemu-guest-agent
was not disabled even though it's a small service; because we verified the VPS runs on KVM and actually uses it.
🌐 DNS Hardening
The DNS server is one of the critical services that must be separately secured.
| Security Measure | Description |
|---|---|
| Disable Recursion | On authoritative servers, disable the recursive feature to prevent them from being used for amplification attacks. |
| Rate Limiting | To prevent DDoS attacks and reduce server load, configure the maximum number of allowed responses per second. |
| Zone Transfer Management | Restrict zone transfers to specific IPs only and use TSIG signing for authentication. |
| Enable DNSSEC | For authenticating and verifying DNS response integrity and preventing cache poisoning attacks. |
| Encrypted Traffic (DoT/DoH) | For protecting user privacy and encrypting traffic between client and resolver. |
Verify DNS from Outside
To ensure DNS is properly configured, test from outside:
dig @your_server_ip yourdomain.com nslookup yourdomain.com your_server_ip
🔍 External Verification
Before declaring the work complete, be sure to test the services from the perspective of an internet user:
1. Test SSH from outside
# From your own system (not on the server) ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no admin@your_server_ip # You should get "Permission denied"
2. Test HTTP and HTTPS
curl -I http://yourdomain.com # Should return 301 Moved Permanently → HTTPS curl -I https://yourdomain.com # Should return 200 OK
3. Check security headers
curl -I https://yourdomain.com | grep -E "X-Content-Type-Options|Strict-Transport-Security"
✅ Final Confirmation
After completing all steps, verify the following status:
- ✅ SSH key login for
adminuser works. - ✅
adminuser has root access viasudo. - ✅ Direct
rootlogin is disabled. - ✅ Password authentication for SSH is disabled; only SSH keys are accepted.
- ✅ Firewall is active and only ports 22, 80, and 443 are open.
- ✅ HTTP redirects to HTTPS.
- ✅ SSL certificate is valid and auto-renewal is enabled.
- ✅ Unnecessary services are disabled.
- ✅ Essential services (polkit, qemu-guest-agent) are preserved.
- ✅ DNS is properly configured and accessible from outside.
- ✅
X-Content-Type-Options: nosniffheader is active.
🎯 By completing these steps, you increase your server's security by up to 90% against common attacks.
🔧 The Remaining 10%: Advanced Measures
For maximum security, consider these additional measures:
1. Change the Default SSH Port
# In /etc/ssh/sshd_config Port 2222 # Or any number above 1024
Why? Automated attacks typically only scan port 22.
2. Restrict Allowed IPs (Firewall)
# With UFW sudo ufw allow from 192.168.1.0/24 to any port 22 # With iptables iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPT iptables -A INPUT -p tcp --dport 22 -j DROP
Why? Only specific IPs (e.g., internal network or office) are allowed to connect.
3. Use Fail2ban
sudo apt install fail2ban -y sudo systemctl enable fail2ban
Why? After several failed login attempts, it blocks the attacker's IP for a specified duration.
4. Fully Disable SSH for root User
# In /etc/ssh/sshd_config PermitRootLogin prohibit-password # Don't allow login even with key
5. Set Timeout for Idle Sessions
# In /etc/ssh/sshd_config ClientAliveInterval 300 ClientAliveCountMax 2
Why? Disconnects idle sessions after 10 minutes.
6. Use SSH Keys with Stronger Algorithms
# Use ED25519 instead of RSA ssh-keygen -t ed25519 -a 100 -C "your_email@example.com"
Why? ED25519 is more secure and faster than RSA.
7. Enable Logging and Monitoring
# In /etc/ssh/sshd_config SyslogFacility AUTH LogLevel VERBOSE # Check logs sudo tail -f /var/log/auth.log | grep sshd
8. Use Two-Factor Authentication (2FA)
# Install Google Authenticator sudo apt install libpam-google-authenticator google-authenticator
Then in /etc/ssh/sshd_config:
ChallengeResponseAuthentication yes AuthenticationMethods publickey,password publickey,keyboard-interactive
9. Regularly Update OpenSSH
sudo apt update && sudo apt upgrade openssh-server
Why? Security bugs discovered in newer versions are fixed.
🔍 Security Verification Tools
Check current settings
sudo sshd -T | grep -E "permitrootlogin|passwordauthentication|pubkeyauthentication"
Check login logs
sudo grep "Failed password" /var/log/auth.log | tail -20
View active connections
sudo ss -tulpn | grep -E ":(22|80|443)"
📊 Summary of the Remaining 10%
| Solution | Difficulty Level | Security Impact |
|---|---|---|
| Change SSH port | Easy | ⭐⭐ |
| Restrict IPs | Medium | ⭐⭐⭐⭐ |
| Fail2ban | Medium | ⭐⭐⭐⭐ |
| ED25519 keys | Easy | ⭐⭐⭐⭐ |
| 2FA | Hard | ⭐⭐⭐⭐⭐ |
| Regular updates | Easy | ⭐⭐⭐⭐⭐ |
📌 Final Recommendation
To achieve 100% relative security, add at least these three items to the previous 90%:
- Change SSH port (simplest and most effective)
- Install Fail2ban (automatic protection against attacks)
- Restrict allowed IPs (if possible)
With these steps, your server security reaches a very high level and common attacks become practically ineffective.
🏁 Final Summary
SSH hardening is one of the most fundamental and effective security measures for any Linux server. By implementing these steps:
- ✅ You increase your server's security by up to 90% against common attacks
- ✅ You prevent unauthorized access
- ✅ You make login logs more transparent and traceable
Next step: After completing this work, you can move on to more advanced topics like setting up Fail2ban, changing the SSH port, and using 2FA to bring your server security to an even higher level.
📌 Final Reminder
Security is a continuous process, not a one-time task. Always check your logs and keep the system updated.