iptables: Allow connections on a port to only "safe" hosts

You can use the -d switch to iptables rules which makes the rule work for just the address supplied and then block everything else e.g.

iptables -I OUTPUT -p tcp --dport 587 -j DROP
iptables -I OUTPUT -d smtp.gmail.com -p tcp -m tcp --dport 587 -j ACCEPT

should do what you want. This initially inserts a DROP all outgoing connections on port 587 rule into the beginning of the OUTPUT chain. It then inserts an allow rule for smtp.gmail.com port 587 into the beginning of the OUTPUT chain. This has the effect of allowing connections to smtp.gmail.com:587 and blocking everything else on port 587 e.g.

iptables -L OUTPUT -n
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     tcp  --  0.0.0.0/0            173.194.66.109      tcp dpt:587
ACCEPT     tcp  --  0.0.0.0/0            173.194.66.108      tcp dpt:587
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0           tcp dpt:587

Note that smtp.gmail.com resolves to two ip addresses which is why there are two ACCEPT rules above. The name is only resolved once when the rule is added to the kernel so if the addresses are changed then connections to gmail will be blocked too and you would need to reload the rules.


You need to allow gmail before you deny access. Otherwise you don't get to the allow rule.

Google may use any address in their ASN for Gmail servers, you you will want to allow all the blocks. You may want to look at using an ipset to handle the blocks involved. You may also find their SPF record provides a decent list of network blocks for your requirements. If you reject, rather than drop outgoing requests your users may not notice a few missing blocks.

A tool like Shorewall will allow you to implement your policies more easily. In your case I would have an outgoing REJECT policy and only allow desired traffic out. In that case you wouldn't need a specific rule to prevent access to the the submission port. Shorewall also enables easy configuration of logging for your firewall.


I'd suggest automating this whole thing so that you don't have to continue to worry about it.

The iptables -I OUTPUT -p tcp --dport 587 -j DROP should be included in your basic system configuration, which probably lives in /etc/sysconfig/iptables on your machine.

Drop this script into an editor and save it someplace, then mark it executable with chmod. As an example, I placed it in /opt/sysadminscripts/iptables.smtp.gmail.sh

#!/bin/bash

IPLIST=$(host smtp.gmail.com | grep 'has address' | awk '{print $4}')

for x in $IPLIST
do
        $(iptables -L -nv | grep "$x" >> /dev/null)
        if [[ "$?" -ne '0' ]]; then
                $(iptables -I OUTPUT  -p tcp -m tcp -d "$x" --dport 587 -j ACCEPT)
        fi
done

Then add an entry in your system crontab similar to this:

*/10 * * * * root /opt/sysadminscripts/iptables.smtp.gmail.sh

Make sure that the line is NOT the last line in your /etc/crontab. If the line doesn't have a terminating line feed, it won't run. (I recommend keeping a comment as the last line of /etc/crontab.)