Tuesday, December 23, 2008

Installing the Wyse 4: configure the firewall

The Wyse will be used as my gateway to the internet. There are a lot of out of the box Linux (and BSD) firewall distributions out there. Some of them run from a bootable CD, others can even run from a single floppy. The problem with these distributions is they don't have all the necessary software I need. Next to the firewall software, I will need the following additional services:
  • samba: to use as server for potential windows machines on the network
  • LDAP: to serve as central storage for user accounts and their metadata
  • cups: to use as centralized printing server for all kinds of machines on the network
  • other services I might have forgotten (like subversion, squid, dansguardian, ....)
I have to admit, though, that Endian comes close to what I need, but I just like to do stuff all myself :)
My old Etch firewall was running some custom firewall script I created by hand, but this time, I wanted to use a tool called fwbuilder to create the script.
fwbuilder is a GUI available for most popular OS'es out there (including Mac OS X), capable of generating firewall scripts for a variety of platforms. Platforms include Linux (even old kernels), BSD, CISCO, ... . It also has some pre built templates, to get you started quickly.
So, first you need to download and install fwbuilder for your OS. In Ubuntu (or any Debian based distro), just type apt-get install fwbuilder to download and install the software.
Fire up fwbuilder and create a new object file, which I called home.fwb. Now, right click on Firewalls and select new Firewall from the pop-up menu.
In the next screen, select or enter the following:
  • name: your firewall its name
  • firewall software: select iptables if your firewall is running Linux 2.4 or higher
  • firewall OS: select Linux 2.4/2.6
Be sure to also check Use preconfigured template for firewall objects to get a basic set of rules.
In the next screen, I selected fw template 2 since this one suits my needs. Now click finish.
That's it. You now have a set of rules for a machine running Linux 2.4 or higher with 2 network cards. eth0 should be connected to the internet and has a dynamically assigned network address. eth1 should be connected to the internal network and has a static assigned address. By default, this is 192.168.1.1/255.255.255.0, which I changed to 192.168.1.254/255.255.255.0. You can change this underneath User -> Firewalls -> Your Firewall -> inside -> eth1:ip.
By default, access from the internal network is limited to DNS queries and SSH (Rule 2). Access from the firewall to the internet is limited to DNS queries only (Rule 5). We will need to change this, because we need to be able to upgrade Debian every now and then and we want to have access from the internal network to some of the services mentioned above.
The screenshot below shows my modifications. Rule 5 now allows requests to ntp(to be able to synchronize our clock) and HTTP (to be able to fetch new Debian packages from the internet). Rules 6 and 7 make sure that our logs are not flooded by broadcasts and multicasts from the outside world. Rule 2 was changed, as I already said, to allow access to additional services running on the firewall.

To generate an iptables script from the rules we just created, just select Rules -> Compile. If you transfer the script to the firewall, make it executable (chmod +x) and launch it, you should be able to connect from any machine connected to eth1 with the internet.
Below are some additional scripts I created to start and stop the firwall from /etc/init.d.
The firewall startup script (/etc/init.d/firewall):
#! /bin/sh
#

set -e

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="firewall service"
NAME="firewall"
SCRIPT_START="/etc/init.d/rc.start-firewall"
SCRIPT_STOP="/etc/init.d/rc.flush-firewall"

# Gracefully exit if the package has been removed.
test -x $SCRIPT_START || exit 0
test -x $SCRIPT_STOP || exit 0

#
# Function that starts the daemon/service.
#
d_start() {
$SCRIPT_START
}

#
# Function that stops the daemon/service.
#
d_stop() {
$SCRIPT_STOP
}

case "$1" in
start)
echo "Starting $DESC: $NAME"
d_start
;;
stop)
echo "Stopping $DESC: $NAME"
d_stop
;;
restart|force-reload)
#
# If the "reload" option is implemented, move the "force-reload"
# option to the "reload" entry above. If not, "force-reload" is
# just the same as "restart".
#
echo "Restarting $DESC: $NAME"
d_stop
sleep 1
d_start
;;
*)
echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
exit 1
;;
esac

exit 0

The script referenced in the above script to start the firewall (/etc/init.d/rc.start-firewall) is the script created by fwbuilder.
The script referenced in the above script to stop the firewall (/etc/init.d/rc.flush-firewall):
#!/bin/sh
#
# Stop firewall.
#

. /etc/default/firewall

log "Flushing firewall ..."

#
# reset the default policies in the filter table.
#
$IPTABLES -P INPUT ACCEPT
$IPTABLES -P FORWARD ACCEPT
$IPTABLES -P OUTPUT ACCEPT

#
# reset the default policies in the nat table.
#
$IPTABLES -t nat -P PREROUTING ACCEPT
$IPTABLES -t nat -P POSTROUTING ACCEPT
$IPTABLES -t nat -P OUTPUT ACCEPT

#
# reset the default policies in the mangle table.
#
$IPTABLES -t mangle -P PREROUTING ACCEPT
$IPTABLES -t mangle -P OUTPUT ACCEPT

#
# flush all the rules in the filter and nat tables.
#
$IPTABLES -F
$IPTABLES -t nat -F
$IPTABLES -t mangle -F
#
# erase all chains that's not default in filter and nat table.
#
$IPTABLES -X
$IPTABLES -t nat -X
$IPTABLES -t mangle -X

log "... flushed"

The common script used for logging and constants (/etc/default/firewall):
#!/bin/sh
#
# Firewall resources.
#

LOG_FILE="/var/log/firewall.log"

DEBUG="OFF"
debug () {
[ $DEBUG == "ON" ] && echo "[$0]: $1" | $TEE -a $LOG_FILE
}

log () {
echo "[$0]: $1" | $TEE -a $LOG_FILE
}

export PATH="/sbin:/bin:/usr/sbin:/usr/bin"

# Check if all the commands are there.
#
alias which='type -p'
IPTABLES=`which iptables` || IPTABLES="/sbin/iptables"
IFCONFIG=`which ifconfig` || IFCONFIG="/sbin/ifconfig"
IPSC=`which ipsc` || IPSC="/usr/bin/ipsc"
AWK=`which awk` || AWK="/usr/bin/awk"
SED=`which sed` || SED="/bin/sed"
TEE=`which tee` || TEE="/usr/bin/tee"

REQCMDS="$IPTABLES $IFCONFIG $AWK $SED $TEE"
for i in $REQCMDS; do
if [ -x $i ]; then
debug " found $i"
else
echo "I need $i for executing this script, bailing out."
exit 0
fi
done

# Get the IP address for a specified interface. IP is returned in $IP.
#
get_ip_for_interface () {
IP=""
IP=`$IFCONFIG $1 2>/dev/null \
| $AWK '/inet addr:/ {print $2}' \
| $SED 's/addr://'`
debug "found IP: $IP for $1"
}

# Get the subnet mask for a specified interface. Mask is returned in $MASK.
#
get_mask_for_interface () {
MASK=""
MASK=`$IFCONFIG $1 2>/dev/null \
| $AWK '/ Mask:/ {print $4}' \
| $SED 's/Mask://'`
debug "found MASK: $MASK for $1"
}

# Get the net for a specified interface. Net is returned in $NET.
#
get_net_for_interface () {
NET=""
NET=`$IPSC -i $1 2>/dev/null \
| $AWK '/Network address:/{print $3}'`
debug "found NET: $NET for $1"
}

In the next posts, we will get our hands dirty and start configuring cups, LDAP and samba.

No comments: