Iptables: How-to Share your internet connection — page 2

6 minute read

3. Iptables Script

OK, now that we know the really basis, let see what the script is going to look like.

In this example, I assume that eth0 is the interface connected to the Internet, eth1 is the one connected to our local network.

#!/bin/sh
#
# this script requires iptables package to be
# installed on your machine


# Where to find iptables binary
IPT="/sbin/iptables"

# The network interface you will use
# WAN is the one connected to the internet
# LAN the one connected to your local network
WAN="eth0"
LAN="eth1"
# First we need to clear up any existing firewall rules
# and chain which might have been created
$IPT -F
$IPT -F INPUT
$IPT -F OUTPUT
$IPT -F FORWARD
$IPT -F -t mangle
$IPT -F -t nat
$IPT -X

# Default policies: Drop any incoming packets
# accept the rest.
$IPT -P INPUT DROP
$IPT -P OUTPUT ACCEPT
$IPT -P FORWARD ACCEPT

# To be able to forward traffic from your LAN
# to the Internet, we need to tell the kernel
# to allow ip forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

# Masquerading will make machines from the LAN
# look like if they were the router
$IPT -t nat -A POSTROUTING -o $WAN -j MASQUERADE

# If you want to allow traffic to specific port to be
# forwarded to a machine from your LAN
# here we forward traffic to an HTTP server to machine 192.168.0.2
#$IPT -t nat -A PREROUTING -i $WAN -p tcp --dport 80 -j DNAT --to 192.168.0.2:80
#$IPT -A FORWARD -i $WAN -p tcp  --dport 80 -m state --state NEW -j ACCEPT
# For a whole range of port, use:
#$IPT -t nat -A PREROUTING -i $WAN -p tcp --dport 1200:1300 -j DNAT --to 192.168.0.2
#$IPT -A FORWARD -i $WAN -p tcp  --dport 1200:1300 -m state --state NEW -j ACCEPT

# Do not allow new or invalid connections to reach your internal network
$IPT -A FORWARD -i $WAN -m state --state NEW,INVALID -j DROP

# Accept any connections from the local machine
$IPT -A INPUT -i lo -j ACCEPT
# plus from your local network
$IPT -A INPUT -i $LAN -j ACCEPT

# Here we define a new chain which is going to handle
# packets we don't want to respond to
# limit the amount of logs to 10/min
$IPT -N Firewall
$IPT -A Firewall -m limit --limit 10/minute -j LOG --log-prefix "Firewall: "
$IPT -A Firewall -j DROP

# log those packets and inform the sender that the packet was rejected
$IPT -N Rejectwall
$IPT -A Rejectwall -m limit --limit 10/minute -j LOG --log-prefix "Rejectwall: "
$IPT -A Rejectwall -j REJECT
# use the following instead if you want to simulate that the host is not reachable
# for fun though
#$IPT -A Rejectwall -j REJECT  --reject-with icmp-host-unreachable

# here we create a chain to deal with unlegitimate packets
# and limit the number of alerts to 10/min
# packets will be drop without informing the sender
$IPT -N Badflags
$IPT -A Badflags -m limit --limit 10/minute -j LOG --log-prefix "Badflags: "
$IPT -A Badflags -j DROP

# A list of well known combination of Bad TCP flags
# we redirect those to the Badflags chain
# which is going to handle them (log and drop)
$IPT -A INPUT -p tcp --tcp-flags ACK,FIN FIN -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ACK,PSH PSH -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ACK,URG URG -j Badflags
$IPT -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j Badflags
$IPT -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j Badflags
$IPT -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ALL ALL -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ALL NONE -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ALL FIN,PSH,URG -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ALL SYN,FIN,PSH,URG -j Badflags
$IPT -A INPUT -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j Badflags

# Accept certain icmp message, drop the others
# and log them through the Firewall chain
# 0 => echo reply
$IPT -A INPUT -p icmp --icmp-type 0 -j ACCEPT
# 3 => Destination Unreachable
$IPT -A INPUT -p icmp --icmp-type 3 -j ACCEPT
# 11 => Time Exceeded
$IPT -A INPUT -p icmp --icmp-type 11 -j ACCEPT
# 8 => Echo
# avoid ping flood
$IPT -A INPUT -p icmp --icmp-type 8 -m limit --limit 1/second -j ACCEPT
$IPT -A INPUT -p icmp -j Firewall

# Accept ssh connections from the Internet
$IPT -A INPUT -i $WAN -p tcp --dport 22 -j ACCEPT
# or only accept from a certain ip
#$IPT -A INPUT -i $WAN -s 125.124.123.122 -p tcp --dport 22 -j ACCEPT

# Accept related and established connections
$IPT -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

# Drop netbios from the outside, no log, just drop
$IPT -A INPUT -p udp --sport 137 --dport 137 -j DROP

# Finally, anything which was not allowed yet
# is going to go through our Rejectwall rule
$IPT -A INPUT -j Rejectwall

3.1. Iptables default settings

First of all, we define where iptables binary is located and to make the script easier to attapt to other situation, we define the interface as WAN and LAN.

So, if your machine uses eth1 as the interface connected to the Internet and eth0 connected to your local network, simply change:

WAN="eth0"
LAN="eth1"

to WAN="eth1" LAN="eth0"

Then we clean up iptables by flushing all the chain and tables:

$IPT -F xxx

and deleting all the optional user-defined chains:

$IPT -X

Then we define the default policies:

$IPT -P xxx

Which is to DROP any packet which is destinated to the local machine if they were not accepting, ACCEPT any packet which is going out of the local machine or going to/coming from our LAN if they were not discarded yet.

3.2. To and From Local Network

Because we want to be able to forward traffic, we need to say so to the kernel. This is what is done by setting /proc/sys/net/ipv4/ip_forward to 1.

Then, we need to tell the kernel to masquerade all outgoing traffic. This is what is achieved by triggering:

$IPT -t nat -A POSTROUTING -o $WAN -j MASQUERADE

Masquerading has the effect of allowing all computer from your internal network to access the internet. These machines will be seen as if there were the router itself.

Now, suppose you want your apache server on machine 192.168.0.2 to be visible from the outside. You need to tell the firewall to send those packets to machine 192.168.0.2 on port 80, this is what is achieved with:

$IPT -t nat -A PREROUTING -i $WAN -p tcp --dport 80 -j DNAT --to 192.168.0.2:80

But then, you also need to accept new connection on that specific port because, as you will see later on, we by default forbid NEW and INVALID connections coming from the outside to be forwarded.

So, for that specific service, we will allow NEW connections (RELATED and ESTABLISHED being allowed by default) to be forwarded:

$IPT -A FORWARD -i $WAN -p tcp --dport 80 -m state --state NEW -j ACCEPT

And now, let discard any NEW and INVALID connections:

$IPT -A FORWARD -i $WAN -m state --state NEW,INVALID -j DROP

To be able to have your local connection to work properly, you need to accept everything on localhost. This is done with:

# Accept any connections from the local machine
 $IPT -A INPUT -i lo -j ACCEPT

We do not need to use the statement $IPT -A OUTPUT -i lo -j ACCEPT because the default OUTPUT policy is set to ACCEPT If your default policy is different, you might have to add this statement

Then, because in our example we trust our local network (not a wise thing to do though), we need to allow any incoming connections from our LAN:

# plus from your local network
$IPT -A INPUT -i $LAN -j ACCEPT

Now let see how we can define custom chains.