Introduction
If you’re responsible for a production‑grade web stack, the difference between a well‑hardened server and a vulnerable one can be measured in minutes of downtime or a costly data breach. This tutorial walks through seven practical steps to lock down Nginx and the underlying Linux host without sacrificing performance.
1. Enforce Strong TLS Everywhere
TLS is the first line of defense against eavesdropping. Modern browsers expect TLS 1.3 or at least TLS 1.2 with strong ciphers.
# /etc/nginx/conf.d/ssl.conf
ssl_protocols TLSv1.3 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers \
"EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_stapling on;
ssl_stapling_verify on;
# Enable HTTP/2 for better multiplexing
listen 443 ssl http2;
- Use a certificate from a trusted CA (Let’s Encrypt works fine for most use‑cases).
- Enable OCSP stapling to reduce latency.
- Turn off SSLv3, TLS 1.0, and TLS 1.1.
2. Restrict Inbound Traffic with a Minimal Firewall
A host‑level firewall should only allow the ports you actually need. Below is a concise iptables
rule set that blocks everything by default and opens only HTTP, HTTPS, SSH, and health‑check ports.
# Flush existing rules
iptables -F
iptables -X
# Default policies – DROP everything
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Allow loopback and established connections
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Open required ports
iptables -A INPUT -p tcp --dport 22 -j ACCEPT # SSH
iptables -A INPUT -p tcp --dport 80 -j ACCEPT # HTTP
iptables -A INPUT -p tcp --dport 443 -j ACCEPT # HTTPS
iptables -A INPUT -p tcp --dport 9100 -j ACCEPT # Prometheus metrics (optional)
# Log and drop everything else (optional)
iptables -A INPUT -j LOG --log-prefix "iptables-drop: "
iptables -A INPUT -j DROP
Persist the rules with iptables-save > /etc/iptables/rules.v4
(Debian/Ubuntu) or the appropriate service for your distro.
3. Thwart Brute‑Force Logins with Fail2Ban
Fail2Ban watches log files for repeated failed authentication attempts and injects temporary iptables
bans.
# /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
After editing, restart the service: systemctl restart fail2ban
. Adjust maxretry
and bantime
to match your security posture.
4. Harden SSH Access
- Disable password authentication – enforce key‑based logins only.
- Change the default port (optional but adds obscurity).
-
Limit users with the
AllowUsers
directive. -
Enable
PermitRootLogin no
.
# /etc/ssh/sshd_config
Port 2222
Protocol 2
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
AllowUsers deployer admin
Reload SSH: systemctl reload sshd
.
5. Automate Secure Backups
Never rely on a single point of failure. A simple rsync
‑over‑SSH script combined with cron
can provide daily snapshots.
#!/bin/bash
# /usr/local/bin/backup.sh
SRC="/var/www/html"
DEST="backup@backup‑server:/backups/$(hostname)/$(date +%F)"
rsync -a --delete -e ssh "$SRC" "$DEST"
Add to crontab
(run at 02:30 AM):
30 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1
For added resilience, rotate snapshots with logrotate
‑style retention policies.
6. Keep the System Patched Automatically
Enable unattended upgrades for security patches while leaving major version upgrades manual.
# Debian/Ubuntu
apt install unattended-upgrades
dpkg-reconfigure unattended-upgrades
On RHEL‑based systems, enable yum-cron
:
yum install yum-cron
systemctl enable --now yum-cron
Review /etc/apt/apt.conf.d/50unattended-upgrades
to ensure only -security
repos are auto‑installed.
7. Deploy a Lightweight WAF with Nginx
Nginx’s ngx_http_modsecurity_module
(or the open‑source ModSecurity
with the OWASP CRS
) can block common OWASP Top 10 attacks.
# /etc/nginx/modsecurity.conf
Include /usr/local/modsecurity-crs/base_rules/*.conf;
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess Off
SecRuleRemoveById 950005 # Example: disable a noisy rule
Enable the module in your main config:
load_module modules/ngx_http_modsecurity_module.so;
server {
listen 443 ssl;
modsecurity on;
modsecurity_rules_file /etc/nginx/modsecurity.conf;
# ... other directives ...
}
Test the configuration with nginx -t
and reload.
Conclusion
Hardening a Linux box that runs Nginx doesn’t require a massive overhaul; it’s a series of incremental, repeatable steps. By enforcing TLS 1.3, locking down the firewall, automating fail2ban, tightening SSH, scheduling encrypted backups, keeping the OS patched, and adding a modest WAF, you’ll raise the security bar dramatically while preserving the low‑latency performance that Nginx is known for.
If you’re looking for a reliable partner to review or host your hardened stack, consider checking out https://lacidaweb.com for a no‑pressure conversation.
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.