]> git.ipfire.org Git - network.git/commitdiff
Initial import of a lot of firewall code.
authorMichael Tremer <michael.tremer@ipfire.org>
Mon, 28 May 2012 07:55:21 +0000 (07:55 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Mon, 28 May 2012 07:56:51 +0000 (07:56 +0000)
This creates a basic firewall for IPv4.

Makefile
firewall [new file with mode: 0755]
functions.constants
functions.events
functions.firewall [new file with mode: 0644]
functions.iptables [new file with mode: 0644]
functions.policy [new file with mode: 0644]
functions.ppp
functions.util
systemd/firewall.service [new file with mode: 0644]

index a7cbeae9f9c14c05de56733ed39f0a6e9090f02b..3d2300c0893b7d779ed8612e908d9d38051a33e5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -27,6 +27,7 @@ install:
        -mkdir -pv $(DESTDIR)$(systemdunitdir)
        -mkdir -pv $(DESTDIR)$(tmpfilesdir)
 
+       install -m 755 -v firewall $(DESTDIR)$(sbindir)
        install -m 755 -v network $(DESTDIR)$(sbindir)
 
        cp -rfv {hooks,header*,functions*} $(DESTDIR)$(libdir)/network/
diff --git a/firewall b/firewall
new file mode 100755 (executable)
index 0000000..ca4d16c
--- /dev/null
+++ b/firewall
@@ -0,0 +1,69 @@
+#!/bin/bash
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2012  IPFire Network Development Team                         #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+. /usr/lib/network/functions
+
+function cli_start() {
+       firewall_start
+}
+
+function cli_stop() {
+       firewall_stop
+}
+
+# Parse the command line
+while [ $# -gt 0 ]; do
+       case "${1}" in
+               -d|--debug)
+                       DEBUG=1
+                       log DEBUG "Enabled debugging mode"
+                       ;;
+               *)
+                       action=${1}
+                       ;;
+       esac
+       shift
+       [ -n "${action}" ] && break
+done
+
+# Process the given action
+case "${action}" in
+       start|restart|reload)
+               cli_start $@
+               ;;
+
+       stop)
+               cli_stop $@
+               ;;
+
+       ""|help|--help|-h)
+               cli_usage root
+               exit ${EXIT_OK}
+               ;;
+
+       *)
+               error "Invalid command given: ${action}"
+               cli_usage usage
+               exit ${EXIT_CONF_ERROR}
+               ;;
+esac
+
+exit ${EXIT_OK}
index 56baa6e398dbb3258f9d00bc69f2ea5ec198e4c5..2c550b9fec3f3c424ddb6867c56b78cd0a17a3d3 100644 (file)
@@ -85,3 +85,9 @@ DEVICE_PRINT_LINE1="    %-24s %s\n"
 PORT_PATTERN="pN"
 PORT_PATTERN_ACCESSPOINT="apN"
 PORT_PATTERN_WIRELESS="wN"
+
+# This variable is used to point to a directory
+# in which the iptables ruleset will be generated.
+IPTABLES_TMPDIR=
+
+FIREWALL_LOG_FACILITY="syslog"
index 5416e1feb5973473440a30be1421dc6500e8a57f..5b5ba80093902deb146c0ef7956b8c640eaef465 100644 (file)
@@ -29,10 +29,23 @@ function event_emit() {
        # At a later point of time, we need to find another solution how to
        # react on those events.
        #initctl emit ${event} $@
+
+       case "${event}" in
+               firewall-reload)
+                       # Try to reload the firewall service.
+                       __event_firewall_reload
+                       ;;
+       esac
 }
 
