I have this in my rc.local on my new Ubuntu server:

iptables -F

iptables -A INPUT -i eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
iptables -A OUTPUT -o eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT

iptables -A INPUT -i eth0 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -o eth0 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT

iptables -A OUTPUT -o eth0 -p tcp --dport 9418 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -i eth0 -p tcp --sport 9418 -m state --state ESTABLISHED -j ACCEPT

iptables -A OUTPUT -o eth0 -p tcp --dport 5000 -m state --state NEW,ESTABLISHED -j ACCEPT # Heroku
iptables -A INPUT -i eth0 -p tcp --sport 5000 -m state --state ESTABLISHED -j ACCEPT # Heroku

iptables -A INPUT -p udp -s --source-port 53 -d 0/0 --destination-port 1024:65535 -j ACCEPT
iptables -A INPUT -p udp -s --source-port 53 -d 0/0 --destination-port 1024:65535 -j ACCEPT

iptables -A OUTPUT -o eth0 -p tcp --dport 443 -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A INPUT -i eth0 -p tcp --sport 443 -m state --state ESTABLISHED -j ACCEPT

iptables -P INPUT DROP
iptables -P FORWARD DROP

9418 is Git's port. 5000 is a port used to manage Heroku apps. And and are our DNS servers.

Do you think that this is secure? Can you see any holes here?

Update: Why is it important to block OUTPUT? This machine will be used only by me.

  • 1
    The policy for OUTPUT is missing. – Ladadadada Apr 2 '12 at 15:09
  • 1
    Why not use two lines to accept everything ESTABLISHED, drop everything INVALID, then you only need to care about the NEW. – b0fh Apr 2 '12 at 15:11
  • You're very overly specific IMHO. I don't think it's very useful to specify ESTABLISHED everytime, same for the incoming/outgoing interface. EDIT: basically what b0fh just said. – gparent Apr 2 '12 at 15:11

The only major hole I can see is IPv6. You'll need ip6tables for that. If any of your services listen on IPv6 (and many of them, including ssh, listen on IPv6 by default) then an attacker can use that to completely bypass all the rules you have above. Networks will use IPv6 in preference to IPv4 when it is available.

Assuming your OUTPUT policy is DROP, you have restricted IPv4 pretty well.

Skipping the RELATED option could mean that you get timeout rather than instant rejects when a service goes down and stops listening as I understand TCP RST packets are neither NEW or ESTABLISHED in this context.

To answer the update on your question: For when it's not only used by you.

No matter how careful we all are, the possibility always remains that we've missed something or we are momentarily careless and we allow someone else some measure of control over our box. The first thing an attacker will do once they have a toe-hold is to download a privilege escalation kit, a rootkit, their command and control system and whatever they actually want to run on the box. Restricting outbound connections means they can't download that privilege escalation kit meaning they are stuck running as the Apache user or the Git user or whatever they managed to attack. Without root privileges, they can't hide and they can't modify the firewall. This won't stop an attacker forever but it might stop him for long enough for you to notice he's there or frustrate him enough that he gives up and goes somewhere easier.

The rules above mean that a remote file inclusion attack will only work if the remote file is hosted on SSL. If you put an proxy server in between this box and the internet and have it only allow certain URL patterns, you can stop RFI in its tracks while still allowing normal operation. This is defense in depth.

  • Assuming what ever is next hop support ipv6 yes, if not then your argument is not valid. – 3molo Apr 2 '12 at 16:15
  • If the attacker has access to the system can't they just send the attack kit along with the commands? – TheLQ Apr 4 '12 at 21:28

In addition to the comments posted by the other guys about missing OUTPUT chain and repeated ESTABLISHED rules, you can restrict protocols such as SSH (TCP/port 22) to specific IPs if applicable.

To check your setup, you can try NMAP to check for open ports. This can be more useful in case your firewall rules becomes more complicated.


You should run the script from a pre-up line in /etc/network/interfaces (see man 5 interfaces)so that the firewall rules are in place BEFORE the network is available and services are started. rc.local is the very last thing run, so there is an unnecessary window of time where things are unprotected.

Its also wise to keep in mind that this type of thing you are doing is not some kind of magic bullet. If there are other services you are staring but are trying to prevent access to, you shouldn't be starting them in the first place. If the idea here is to prevent access to services which you don't intend to start, this is implying that there are untrustworthy people already doing stuff on your machine? In which case you've already lost, and its safe to assume that at the point that you have malicious users running code on your box, that your firewall rules could also be manipulated.


i open-sourced iptables-boilerplate on github some days ago. its essentially a good set of predefined iptable-rules and is extensivly commented.

maybe using and reading could help you

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.