A server firewall decides which network traffic is allowed to reach your server and which is dropped. Without one, every service listening on the machine, including databases and admin tools that were never meant to be public, is reachable by anyone on the internet. Linux has a powerful firewall built into the kernel, and several tools make it easier to manage. This article explains how the pieces fit together and shows practical configurations with iptables, UFW and CSF.
How Linux firewalls fit together
The actual packet filtering happens inside the Linux kernel, in a framework called Netfilter. You do not talk to Netfilter directly; you use a tool that writes rules into it:
- iptables: the classic command-line tool, used for many years and still widely documented.
- nftables (
nft): the modern replacement, default on current Debian, Ubuntu and RHEL-family releases. On these systems theiptablescommand is often a compatibility layer that writes nftables rules. - UFW (Uncomplicated Firewall): a simple front end, standard on Ubuntu.
- firewalld: a zone-based front end, standard on RHEL, AlmaLinux and Rocky Linux.
- CSF (ConfigServer Security & Firewall): a front end popular on cPanel and other hosting servers, with built-in login failure detection.
Use one front end per server. Running UFW and firewalld, or CSF and UFW, together leads to rules overwriting each other and confusing results.
Server firewall core concepts
- Default policy: what happens to traffic no rule matches. For incoming traffic on a server it should be deny (drop).
- Rule order: rules are checked top to bottom, and the first match wins.
- Stateful filtering: the firewall tracks connections, so replies to traffic your server started (such as downloading updates) are allowed back in automatically.
- Inbound vs outbound: most servers restrict inbound and allow outbound. Restricting outbound adds protection against malware "phoning home", at the cost of more maintenance.
A minimal iptables ruleset
Understanding raw iptables helps even if you use a front end, because it shows what the tools generate. A typical web server policy:
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -s 203.0.113.10 -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
iptables -A INPUT -p icmp -j ACCEPT
iptables -P INPUT DROP
In order: allow the loopback interface (local services talking to each other), allow replies to existing connections, allow SSH only from one office IP, allow web traffic from anywhere, allow ICMP (ping and important network error messages), then drop everything else. Remember that IPv6 needs its own rules via ip6tables; a server with IPv6 enabled but unfiltered is wide open on that address.
Rules set this way disappear on reboot unless saved, for example with the iptables-persistent package on Debian/Ubuntu. This is one reason front ends are preferred.
UFW: the simplest option
UFW handles IPv4 and IPv6, persistence and sensible defaults for you:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 203.0.113.10 to any port 22 proto tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw limit 22/tcp # optional: rate-limit repeated connections
sudo ufw enable
sudo ufw status verbose
Delete rules by number with sudo ufw status numbered and sudo ufw delete 3. A useful extra: sudo ufw allow from 10.0.0.0/24 to any port 3306 lets an application server on a private network reach MySQL while keeping it closed to the internet.
Docker caveat: Docker inserts its own rules for published container ports, which can bypass UFW. A container started with -p 8080:80 may be publicly reachable even if UFW does not allow 8080. Bind published ports to localhost (-p 127.0.0.1:8080:80) when only a local reverse proxy needs them.
CSF: popular on hosting servers
CSF combines a stateful firewall with a login failure daemon (LFD) that watches logs for brute-force attempts against SSH, mail, FTP and control panels, then blocks offending IPs. It integrates with cPanel's WHM and is common on shared hosting servers. Its main configuration is /etc/csf/csf.conf:
TESTING = "0" # must be 0 for production; 1 auto-clears rules
TCP_IN = "20,21,22,25,53,80,110,143,443,465,587,993,995,2083,2087"
TCP_OUT = "20,21,22,25,53,80,110,113,443,587"
LF_SSHD = "5" # block after 5 failed SSH logins
Day-to-day commands:
csf -r # reload rules
csf -a 198.51.100.7 # permanently allow an IP
csf -d 192.0.2.66 # permanently deny an IP
csf -g 192.0.2.66 # search rules for an IP
Trim TCP_IN to the ports you actually use; default lists include services many servers do not run. Check the current status of CSF's development before choosing it for a new server, as its maintenance arrangements have changed over time.
Cloud firewalls add a second layer
Cloud providers offer network-level firewalls (security groups on AWS, network security groups on Azure, VPC firewall rules on Google Cloud). They filter traffic before it reaches the server and cannot be disabled by someone who compromises the machine. Use them together with the host firewall: the cloud firewall as the outer wall, the host firewall as the inner one.
Avoid locking yourself out
- Always allow your SSH access before enabling the firewall or setting a default deny policy.
- Keep an existing SSH session open while testing changes.
- Know how to reach the provider's web or recovery console.
- For risky changes, schedule an automatic rollback, for example
sudo ufw disableviaat now + 5 minutes, and cancel it once you confirm access.
Test from the outside
A firewall's configuration file shows what you intended; an external test shows what is actually reachable. List what is listening locally with sudo ss -tulpn, then check from outside with our port checker. Anything open that you did not expect deserves investigation. Firewall design and ongoing review are part of our server management service.
Key takeaways
- Netfilter does the filtering; iptables, nftables, UFW, firewalld and CSF are ways to manage it. Pick one.
- Default-deny inbound, allow established connections, and open only the ports you need.
- Restrict admin ports by source IP, filter IPv6 too, and watch for Docker bypassing UFW.
- Layer a cloud firewall on top, and verify open ports from outside.