How to limit certain ports to specific source IPs while keeping all other ports open using iptables

Most online tutorials on iptables suggest to block all ports and DROP all unmatched traffic by default.  While such configuration is most secure and should be done on many home users’ and production hosts, it does not work in our environment.  Many of the machines in our lab are development machines, which means any of us can throw some services or containers on the machine, pick an arbitrary port to host them and access them from somewhere else.  But every once in a while, we feel that the access to certain services should be limited to a few specific source IPs, so we want to block these ports to all traffic except those from trusted nodes, while keeping other ports open so other folks can continue do their work.

Our machines run Redhat Enterprise Linux, and the default iptables rules found in /etc/sysconfig/iptables look like the following:

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:RH-Firewall-1-INPUT - [0:0]
-A INPUT -j RH-Firewall-1-INPUT
-A FORWARD -j RH-Firewall-1-INPUT
-A RH-Firewall-1-INPUT -i lo -j ACCEPT
-A RH-Firewall-1-INPUT -i eth0 -j ACCEPT
...
-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
COMMIT

The key here is the line just before the “…”, which says the firewall shall ACCEPT all traffic coming from the network interface eth0, and this is what makes our dev machines quite open.

The line after the “…” and just before “COMMIT” is a secure catch-all that REJECTs all traffic that does not match any rules defined above.  In the case of this particular machine, this line has no effects because the machine has only one network card.  However, if our machine had other network cards (e.g. eth1), then all unmatched traffic coming from these interfaces will be REJECTed.

The difference between DROP and REJECT is that DROP just ignores the request, whereas REJECT sends a message back to the requester.  It is preferably to use DROP to not create extra traffic load to your network.

Now suppose we have a service that listens on TCP ports 65000 and 65443, and we want to limit its access to IPs 100.101.102.103, and 200.201.202.x, where x can be 96~111.  The first thing we want to do is add some extra blank lines before the “ACCEPT all traffic from eth0” line, because all our rules must go before this line into the blank area.

The first two rules are to ACCEPT traffic from these two IP ranges:

...
# RULE 1:  accept requests from my best pal Joe's workstation, besides, he paid me $50 to have access to this service
-A RH-Firewall-1-INPUT -m comment --comment "Joe volunteered to test this service"  -s 100.101.102.103 -p tcp -m multiport --dports 65000,65443 -j ACCEPT

# RULE 2: accept requests from other workstations from our lab.
-A RH-Firewall-1-INPUT -m comment --comment "this service is available to all lab workstations" -s 200.201.202.96/28 -p tcp -m multiport --dports 65000,65443 -j ACCEPT
...

A few things worth noting here. There are two places to add comments. One is the use the typical Linux hash in the rules file itself – this comment is visible only by looking at the file. The other place is using the -m comment extension. This comment will show up if someone runs the command /sbin/iptables -L.

Here we also use the -m multiport extension to combine all ports used by this service in the same rule. Without it, you would have to list each port on its own line using –dport (notice the singular form).

Since RULE 1 involves a specific IP, it is listed after the “-s”.  But in RULE 2 we have a number of IPs.  It happens so the range of the IPs go from 96~111, which in their binary form are 01100000 ~ 01101111, so we can conveniently use a subnet mask of 28 bits to mask out the last 4 bits.   However, if the range of IPs does not fall nicely into a subnet range (which is probably the more common case), you could use the -m iprange exension instead:

# RULE 2 alternative using -m iprange:
-A RH-Firewall-1-INPUT -m comment --comment "this service is available to all lab workstations, including the workstation for our new intern Asok 200.201.202.112" -m --src-range 200.201.202.96-200.201.202.112 -p tcp -m multiport --dports 65000,65443 -j ACCEPT

Now we need to add a rule to DROP all other traffic to these ports. If we don’t add it, the “accepting all traffic on eth0” rule below would still make our service wide open.

# RULE 3: drop all other requests to the service
-A RH-Firewall-1-INPUT -m comment --comment "no one else allowed to use this service" -p tcp -m multiport --dports 65000,65443 -j DROP

Save this file and restart iptables:

> sudo service iptables restart

Below is the complete listing of /etc/sysconfig/iptables

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:RH-Firewall-1-INPUT - [0:0]
-A INPUT -j RH-Firewall-1-INPUT
-A FORWARD -j RH-Firewall-1-INPUT
-A RH-Firewall-1-INPUT -i lo -j ACCEPT
...
# RULE 1:  accept requests from my best pal Joe's workstation, besides, he paid me $50 to have access to this service
-A RH-Firewall-1-INPUT -m comment --comment "Joe volunteered to test this service"  -s 100.101.102.103 -p tcp -m multiport --dports 65000,65443 -j ACCEPT
# RULE 2 alternative using -m iprange:
-A RH-Firewall-1-INPUT -m comment --comment "this service is available to all lab workstations, including the workstation for our new intern Asok 200.201.202.112" -m --src-range 200.201.202.96-200.201.202.112 -p tcp -m multiport --dports 65000,65443 -j ACCEPT
# RULE 3: drop all other requests to the service
-A RH-Firewall-1-INPUT -m comment --comment "no one else allowed to use this service" -p tcp -m multiport --dports 65000,65443 -j DROP
...
-A RH-Firewall-1-INPUT -i eth0 -j ACCEPT
-A RH-Firewall-1-INPUT -j REJECT --reject-with icmp-host-prohibited
COMMIT
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s