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
}