# A better iptables firewall?

## The_Great_Sephiroth

I am trying to create a better firewall for my work laptop. I intend on allowing SSH and SMB through for Windows file-sharing and remote access should I need it. Currently I have the following setup.

```

-P INPUT DROP

-P FORWARD DROP

-P OUTPUT DROP

-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

-A INPUT -m state --state INVALID -j DROP

-A INPUT -i enp0s25 -p tcp -m tcp -m multiport --dports 445,135,139,22 -m state --state NEW -j ACCEPT

-A INPUT -i enp0s25 -p udp -m udp -m multiport --dports 138,137 -m state --state NEW -j ACCEPT

-A INPUT -i wlp12s0 -p tcp -m tcp -m multiport --dports 445,135,139,22 -m state --state NEW -j ACCEPT

-A INPUT -i wlp12s0 -p udp -m udp -m multiport --dports 138,137 -m state --state NEW -j ACCEPT

-A INPUT -i lo -m state --state NEW -j ACCEPT

-A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT

-A FORWARD -m state --state INVALID -j DROP

-A FORWARD -m state --state NEW -j ACCEPT

-A FORWARD -i enp0s25 -p tcp -m tcp -m multiport --dports 445,135,139,22 -m state --state NEW -j ACCEPT

-A FORWARD -i enp0s25 -p udp -m udp -m multiport --dports 138,137 -m state --state NEW -j ACCEPT

-A FORWARD -i wlp12s0 -p tcp -m tcp -m multiport --dports 445,135,139,22 -m state --state NEW -j ACCEPT

-A FORWARD -i wlp12s0 -p udp -m udp -m multiport --dports 138,137 -m state --state NEW -j ACCEPT

-A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

-A OUTPUT -m state --state INVALID -j DROP

-A OUTPUT -m state --state NEW -j ACCEPT

```

This was created using Firewall Builder. I do not know why the FORWARD chain is used. I am not running my laptop as a router.

Now I have worked on the following bash script today. It should setup what I believe is a better firewall, but I would like opinions first.

```

#!/bin/bash

# Clear all existing rules

iptables -P INPUT DROP

iptables -P OUTPUT ACCEPT

iptables -P FORWARD DROP

# Protect against port-scans

iptables -A INPUT -p tcp -m recent --update --seconds 60 --name TCP-PORTSCAN -j REJECT --reject-with tcp-rst

iptables -A INPUT -p udp -m recent --update --seconds 60 --name UDP-PORTSCAN -j REJECT --reject-with icmp-port-unreachable

iptables -A INPUT -p tcp -m recent --set --name TCP-PORTSCAN -j REJECT --reject-with tcp-rst

iptables -A INPUT -p udp -m recent --set --name UDP-PORTSCAN -j REJECT --reject-with icmp-port-unreachable

# Accept existing connections

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

# Drop invalid packets

iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

# Limit 'ping' requests

iptables -A INPUT -p icmp --icmp-type echo-request -m limit --limit 30/min --limit-burst 8 -j ACCEPT

iptables -A INPUT -p icmp --icmp-type echo-request -j DROP

# Protect against spoofing

iptables -t raw -I PREROUTING -m rpfilter --invert -j DROP

# Allow specific services

iptables -A INPUT -p tcp -m tcp -m multiport --dports 445,135,139,22 -m state --state NEW -j ACCEPT

iptables -A INPUT -p udp -m udp -m multiport --dports 138,137 -m state --state NEW -j ACCEPT

# Allow everything on loopback

iptables -A INPUT -i lo -j ACCEPT

# Reject everything else

iptables -A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable

iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset

iptables -A INPUT -j REJECT --reject-with icmp-proto-unreachable

```

I studied the guide at Arch to come up with this. It is adapted of course, not a direct copy. What do you think?

*UPDATE*

Apparently I am not understanding this yet. I just tested the script. With my old firewall and without one, I can ping google. With my new one I cannot.

