Suresh locks down the open server
ufw, firewalld, fail2ban, nmap — network security that takes an afternoon
Suresh inherited a server that was accessible from everywhere. Port 22 (SSH) was open to the internet. Port 3306 (MySQL) was open to the internet. The application ports were open to the internet. It had been running this way for 2 years.
He ran a port scan from his laptop using an online tool. 14 ports were publicly accessible. He could see the MySQL port. He could see Redis. He could see the internal admin panel.
He spent one afternoon locking it down. Here is what he did.
UFW — UNCOMPLICATED FIREWALL (UBUNTU/DEBIAN)
ufw is a frontend for iptables that makes firewall rules readable and manageable.
sudo ufw status # see current rules and whether ufw is active
sudo ufw status verbose # more detail
sudo ufw status numbered # show rule numbers (needed for deletion)# Enable ufw (CAREFUL: enable SSH first or you will lock yourself out):
sudo ufw allow 22/tcp # ALWAYS do this first
sudo ufw enable # now enableBASIC RULES
# Allow by port number:
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw allow 22/tcp # SSH# Deny a port:
sudo ufw deny 3306/tcp # block MySQL from outside# Allow by service name:
sudo ufw allow ssh # same as allow 22/tcp
sudo ufw allow http # same as allow 80/tcp
sudo ufw allow https # same as allow 443/tcp# Allow a specific IP to access a specific port:
sudo ufw allow from 10.0.0.5 to any port 5432 # only this IP can reach PostgreSQL
sudo ufw allow from 10.0.0.0/24 to any port 5432 # only this subnet can reach PostgreSQL# Allow from an IP on any port (trusted server):
sudo ufw allow from 10.0.0.10# Delete a rule:
sudo ufw status numbered # get the number
sudo ufw delete 3 # delete rule number 3# Reset all rules (start over):
sudo ufw resetSURESH'S LOCKDOWN
# Step 1: Only allow what is needed from the internet:
sudo ufw default deny incoming # block everything by default
sudo ufw default allow outgoing # allow all outboundsudo ufw allow 22/tcp # SSH (will restrict to specific IPs later)
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS# Step 2: Allow internal services only from the internal network:
sudo ufw allow from 10.0.0.0/24 to any port 5432 # PostgreSQL
sudo ufw allow from 10.0.0.0/24 to any port 6379 # Redis
sudo ufw allow from 10.0.0.0/24 to any port 8080 # Internal app port
sudo ufw allow from 10.0.0.0/24 to any port 9200 # Elasticsearch# Step 3: Restrict SSH to known IPs:
sudo ufw delete allow 22/tcp # remove the open SSH rule
sudo ufw allow from 203.0.113.10 to any port 22 # only office IP
sudo ufw allow from 203.0.113.11 to any port 22 # only VPN IP# Step 4: Enable:
sudo ufw enable
sudo ufw status verboseFIREWALLD — CENTOS AND RHEL
sudo firewall-cmd --state # running?
sudo firewall-cmd --list-all # all rules# Add rules:
sudo firewall-cmd --add-service=http --permanent
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --add-port=8080/tcp --permanent# Block a service:
sudo firewall-cmd --remove-service=mysql --permanent# Allow a specific IP to a port:
sudo firewall-cmd --add-rich-rule='rule family=ipv4 source address=10.0.0.5 port protocol=tcp port=5432 accept' --permanent# Apply changes:
sudo firewall-cmd --reloadIPTABLES — THE UNDERLYING RULES
ufw and firewalld both write iptables rules. You can read them directly:
sudo iptables -L -n -v # all rules with packet counts
sudo iptables -L INPUT -n # just the INPUT chain (inbound traffic rules)
sudo iptables-save # export all rules to stdoutFAIL2BAN — AUTOMATIC IP BANNING
Fail2ban watches log files and bans IPs that fail authentication repeatedly. Essential for any server with SSH exposed to the internet.
sudo apt install fail2ban# Default config bans IPs with 5 failed SSH logins in 10 minutes for 10 minutes:
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local# Key settings:
[sshd]
enabled = true
maxretry = 3 # ban after 3 failures (not 5)
findtime = 600 # within 10 minutes
bantime = 3600 # ban for 1 hoursudo systemctl enable fail2ban && sudo systemctl start fail2ban# Check status:
sudo fail2ban-client status sshd # see SSH jail stats
sudo fail2ban-client status sshd | grep "Banned IP" # currently banned IPs# Unban an IP you accidentally locked out:
sudo fail2ban-client set sshd unbanip 203.0.113.10PORT SCANNING YOUR OWN SERVER
Before and after any firewall changes, scan your own server to see what is exposed:
nmap -sV YOUR_SERVER_IP # scan from your laptop
sudo nmap -sV localhost # scan from the server itself# Quick open port check:
nmap -p 1-65535 --open YOUR_SERVER_IPAfter Suresh finished, a re-scan showed only ports 80, 443, and 22 (restricted to 2 IPs) visible from the internet. The MySQL, Redis, Elasticsearch, and admin panel ports were invisible. The server that had been exposed for 2 years was secured in one afternoon.
sudo ufw default deny incoming then explicitly allow only what is needed — deny by default is the right starting point
Always allow SSH before enabling ufw — sudo ufw allow 22/tcp then sudo ufw enable — wrong order locks you out
Restrict database ports to internal network IPs only — MySQL and PostgreSQL should never be open to the internet
fail2ban watches logs and auto-bans IPs with repeated failures — install it on any server with SSH exposed
nmap your own server before and after changes — see exactly what an attacker would see from the internet