Introduction
If you’re an SRE managing web traffic on Ubuntu, you’ve probably seen the headache that comes with brute‑force attacks, outdated cipher suites, and misconfigured firewalls. Nginx is a rock‑solid reverse proxy, but out‑of‑the‑box it isn’t hardened for the hostile internet. This tutorial walks you through seven practical steps to lock down Nginx using TLS, a strict firewall, and Fail2Ban. By the end you’ll have a server that not only serves content fast, but also survives the most common attack vectors.
1. Keep the OS and Packages Updated
Security starts with a clean foundation. On Ubuntu, enable unattended upgrades for critical patches:
sudo apt-get install unattended-upgrades sudo dpkg-reconfigure --priority=low unattended-upgrades
Make sure the unattended-upgrades
config includes the -security
repo. Regularly verify with:
sudo unattended-upgrade --dry-run --debug
2. Install Nginx from the Official Repository
Avoid third‑party PPAs; they may lag behind security patches. Install the latest stable release:
sudo apt-get update sudo apt-get install nginx
After installation, lock the version to prevent accidental downgrades:
sudo apt-mark hold nginx
3. Obtain a Strong TLS Certificate
Let’s Encrypt offers free, automatically renewing certificates. Install certbot
and request a certificate for your domain:
sudo apt-get install certbot python3-certbot-nginx sudo certbot --nginx -d example.com -d www.example.com
During the interactive flow, select "Redirect HTTP to HTTPS". Certbot will also create a robust Nginx snippet that enforces modern TLS settings.
4. Harden the Nginx TLS Configuration
Even with Let’s Encrypt, you should audit the cipher suite and protocol list. Replace the default snippet (/etc/nginx/snippets/ssl-params.conf
) with the following hardened version:
# /etc/nginx/snippets/ssl-params.conf ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers on; ssl_ciphers \ "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"; ssl_ecdh_curve secp384r1; ssl_session_timeout 1d; ssl_session_cache shared:SSL:10m; ssl_stapling on; ssl_stapling_verify on; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; add_header X-Content-Type-Options nosniff; add_header X-Frame-Options DENY; add_header X-XSS-Protection "1; mode=block";
Include this snippet in your server block:
server { listen 443 ssl http2; include snippets/ssl-params.conf; ... }
Reload Nginx:
sudo systemctl reload nginx
5. Restrict Access with UFW (Uncomplicated Firewall)
A minimal firewall reduces the attack surface. Allow only HTTP/HTTPS, SSH (restricted to a specific IP range), and Fail2Ban’s ssh
jail:
sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp sudo ufw enable
Check the status:
sudo ufw status verbose
6. Deploy Fail2Ban to Thwart Brute‑Force Attempts
Fail2Ban monitors log files and bans offending IPs. Install and enable the service:
sudo apt-get install fail2ban sudo systemctl enable fail2ban
Create a local jail configuration (/etc/fail2ban/jail.local
) that protects both SSH and Nginx:
[sshd] enabled = true port = ssh logpath = %(sshd_log)s maxretry = 5 bantime = 1h [nginx-http-auth] enabled = true port = http,https filter = nginx-http-auth logpath = /var/log/nginx/error.log maxretry = 3 bantime = 2h
The nginx-http-auth
filter is shipped with Fail2Ban and watches for 401
responses. Restart the daemon:
sudo systemctl restart fail2ban
You can view bans with:
sudo fail2ban-client status nginx-http-auth
7. Automate Certificate Renewal and Nginx Reload
Let’s Encrypt certificates expire after 90 days. Certbot installs a systemd timer, but verify it works:
sudo systemctl list-timers | grep certbot
If you prefer a manual cron job, add this line to /etc/crontab
:
0 3 * * * root certbot renew --quiet --post-hook "systemctl reload nginx"
Now the server will automatically fetch a fresh cert and reload Nginx without human intervention.
Monitoring and Logging
A hardened server is only as good as its visibility. Install ufw
and fail2ban
logs into a central syslog server, or use a lightweight stack like rsyslog
+ logrotate
. Example logrotate
snippet for Nginx:
/var/log/nginx/*.log { daily missingok rotate 14 compress delaycompress notifempty create 0640 www-data adm sharedscripts postrotate [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid` endscript }
Conclusion
By following these seven steps—regular updates, strong TLS, a tight firewall, and Fail2Ban—you’ll dramatically reduce the risk of common web‑server attacks while keeping performance intact. Remember to test your configuration with tools like SSL Labs and fail2ban-client to verify that bans are applied as expected.
If you need a quick sanity‑check or want to explore more advanced hardening patterns, the team at https://lacidaweb.com offers practical guides and community support for Linux‑based web stacks.
Top comments (0)