While UFW (Uncomplicated Firewall) provides an excellent abstraction layer for managing firewall rules, there are cases where you need direct iptables access for advanced configurations. UFW actually builds upon iptables, meaning your custom rules can coexist - if implemented correctly.
The key locations for persistent iptables rules in Ubuntu are:
/etc/ufw/before.rules # Rules applied before UFW processes its rules
/etc/ufw/after.rules # Rules applied after UFW's rules
/etc/ufw/user.rules # Alternative for user-specific rules
Let's say we want to log all dropped packets (which UFW doesn't provide by default):
# Edit the after.rules file
sudo nano /etc/ufw/after.rules
# Add these lines before the COMMIT line:
-A ufw-after-logging-input -j LOG --log-prefix "[UFW DROP] " --log-level 4
-A ufw-after-logging-forward -j LOG --log-prefix "[UFW DROP] " --log-level 4
After adding your rules, you need to:
# Save the file and reload UFW
sudo ufw disable
sudo ufw enable
This ensures your rules persist across reboots since they're stored in UFW's configuration files.
Here's how to implement rate limiting for SSH connections (something UFW can't do natively):
# Add to /etc/ufw/before.rules
# SSH brute-force protection
-A ufw-before-input -p tcp --dport 22 -m state --state NEW -m recent --set
-A ufw-before-input -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP
Always verify your rules after implementation:
# Check all active iptables rules
sudo iptables -L -n -v
# Get more detailed output including line numbers
sudo iptables -L -n -v --line-numbers
- Always add comments to your custom rules for future reference
- Test rules manually before making them persistent
- Document changes in /etc/ufw/ufw.conf
- Consider using ufw allow/deny commands where possible
While UFW (Uncomplicated Firewall) provides a user-friendly interface for iptables, it's intentionally designed to be simple - which means it can't handle every possible networking scenario. When you need complex rules beyond UFW's capabilities, you'll need to work directly with iptables.
The proper location for persistent iptables rules in Ubuntu is the /etc/iptables.rules
file. However, we'll need to create a system service to load these rules at boot. Here's the complete process:
# First save your current rules (including UFW rules) sudo iptables-save | sudo tee /etc/iptables.rules # Create a new systemd service file sudo nano /etc/systemd/system/iptables-restore.service
Paste this content into your service file:
[Unit] Description=Restore iptables rules After=network.target [Service] Type=oneshot ExecStart=/sbin/iptables-restore /etc/iptables.rules [Install] WantedBy=multi-user.target
To ensure your custom rules play nicely with UFW, follow these steps:
# Enable and start the service sudo systemctl enable iptables-restore.service sudo systemctl start iptables-restore.service # Check UFW status to confirm everything works sudo ufw status
Let's say you need to rate-limit incoming SSH connections:
# Add this to /etc/iptables.rules before COMMIT -A INPUT -p tcp --dport 22 -m connlimit --connlimit-above 3 -j REJECT -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP
After making changes, always verify your rules:
# Check current rules sudo iptables -L -n -v # Test the new rules functionality # For our SSH example: for i in {1..5}; do ssh localhost; done
Remember that:
- UFW changes won't automatically update your custom rules file
- After modifying UFW rules, resave your iptables configuration
- Your custom rules won't appear in
ufw status
output