```

-P INPUT DROP

-P FORWARD DROP

-P OUTPUT ACCEPT

-A INPUT -p tcp -m recent --update --seconds 60 --name TCP-PORTSCAN --mask 255.255.255.255 --rsource -j REJECT --reject-with tcp-reset

-A INPUT -p udp -m recent --update --seconds 60 --name UDP-PORTSCAN --mask 255.255.255.255 --rsource -j REJECT --reject-with icmp-port-unreachable

-A INPUT -p tcp -m recent --set --name TCP-PORTSCAN --mask 255.255.255.255 --rsource -j REJECT --reject-with tcp-reset

-A INPUT -p udp -m recent --set --name UDP-PORTSCAN --mask 255.255.255.255 --rsource -j REJECT --reject-with icmp-port-unreachable

-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

-A INPUT -m conntrack --ctstate INVALID -j DROP

-A INPUT -p icmp -m icmp --icmp-type 8 -m limit --limit 30/min --limit-burst 8 -j ACCEPT

-A INPUT -p icmp -m icmp --icmp-type 8 -j DROP

-A INPUT -p tcp -m tcp -m multiport --dports 445,135,139,22 -m state --state NEW -j ACCEPT

-A INPUT -p udp -m udp -m multiport --dports 138,137 -m state --state NEW -j ACCEPT

-A INPUT -i lo -j ACCEPT

-A INPUT -p udp -j REJECT --reject-with icmp-port-unreachable

-A INPUT -p tcp -j REJECT --reject-with tcp-reset

-A INPUT -j REJECT --reject-with icmp-proto-unreachable

-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

-A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

```

That is the iptables setup after running my script. I will continue to work on this, but could somebody give me some pointers?

----------

## el muchacho

Indeed i'm not sure i understand the forward chains. It looks like you have two interfaces ? What's the context exactly ?

(also, should you use it, you would need a sysctl change to enable ip_forward)

Anyway, you seem to block the icmp type 8 which are the pings.

Try commenting these out and see. Or try a basic wget instead of the ping.

But as far as I know, iptables is in general considered very good for most of the situation for personnal use and small networks.

----------

## el muchacho

One last thing: you should allow loopback interfaces with:

```

iptables -A INPUT -i lo -j ACCEPT

iptables -A OUTPUT -o lo -j ACCEPT

iptables -A INPUT -s 127.0.0.0/8 -d 127.0.0.0/8 -i lo -j ACCEPT
```

----------

## Hu

 *The_Great_Sephiroth wrote:*   

> This was created using Firewall Builder. I do not know why the FORWARD chain is used. I am not running my laptop as a router.
> 
> 

 The FORWARD chain is for routing.  The tool may generate rules for the FORWARD chain without checking whether you plan to route anything.  If so, the rules are unnecessary and can be removed.

 *The_Great_Sephiroth wrote:*   

> Now I have worked on the following bash script today. It should setup what I believe is a better firewall, but I would like opinions first.
> 
> 

 Initializing iptables via bash script is always worse than initializing it via iptables-restore because the script is not atomic.

 *The_Great_Sephiroth wrote:*   

> 
> 
> ```
> 
> # Protect against port-scans
> ...

 Port-scanners thank you for your active response.  Actively refusing the inquiry makes their scan go faster.

 *The_Great_Sephiroth wrote:*   

> 
> 
> ```
> iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
> 
> ...

 Why do you have a special ACCEPT rule for OUTPUT when the default rule is also ACCEPT?  This just makes the kernel work harder to get the same result.

 *The_Great_Sephiroth wrote:*   

> 
> 
> ```
> iptables -A FORWARD -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
> 
> ...

 You said you are not acting as a router.  Why are you setting rules specific to forwarding?

 *The_Great_Sephiroth wrote:*   

> 
> 
> ```
> # Limit 'ping' requests
> 
> ...

 Why limit ping?  If you want to be hidden, drop all ping.  If you want to be visible, allow ping.

 *The_Great_Sephiroth wrote:*   

