A few weeks ago, someone posted on Ask Ubuntu asking for a minimal IPtables setup for LAMP servers.
As you can guess by the only answer there, I posted the following iptables commands for this:
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p udp --dport 80 -j ACCEPT
iptables -A INPUT -j REJECT --reject-with icmp-host-unreachable
The above is extraordinarily minimal. Time to go through this, and figure out what I should have included, and what I intentionally left out.
First, we’ll look at the first command, iptables -A INPUT -i lo -j ACCEPT. This adds a rule which states that “Any traffic originating on the loopback interface, or localhost, is allowed.”.
The next rule, iptables -A INPUT -m conntrack –ctstate ESTABLISHED,RELATED -j ACCEPT, uses a specialized module, called ‘conntrack’, which tracks connection states. This rule states that pre-established connections, and related connections, are accepted and not blocked.
The next rule, iptables -A INPUT -p tcp –dport 22 -j ACCEPT, accepts all traffic on port 22 (for default SSH installations and setups).
The next two rules, iptables -A INPUT -p tcp –dport 80 -j ACCEPT
and iptables -A INPUT -p udp –dport 80 -j ACCEPT, accept HTTP traffic on Port 80. Since HTTP traffic is both TCP and UDP, you need to have both.
The last rule in the above set, iptables -A INPUT -j REJECT –reject-with icmp-host-unreachable, uses a specialized target chain called “REJECT” which rejects the packets. The specific ICMP packet that will be used to reject the packet is “host unreachable” which terminates the networking connection to the server.
Now apparently, there are a few things I intentionally left out. Unless your server specifically hosts MySQL databases for external servers, you don’t need to allow MySQL traffic. Therefore, I did not add a rule to allow traffic from outside of the server itself related to MySQL (local mysql traffic is handled via the loopback rule that we first added). PHP has no reason to listen externally for requests, so it listens only on 127.0.0.1:9000 (in PHP 5.3.x, on Ubuntu Precise), or on the UNIX socket /var/run/php-fpm.sock (or similar, in PHP 5.4.x, on Ubuntu Quantal and later). Therefore, since PHP traffic will only be local, it’ll also be covered by that first rule, so no rule is needed for the PHP traffic.
I intentionally left out filtering of ICMP packets, such as pings. For an absolutely minimal setup of IPTables, you probably won’t want people to send you large pings (Pings of Death), or other ICMP packets outside of local traffic. I did not add ICMP filtering rules because I generally to not accept ICMP packets such as pings from external networks. I have a specialized program that establishes a TCP connection via HTTP and Curl automatically from my servers to one central server on an Amazon EC2 instance. The nginx access.log therefore will actually list those connections, and since I run that connection script every two minutes, its a semi-effective way of determining if the server infrastructure is online or not. If it’s not, i’ve got a nag-script that emails me if one misses a scheduled check-in several times, or if it hasnt responded in the past hour.