This article was published over 2 years ago. Some information may be outdated.
Secure your servers using iptables
iptables is a firewall utility for Linux that lets you define rules for incoming and outgoing connections. You can restrict traffic to specific ports like 80, 443, and 22, and drop everything else.
You can also allow or deny connections from specific IP addresses.
Installation
Install iptables on Ubuntu:
sudo apt-get install -y iptables iptables-persistent
By default, iptables rules live in RAM and are lost on reboot. The iptables-persistent package solves that (covered later).
Understanding the basics
iptables organizes rules into chains. Three chains exist by default:
- INPUT: Traffic inbound to the server.
- FORWARD: Traffic forwarded (routed) to other locations.
- OUTPUT: Traffic outbound from the server.
To allow port 80, you append a rule to the INPUT chain telling iptables to accept traffic on that port.
List all current rules:
sudo iptables -L -v
Expected output on a fresh system:
Chain INPUT (policy ACCEPT 9 packets, 636 bytes)
pkts bytes target prot opt in out source destination
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 5 packets, 568 bytes)
pkts bytes target prot opt in out source destination
No rules are defined yet. The default policy is ACCEPT for all chains.
iptables uses two policies:
ACCEPT: allow the connection.DROP: silently discard the connection.
The default policy determines what happens when traffic matches no rules. If you have allowed ports 80, 443, and 22, and someone connects on port 8800, the default policy decides the outcome.
The default ACCEPT policy means any port is open unless explicitly blocked. That is insecure. Switching to DROP inverts the model: everything is blocked unless you explicitly allow it.
Avoid self-blocking
This is critical. If you change the default policy to DROP without first allowing established connections, you will be locked out of the server immediately.
If you persist those rules, there is no way to reconnect. Follow the commands in this post in order.
Allow established and related connections before doing anything else:
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT
Your current SSH session (and any other active connections) will now survive policy changes.
Loopback Network
Allow loopback traffic so the server can communicate with itself on 127.0.0.1:
sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A OUTPUT -o lo -j ACCEPT
This is necessary for services running on the same machine -- MySQL, Redis, and anything else bound to localhost.
By default, MySQL uses
127.0.0.1in thebind-addressflag to determine where it listens for connections. Read more aboutbind-addresshere.
Breaking down the flags:
-A INPUT: append a rule to theINPUTchain.-i lo: match the loopback network interface.-j ACCEPT: accept the connection.
Allowing Specific Ports
Open the ports you actually need:
## Accept connections from port 22 (SSH)
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
## Accept connections from port 80 (HTTP)
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
## Accept connections from port 443 (HTTPS)
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
You can also block specific IP addresses using the -s flag:
## Block an IP address
sudo iptables -A INPUT -s 210.10.85.65 -j DROP
Now that the necessary ports are open, drop everything else:
sudo iptables -A INPUT -j DROP
Persist the rules so they survive reboots:
sudo service netfilter-persistent save
sudo service netfilter-persistent restart
Flushing
Remove all rules from every chain:
sudo iptables -F INPUT
sudo iptables -F OUTPUT
sudo iptables -F FORWARD
Where to go from here?
For more on iptables, read:
Iptables Essentials: Common Firewall Rules and Commands | DigitalOcean
Summary
- Always allow established connections first -- changing the default policy to
DROPwithout this step locks you out of the server permanently. - Switch the default policy to
DROPso that only explicitly allowed ports are accessible, rather than blocking ports one by one. - Allow loopback traffic for services like MySQL and Redis that bind to
127.0.0.1. - Use
iptables-persistentto save rules across reboots -- without it, your firewall configuration is lost on restart. - Order matters -- follow the sequence in this post to avoid self-blocking.