When configuring a production web server, your iptables ruleset should implement these security principles:
# Basic structure for a secure web server
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
Let's enhance the original rules with modern security practices:
# Loopback and local traffic
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -d 127.0.0.0/8 -j DROP # More secure than REJECT
# Connection state handling
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Modern replacement for state module
-A INPUT -m conntrack --ctstate INVALID -j DROP
# Web traffic rules with rate limiting
-A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j REJECT
-A INPUT -p tcp --dport 443 -m connlimit --connlimit-above 50 -j REJECT
Beyond basic brute force prevention, implement these measures:
# SSH protection with recent module
-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set --name SSH
-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 3 --name SSH -j DROP
-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT
# Alternative: Use ipset for more efficient blocking
ipset create SSH_blacklist hash:ip timeout 3600
-A INPUT -p tcp --dport 22 -m set --match-set SSH_blacklist src -j DROP
Modern best practices for ping handling:
# Rate-limited ICMP (ping) responses
-A INPUT -p icmp --icmp-type echo-request -m limit --limit 1/second -j ACCEPT
-A INPUT -p icmp --icmp-type echo-request -j DROP
Effective logging setup:
# Targeted logging for dropped packets
-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j LOG --log-prefix "SSH attempt: "
-A INPUT -j LOG --log-prefix "Dropped: " --log-level 4
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
Complete with default policies and IPv6 considerations:
# Default policies
-P INPUT DROP
-P FORWARD DROP
-P OUTPUT ACCEPT
# IPv6 equivalent (ip6tables)
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# ... similar rules for IPv6
To make rules persistent across reboots:
# For Debian/Ubuntu
apt install iptables-persistent
netfilter-persistent save
# For CentOS/RHEL
yum install iptables-services
service iptables save
When securing a Linux webserver, iptables remains the first line of defense. A properly configured ruleset should balance accessibility with security, especially for standard web services. Here's an optimized version of the ruleset with security enhancements:
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
# Localhost traffic
-A INPUT -i lo -j ACCEPT
-A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT
# Connection tracking
-A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# HTTP/HTTPS with rate limiting
-A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j REJECT
-A INPUT -p tcp --dport 443 -m connlimit --connlimit-above 50 -j REJECT
-A INPUT -p tcp --dport 80 -j ACCEPT
-A INPUT -p tcp --dport 443 -j ACCEPT
# SSH protection with modern techniques
-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set
-A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 -j DROP
-A INPUT -p tcp --dport 22 -j ACCEPT
# ICMP (ping)
-A INPUT -p icmp --icmp-type 8 -m limit --limit 1/second -j ACCEPT
# Logging and final policies
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables_dropped: "
-A INPUT -j REJECT --reject-with icmp-host-prohibited
COMMIT
The enhanced configuration includes several critical security measures:
- Default DROP policy for INPUT and FORWARD chains
- Connection rate limiting for web ports (prevents DDoS)
- Modern conntrack module instead of deprecated state module
- More aggressive SSH brute force protection (60 seconds instead of 180)
- ICMP rate limiting to prevent ping floods
- Proper ICMP rejection message for blocked packets
For production deployment, consider this bash script template:
#!/bin/bash
# Flush existing rules
iptables -F
iptables -X
# Set default policies
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
# Allow localhost
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT
# Allow established connections
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
# Web server rules
iptables -A INPUT -p tcp --dport 80 -m connlimit --connlimit-above 50 -j REJECT
iptables -A INPUT -p tcp --dport 443 -m connlimit --connlimit-above 50 -j REJECT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# SSH protection
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --set --name SSH
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 4 --name SSH -j DROP
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
# ICMP rules
iptables -A INPUT -p icmp --icmp-type 8 -m limit --limit 1/second -j ACCEPT
# Logging
iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables_dropped: " --log-level 7
# Save rules
iptables-save > /etc/iptables.rules
For long-term server security:
- Regularly review iptables logs (/var/log/messages or /var/log/syslog)
- Consider implementing ipset for managing large IP blacklists
- Update the ruleset when adding new services
- Combine with TCP wrappers (/etc/hosts.allow and /etc/hosts.deny) for defense in depth