-function event_firewall_reload() {
-       event_emit firewall-reload
+function __event_firewall_reload() {
+       local service="firewall"
+
+       # Reload the firewall service when it has
+       # been started earlier.
+       if service_is_running ${service}; then
+               service_restart ${service}
+       fi
 }
 
 function event_interface_up() {
diff --git a/functions.firewall b/functions.firewall
new file mode 100644 (file)
index 0000000..8032a33
--- /dev/null
@@ -0,0 +1,118 @@
+#!/bin/bash
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2012  IPFire Network Development Team                         #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+# High-level function which will create a ruleset for the current firewall
+# configuration and load it into the kernel.
+function firewall_start() {
+       firewall_lock_acquire
+
+       # Initialize an empty iptables ruleset.
+       iptables_init DROP
+
+       # Add default chains.
+       firewall_tcp_state_flags
+       firewall_connection_tracking
+
+       # Add policies for every zone.
+       policy_add_localhost
+
+       local zone
+       for zone in $(zones_get_all); do
+               policy_add_zone ${zone}
+       done
+
+       # Commit the new ruleset.
+       iptables_commit
+
+       firewall_lock_release
+}
+
+function firewall_stop() {
+       firewall_lock_acquire
+
+       # Initialize an empty firewall ruleset
+       # with default policy ACCEPT.
+       iptables_init ACCEPT
+
+       # Commit it.
+       iptables_commit
+
+       firewall_lock_release
+}
+
+function firewall_lock_acquire() {
+       lock_acquire ${RUN_DIR}/.firewall_lock
+
+       # Make sure the lock is released after the firewall
+       # script has crashed or exited early.
+       trap firewall_lock_release EXIT TERM KILL
+
+       # Create a directory where we can put our
+       # temporary data in the most secure way as possible.
+       IPTABLES_TMPDIR=$(mktemp -d)
+}
+
+function firewall_lock_release() {
+       if isset IPTABLES_TMPDIR; then
+               # Remove all temporary data.
+               rm -rf ${IPTABLES_TMPDIR}
+
+               # Reset the tempdir variable.
+               IPTABLES_TMPDIR=
+       fi
+
+       # Reset the trap.
+       trap true EXIT TERM KILL
+
+       lock_release ${RUN_DIR}/.firewall_lock
+}
+
+function firewall_tcp_state_flags() {
+       log INFO "Creating TCP State Flags chain..."
+       iptables_chain_create BADTCP_LOG
+       iptables -A BADTCP_LOG -p tcp -j $(iptables_LOG "Illegal TCP state: ")
+       iptables -A BADTCP_LOG -j DROP
+
+       iptables_chain_create BADTCP
+       iptables -A BADTCP -p tcp --tcp-flags ALL NONE -j BADTCP_LOG
+       iptables -A BADTCP -p tcp --tcp-flags SYN,FIN SYN,FIN -j BADTCP_LOG
+       iptables -A BADTCP -p tcp --tcp-flags SYN,RST SYN,RST -j BADTCP_LOG
+       iptables -A BADTCP -p tcp --tcp-flags FIN,RST FIN,RST -j BADTCP_LOG
+       iptables -A BADTCP -p tcp --tcp-flags ACK,FIN FIN     -j BADTCP_LOG
+       iptables -A BADTCP -p tcp --tcp-flags ACK,PSH PSH     -j BADTCP_LOG
+       iptables -A BADTCP -p tcp --tcp-flags ACK,URG URG     -j BADTCP_LOG
+
+       iptables -A INPUT   -p tcp -j BADTCP
+       iptables -A OUTPUT  -p tcp -j BADTCP
+       iptables -A FORWARD -p tcp -j BADTCP
+}
+
+function firewall_connection_tracking() {
+       log INFO "Creating Connection Tracking chain..."
+       iptables_chain_create CONNTRACK
+       iptables -A CONNTRACK -m state --state ESTABLISHED,RELATED -j ACCEPT
+       iptables -A CONNTRACK -m state --state INVALID -j $(iptables_LOG "INVALID packet: ")
+       iptables -A CONNTRACK -m state --state INVALID -j DROP
+
+       iptables -A INPUT   -j CONNTRACK
+       iptables -A OUTPUT  -j CONNTRACK
+       iptables -A FORWARD -j CONNTRACK
+}
diff --git a/functions.iptables b/functions.iptables
new file mode 100644 (file)
index 0000000..cf1e476
--- /dev/null
@@ -0,0 +1,190 @@
+#!/bin/bash
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2012  IPFire Network Development Team                         #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+function iptables() {
+       local arg
+       local args
+       local table
+
+       # Check if the directory where we put our rules in is set and
+       # exists.
+       assert isset IPTABLES_TMPDIR
+       assert [ -d "${IPTABLES_TMPDIR}" ]
+
+       table=filter
+
+       # Parsing arguments
+       while [ $# -gt 0 ]; do
+               case "${1}" in
+                       -t)
+                               table=${2}
+                               shift 2
+                               ;;
+                       -A)
+                               args="${args} -A ${2^^}"
+                               shift 2
+                               ;;
+                       *)
+                               args="${args} ${1}"
+                               shift
+                               ;;
+               esac
+       done
+
+       echo "${args:1:${#args}}" >> ${IPTABLES_TMPDIR}/${table}
+}
+
+function iptables_init() {
+       local policy=${1}
+       assert isoneof policy ACCEPT DROP
+
+       iptables "* filter"
+       iptables_chain_create -t filter INPUT       ${policy}
+       iptables_chain_create -t filter OUTPUT      ${policy}
+       iptables_chain_create -t filter FORWARD     ${policy}
+
+       iptables -t mangle "* mangle"
+       iptables_chain_create -t mangle PREROUTING  ACCEPT
+       iptables_chain_create -t mangle INPUT       ACCEPT
+       iptables_chain_create -t mangle OUTPUT      ACCEPT
+       iptables_chain_create -t mangle FORWARD     ACCEPT
+       iptables_chain_create -t mangle POSTROUTING ACCEPT
+
+       iptables -t nat "* nat"
+       iptables_chain_create -t nat    PREROUTING  ACCEPT
+       iptables_chain_create -t nat    OUTPUT      ACCEPT
+       iptables_chain_create -t nat    POSTROUTING ACCEPT
+}
+
+function iptables_commit() {
+       local chain
+
+       # Check if the directory where we put our rules in is set and
+       # exists.
+       assert isset IPTABLES_TMPDIR
+       assert [ -d "${IPTABLES_TMPDIR}" ]
+
+       log INFO "Committing firewall configuration..."
+       iptables -t filter "COMMIT"
+       iptables -t mangle "COMMIT"
+       iptables -t nat    "COMMIT"
+
+       local iptables_ruleset="${IPTABLES_TMPDIR}/commit"
+       : > ${iptables_ruleset}
+
+       # Concat the rules for every chain into one file.
+       local table
+       for table in filter mangle nat; do
+               cat ${IPTABLES_TMPDIR}/${table} \
+                       >> ${iptables_ruleset} 2>/dev/null
+       done
+
+       log DEBUG "Dumping iptables ruleset"
+       local counter=1
+       local line
+       while read line; do
+               line=$(printf "%4d | %s\n" "${counter}" "${line}")
+               log DEBUG "${line}"
+
+               counter=$(( $counter + 1 ))
+       done < ${iptables_ruleset}
+       
+       iptables-restore < ${iptables_ruleset}
+}
+
+function iptables_chain_create() {
+       local args
+       if [ "${1}" = "-t" ]; then
+               args="${1} ${2}"
+               shift 2
+       fi
+
+       iptables ${args} ":$1 ${2--} [0:0]"
+}
+
+function iptables_LOG() {
+       local prefix=${1}
+
+       if [ "${FIREWALL_LOG_FACILITY}" = "syslog" ]; then
+               echo -n "LOG"
+               [ -n "$prefix" ] && echo -n " --log-prefix \"$prefix\""
+       else
+               echo -n "NFLOG"
+               [ -n "$prefix" ] && echo -n " --nflog-prefix \"$prefix\""
+               echo -n " --nflog-threshold 30"
+       fi
+       echo
+}
+
+function iptables_protocol() {
+       local PROTO
+       PROTO=$1
+       for proto in tcp udp esp ah; do
+               if [ "$PROTO" = "$proto" ]; then
+                       echo "-p $PROTO"
+                       break
+               fi
+       done
+}
+
+IPTABLES_PORT=0
+IPTABLES_MULTIPORT=1
+IPTABLES_PORTRANGE=2
+
+function _iptables_port_range() {
+       grep -q ":" <<< $@
+}
+
+function _iptables_port_multiport() {
+       grep -q "," <<< $@
+}
+
+function _iptables_port() {
+       if _iptables_port_range "$@"; then
+               echo $IPTABLES_PORTRANGE
+       elif _iptables_port_multiport "$@"; then
+               echo $IPTABLES_MULTIPORT
+       else
+               echo $IPTABLES_PORT
+       fi
+}
+
+function iptables_source_port() {
+       [ -z "$@" ] && return
+       local type
+       type=$(_iptables_port $@)
+       if [ "$type" = "$IPTABLES_MULTIPORT" ]; then
+               echo "-m multiport --source-ports $@"
+       else
+               echo "--sport $@"
+       fi
+}
+
+function iptables_destination_port() {
+       [ -z "$@" ] && return
+       local type
+       type=$(_iptables_port $@)
+       if [ "$type" = "$IPTABLES_MULTIPORT" ]; then
+               echo "-m multiport --destination-ports $@"
+       else
+               echo "--dport $@"
+       fi
+}
diff --git a/functions.policy b/functions.policy
new file mode 100644 (file)
index 0000000..3871720
--- /dev/null
@@ -0,0 +1,118 @@
+#!/bin/bash
+###############################################################################
+#                                                                             #
+# IPFire.org - A linux based firewall                                         #
+# Copyright (C) 2012  IPFire Network Development Team                         #
+#                                                                             #
+# This program is free software: you can redistribute it and/or modify        #
+# it under the terms of the GNU General Public License as published by        #
+# the Free Software Foundation, either version 3 of the License, or           #
+# (at your option) any later version.                                         #
+#                                                                             #
+# This program is distributed in the hope that it will be useful,             #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of              #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               #
+# GNU General Public License for more details.                                #
+#                                                                             #
+# You should have received a copy of the GNU General Public License           #
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.       #
+#                                                                             #
+###############################################################################
+
+function policy_add_zone() {
+       local zone=${1}
+       assert isset zone
+
+       log DEBUG "Creating firewall policy for zone '${zone}'."
+
+       local chain="ZONE_${zone}"
+       chain=${chain^^}
+
+       # Create filter chain.
+       iptables_chain_create ${chain}
+       iptables -A INPUT   -i ${zone} -j ${chain}
+       iptables -A FORWARD -i ${zone} -j ${chain}
+       iptables -A FORWARD -o ${zone} -j ${chain}
+       iptables -A OUTPUT  -o ${zone} -j ${chain}
+
+       # Leave some space for own rules right at the beginning
+       # to make it possible to overwrite _everything_.
+       iptables_chain_create ${chain}_CUSTOM
+       iptables -A ${chain} -j ${chain}_CUSTOM
+
+       # Intrusion Prevention System
+       iptables_chain_create ${chain}_IPS
+       iptables -A ${chain} -i ${zone} -j ${chain}_IPS
+
+       # Port forwarding chain.
+       iptables_chain_create ${chain}_PORTFW
+       iptables -A ${chain} -i ${zone} -j ${chain}_PORTFW
+
+       # Outgoing firewall
+       iptables_chain_create ${chain}_OUTFW
+       iptables -A ${chain} -o ${zone} -j ${chain}_OUTFW
+
+       # Policy rules
+       iptables_chain_create ${chain}_POLICY
+       iptables -A ${chain} -j ${chain}_POLICY
+
+       # Create mangle chain.
+       iptables_chain_create -t mangle ${chain}
+       iptables -t mangle -A PREROUTING  -i ${zone} -j ${chain}
+       iptables -t mangle -A POSTROUTING -o ${zone} -j ${chain}
+
+       # Quality of Service
+       iptables_chain_create -t mangle ${chain}_QOS_INC
+       iptables -t mangle -A ${chain} -i ${zone} -j ${chain}_QOS_INC
+       iptables_chain_create -t mangle ${chain}_QOS_OUT
+       iptables -t mangle -A ${chain} -o ${zone} -j ${chain}_QOS_OUT
+
+       # Create NAT chain.
+       iptables_chain_create -t nat ${chain}
+       iptables -t nat -A PREROUTING  -i ${zone} -j ${chain}
+       iptables -t nat -A POSTROUTING -o ${zone} -j ${chain}
+
+       # Network Address Translation
+       iptables_chain_create -t nat ${chain}_NAT
+       iptables -t nat -A ${chain} -i ${zone} -j ${chain}_NAT
+
+       # Portforwarding
+       iptables_chain_create -t nat ${chain}_PORTFW
+       iptables -t nat -A ${chain} -i ${zone} -j ${chain}_PORTFW
+
+       # UPNP
+       iptables_chain_create -t nat ${chain}_UPNP
+       iptables -t nat -A ${chain} -j ${chain}_UPNP
+
+       # After the chains that are always available have been
+       # created, we will add a custom policy to every single
+       # zone.
+
+       # Local zones are currently allowed to access everything.
+       if zone_is_local ${zone}; then
+               policy_allow_all ${zone} ${chain}
+
+       # Uplink connections are not.
+       else
+               : # XXX TODO
+       fi
+}
+
+function policy_add_localhost() {
+       log DEBUG "Creating firewall policy for localhost..."
+
+       # Accept everything on lo
+       iptables -A INPUT  -i lo -j ACCEPT
+       iptables -A OUTPUT -o lo -j ACCEPT
+}
+
+function policy_allow_all() {
+       local zone=${1}
+       assert isset zone
+
+       local chain=${2}
+       assert isset chain
+
+       # Just accept everything.
+       iptables -A ${chain}_POLICY -j ACCEPT
+}
index 78b967e44d65679fed6cc13f7f9923e0689eecc3..9c793b43bcf4210a7704c5d11e247814761d237e 100644 (file)
@@ -31,7 +31,7 @@ function ppp_common_ip_pre_up() {
        routing_db_from_ppp ${zone} ipv4
 
        # Request firewall reload
-       event_firewall_reload
+       event_emit firewall-reload
 
        return ${EXIT_OK}
 }
index bb3f64841c575848e18ad8e39204d3f2ff71ce62..6aee47d108cf6bbcd1c42351e36d5e4a8963a8a7 100644 (file)
@@ -324,16 +324,8 @@ function cmd() {
        return ${ret}
 }
 
-function uppercase() {
-       local input
-       read input
-       echo "${input^^}"
-}
-
-function lowercase() {
-       local input
-       read input
-       echo "${input,,}"
+function cmd_quiet() {
+       cmd $@ &>/dev/null
 }
 
 function seq() {
diff --git a/systemd/firewall.service b/systemd/firewall.service
new file mode 100644 (file)
index 0000000..7d6b64e
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Description=Firewall Ruleset Manager
+Before=network.service
+
+[Service]
+Type=oneshot
+RemainAfterExit=yes
+ExecStart=/usr/sbin/firewall start
+ExecStop=/usr/sbin/firewall stop
+
+[Install]
+WantedBy=multi-user.target