After working with various PaaS solutions for years, I decided to set up a VPS with Contabo for hosting a few personal projects. While nothing particularly sensitive will be hosted there, I still wanted to implement proper security practices from the start.
Even simple hobby projects can become targets for automated attacks or get compromised for cryptocurrency mining. Here’s my systematic approach to hardening a fresh Ubuntu VPS, covering the essential security fundamentals.
The Starting Point
Fresh VPS with Ubuntu, an admin
user with sudo privileges, and SSH key authentication already configured during setup. Time to implement proper security measures.
Step 1: SSH Hardening
The default SSH configuration needed immediate attention. Running SSH on the default port 22 makes your server a target for automated scanning tools that constantly probe this port.
sudo nano /etc/ssh/sshd_config
Key changes made:
Port 2222 # Move away from the default port
PermitRootLogin no # Disable direct root access
PasswordAuthentication no # Enforce key-based authentication
PubkeyAuthentication yes # Enable SSH keys
MaxAuthTries 3 # Limit failed attempts
AllowUsers admin # Restrict user access
Important: Always test your SSH configuration in a separate terminal session before closing your current connection. This prevents accidental lockouts.
Step 2: Firewall Configuration
Ubuntu’s UFW provides straightforward firewall management. The approach is to deny all incoming connections by default, then explicitly allow only necessary services.
# Set restrictive defaults
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow essential services
sudo ufw allow 2222/tcp # SSH on custom port
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
# Enable firewall
sudo ufw enable
Critical note: Always configure firewall rules for your new SSH port before changing the SSH configuration. Otherwise, you’ll lose access to your server.
Step 3: Automated Intrusion Prevention with Fail2Ban
Fail2Ban monitors log files and automatically bans IP addresses showing suspicious behavior, such as repeated failed login attempts.
sudo apt install fail2ban
Modern Ubuntu systems use systemd journaling instead of traditional log files, which requires specific configuration:
sudo nano /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600 # Ban duration in seconds
findtime = 600 # Time window for counting failures
maxretry = 3 # Maximum attempts before ban
[sshd]
enabled = true
port = ssh
filter = sshd
backend = systemd # Use systemd journal
journalmatch = _SYSTEMD_UNIT=ssh.service
Step 4: Docker Security Configuration
Since applications run in Docker containers, the Docker daemon requires security hardening. Create /etc/docker/daemon.json
:
{
"live-restore": true,
"userland-proxy": false,
"no-new-privileges": true,
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
The no-new-privileges
setting is particularly important as it prevents containers from escalating their privileges during runtime, blocking a common attack vector.
Current Security Status
The VPS now has:
- SSH access secured with custom port and key-only authentication
- Firewall blocking unauthorized connections
- Automated intrusion detection and response
- Hardened Docker daemon configuration
This setup effectively blocks automated attacks and provides a solid security foundation for hosting web applications.
Coming Up in Part 2
The next article will cover:
- Setting up a reverse proxy with Nginx in Docker
- SSL certificate management with Let’s Encrypt
- Container security best practices
- System monitoring and maintenance
These foundational security measures provide essential protection while maintaining accessibility for legitimate use.