Firewall

Basic Firewall with iptables

Firewall Scripts

#!/bin/bash
# /etc/firewall/firewall.conf
#
# Iptables
FW4="/sbin/iptables"
FW6="/sbin/ip6tables"

# delete existing rules
/etc/firewall/firewall.stop

# Standard rules
$FW4 -P INPUT   ACCEPT
$FW4 -P FORWARD DROP
$FW4 -P OUTPUT  ACCEPT
$FW6 -P INPUT   ACCEPT
$FW6 -P FORWARD DROP
$FW6 -P OUTPUT  ACCEPT

# IP Protocol specific rules
$FW4 -A INPUT -p icmp -j ACCEPT -m comment --comment "allow ping"
$FW6 -A INPUT -p icmpv6 -j ACCEPT -m comment --comment "allow ping6"

for FW in {$FW4,$FW6}; do
       $FW -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
       $FW -A INPUT -i lo -j ACCEPT -m comment --comment "Allow inbound traffic on lo"
       # Optionally allow inbound ssh
       #$FW -A INPUT -p tcp --dport 22 -j ACCEPT -m comment --comment "Allow inbound ssh"
       # Drop the rest
       $FW -A INPUT -j DROP
done
#!/bin/sh
# /etc/firewall/firewall.stop
# 
# Iptables
FW4="/sbin/iptables"
FW6="/sbin/ip6tables"

# delete existing chains & rules
$FW4 -F
$FW4 -X
$FW6 -F
$FW6 -X

Init Script for Sysvinit

#!/bin/bash
# /etc/init.d/firewall
#

### BEGIN INIT INFO
# Provides:          firewall
# Required-Start:    networking
# Required-Stop:     
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start firewall
### END INIT INFO

# Basic functions
. /lib/lsb/init-functions

case "$1" in
  start|restart)
    log_begin_msg "Starting Firewall"
    . /etc/firewall/firewall.conf
    log_end_msg 0
    ;; 
  stop)
    log_begin_msg "Stopping Firewall"
    . /etc/firewall/firewall.stop
    log_end_msg 0
    ;; 
  status)
    /sbin/iptables -L -v -n
    ;; 
  *) 
    echo "Usage: /etc/init.d/firewall {start|stop|restart|status}"
    exit 1
    ;; 
esac

exit 0

BSD Style Init Script

#!/bin/sh
# /etc/rc.d/rc.firewall
# Start/stop/restart the firewall:

failure(){
        echo "[Failed]"
        exit -1
}

success(){
        echo "[Ok]"
}

firewall_start() {
        echo -n "Starting firewall ... "
        . /etc/firewall/firewall.conf || failure
        success
}

firewall_stop() {
        echo -n "Stopping firewall ... "
        . /etc/firewall/firewall.stop || failure
        success
}

firewall_restart() {
        firewall_stop
        firewall_start
}

firewall_status() {
        /usr/sbin/iptables -L
}

case "$1" in
'start')
  firewall_start
  ;;  
'stop')
  firewall_stop
  ;;  
'status')
  firewall_status
  ;;  
'restart')
  firewall_restart
  ;;  
*)
  echo "usage $0 start|stop|restart|status"
esac

Systemd Service File

# /etc/systemd/system/firewall.service
[Unit]
Description=Custom iptables-based firewall
Wants=network.target
Before=network.target

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/etc/firewall/firewall.conf
ExecReload=/etc/firewall/firewall.conf
ExecStop=/etc/firewall/firewall.stop

[Install]
WantedBy=multi-user.target

Upstart Init Script

# Firewall - Firewall service job file
# /etc/init/firewall.conf

description "Firewall"
author "edr"

# When to start the service
start on runlevel [2345] 

# When to stop the service
stop on runlevel [016]

# Stanzas
#
# Stanzas control when and how a process is started and stopped
# See a list of stanzas here: [http://upstart.ubuntu.com/wiki/Stanzas](http://upstart.ubuntu.com/wiki/Stanzas)

