Minimal Firewall Policy

A Minimal Firewall Policy for a Web Server

A web server needs few open doors: deny inbound by default, open 80 and 443 to the world, open 22 only to the addresses you administer from, and nothing else. MySQL 524 (3306, 33060), PHP-FPM (9000), Redis 2,763 (6379) and Memcached 4,600 (11211) serve local processes and must never face the internet. Keep the policy in a script that can be reviewed and rerun on the next server:

firewall.sh: the whole inbound policy, reproducibleShell
#!/usr/bin/env bash
# firewall.sh: the complete inbound policy for one web server (run as root)
set -euo pipefail
ADMIN_IP="${ADMIN_IP:?set ADMIN_IP to the address you administer from}"
ufw --force reset > /dev/null            # start from nothing; rules are re-created below
ufw default deny incoming
ufw default allow outgoing
ufw default deny routed
ufw limit from "$ADMIN_IP" to any port 22 proto tcp comment 'SSH, admin only'
ufw allow 80,443/tcp comment 'Web'
ufw logging low
ufw --force enable
ufw status verbose
Output
...
To                         Action      From
--                         ------      ----
22/tcp                     LIMIT IN    198.51.100.7               # SSH, admin only
80,443/tcp                 ALLOW IN    Anywhere                   # Web
80,443/tcp (v6)            ALLOW IN    Anywhere (v6)              # Web

Run it as sudo ADMIN_IP=198.51.100.7 bash firewall.sh. The SSH rule has no v6 twin because its source is an IPv4 address, and status verbose reports routed traffic as disabled because this kernel forwards no packets. ufw 280 reset saves a timestamped copy of each rules file in /etc/ufw/, so a bad run can be undone. After every change, rerun the probe from Port Closed? from outside, and before tightening SSH on a cloud server, make sure you have a way in that does not use port 22, such as the provider's browser console.