Why a host firewall
Security groups and network firewalls protect from outside, but the host firewall is the last layer on the machine itself: if a service accidentally listens on 0.0.0.0 (a database, Redis, a dashboard), the firewall can still block it. The principle is default deny inbound: block every incoming connection, open only the ports needed, and restrict the source where possible.
Both tools below write their rules to the kernel's nftables (or iptables).
ufw (Ubuntu)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH # allow SSH BEFORE enabling
sudo ufw allow from 10.0.0.0/8 to any port 5432 proto tcp
sudo ufw enable
sudo ufw status verbose
ufw limit ssh automatically blocks IPs that open too many connections in a short time. Delete rules with sudo ufw status numbered and then sudo ufw delete <number>.
firewalld (RHEL, Rocky, Amazon Linux)
firewalld groups rules into zones (public, internal, trusted); each interface belongs to one zone.
sudo firewall-cmd --get-active-zones
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-all
Without --permanent a rule lasts only until the next reload or reboot; with --permanent but no --reload it is not applied yet.
flowchart LR I["Internet"] --> SG["Security group / NSG"] SG --> FW["Host firewall<br/>ufw / firewalld"] FW --> S["Service :443"] FW -. "blocked" .-> DB["PostgreSQL :5432"]
Traps
- Running
ufw enableover SSH beforeallow OpenSSH: you lock yourself out. - Assuming ufw blocks ports published by Docker: Docker inserts its own rules ahead of ufw, so bind to
127.0.0.1or use theDOCKER-USERchain. - Opening a port to
0.0.0.0/0when only a few IPs need access.
| Command | Meaning |
|---|---|
sudo ufw status verbose |
Status and default policies |
sudo firewall-cmd --list-all |
Rules of the current zone |
sudo nft list ruleset |
The actual rules in the kernel |