Introduction
If you’re a DevOps lead responsible for a fleet of web servers, the first line of defense is a rock‑solid Nginx configuration paired with a tight firewall. In this tutorial we’ll walk through seven practical steps that bring your Nginx instance from “just working” to “battle‑ready.” The focus is on TLS hardening, firewall rule design, and complementary OS‑level safeguards such as Fail2Ban and SSH key enforcement.
1. Grab a Free, Trusted TLS Certificate
Let’s Encrypt is the de‑facto standard for automated, free certificates. Install the certbot
client on your Linux host and request a cert for your domain:
sudo apt-get update && sudo apt-get install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com
The wizard will:
- Verify domain ownership via HTTP‑01 challenge.
- Generate a certificate bundle in
/etc/letsencrypt/live/example.com/
. - Auto‑reload Nginx after successful issuance.
Tip: Use the --dry-run
flag to test renewal before you go live.
2. Harden the Nginx SSL Configuration
A weak cipher suite or outdated protocol can undo all your hard work. Replace the default ssl.conf
with a curated block:
# /etc/nginx/snippets/secure_ssl.conf
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers \
'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1h;
ssl_stapling on;
ssl_stapling_verify on;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options SAMEORIGIN;
add_header X-XSS-Protection "1; mode=block";
Then include the snippet in your server block:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
include snippets/secure_ssl.conf;
server_name example.com www.example.com;
# … other directives …
}
This configuration:
- Enforces TLS 1.2/1.3 only.
- Uses strong, forward‑secrecy ciphers.
- Enables HSTS with a two‑year max‑age.
- Adds common security headers.
3. Lock Down the Host Firewall
A well‑crafted firewall reduces the attack surface dramatically. Below we use UFW (Uncomplicated Firewall) for readability, but the same rules translate to iptables
or nftables
.
# Allow SSH (rate‑limited)
sudo ufw limit OpenSSH
# Allow HTTP/HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# Deny everything else
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Enable the firewall
sudo ufw enable
Why rate‑limit SSH? It caps the number of connection attempts per minute, slowing down brute‑force attacks.
4. Deploy Fail2Ban to Thwart Brute‑Force Attempts
Fail2Ban watches log files for suspicious patterns and automatically inserts temporary iptables
blocks. Install and enable a basic jail for Nginx and SSH:
sudo apt-get install -y fail2ban
Create /etc/fail2ban/jail.local
:
[sshd]
enabled = true
port = ssh
logpath = %(sshd_log)s
maxretry = 5
bantime = 3600
[nginx-http-auth]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 3
bantime = 7200
Restart the service:
sudo systemctl restart fail2ban
Now any repeated failed login or HTTP auth attempt will be blocked for the specified bantime
.
5. Enforce SSH Key Authentication Only
Password logins are a common vector. Convert all user accounts to key‑only access:
# Edit /etc/ssh/sshd_config
PasswordAuthentication no
ChallengeResponseAuthentication no
PubkeyAuthentication yes
Reload the daemon:
sudo systemctl reload sshd
Distribute public keys via a secure channel (e.g., ssh-copy-id
) and keep the private key in a password‑protected vault like ssh‑agent
or a hardware token.
6. Automate Certificate Renewal & Reload
Let’s Encrypt certificates are only valid for 90 days. Certbot already installs a systemd timer, but you should verify it runs:
systemctl list-timers | grep certbot
Add a post‑renewal hook to guarantee Nginx picks up the fresh cert without manual reload:
# /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
#!/bin/sh
systemctl reload nginx
Make it executable:
sudo chmod +x /etc/letsencrypt/renewal-hooks/post/reload-nginx.sh
7. Back Up Nginx Configurations Regularly
A misconfiguration can bring your site down. Use rsync
or a simple cron job to push the /etc/nginx/
directory to a secure backup location:
# /etc/cron.daily/nginx-backup
#!/bin/sh
BACKUP_DIR=/var/backups/nginx-$(date +%F)
mkdir -p "$BACKUP_DIR"
rsync -a /etc/nginx/ "$BACKUP_DIR/"
# Optional: upload to remote storage (e.g., S3)
Store backups off‑site and rotate them weekly to keep storage costs low.
Conclusion
By following these seven steps you’ll have a Nginx front‑end that:
- Serves traffic over strong TLS only.
- Rejects unwanted network traffic at the OS level.
- Automatically blocks brute‑force attempts.
- Relies on SSH keys instead of passwords.
- Keeps its own configuration safely versioned and backed up.
Implementing the checklist today will dramatically raise the security posture of any production web service. For deeper dives into Linux hardening and managed hosting, you might find the resources at https://lacidaweb.com useful.
Top comments (0)