'''IMPORTANT:''' 192.0.2.0/24 is being used as the network in the examples below. Please update the ip addresses before using any of the examples!!
Going on the basis that the fastest way to learn something is to see an example, the following example will allow all SMTP, DNS, POP3, HTTP and HTTPS traffic while restricting SSH access to 192.0.2.0/24:
# Flush any pre-existing rules
iptables -F INPUT
iptables -F OUTPUT
iptables -F FORWARD
# Set the default input policy to DROP all
iptables -P INPUT DROP
# By default allow all traffic out
iptables -P OUTPUT ACCEPT
# Disallow forwarding
iptables -P FORWARD DROP
# Allow preexisting connections. This should be at the start of the rule set
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow SMTP
iptables -A INPUT -p tcp --dport 25 -j ACCEPT
# Allow DNS
iptables -A INPUT -p udp --dport 53 -j ACCEPT
iptables -A INPUT -p tcp --dport 53 -j ACCEPT
# Allow HTTP and HTTPS
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# Allow POP3
iptables -A INPUT -p tcp --dport 110 -j ACCEPT
# Allow SSH from 192.0.2.0/24
iptables -A INPUT -p tcp -s 192.0.2.0/24 --dport 22 -j ACCEPT
# Allow traffic over lo
iptables -A INPUT -i lo -j ACCEPT
After running the copying the above in as root on the command line, you should be able to run `iptables -L --line-numbers` and see something like:
Chain INPUT (policy DROP)
num target prot opt source destination
1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
2 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:25
3 ACCEPT udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:53
4 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:53
5 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80
6 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443
7 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:110
8 ACCEPT tcp -- 192.0.2.0/24 0.0.0.0/0 tcp dpt:22
9 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy DROP)
num target prot opt source destination
Chain OUTPUT (policy ACCEPT)
num target prot opt source destination
Of course the first thing to note there is that there's no corresponding outgoing rules. This is because iptables is "stateful". What this means is that once the initial connection has been created, iptables will keep track of it and allow all related packets in and out.
Also, the above rule set applies to all ips on the server, hence all the 0.0.0.0/0 in the destination column. If your server has two ips and you want to restrict mail and dns to one ip, while both ips have the HTTP ports open this will have to change. Presuming that the two ips are 192.0.2.2 and 192.0.2.3, and you only want SSH, Mail and DNS on 192.0.2.2, the ruleset would then become:
# Flush any pre-existing rules
iptables -F INPUT
iptables -F OUTPUT
iptables -F FORWARD
# Set the default input policy to DROP all
iptables -P INPUT DROP
# By default allow all traffic out
iptables -P OUTPUT ACCEPT
# Disallow forwarding
iptables -P FORWARD DROP
# Allow preexisting connections. This should be at the start of the rule set
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# Allow SMTP On 192.0.2.2
iptables -A INPUT -p tcp -d 192.0.2.2 --dport 25 -j ACCEPT
# Allow DNS On 192.0.2.2
iptables -A INPUT -p udp -d 192.0.2.2 --dport 53 -j ACCEPT
iptables -A INPUT -p tcp -d 192.0.2.2 --dport 53 -j ACCEPT
# Allow HTTP and HTTPS
iptables -A INPUT -p tcp -d 192.0.2.2 --dport 80 -j ACCEPT
iptables -A INPUT -p tcp -d 192.0.2.2 --dport 443 -j ACCEPT
iptables -A INPUT -p tcp -d 192.0.2.3 --dport 80 -j ACCEPT
iptables -A INPUT -p tcp -d 192.0.2.3 --dport 443 -j ACCEPT
# Allow POP3 On 192.0.2.2
iptables -A INPUT -p tcp -d 192.0.2.2 --dport 110 -j ACCEPT
# Allow SSH from 192.0.2.0/24 On 192.0.2.2
iptables -A INPUT -p tcp -s 192.0.2.0/24 -d 192.0.2.2 --dport 22 -j ACCEPT
# Allow traffic over lo
iptables -A INPUT -i lo -j ACCEPT
If you now run `iptables -L --line-numbers -n` you should get:
Chain INPUT (policy DROP)
num target prot opt source destination
1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
2 ACCEPT tcp -- 0.0.0.0/0 192.0.2.2 tcp dpt:25
3 ACCEPT udp -- 0.0.0.0/0 192.0.2.2 udp dpt:53
4 ACCEPT tcp -- 0.0.0.0/0 192.0.2.2 tcp dpt:53
5 ACCEPT tcp -- 0.0.0.0/0 192.0.2.2 tcp dpt:80
6 ACCEPT tcp -- 0.0.0.0/0 192.0.2.2 tcp dpt:443
7 ACCEPT tcp -- 0.0.0.0/0 192.0.2.3 tcp dpt:80
8 ACCEPT tcp -- 0.0.0.0/0 192.0.2.3 tcp dpt:443
9 ACCEPT tcp -- 0.0.0.0/0 192.0.2.2 tcp dpt:110
10 ACCEPT tcp -- 192.0.2.0/24 192.0.2.2 tcp dpt:22
11 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy DROP)
num target prot opt source destination
Chain OUTPUT (policy ACCEPT)
num target prot opt source destination
When an incoming connection comes into the server, iptables will go through the rules in order. So, for example, when 192.0.2.20 tries to connect to SSH for the first time on 192.0.2.2, iptables goes through all the rules until it gets to rule 8, and then it accepts the connection. When the next packet in the same session comes in, iptables see it as an already established connection and triggers on the first rule. This is why the "-m state --state ESTABLISHED,RELATED" rule needs to be kept near the start.
The default policy on the INPUT chain is DROP. What this means is that if a connection comes in that isn't allowed by any of the ACCEPT rules, it's dropped silently. The alternative to DROP is REJECT. This actually straight out tells the connecting ip that the port is closed. It's personal preference as to which you use.
So, what happens if one ip, say 192.0.2.50, is hammering POP3 with a brute attack? It's possible to insert rule in anywhere within the rule set. In this case, rule number 9 is the one allowing POP3 access. The rule denying access to the ip has to be *BEFORE* that. The easiest way to do this is to insert the rule at rule 9, pushing rule 9 down to 10. This can be done by running the following the command:
iptables -I INPUT 9 -p tcp -s 192.0.2.50 -d 192.0.2.2 --dport 110 -j REJECT
If you look at the output of `iptables -L --line-numbers -n` it will show something like the following.
Chain INPUT (policy DROP)
num target prot opt source destination
1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
2 ACCEPT tcp -- 0.0.0.0/0 192.0.2.2 tcp dpt:25
3 ACCEPT udp -- 0.0.0.0/0 192.0.2.2 udp dpt:53
4 ACCEPT tcp -- 0.0.0.0/0 192.0.2.2 tcp dpt:53
5 ACCEPT tcp -- 0.0.0.0/0 192.0.2.2 tcp dpt:80
6 ACCEPT tcp -- 0.0.0.0/0 192.0.2.2 tcp dpt:443
7 ACCEPT tcp -- 0.0.0.0/0 192.0.2.3 tcp dpt:80
8 ACCEPT tcp -- 0.0.0.0/0 192.0.2.3 tcp dpt:443
9 REJECT tcp -- 192.0.2.50 192.0.2.2 tcp dpt:110 reject-with icmp-port-unreachable
10 ACCEPT tcp -- 0.0.0.0/0 192.0.2.2 tcp dpt:110
11 ACCEPT tcp -- 192.0.2.0/24 192.0.2.2 tcp dpt:22
12 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy DROP)
num target prot opt source destination
Chain OUTPUT (policy ACCEPT)
num target prot opt source destination
To remove the rule later on, just run:
iptables -D INPUT 9
Of course if the ip in question is really causing a general issue, you can run:
iptables -I INPUT 1 -s 192.0.2.50 -j DROP
This will drop ANY packet coming from 192.0.2.50 coming into the server before any other rules are even considered.
Automatically Starting Up IPTables
Centos/RHEL
When you're happy with your rule set run: service iptables save This will save the ruleset in /etc/sysconfig/iptables.
To make sure that they are applied on startup, run chkconfig iptables on
Debian/Ubuntu
Debian Lenny And Squeeze
Create a file called '''/etc/network/if-pre-up.d/iptables_v4'''. This file should have 755 permissions and have content like the following:
#!/bin/bash
iptables -F INPUT
iptables -F OUTPUT
........ Rest of rules.
This will then be called before the network interfaces are bought up.
General Tips
Restrict Mail To "mail" User
A common method for sending spam is to upload a perl script to a compromised site, and then send out the mail directly on port 25. This can bypass the local mail server and make it very awkward to track down what was responsible for sending the spam further down the line.
Fortunately, this is very simple to stop with iptables. As before, the fastest way is probably to use an example:
# Allow the mail user to make outbound connections on port 25
iptables -A OUTPUT -m owner --uid-owner mail -p tcp --dport 25 -j ACCEPT
# Allow connections to localhost
iptables -A OUTPUT -p tcp -d 127.0.0.1 --dport 25 -j ACCEPT
# Block all other connections on port 25.
# Only needed if the default policy for OUTPUT is ACCEPT
iptables -A OUTPUT -p tcp --dport 25 -j REJECT
This won't stop spam from going out, however it will mean that mail can only be sent out by the mail user which in general means that it will have to go through the local MTA which means it will be logged.
Also, please be aware that the local MTA doesn't always run as ''mail''. For example, if you're using Postfix, it will run as the ''postfix'' user. The only change required in this case is to change the user after ''--uid-owner'' to ''postfix''.
This requires the ipt_owner kernel module to be available.
Grouping IPs
It's often handy to be able to group ips together separately for different rules. For example, rather than having multiple rules for hosts allowed to access SSH, you can have one rule in the INPUT table which then references another set of rules.
For example, let's remove '''iptables -A INPUT -p tcp -s 192.0.2.0/24 -d 192.0.2.2 --dport 22 -j ACCEPT''' from the main example above and give 198.51.100.0/24 and 203.0.113.0/24 access as well.
# Create the new chain.
iptables -N SSH_ACCESS
# Add the ranges we wish to allow access to
iptables -A SSH_ACCESS -s 192.0.2.0/24 -j ACCEPT
iptables -A SSH_ACCESS -s 198.51.100.0/24 -j ACCEPT
iptables -A SSH_ACCESS -s 203.0.113.0/24 -j ACCEPT
# Not needed if the default policy is DROP or REJECT
iptables -A SSH_ACCESS -j REJECT
# Add the input rule
iptables -A INPUT -p tcp -d 192.0.2.2 --dport 22 -j SSH_ACCESS
Now when someone attempts to access port 22 on 192.0.2.2, iptables will look at the SSH_ACCESS chain, and see if the source ip is there. If it is, it will ACCEPT the connection. If it's not listed, the connection is rejected.
The other advantage to this method is that you can manage the SSH hosts independently of the INPUT table. So for example you can run '''iptables -L SSH_ACCESS --line-numbers -n -v''' and you will see something like:
Chain SSH_ACCESS (1 references)
num pkts bytes target prot opt in out source destination
1 0 0 ACCEPT all -- * * 192.0.2.0/24 0.0.0.0/0
2 0 0 ACCEPT all -- * * 198.51.100.0/24 0.0.0.0/0
3 0 0 ACCEPT all -- * * 203.0.113.0/24 0.0.0.0/0
4 0 0 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
The individual rules can be managed exactly the same as they are in any other chain.
To remove a chain like SSH_ACCESS, first remove any references to it, in this case:
iptables -D INPUT -p tcp -d 192.0.2.2 --dport 22 -j SSH_ACCESS
Then flush SSH_ACCESS and remove it:
iptables -F SSH_ACCESS
iptables -X SSH_ACCESS
Commenting
In all examples so far, all that has been used are bash comments. It is possible to put comments in the rules themselves for self documentation purposes. For example:
iptables -A INPUT -p tcp --dport 80 -j ACCEPT -m comment --comment "Allow inbound HTTP"
When iptables -L INPUT is run, you should see something like:
ACCEPT tcp -- anywhere anywhere tcp dpt:www /* Allow inbound HTTP */
This requires the ipt_commment module to be available.
Quick And Nasty Outbound NAT For Virtualisation
This is a handy rule which works when using multiple virtual servers on a bridged network where you want the VPS hosts to be able to get outbound access through the host server. This presumes that:
* The host server has external connectivity
* The host server has an ip on the same range as the virtual servers which is on the same bridge
* The virtual servers have the above ip as their default gateway.
# Allow IPv4 Forwarding
sysctl -w net.ipv4.ip_forward=1
# NAT all traffic from 192.0.2.0/24 which isn't destined for 192.0.2.0/24
iptables -t nat -A POSTROUTING -s 192.0.2.0/24 ! -d 192.0.2.0/24 -j MASQUERADE
This will NAT any traffic not destined for 192.0.2.0/24 and send it out over the host servers default gateway.
Removing State Tracking
By default, IPTables will keep track of state. What this means is that iptables will keep track of what ips are connecting to what port and the corresponding connections back out. The main advantage to this is that it makes it harder to spoof connections. However, there's a disadvantage in that there has to be a table kept in memory for keeping track of all the connections. The vast majority of the time, this isn't a problem, however it can cause issues with a service that does a high volume of connections, for example DNS. The conntrack command allows the viewing and manipulation of the states table.
It's possible to disable the connection tracking on IPTables. For example, the following will disable connection tracking on all DNS UDP packets:
# Flush anything from the PREROUTING and OUTPUT raw tables.
iptables -t raw -F PREROUTING
iptables -t raw -F OUTPUT
# Hook into the PREROUTING table and stop the tracking of inbound UDP/53 packets.
iptables -t raw -I PREROUTING -p udp --dport 53 -j NOTRACK
# Hook into the OUTPUT table and stop the tracking of inbound UDP/53 packets.
iptables -t raw -I OUTPUT -p udp --dport 53 -j NOTRACK
# Allow DNS traffic
iptables -A INPUT -p udp --dport 53 -j ACCEPT -m comment --comment "Allow all DNS"
iptables -A INPUT -p udp --sport 53 -j ACCEPT -m comment --comment "Allow all DNS"
The important part here is that you have open the firewall where packets have either the source port or destination port set as port 53.
Comments
0 comments
Article is closed for comments.