LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Security (http://www.linuxquestions.org/questions/linux-security-4/)
-   -   Request For Comments on Firewall script (http://www.linuxquestions.org/questions/linux-security-4/request-for-comments-on-firewall-script-715171/)

niels.horn 03-28-2009 05:11 PM

Request For Comments on Firewall script
 
Over the last week I set up a spare box to serve as a gateway / router / firewall / proxy (squid) / contentfilter (dansguardian).

Everything works fine, but I'm not sure if my firewall script is safe enough.
I read many things about spoofing, worms, syn-attacks and more, and have implemented nothing of the kind, just the basic things like closing all ports that don't need to stay open.

To clarify things, my setup is like this:

Code:

ADSL-line
    |
ADSL-modem
    |
gateway/firewall/proxy/content-filter
 ___|_________________
 |  |  |  |  |  |
PC1 PC2 PC3 PC4 PC5 PC6

Four PCs run Linux (Slackware), like the gataway, and two run Windows.

The script I wrote is like this:
Code:

#!/bin/bash
#
# rc.firewall        Setup basic firewall
#
# Version:        0.2.1 (beta) - Saturday, Mar 28, 2009
#
# Author:        Niels Horn (niels.horn@gmail.com)


###################
## Configuration ##
###################

### Simulate Only: put =Y to not really do anything, just simulate & test
#                  put =N to really configure your firewall
SIMULATE_ONLY=N

### Interface names in variables
#        - IF_EXT is the one that connects to the internet
#        - IF_INT connects to the LAN
IF_EXT=eth0
IF_INT=eth1

### Proxy configuration
# HTTP_PROXY: How to handle http-requests
#        use =none        to allow port 80 to pass normally
#        use =block        to block direct outgoing http requests
#                                (to force users to use proxy)
#        use =redirect        to redirect all requests to local port
#                                (=transparent proxy, set HTTP_PROXY_PORT)
HTTP_PROXY=block
#HTTP_PROXY_PORT=8080

### Allowed traffic from LAN to gateway
#        use =ALL to allow all traffic, or
#        use =<port>,<protocol>,<description> to limit ports where:
#                <port>                = tcp port to allow
#                <protocol>        = protocol (tcp | udp)
#                <description>        = service (only for information)
#                ** Separate entries with a space **
#                Examples:
#                GTW_ALLOWED="ALL"
#                GTW_ALLOWED="22,tcp,ssh 53,tcp,dns 8080,tcp,proxy"
#GTW_ALLOWED="ALL"
GTW_ALLOWED="\
8080,tcp,proxy \
8822,tcp,ssh"

### Allowed outgoing traffic
#        use =ALL to allow all outgowing traffic, or
#        use =<port>,<protocol>,<description> to limit ports where:
#                <port>                = tcp port to allow
#                <protocol>        = protocol (tcp | udp)
#                <description>        = service (only for information)
#                ** Separate entries with a space **
#                Examples:
#                OUT_ALLOWED="ALL"
#                OUT_ALLOWED="25,tcp,smtp 53,tcp,dns 80,tcp,http 110,tcp,pop3"
#                NOTE: 443 (https) is used for Windows Live Id
#OUT_ALLOWED="ALL"
OUT_ALLOWED="\
21,tcp,ftp \
22,tcp,ssh \
25,tcp,smtp \
53,udp,dns \
110,tcp,pop3 \
123,udp,ntp \
143,tcp,imap \
443,tcp,https \
587,tcp,smtps-gmail \
873,tcp,rsync \
995,tcp,pop3s \
1024:65535,tcp,user-ports \
1024:65535,udp,user-ports"

### Allowed incoming traffic & define NAT
#        use =<ext_port>,<protocol>,<ip>,<int_port>,<description> where:
#                <ext_port>        = tcp port to open on outside
#                <protocol>        = protocol (tcp | udp)
#                <ip>                = ip of server on LAN
#                <int_port>        = port on which server id listening
#                <description>        = informational only
#                ** Separate entries with a space **
#                Examples:
#                IN_ALLOWED="" (allows nothing at all)
#                IN_ALLOWED="22,tcp,192.168.1.100,22,SSH"
#                IN_ALLOWED="8000,tcp,192.168.1.101,80,Webserver"
#                IN_ALLOWED=
IN_ALLOWED="\
8822,tcp,192.168.2.101,22,SSH"


####################
## Pre-processing ##
####################

echo
echo "Setting up a basic router / firewall:"
echo "====================================="
echo

if [ "$SIMULATE_ONLY" != "N" ]; then
        IPT="true "
        echo "(** Simulating only... No real changes will be applied! **)"
        echo
else
        IPT="iptables"
fi


#################################
## Get additional network info ##
#################################

# Get IP-addresses
IP_EXT=`ifconfig $IF_EXT | grep 'inet addr' | awk '{print $2}' | sed -e 's/.*://'`
IP_INT=`ifconfig $IF_INT | grep 'inet addr' | awk '{print $2}' | sed -e 's/.*://'`

# Networks:
NET_EXT="0.0.0.0/0"
NET_INT=`route -n | grep -v '^0.0.0.0' | grep $IF_INT | awk '{print $1"/"$3}'`

# Print collected information:
echo "Connection IF    IP              Network"
echo "---------- ----- --------------- -------------------------------"
printf "%-10s %-5s %-15s %s\n" LAN      $IF_INT $IP_INT $NET_INT
printf "%-10s %-5s %-15s %s\n" Internet $IF_EXT $IP_EXT $NET_EXT
echo


####################
## Clean up first ##
####################

# Flush existing chains
$IPT --flush
$IPT --table nat    --flush
$IPT --table mangle --flush

# Delete any existing user chains
$IPT --delete-chain
$IPT --table nat    --delete-chain
$IPT --table mangle --delete-chain


###################################################
## Set default policies to drop (we're paranoid) ##
###################################################

$IPT --policy INPUT  DROP
$IPT --policy OUTPUT  DROP
$IPT --policy FORWARD DROP


#################
## INPUT rules ##
#################

echo "INPUT RULES ***********************************************************"
echo

### From lo (Loopback)
# First trust traffic from our own server - accept input from lo
$IPT -A INPUT -i lo -j ACCEPT

### Basic I/O
#Allow icmp traffic internal->external & external->firewall
$IPT -A INPUT -i $IF_INT -p icmp -s $NET_INT -d $NET_EXT -j ACCEPT
$IPT -A INPUT -i $IF_EXT -p icmp -s $NET_EXT -d $IP_EXT  -j ACCEPT

### From IF_INT (LAN)
# Block NETBIOS Broadcasts
echo "Blocking NETBIOS Broadcasts from LAN..."
$IPT -A INPUT -i $IF_INT -p udp --dport 137:139 -j DROP
echo

# What will we allow to arrive from the LAN
if [ "$GTW_ALLOWED" = "ALL" ]; then
        echo "All traffic from LAN allowed..."
        $IPT -A INPUT -i $IF_INT -s $NET_INT -j ACCEPT
        echo
else
        echo "Allowing established / related traffic from LAN..."
        $IPT -A INPUT -i $IF_INT -s $NET_INT -m state \
                --state ESTABLISHED,RELATED -j ACCEPT
        echo
        echo "Opening allowed ports from LAN..."
        echo
        echo "Port  Prot Description"
        echo "----- ---- -----------------------------------"
        for filter in $GTW_ALLOWED; do
                port=`echo $filter | awk -F , {'printf $1'}`
                prot=`echo $filter | awk -F , {'printf $2'}`
                desc=`echo $filter | awk -F , {'printf $3'}`
                $IPT -A INPUT -i $IF_INT -p $prot -s $NET_INT \
                        --dport $port -j ACCEPT
                printf "%5s %-4s %s\n" $port $prot $desc
        done
        echo
fi

### From IF_EXT (Internet)
# Block those annoying multicasts from ADSL modems...
echo "Blocking external multicast packets..."
$IPT -A INPUT -i $IF_EXT -d 224.0.0.0/24 -j DROP
echo

# Block annoying Windows Messenger PopUp spamming
echo "Blocking Windows Messenger PopUp packets..."
$IPT -A INPUT -i $IF_EXT -p udp --dport 1026 -j DROP
echo

# Traffic from IF_EXT claiming to be internal = spoofing -> reject
echo "Blocking IP Spoofing..."
$IPT -A INPUT -i $IF_EXT -s $NET_INT -j LOG --log-level 6 --log-prefix "FW: Spoofing: "
$IPT -A INPUT -i $IF_EXT -s $NET_INT -j REJECT
echo

# Allow incoming established / related traffic
echo "Allowing external incoming established / related traffic..."
$IPT -A INPUT -i $IF_EXT -s $NET_EXT -d $IP_EXT -m state \
        --state ESTABLISHED,RELATED -j ACCEPT
echo

# Allow incoming traffic as defined in IN_ALLOWED
echo "Opening allowed incoming ports..."
echo
echo "Port  Prot Description"
echo "----- ---- -----------------------------------"
for filter in $IN_ALLOWED; do
        port=`echo $filter | awk -F , {'printf $1'}`
        prot=`echo $filter | awk -F , {'printf $2'}`
        desc=`echo $filter | awk -F , {'printf $5'}`
        $IPT -A INPUT -i $IF_EXT -p $prot -s $NET_EXT \
                -d $IP_EXT --dport $port -j ACCEPT
        printf "%5s %-4s %s\n" $port $prot $desc
done
echo

### Anything else is not allowed and will be logged!
echo "Rejecting anything not allowed..."
#$IPT -A INPUT -j LOG --log-level 6 --log-prefix "FW: INPUT Not allowed: "
$IPT -A INPUT -j REJECT
echo


##################
## OUTPUT rules ##
##################

echo "OUTPUT RULES **********************************************************"
echo

### To lo (Loopback)
# Traffic to loopback is ok
$IPT -A OUTPUT -o lo -j ACCEPT

### To IF_INT (LAN)
# Out to IF_INT for local network is ok
$IPT -A OUTPUT -o $IF_INT -d $NET_INT -j ACCEPT

### To IF_EXT (internet)
# Out of IF_EXT for local network is not ok! (routing problem...)
echo "Blocking routing errors..."
$IPT -A OUTPUT -o $IF_EXT -d $NET_INT \
        -j LOG --log-level 6 --log-prefix "FW: Routing problem: "
$IPT -A OUTPUT -o $IF_EXT -d $NET_INT -j REJECT
echo

# Rest to IF_EXT if ok
$IPT -A OUTPUT -o $IF_EXT -j ACCEPT

### Anything else is not allowed and will be logged!
echo "Rejecting anything not allowed..."
#$IPT -A OUTPUT -j LOG --log-level 6 --log-prefix "FW: OUTPUT Not allowed: "
$IPT -A OUTPUT -j REJECT
echo


###################
## FORWARD rules ##
###################

echo "FORWARD RULES *********************************************************"
echo

### Redirect port 80 if we use transparent proxy
if [ "$HTTP_PROXY" = "redirect" ]; then
        echo "Redirecting http-requests to proxy..."
        $IPT -t nat -A PREROUTING -i $IF_INT -p tcp --dport 80 \
                -j REDIRECT --to-port $HTTP_PROXY_PORT
        echo
fi

### Network Address Translation (NAT)
echo "Building NAT-table"
echo
echo "Port  Prot To server      Port  Description"
echo "----- ---- --------------- ----- -----------------------------------"
for filter in $IN_ALLOWED; do
        port_in=`echo $filter | awk -F , {'printf $1'}`
        protocol=`echo $filter | awk -F , {'printf $2'}`
          server=`echo $filter | awk -F , {'printf $3'}`
        port_out=`echo $filter | awk -F , {'printf $4'}`
            desc=`echo $filter | awk -F , {'printf $5'}`
        # Include in NAT table
        $IPT -t nat -A PREROUTING -i $IF_EXT -p $protocol --dport $port_in \
                -j DNAT --to-destination $server:$port_out
        # Accept in FORWARD chain
        #$IPT -A FORWARD -i $IF_EXT -p $protocol --dport $port_in -j ACCEPT
        $IPT -A FORWARD -i $IF_EXT -p $protocol --dport $port_out -d $server \
                -j ACCEPT
        printf "%5s %-4s %-15s %5s %s\n" $port_in $protocol $server $port_out $desc
done
echo

# Allow existing inbound
$IPT -A FORWARD -i $IF_EXT -o $IF_INT -m state \
        --state ESTABLISHED,RELATED -j ACCEPT

# If HTTP_PROXY=block, Drop direct http requests from LAN
if [ "$HTTP_PROXY" = "block" ]; then
        echo "Blocking outgoing direct http requests..."
        $IPT -A FORWARD -i $IF_INT -p tcp --dport 80 -j DROP
        echo
fi

# Other outbound traffic
if [ "$OUT_ALLOWED" = "ALL" ]; then
        echo "Allowing all other outgoing traffic..."
        $IPT -A FORWARD -i $IF_INT -o $IF_EXT -j ACCEPT
        echo
else
        echo "Opening allowed outgoing ports..."
        echo
        echo "Port(s)    Prot Description"
        echo "----------- ---- -----------------------------------"
        for filter in $OUT_ALLOWED; do
                port=`echo $filter | awk -F , {'printf $1'}`
                prot=`echo $filter | awk -F , {'printf $2'}`
                desc=`echo $filter | awk -F , {'printf $3'}`
                $IPT -A FORWARD -i $IF_INT -p $prot --dport $port -j ACCEPT
                printf "%11s %-4s %s\n" $port $prot $desc
        done
        echo
fi

### Anything else is not allowed and will be logged!
echo "Rejecting anything not allowed..."
#$IPT -A FORWARD -j LOG --log-level 6 --log-prefix "FW: FORWARD Not allowed: "
$IPT -A FORWARD -j REJECT
echo


#################
## POSTROUTING ##
#################

# Anything that passed through FORWARD must be MASQed
$IPT -t nat -A POSTROUTING -o $IF_EXT -j MASQUERADE
#$IPT -t nat -A POSTROUTING -o $IF_INT -j MASQUERADE


##############################
## Enable routing in kernel ##
##############################

echo "Setting kernel options..."

# Enable forwarding
echo 1 > /proc/sys/net/ipv4/ip_forward

# Enable reverse-path filter (prevents spoofing)
#        This rejects packets coming in from a source address
#        not matching the routing table
echo 1 > /proc/sys/net/ipv4/conf/all/rp_filter

echo


# All done...
echo "Firewall configuration complete ***************************************"

So my questions are:
- What have I forgotten that I will regret later on?
- What should I include to protect me from worms, viruses, etc. (since there are two Windows machines on the network)
- Anything else that might be stupid that I have done...

Thanks for any suggestions!
I'm no safety-guru, just a guy learning more about iptables...

unSpawn 03-29-2009 06:46 AM

Looks pretty OK I think except most people put the sysctls in first. (On the topic of sysctls, check out if you have sane defaults for log_martians, accept_source_route and accept_redirects and network perf?) What you could add is a switch for enabling those logging rules you commented out as it may help with troubleshooting and it's always good to see what gets rejected anyway. If you enable logging you might want to -m limit --limit it. Filtering you could add: --state INVALID, ! --syn -m state --state NEW and --tcp-option (64|128), bogons, ICMP types you do not want to see (say quench, redirs), SYN flood. But as I said before, it looks pretty much like what you'd want to have. Good start definately.

niels.horn 03-29-2009 07:44 AM

OK, thanks for all the suggestions!
I will do some research on all of them and improve my script.

I commented out the lines that log the rejected packets because my log file was growing like crazy :)
I read about the 'limit' option so I will study that first.

If I encounter any problems I'll come back for more help.

<edit>
I was thinking of defining logging with a variable, like:
0 = log nothing (bad idea)
1 = log attacks only
2 = log attacks & illegal traffic attempts
3 = log everything rejected
</edit>

unSpawn 03-29-2009 09:10 AM

Depends on how you look at it. Even if your log would go crazy, I mean you shouldn't read anyway but use something like Logwatch, fwlogwatch or another reporting tool to tally things for you, right? For me I try to filter things out in several chains: bogons ranges first, then invalid packets, then certain protocols, then certain ICMP traffic, then "trusted" networks and what remains gets filtered more specifically. That way the logs don't overflow while still retaining a good overview of what's going on. One nice thing about filtering is it can show you unexpected things. For instance I found that Firefox-3 (when you select an entry in the bookmarks thingie by single-mouse-clicking it) immediately sends a request to that URI. That isn't a flaw or disclosure problem itself but to say it's expected (or even remotely useful) would be pushing it...

win32sux 03-29-2009 09:21 AM

Quote:

Originally Posted by unSpawn (Post 3491525)
I found that Firefox-3 (when you select an entry in the bookmarks thingie by single-mouse-clicking it) immediately sends a request to that URI. That isn't a flaw or disclosure problem itself but to say it's expected (or even remotely useful) would be pushing it...

I hadn't noticed this behavior before. What a weird feature. Yikes.

niels.horn 03-29-2009 09:38 AM

So many new things to learn... ;)
I've been in the IT business for over 25 years and still feel like a newbie at times :D

I *was* checking the log manually, but had already found out it is not the way to do it. I actually started looking for a tool that could read the log for me and e-mail me / SMS me / wail a horn / whatever in case of trouble.

I still have a lot of learning to do - and having fun at doing so.

CoffeeKing!!! 04-04-2009 01:17 PM

Quote:

Originally Posted by niels.horn (Post 3491553)
So many new things to learn... ;)
I've been in the IT business for over 25 years and still feel like a newbie at times :D

I *was* checking the log manually, but had already found out it is not the way to do it. I actually started looking for a tool that could read the log for me and e-mail me / SMS me / wail a horn / whatever in case of trouble.

I still have a lot of learning to do - and having fun at doing so.

Have you found any programs that do what you stated above?

niels.horn 04-04-2009 02:32 PM

Quote:

Originally Posted by CoffeeKing!!! (Post 3498473)
Have you found any programs that do what you stated above?

I am studying logwatch as I write this... I'll report back when I get things working :)


All times are GMT -5. The time now is 02:01 PM.