> 
> 
> ```
> iptables -t raw -I PREROUTING -m rpfilter --invert -j DROP
> ```
> ...

 This is more easily achieved by enabling the reverse path filter for the interface.

 *The_Great_Sephiroth wrote:*   

> 
> 
> ```
> # Reject everything else
> 
> ...

 Why reject instead of DROP?

 *el muchacho wrote:*   

> One last thing: you should allow loopback interfaces with:
> 
> ```
> 
> iptables -A INPUT -i lo -j ACCEPT
> ...

 

These two rules are redundant.  The former is sufficient.

----------

## user118696

WOW Hu ! Great answer ! Sums it all to me. Take good note of these changes !

----------

## charles17

 *The_Great_Sephiroth wrote:*   

> That is the iptables setup after running my script. I will continue to work on this, but could somebody give me some pointers?

 

Some help can be found on the wiki for kernel settings and a ruleset.  

You're welcome to improve this wiki article with your experience and with help you are getting from this thread.

----------

## szatox

 *Quote:*   

> Initializing iptables via bash script is always worse than initializing it via iptables-restore because the script is not atomic. 

 

What's so bad about initializing iptables not being atomic?

It would be nice to have it up before network starts and at this point what's the difference if there is no traffic to be filtered?

Another scenario: you took firewall down with your network still active and now want to bring it up. Do you really care for a glitch?

Where's the pitfall I don't see?

----------

## Hu

Suppose you have these firewall rules:

```
iptables -A INPUT -m check-for-evil-bit -j DROP # Drop bad stuff

iptables -A INPUT -j ACCEPT # Accept everything else
```

If the -m check-for-evil-bit rule fails to load, perhaps due to missing kernel support or a syntax error, a bash script will blow past it and load only the allow rule, leaving the system able to accept "bad stuff" until the administrator notices and fixes it.  If you do the same with iptables-restore, the entire load will fail, leaving you with whatever rules you had before running iptables-restore.  For a secure firewall, start by loading a minimal set of cannot-fail rules that offer no services to the outside, then use iptables-restore to load the complicated production ruleset.  If you run iptables-restore as a preup for the network, and fail the up operation on failure to load the rules, then the network will not come up at all if the rules are bad.  This may be annoying for the user(s), but it is a fail-secure design and easy to implement.

Most hand-written scripts start off by flushing the chains, so running such a script and having a single failure partway through would again leave a potentially important rule missing.  To make matters worse, many users who get a working bash script seem inclined to put it into long-term service, so every kernel change thereafter can potentially break it and may go unnoticed.  I am willing to trust an administrator to notice errors when he runs the script interactively.  I am worried about trusting that every kernel upgrade will leave the script working and that, if it did break, an administrator would happen to be watching the boot closely enough to notice and fix it.

----------

## The_Great_Sephiroth

The script is for quick testing, not for loading at startup. Gentoo saves my state at shutdown and loads it at startup. I don't try to insert scripts there.

Now I was reading that using the responses would be better (and faster) rather than just dropping the packet. Are you saying I should just drop packets? Also, I forgot to remove the "connected/established" lines.

----------

## szatox

 *Quote:*   

> . Are you saying I should just drop packets?

  You either accept packet or drop it. There is no point in saing "nobody is home" when you hear knocking on your door.

Don't waste your bandwith, don't let them know the address is valid. That timing out connection takes longer than reset is a very nice side effect. Remember there is no connection at all at your side, and the purpose of firewall is keeping bad guys at bay. Slowing bad guys down won't hurt you.

Thanks for your input Hu, now I get the point

----------

## The_Great_Sephiroth

Would I need the forward table for VirtualBox of VPN functionality? I normally use VBox for OS X, Windows 7, and XP. I do not want to interfere with them functioning.

----------