# Specify the process/command to start
script
        . /etc/firewall/firewall.conf
end script

Runit run script

#!/bin/sh
FIREWALL_CONF="/etc/firewall/firewall.conf"
[ ! -e $FIREWALL_CONF ] && exit 0
#iptables-restore /etc/iptables/iptables.rules
. $FIREWALL_CONF
exec chpst -b iptables pause

Finally

chmod +x /etc/firewall/firewall.conf
chmod +x /etc/firewall/firewall.stop
# only for initV
chmod +x /etc/init.d/firewall
insserv /etc/init.d/firewall
# only for BSD style init
chmod +x /etc/rc.d/rc.firewall

Application Specific Rules

If e.g. you want to run some applications without internet connection you can do so by running them under special group ids.

Create a group called no-internet and add your user to it.

addgroup no-internet
usermod -a -G no-internet <user>

Add the following rule to your firewall script:

$FW -A OUTPUT -m owner --gid-owner no-internet -j REJECT

Run the application with the following command:

sg no-internet 'command to run application'

Or use a small wrapper script like the following

#!/bin/bash
# Runs a command with gid "no-internet" 
if [ $# -eq 0 ];then
       echo "Usage: $(basename $0) <command>"
       exit -1;
fi
CMD="sg no-internet '$@'"
eval $CMD

And run it like this

ni command to run application

E.g.

ni ping www.google.de

... should produce the following output:

PING www.google.de (173.194.113.175) 56(84) bytes of data.
ping: sendmsg: Operation not permitted
ping: sendmsg: Operation not permitted

To verify that the process is really running with the right group id you can use the following command:

ps -eo uid,gid,args

possibly followed by a grep on the application name.

Working with IPSETS

To create rules on large black or whitelists use ipsets.

The following loop would read all ips from a text file and add them to an ipset:

#!/bin/sh
# File: /etc/firewall/ipset_whitelist.conf
IPSET="/usr/sbin/ipset"

$IPSET destroy whitelist
$IPSET create whitelist hash:net

for ip in $(sed -e s/#.*//g /etc/firewall/white.list) ; do
        $IPSET add whitelist $ip 
done

The file /etc/firewall/white.list would contain one ip per line. Descriptive comments may be started with # Example:

141.76.2.4         # ftp.de.debian.org
212.211.132.32     # security.debian.org
212.211.132.250    # security.debian.org
198.145.20.140     # kernel.org
149.20.4.69        # pub.kernel.org
199.204.44.194     # pub.kernel.org

You could then create rules against the ipset:

# Whitelist
. /etc/firewall/ipset_whitelist.conf
$FW -A OUTPUT -m set --match-set whitelist dst -j ACCEPT

Logging blocked Packets

Before dropping a packet, send it to the LOG target:

$FW -A OUTPUT -j LOG --log-level $LOG_LEVEL --log-prefix 'OUT_FIREWALL '
$FW -A OUTPUT -j REJECT

Create an entry in /etc/rsyslog.conf to write them in a separate logfile, right before the catch all rules for /var/log/messages etc.

#
# IPTABLES
#
:msg, contains, "FIREWALL"       /var/log/iptables/blocked.log
& ~

The ampersand just means that the same selector should be applied for another rule, and the ~ means drop logging for this selector. This should prevent the packets from showing up in /var/log/messages after having been redirected to /var/log/iptables/blocked.log. However, on my machine this did not work, don't know why. So I did

:msg, contains, "FIREWALL"       /var/log/iptables/blocked.log
:msg, contains, "FIREWALL"       ~

which worked as expected.

Finally, to stop the logging from being shown in the tty consoles, run

dmesg --console-off

Logrotate config to keep log file sizes manageable

And put a config file for logrotate to /etc/logrotate.d/

compress
/var/log/iptables/*.log {
        rotate 10
        daily
        maxsize 10M
        missingok
}