A web server with no firewall is an open invitation. iptables/nftables provide stateful packet filtering, NAT, and port redirection. TCP Wrappers adds host-based access control at the application layer. Together they create a layered defense. This lesson moves from basic 'allow HTTP' to crafting a default-drop policy with connection tracking, rate limiting, and integration with fail2ban.
iptables has three default chains: INPUT, OUTPUT, FORWARD. The filter table is for firewall rules, the nat table for address translation. The most critical step: set the default policy for INPUT and FORWARD to DROP. Then create rules to allow established/related traffic, loopback, and specific services (SSH, HTTP, HTTPS). Always put the conntrack rule early to avoid blocking legitimate responses.
# Flush existing rules and set default drop
sudo iptables -F
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPTThis allows SSH only from the local subnet, plus return traffic for outbound connections. Everything else is dropped.
nftables replaces iptables with a unified framework, better performance, and a more readable syntax. The rules are defined in /etc/nftables.conf. A simple script can create a table for inet (both IPv4/6) with similar logic. nftables also supports sets and maps for large-scale filtering. Use nft list ruleset to see the active configuration.
# /etc/nftables.conf snippet
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
ct state established,related accept
iif lo accept
tcp dport 22 ip saddr 192.168.1.0/24 accept
}
}💡 Use nftables atomic replacement: nft -f /etc/nftables.conf applies the entire ruleset in one transaction, preventing the brief moment of exposure that can happen with sequential iptables commands.
TCP Wrappers (tcpd) uses /etc/hosts.allow and /etc/hosts.deny to control access to services that are compiled with libwrap. Although many modern services (like nginx) don't use it, it's still effective for sshd, vsftpd, and some legacy daemons. The syntax is simple: ALL: 192.168.1. in hosts.allow, and ALL: ALL in hosts.deny to deny by default.
⚠️ A misconfigured firewall can lock you out. Always test with a cron job that flushes rules after 5 minutes if you lose connectivity, or use a management console.
Verify exercises to earn ★ 160 XP and unlock next lab level.