DEV Community

Cover image for Securing a VPS: Essential Hardening Steps for Web Developers (Part 1)
Tomas Hornak
Tomas Hornak

Posted on • Edited on

Securing a VPS: Essential Hardening Steps for Web Developers (Part 1)

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 
Enter fullscreen mode Exit fullscreen mode

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 
Enter fullscreen mode Exit fullscreen mode

Then restart SSHD:

sudo systemctl restart sshd 
Enter fullscreen mode Exit fullscreen mode

Important: Always test your SSH configuration in a separate terminal session before closing your current connection. This prevents accidental lockouts. Also, ideally, don't close your connection before Step 2.

Step 2: Firewall Configuration

Linux'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 
Enter fullscreen mode Exit fullscreen mode

Critical note: It's recommended to perform Steps 1 & 2 during one SSH session to avoid locking yourself out of access.

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 
Enter fullscreen mode Exit fullscreen mode

Modern Ubuntu systems use systemd journaling instead of traditional log files, which requires specific configuration:

sudo nano /etc/fail2ban/jail.local 
Enter fullscreen mode Exit fullscreen mode
[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 
Enter fullscreen mode Exit fullscreen mode

After that, restart the service for it to use the updated configuration:

sudo systemctl restart fail2ban 
Enter fullscreen mode Exit fullscreen mode

Step 4: Docker Security Configuration

I am a big fan of Docker. I decided to go full docker. It will bring little few more steps but we'll get better security as all services accessible from internet will run in an isolated docker containers.

It also keeps our host machine clean of unecessary installed packages.

Installation is pretty simple and a lot of VPS providers offers fresh install with docker pre-istalled.

When docker is installed create /etc/docker/daemon.json with following content:

{ "live-restore": true, "userland-proxy": false, "no-new-privileges": true, "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } } 
Enter fullscreen mode Exit fullscreen mode

The no-new-privileges setting is particularly important as it prevents containers from escalating their privileges during runtime, blocking a common attack vector.

As before, don't forget to restart the service:

sudo systemctl restart docker 
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (1)

Collapse
 
leob profile image
leob

"Since applications run in Docker containers" - well, that's a big and unproven assumption :-) ... but for the rest, yeah nice, some useful tips/guidelines :)