Server Security Hardening: Essential Steps#
Having a server exposed to the Internet without minimal security configuration is leaving the door wide open. In this article, I’ve compiled the steps I apply on my own servers to reduce the attack surface without complicating day-to-day management.
SSH: the first line of defense#
The SSH service is the most attacked entry point on any Linux server. These are the most important settings in /etc/ssh/sshd_config:
# Deshabilitar acceso root
PermitRootLogin no
# Solo autenticación por clave pública
PasswordAuthentication no
ChallengeResponseAuthentication no
# Limitar intentos de autenticación
MaxAuthTries 3
# Desactivar forwarding innecesario
X11Forwarding no
AllowTcpForwarding noAfter each change:
sudo systemctl reload ssh # Ubuntu/Debian
sudo systemctl reload sshd # CentOS/RHELWhy PermitRootLogin no and not prohibit-password? Although prohibit-password already blocks root access by password, leaving root login by key enabled is still a risk: if that key is compromised, the attacker has full system access without needing to escalate privileges.
Changing the SSH port (security through obscurity)#
Changing the default port (22) is not real security, but it eliminates the noise from internet bots that continuously scan that port:
Port 2222 # cualquier número entre 1024 y 65535On the router or firewall, create the NAT rule to redirect the external port to internal port 22, or configure UFW to accept the new port.
Firewall with UFW#
UFW (Uncomplicated Firewall) simplifies iptables management:
# Política por defecto: denegar todo
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Permitir solo lo necesario
sudo ufw allow 2222/tcp # SSH en puerto personalizado
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
# Activar
sudo ufw enable
sudo ufw status verboseAutomatic security updates#
Servers that don’t get updated eventually become vulnerable. unattended-upgrades applies security patches without manual intervention:
sudo apt install unattended-upgrades
sudo dpkg-reconfigure unattended-upgradesTo verify it’s active:
systemctl is-active unattended-upgradesThe configuration file is located at /etc/apt/apt.conf.d/50unattended-upgrades. By default it only applies security updates, which is the correct behavior for production.
User management#
Principle of least privilege#
Each user should only have the permissions they need. Regularly review which users have an active shell:
grep -v nologin /etc/passwd | grep -v falseTo disable a user without deleting them:
sudo usermod -s /usr/sbin/nologin usuario
sudo passwd -l usuario # Bloquear contraseñasudo without password — thoughtfully#
It’s tempting to put NOPASSWD in sudoers to avoid typing the password, but limit it to the specific commands that need it:
# Bien: solo para comandos concretos
usuario ALL=(ALL) NOPASSWD: /usr/bin/docker, /usr/bin/systemctl restart nginx
# Mal: acceso total sin contraseña
usuario ALL=(ALL) NOPASSWD: ALLBrute force protection with fail2ban#
fail2ban monitors system logs and automatically blocks IPs that make too many failed attempts:
sudo apt install fail2banBasic configuration in /etc/fail2ban/jail.local:
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
[sshd]
enabled = true
port = 2222
logpath = %(sshd_log)s
backend = %(sshd_backend)ssudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Ver IPs baneadas
sudo fail2ban-client status sshdAuditing: what to review periodically#
Once the server is configured, it’s good to check these points regularly:
# Intentos de acceso fallidos
sudo lastb | head -20
# Puertos escuchando (detectar servicios inesperados)
ss -tlnp
# Binarios con SUID (posibles vectores de escalada de privilegios)
find / -perm -4000 -type f 2>/dev/null
# Actualizaciones pendientes
apt list --upgradable 2>/dev/null | grep -i securityEncryption between servers with WireGuard#
If you have multiple servers that need to communicate (rsync, databases, internal APIs), avoid exposing those services to the Internet. WireGuard offers a fast and simple VPN tunnel with ChaCha20-Poly1305 encryption:
sudo apt install wireguard
wg genkey | tee privatekey | wg pubkey > publickeyTraffic between servers travels encrypted through the tunnel (10.10.0.x) instead of using public IPs. This way, services like rsync or PostgreSQL never get exposed even if someone intercepts network traffic.
I have a specific article on emergency replication with rsync and WireGuard with the complete configuration.
Summary: hardening checklist#
| Action | Priority |
|---|---|
PermitRootLogin no in sshd_config | High |
PasswordAuthentication no | High |
| UFW Firewall active with minimal rules | High |
unattended-upgrades active | High |
| Change SSH port | Medium |
AllowTcpForwarding no | Medium |
MaxAuthTries 3 | Medium |
| fail2ban installed and configured | Medium |
| Review users with active shell | Medium |
| Periodic audit of ports and SUID | Low |
Perfect security does not exist, but applying these steps drastically reduces the probability of being the easy target that automated bots are looking for.
Recommended Equipment#
- Intel N100 Mini PC — Silent and efficient mini PC for 24/7 home server
- YubiKey 5 NFC — Physical security key for 2FA and secure SSH access
Affiliate links. No extra cost for you.