firehol/sbin/firehol.in

9366 lines
249 KiB
Plaintext
Raw Normal View History

#!/bin/bash
2002-09-05 20:57:59 +00:00
#
# FireHOL - A firewall for humans...
2002-09-05 20:57:59 +00:00
#
# Copyright
2002-09-05 20:57:59 +00:00
#
# Copyright (C) 2003-2014 Costa Tsaousis <costa@tsaousis.gr>
# Copyright (C) 2012-2014 Phil Whineray <phil@sanewall.org>
2002-09-05 20:57:59 +00:00
#
# License
2002-09-05 20:57:59 +00:00
#
# 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 2 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/>.
#
# See the file COPYING for details.
2002-10-27 12:47:48 +00:00
#
get_version() {
GIT_REF='$Format:%d,commit-%h$'
local IFS=":(), "
set -- "$GIT_REF"
ver='$Id$'
for i in $@
do
case "$i" in
*[0-9].[0.9]*)
echo "$i" | sed -e 's/^v//'
return 0
;;
commit-[0-9a-zA-Z]*)
ver="$i"
;;
esac
done
echo "$ver"
return 0
}
VERSION=$(get_version)
if [ "$FIREHOL_DEBUGGING" ]; then set -v; set -x; fi
emit_version() {
${CAT_CMD} <<EOF
FireHOL $VERSION
(C) Copyright 2003-2014 Costa Tsaousis <costa@tsaousis.gr>
(C) Copyright 2012-2014 Phil Whineray <phil@firehol.org>
FireHOL is distributed under the GPL v2+.
Home Page: http://firehol.org
-------------------------------------------------------------------------
Get notified of new FireHOL releases by subscribing to the mailing list:
http://lists.firehol.org/mailman/listinfo/firehol-support/
-------------------------------------------------------------------------
EOF
}
# Make sure only root can run us.
if [ ! "${UID}" = 0 ]
then
echo >&2
echo >&2
echo >&2 "Only user root can run FireHOL."
echo >&2
exit 1
fi
# Remember who you are.
FIREHOL_FILE="${0}"
declare -a FIREHOL_ORIGINAL_ARGS=("${@}")
FIREHOL_DEFAULT_WORKING_DIRECTORY="${PWD}"
# Make sure we don't get localized results
export LC_ALL=C
2013-09-25 23:50:30 +00:00
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# GLOBAL DEFAULTS
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
# --- BEGIN OF FIREHOL DEFAULTS ---
# These are the defaults for FireHOL.
# You can set everything system-wide here, or set any or all
# of these to your firewall config file.
# The options set in the firewall config file have the highest
# priority (will overwrite these one).
# FireHOL config directory.
# EVEN IF YOU CHANGE THIS, THE firehol-defaults.conf FILE
# SHOULD STILL EXIST IN /etc/firehol
FIREHOL_CONFIG_DIR="/etc/firehol"
# FireHOL services directory.
# FireHOL will look into this directory for service
# definition files (*.conf).
# Package maintainers may install their service definitions
# in this directory.
# Default: /etc/firehol/services
FIREHOL_SERVICES_DIR="${FIREHOL_CONFIG_DIR}/services"
# Where to permanently save state information?
# Default: /var/spool/firehol
FIREHOL_SPOOL_DIR="/var/spool/firehol"
# Where temporary files should go?
# /var/run is usualy a ram drive, so we prefer to use
# this for temporary files.
# Default: /var/run/firehol
FIREHOL_RUN_DIR="/var/run/firehol"
# Restore instead of Start when possible.
# If set to 1, FireHOL will actually do a 'restore' when a
# 'start' is requested.
# If enabled and the config files have not changed since
# the last successful activation, the last successfuly
# activated firewall will be restored.
# THIS OPTION SHOULD NOT BE ENABLED IF THE FIREWALL CONFIG
# IS USING DYNAMIC DETECTION OF SERVER PORTS OR OTHER DATA
# THAT MAY INFLUENCE THE GENERATED RULES.
# At the other hand, if the firewall is always static
# this option provides fast startup of the firewall.
# Default: 0
FIREHOL_RESTORE_INSTEAD_OF_START="0"
# Enable IPv4 firewall
# Default: 1
ENABLE_IPV4="1"
# Enable IPv6 firewall
# Default: 1
ENABLE_IPV6="1"
# Syslog facility to use when logging FireHOL events.
# This is only used by FireHOL, not the iptables packet
# logging mechanism.
# Default: daemon
FIREHOL_SYSLOG_FACILITY="daemon"
# FireHOL can wait for an interface to come up.
# Set the interface name to wait for, here.
# Default: check the environment variable, if any
WAIT_FOR_IFACE="${WAIT_FOR_IFACE}"
# External program to call on 'start' (successfull or
# failed), 'stop' and 'panic'
# It will be run like this:
# "${FIREHOL_NOTIFICATION_PROGRAM}" "${FIREHOL_CONFIG}" "${result}" "${restored}" "${work_error}" "${work_runtime_error}"
# where
# FIREHOL_CONFIG is the filename of the config
# result is either empty, OK or FAILED
# restored is either NO, OK or FAILED
# work_error is the count of pre-processing errors encountered
# work_runtime_error is the count of post-processing errors encountered
# Default: check the environment variable, if any
FIREHOL_NOTIFICATION_PROGRAM="${FIREHOL_NOTIFICATION_PROGRAM}"
# ----------------------------------------------------------------------
# RUNTIME CONTROL VARIABLES
# These do not affect the final firewall output. They just control how
# FireHOL behaves.
# They can also be set as environment variables of the same name.
# If set to 1, FireHOL will attempt to activate the firewall with
# iptables-restore. This is a lot faster firewall activation.
# The only drawback of this, is that in case of error, FireHOL may be
# unable to identify the exact statement in the firewall config that
# caused the error.
# Default: 1
FIREHOL_FAST_ACTIVATION="${FIREHOL_FAST_ACTIVATION-1}"
# If set to 0, firehol will not try to load the required kernel modules
# Generally, FireHOL is able to detect if a module is compiled in the
# kernel, even if this is set to 1.
# Default: 1
FIREHOL_LOAD_KERNEL_MODULES="${FIREHOL_LOAD_KERNEL_MODULES-1}"
# Firewall Policy during firewall activation
# Default: ACCEPT
# Possible values: ACCEPT, REJECT, DROP
FIREHOL_INPUT_ACTIVATION_POLICY="${FIREHOL_INPUT_ACTIVATION_POLICY-ACCEPT}"
FIREHOL_OUTPUT_ACTIVATION_POLICY="${FIREHOL_OUTPUT_ACTIVATION_POLICY-ACCEPT}"
FIREHOL_FORWARD_ACTIVATION_POLICY="${FIREHOL_FORWARD_ACTIVATION_POLICY-ACCEPT}"
# Do we allow pre-existing connections to continue during activation?
# If this is set to 0 and FIREHOL_FAST_ACTIVATION is also set to 0, then
# every time the firewall is activated, existing connections will be disrupted.
# Default: 1
FIREHOL_ESTABLISHED_ACTIVATION_ACCEPT="${FIREHOL_ESTABLISHED_ACTIVATION_ACCEPT-1}"
# Set this to 1 have firehol load NAT kernel modules
# It will be enabled automatically if nat commands are given in the firewall
# Default: 0
FIREHOL_NAT="${FIREHOL_NAT-0}"
# Set this to 1 to enable rooting of packets in the kernel
# It will be enabled automatically if routers are defined in the firewall
# Default: 0
FIREHOL_ROUTING="${FIREHOL_ROUTING-0}"
# If you want to restore the firewall using the iptables init script of
# your distribution, set here the paths where it expects the rules.
# These settings are only saved when 'save' is requested at the command line.
# Default: unset for automatic detection.
FIREHOL_AUTOSAVE=
FIREHOL_AUTOSAVE6=
# Ready to use values for various distributions:
#
# Gentoo
# Check: /etc/conf.d/iptables and ip6tables
#FIREHOL_AUTOSAVE="/var/lib/iptables/rules-save"
#FIREHOL_AUTOSAVE6="/var/lib/ip6tables/rules-save"
#
# Arch
# Check: /usr/lib/systemd/system/iptables.service and ip6tables.service
#FIREHOL_AUTOSAVE=/etc/iptables/iptables.rules
#FIREHOL_AUTOSAVE6=/etc/iptables/ip6tables.rules
# ----------------------------------------------------------------------
# FIREWALL CONFIGURATION VARIABLES
# These affect the final output firewall.
# They can also be set in the firewall config file.
# The default policy for the interfaces of the firewall.
# This can be controlled on a per interface basis using the
# policy interface subcommand.
# Default: DROP
# Possible Values: DROP REJECT RETURN
DEFAULT_INTERFACE_POLICY="DROP"
# The default policy for the router commands of the firewall.
# This can be controlled on a per interface basis using the
# policy interface subscommand.
# Default: RETURN
# Possible Values: DROP REJECT RETURN
DEFAULT_ROUTER_POLICY="RETURN"
# Should we drop all INVALID packets always?
# INVALID packets as seen by the connection tracker.
# Default: 0
FIREHOL_DROP_INVALID=0
# At the end of the firewall, there may be packets not matched
# anywhere. What to do with them?
# Default: DROP
# Possible Values: DROP REJECT
UNMATCHED_INPUT_POLICY="DROP"
UNMATCHED_OUTPUT_POLICY="DROP"
UNMATCHED_ROUTER_POLICY="DROP"
# The client ports to be used for "default" client ports when the
# client specified is a foreign host.
# Note that FireHOL will ask the kernel for default client ports of
# the local host. This setting only applies to client ports of remote hosts.
# Default: 1024:65535
DEFAULT_CLIENT_PORTS="1024:65535"
# If set to 0, FireHOL will NOT trust interface lo for all traffic, thus
# a firewall could be set up on lo.
# Default: 1
FIREHOL_TRUST_LOOPBACK=1
# ----------------------------------------------------------------------
# IPTABLES PACKETS LOGGING
# LOG mode for iptables
# Default: LOG
# Possible Values: LOG, ULOG, NFLOG
# LOG = syslog
# We recommend to install ulogd and use NFLOG.
FIREHOL_LOG_MODE="LOG"
# Accepts anything iptables accepts for each mode.
# Check: iptables -j LOG --help
# iptables -j ULOG --help
# iptables -j NFLOG --help
# Default: empty
FIREHOL_LOG_OPTIONS=""
# FireHOL can prefix each log with a keyword.
# Default: empty
FIREHOL_LOG_PREFIX=""
# Used only for FIREHOL_LOG_MODE="LOG"
# The syslog level to be used when logging packets.
FIREHOL_LOG_LEVEL="warning"
# For loglimit, these are the frequency and the burst
# of logging. They are applied per logging rule, not across
# the firewall.
FIREHOL_LOG_FREQUENCY="1/second"
FIREHOL_LOG_BURST="5"
# If set to 1, FireHOL will silently drop orphan TCP packets with ACK,FIN set.
# In modern kernels, the connection tracker detects closed sockets
# and removes them from memory before receiving the FIN,ACK from the remote
# party. This makes FireHOL log these packets when they will be received.
# To silently drop these packets, enable this option.
# Default: 1
FIREHOL_DROP_ORPHAN_TCP_ACK_FIN=1
# ----------------------------------------------------------------------
# DEFAULT IP SETS
# FireHOL will overwite these settings with the contents of the files with
# the same names in ${FIREHOL_CONFIG_DIR}.
#
# For example, RESERVED_IPV4 will be set from /etc/firehol/RESERVED_IPV4
# IANA reserved address space that should never appear
RESERVED_IPV4="0.0.0.0/8 127.0.0.0/8 240.0.0.0/4 "
RESERVED_IPV6="::/8 0100::/8 0200::/7 0400::/6 0800::/5 1000::/4 4000::/3 6000::/3 8000::/3 A000::/3 C000::/3 E000::/4 F000::/5 F800::/6 FE00::/9 FEC0::/10"
# Private IPv4 address space
# 10.0.0.0/8 => RFC 1918: IANA Private Use
# 169.254.0.0/16 => Link Local
# 192.0.2.0/24 => Test Net
# 192.88.99.0/24 => RFC 3068: 6to4 anycast & RFC 2544: Benchmarking addresses
# 192.168.0.0/16 => RFC 1918: Private use
PRIVATE_IPV4="10.0.0.0/8 169.254.0.0/16 172.16.0.0/12 192.0.2.0/24 192.88.99.0/24 192.168.0.0/16"
# Private IPv6 address space
# FC00::/7 => Unique Local Unicast
# FE80::/10 => Link Local Unicast
PRIVATE_IPV6="FC00::/7 FE80::/10"
# The multicast address space
MULTICAST_IPV4="224.0.0.0/4"
MULTICAST_IPV6="FF00::/16"
# --- END OF FIREHOL DEFAULTS ---
if [ -f "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" ]
then
source "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" || exit 1
fi
# config file
FIREHOL_CONFIG="${FIREHOL_CONFIG_DIR}/firehol.conf"
# Concurrent run control
FIREHOL_LOCK_FILE="${FIREHOL_RUN_DIR}/firehol.lck"
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# EXTERNAL/SYSTEM COMMANDS MANAGEMENT
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
export PATH="${PATH}:/bin:/usr/bin:/sbin:/usr/sbin"
# External commands FireHOL will need.
# If one of those is not found, FireHOL will refuse to run.
which_cmd() {
local block=1
if [ "a${1}" = "a-n" ]
then
local block=0
shift
fi
unalias $2 >/dev/null 2>&1
local cmd=`which $2 2>/dev/null | head -n 1`
if [ $? -gt 0 -o ! -x "${cmd}" ]
then
if [ ${block} -eq 1 ]
then
echo >&2
echo >&2 "ERROR: Command '$2' not found in the system path."
echo >&2 " FireHOL requires this command for its operation."
echo >&2 " Please install the required package and retry."
echo >&2
echo >&2 " Note that you need an operational 'which' command"
echo >&2 " for FireHOL to find all the external programs it"
echo >&2 " needs. Check it yourself. Run:"
echo >&2
echo >&2 " which $2"
exit 1
fi
return 1
fi
eval $1=${cmd}
return 0
}
# command on demand support.
require_cmd() {
local block=1
if [ "a$1" = "a-n" ]
then
local block=0
shift
fi
# if one is found, return success
for x in "${@}"
do
eval var=`echo ${x} | tr 'a-z-' 'A-Z_'`_CMD
eval val=\$\{${var}\}
if [ -z "${val}" ]
then
which_cmd -n "${var}" "${x}"
test $? -eq 0 && return 0
fi
done
if [ $block -eq 1 ]
then
echo >&2
echo >&2 "ERROR: FIREHOL REQUIRES THESE COMMANDS:"
echo >&2
echo >&2 " ${@}"
echo >&2
echo >&2 " You have requested the use of an optional FireHOL"
echo >&2 " feature that requires certain external programs"
echo >&2 " to be installed in the running system."
echo >&2
echo >&2 " Please consult your Linux distribution manual to"
echo >&2 " install the package(s) that provide these external"
echo >&2 " programs and retry."
echo >&2
echo >&2 " Note that you need an operational 'which' command"
echo >&2 " for FireHOL to find all the external programs it"
echo >&2 " needs. Check it yourself. Run:"
echo >&2
for x in "${@}"
do
echo >&2 " which $x"
done
exit 1
fi
return 1
}
# Currently the following commands are required only when needed.
# (i.e. Command on Demand)
#
# zcat or gzcat or gzip (either or none is fine)
# less or more (either or none is fine)
# ip
# ss
# date
# hostname
# modprobe or insmod
# gawk or awk
# nice (none is fine)
# Commands that are mandatory for FireHOL operation:
which_cmd CAT_CMD cat
which_cmd CUT_CMD cut
which_cmd CHOWN_CMD chown
which_cmd CHMOD_CMD chmod
which_cmd EGREP_CMD egrep
which_cmd EXPR_CMD expr
which_cmd FIND_CMD find
which_cmd FOLD_CMD fold
which_cmd GREP_CMD grep
which_cmd HEAD_CMD head
2014-03-12 22:20:00 +00:00
which_cmd TAIL_CMD tail
which_cmd LSMOD_CMD lsmod
which_cmd MKDIR_CMD mkdir
which_cmd MKTEMP_CMD mktemp
which_cmd MV_CMD mv
which_cmd RM_CMD rm
which_cmd SED_CMD sed
which_cmd SORT_CMD sort
which_cmd SYSCTL_CMD sysctl
which_cmd TOUCH_CMD touch
which_cmd TR_CMD tr
which_cmd UNAME_CMD uname
which_cmd UNIQ_CMD uniq
which_cmd LOGGER_CMD logger
which_cmd FLOCK_CMD flock
if [ ! -f "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" ]
then
"${EGREP_CMD}" "^# --- BEGIN OF FIREHOL DEFAULTS ---" -A 600 "${FIREHOL_FILE}" |\
"${EGREP_CMD}" "^# --- END OF FIREHOL DEFAULTS ---" -B 600 >"${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" || exit 1
"${CHOWN_CMD}" root:root "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" || exit 1
"${CHMOD_CMD}" 600 "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" || exit 1
fi
ENABLE_ACCOUNTING=1
ACCOUNTING_WARNING=0
require_cmd -n nfacct
if [ -z "${NFACCT_CMD}" ]
then
# silently disable accounting here,
# the user will get a warning when the first
# accounting rule is evaluated
ENABLE_ACCOUNTING=0
ACCOUNTING_WARNING=1
fi
if [ ${ENABLE_IPV4} -eq 1 ]
then
require_cmd -n iptables
require_cmd -n iptables-save
require_cmd -n iptables-restore
if [ -z "${IPTABLES_CMD}" ]
then
echo >&2 " WARNING: no iptables command: IPv4 disabled"
ENABLE_IPV4=0
elif [ -z "${IPTABLES_SAVE_CMD}" ]
then
echo >&2 " WARNING: no iptables-save command: IPv4 disabled"
ENABLE_IPV4=0
elif [ -z "${IPTABLES_RESTORE_CMD}" ]
then
echo >&2 " WARNING: no iptables-restore command: IPv4 disabled"
ENABLE_IPV4=0
fi
fi
if [ ${ENABLE_IPV6} -eq 1 ]
then
require_cmd -n ip6tables
require_cmd -n ip6tables-save
require_cmd -n ip6tables-restore
if [ ! -f /proc/net/if_inet6 ]
then
# IPv6 not in use on this system, silently ignore
ENABLE_IPV6=0
elif [ -z "${IP6TABLES_CMD}" ]
then
echo >&2 " WARNING: no ip6tables command: IPv6 disabled"
ENABLE_IPV6=0
elif [ -z "${IP6TABLES_SAVE_CMD}" ]
then
echo >&2 " WARNING: no ip6tables-save command: IPv6 disabled"
ENABLE_IPV6=0
elif [ -z "${IP6TABLES_RESTORE_CMD}" ]
then
echo >&2 " WARNING: no ip6tables-restore command: IPv6 disabled"
ENABLE_IPV6=0
fi
fi
# Special commands
pager_cmd() {
if [ -z "${LESS_CMD}" ]
then
require_cmd -n less more
test -z "${LESS_CMD}" && LESS_CMD="${MORE_CMD}"
test -z "${LESS_CMD}" && LESS_CMD="${CAT_CMD}"
fi
"${LESS_CMD}" "${@}"
}
zcat_cmd() {
require_cmd -n zcat gzcat gzip
test -z "${ZCAT_CMD}" && ZCAT_CMD="${GZCAT_CMD}"
if [ ! -z "${ZCAT_CMD}" ]
then
"${ZCAT_CMD}" "${@}"
return $?
elif [ ! -z "${GZIP_CMD}" ]
then
"${CAT_CMD}" "${@}" | "${GZIP_CMD}" -dc
return $?
fi
echo >&2 " "
echo >&2 " IMPORTANT WARNING:"
echo >&2 " ------------------"
echo >&2 " FireHOL cannot find any of the commands: zcat, gzcat, gzip."
echo >&2 " Make sure you have one of these available in the system path."
echo >&2 " "
return 1
}
gawk_cmd() {
require_cmd -n gawk awk
test -z "${GAWK_CMD}" && GAWK_CMD="${AWK_CMD}"
if [ ! -z "${GAWK_CMD}" ]
then
"${GAWK_CMD}" "${@}"
return $?
fi
echo >&2 " "
echo >&2 " IMPORTANT WARNING:"
echo >&2 " ------------------"
echo >&2 " FireHOL cannot find any of the commands: gawk, awk."
echo >&2 " Make sure you have one of these available in the system path."
echo >&2 " "
return 1
}
2013-09-25 23:50:30 +00:00
modprobe_cmd() {
require_cmd -n modprobe insmod
test -z "${MODPROBE_CMD}" && MODPROBE_CMD="${INSMOD_CMD}"
if [ ! -z "${MODPROBE_CMD}" ]
then
save_for_restore none "${MODPROBE_CMD}" "${@}"
"${MODPROBE_CMD}" "${@}"
status=$?
if [ $status -eq 17 ]
then
# insmod: module already loaded - not a problem
return 0
else
return $status
fi
fi
echo >&2 " "
echo >&2 " IMPORTANT WARNING:"
echo >&2 " ------------------"
echo >&2 " FireHOL cannot find any of the commands: modprobe, insmod."
echo >&2 " Make sure you have one of these available in the system path."
echo >&2 " "
return 1
}
renice_cmd() {
if [ -z "${RENICE_CMD}" ]
then
require_cmd -n renice
test -z "${RENICE_CMD}" && RENICE_CMD=":"
fi
"${RENICE_CMD}" "${@}"
}
2013-09-25 23:50:30 +00:00
firehol_concurrent_run_lock() {
exec 200>"${FIREHOL_LOCK_FILE}"
if [ $? -ne 0 ]; then exit; fi
${FLOCK_CMD} -n 200
if [ $? -ne 0 ]
then
echo >&2 "FireHOL is already running. Exiting..."
exit 1
fi
return 0
}
# Make sure our generated files cannot be accessed by anyone else.
umask 077
# Be nice on production environments
renice_cmd 10 $$ >/dev/null 2>/dev/null
# Initialize iptables
if [ $ENABLE_IPV4 -eq 1 ]
then
${IPTABLES_CMD} -nxvL >/dev/null 2>&1
if [ $? -ne 0 ]
then
echo >&2 " WARNING: error initializing iptables: IPv4 disabled"
ENABLE_IPV4=0
fi
fi
if [ $ENABLE_IPV6 -eq 1 ]
then
${IP6TABLES_CMD} -nxvL >/dev/null 2>&1
if [ $? -ne 0 ]
then
echo >&2 " WARNING: error initializing ip6tables: IPv6 disabled"
ENABLE_IPV6=0
fi
fi
if [ $ENABLE_IPV4 -eq 0 -a $ENABLE_IPV6 -eq 0 ]
then
echo >&2 " ERROR: Neither IPv4 nor IPv6 is available - exiting"
exit 1
fi
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# GLOBAL PREPARATIONS
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2002-09-05 20:57:59 +00:00
# ------------------------------------------------------------------------------
# ----------------------------------------------------------------------
# Directories and files
if [ ! -d "${FIREHOL_RUN_DIR}" ]
then
${MKDIR_CMD} -p "${FIREHOL_RUN_DIR}" || exit 1
"${CHOWN_CMD}" root:root "${FIREHOL_RUN_DIR}" || exit 1
"${CHMOD_CMD}" 700 "${FIREHOL_RUN_DIR}" || exit 1
fi
if [ ! -d "${FIREHOL_SPOOL_DIR}" ]
then
${MKDIR_CMD} -p "${FIREHOL_SPOOL_DIR}" || exit 1
"${CHOWN_CMD}" root:root "${FIREHOL_SPOOL_DIR}" || exit 1
"${CHMOD_CMD}" 700 "${FIREHOL_SPOOL_DIR}" || exit 1
fi
# Create an empty temporary directory we need for this run.
if ! FIREHOL_DIR="`${MKTEMP_CMD} -d "${FIREHOL_RUN_DIR}/firehol-XXXXXXXXXX"`"
then
echo >&2
echo >&2
echo >&2 "Cannot create temporary directory."
echo >&2
exit 1
fi
#FIREHOL_CHAINS_DIR="${FIREHOL_DIR}/chains"
FIREHOL_OUTPUT="${FIREHOL_DIR}/firehol-out.sh"
FIREHOL_SAVED="${FIREHOL_DIR}/firehol-save.sh"
FIREHOL_SAVED6="${FIREHOL_DIR}/firehol-save6.sh"
FIREHOL_TMP="${FIREHOL_DIR}/firehol-tmp.sh"
# ------------------------------------------------------------------------------
# Make sure we automatically cleanup when we exit.
# WHY:
# Even a CTRL-C will call this and we will not leave temp files.
# Also, if a configuration file breaks, we will detect this too.
FIREHOL_CLEAN_TMP=1
FIREHOL_ACTIVATED_SUCCESSFULLY=0
syslog() {
local p="$1"; shift
"${LOGGER_CMD}" -p ${FIREHOL_SYSLOG_FACILITY}.$p -t "FireHOL[$$]" "${@}"
return 0
}
firehol_exit() {
local restored="NO"
if [ \( -f "${FIREHOL_SAVED}" -o -f "${FIREHOL_SAVED6}" \) -a "${FIREHOL_MODE}" = "START" ]
then
echo
echo -n $"FireHOL: Restoring old firewall:"
local status4=0
local status6=0
if [ $ENABLE_IPV4 -eq 1 ]
then
${IPTABLES_RESTORE_CMD} <"${FIREHOL_SAVED}"
status4=$?
fi
if [ $ENABLE_IPV6 -eq 1 ]
then
${IP6TABLES_RESTORE_CMD} <"${FIREHOL_SAVED6}"
status6=$?
fi
if [ $status4 -eq 0 -a $status6 -eq 0 ]
then
local restored="OK"
success $"FireHOL: Restoring old firewall:"
else
local restored="FAILED"
failure $"FireHOL: Restoring old firewall:"
fi
echo
fi
# remove the temporary directory created for this session
if [ ${FIREHOL_ACTIVATED_SUCCESSFULLY} -eq 0 -a ${FIREHOL_CLEAN_TMP} -eq 0 ]
then
echo "FireHOL: temporary files left in ${FIREHOL_DIR}"
else
test -d "${FIREHOL_DIR}" && ${RM_CMD} -rf "${FIREHOL_DIR}"
fi
# syslog
local result=
local notify=0
case "${FIREHOL_MODE}" in
START) if [ ${FIREHOL_ACTIVATED_SUCCESSFULLY} -eq 0 ]
then
syslog emerg "FAILED to activate the firewall from ${FIREHOL_CONFIG}. Last good firewall restoration: ${restored}."
local result="FAILED"
else
syslog info "Successfully activated new firewall from ${FIREHOL_CONFIG}."
local result="OK"
fi
local notify=1
;;
STOP) syslog emerg "Firewall has been stopped. Policy is ACCEPT EVERYTHING!"
local notify=1
;;
PANIC) syslog emerg "PANIC! Machine has been locked. Policy is DROP EVERYTHING!"
local notify=1
;;
*) # do nothing for the rest
local notify=0
;;
esac
# do we have to run a program?
if [ ${notify} -eq 1 ]
then
if [ ! -z "${FIREHOL_NOTIFICATION_PROGRAM}" -a -x "${FIREHOL_NOTIFICATION_PROGRAM}" ]
then
# we just fork it, so that it will not depend on terminal conditions
"${FIREHOL_NOTIFICATION_PROGRAM}" "${FIREHOL_CONFIG}" "${result}" "${restored}" "${work_error}" "${work_runtime_error}" >/dev/null 2>&1 </dev/null &
fi
fi
2015-01-17 16:26:35 +00:00
enable trap
enable exit
2015-01-17 16:26:35 +00:00
trap exit EXIT
if [ ${FIREHOL_ACTIVATED_SUCCESSFULLY} -eq 0 ]
then
exit 1
fi
exit 0
}
# Run our exit even if we don't call exit.
trap firehol_exit EXIT
trap firehol_exit SIGHUP
trap firehol_exit INT
# ------------------------------------------------------------------------------
# Create the directories we need.
if [ ! -d "${FIREHOL_CONFIG_DIR}" ]
then
"${MKDIR_CMD}" "${FIREHOL_CONFIG_DIR}" || exit 1
"${CHOWN_CMD}" root:root "${FIREHOL_CONFIG_DIR}" || exit 1
"${CHMOD_CMD}" 700 "${FIREHOL_CONFIG_DIR}" || exit 1
if [ -f /etc/firehol.conf ]
then
"${MV_CMD}" /etc/firehol.conf "${FIREHOL_CONFIG}" || exit 1
echo >&2
echo >&2
echo >&2 "NOTICE: Your config file /etc/firehol.conf has been moved to ${FIREHOL_CONFIG}"
echo >&2
sleep 5
fi
fi
# Externally defined services can be placed in "${FIREHOL_SERVICES_DIR}"
if [ ! -d "${FIREHOL_SERVICES_DIR}" ]
then
"${MKDIR_CMD}" -p "${FIREHOL_SERVICES_DIR}"
if [ $? -ne 0 ]
then
echo >&2
echo >&2
echo >&2 "FireHOL needs to create the directory '${FIREHOL_SERVICES_DIR}', but it cannot."
echo >&2 "Possibly you have a file with this name, or something else is happening."
echo >&2 "Please solve this issue and retry".
echo >&2
exit 1
fi
"${CHOWN_CMD}" root:root "${FIREHOL_SERVICES_DIR}"
"${CHMOD_CMD}" 700 "${FIREHOL_SERVICES_DIR}"
fi
#"${MKDIR_CMD}" "${FIREHOL_CHAINS_DIR}" || exit 1
"${MKDIR_CMD}" "${FIREHOL_DIR}/fast" || exit 1
"${MKDIR_CMD}" "${FIREHOL_DIR}/fast/tables" || exit 1
"${MKDIR_CMD}" "${FIREHOL_DIR}/fast/table6s" || exit 1
# prepare the file that will hold all modules to be loaded.
# this is needed only when we are going to save the firewall
# with iptables-save.
cat >"${FIREHOL_DIR}/firewall_restore_commands.sh" <<EOFMTL
#!/bin/sh
# Generated by FireHOL to execute additional actions
# to restore the generated firewall.
#
EOFMTL
# Make sure we have a directory for our data.
if [ ! -d "${FIREHOL_SPOOL_DIR}" ]
then
"${MKDIR_CMD}" "${FIREHOL_SPOOL_DIR}" || exit 1
"${CHOWN_CMD}" root:root "${FIREHOL_SPOOL_DIR}" || exit 1
"${CHMOD_CMD}" 700 "${FIREHOL_SPOOL_DIR}" || exit 1
fi
load_ips() {
local v="${1}" # the variable
local f="${2}" # the old file-name
local d="${3}" # the default value
local dt="${4}" # days old
local m="${5}" # additional info for file generation
local c="${6}" # if set, complain if file is missing
# We load from a file with the variable name if found but will use
# the old file name for compatibility
if [ "${f}" != ${v} \
-a -f "${FIREHOL_CONFIG_DIR}/${f}" \
-a -f "${FIREHOL_CONFIG_DIR}/${v}" ]
then
echo >&2 "WARNING "
echo >&2 "Found ${f} and ${v} in '${FIREHOL_CONFIG_DIR}'"
echo >&2 "Using ${v}"
f=${v}
elif [ -f "${FIREHOL_CONFIG_DIR}/${v}" ]
then
f=${v}
else
: # Using the 'old' name
fi
if [ ! -f "${FIREHOL_CONFIG_DIR}/${f}" ]
then
if [ ! -z "${c}" ]
then
echo >&2
echo >&2
echo >&2 "WARNING "
echo >&2 "Cannot find file '${FIREHOL_CONFIG_DIR}/${v}'."
echo >&2 "Using internal default values for variable '${v}' and all inherited ones."
echo >&2
if [ ! -z "${m}" ]
then
echo >&2 "${m}"
echo >&2
fi
fi
eval "export ${v}=\"${d}\""
return 0
fi
if [ ${dt} -gt 0 ]
then
local t=`${FIND_CMD} "${FIREHOL_CONFIG_DIR}/${f}" -mtime +${dt}`
if [ ! -z "${t}" ]
then
echo >&2
echo >&2
echo >&2 "WARNING"
echo >&2 "File '${FIREHOL_CONFIG_DIR}/${f}' is more than ${dt} days old."
echo >&2 "You should update it to ensure proper operation of your firewall."
echo >&2
if [ ! -z "${m}" ]
then
echo >&2 "${m}"
echo >&2
fi
fi
fi
local t=`${CAT_CMD} "${FIREHOL_CONFIG_DIR}/${f}" | ${EGREP_CMD} "^ *[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/[0-9]+ *$"`
local t2=
local i=0
for x in ${t}
do
i=$[i + 1]
t2="${t2} ${x}"
done
local t6=`${CAT_CMD} "${FIREHOL_CONFIG_DIR}/${f}" | ${EGREP_CMD} "^ *((([0-9A-Fa-f]{1,4}:){7}(([0-9A-Fa-f]{1,4})|:))|(([0-9A-Fa-f]{1,4}:){6}(:|((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})|(:[0-9A-Fa-f]{1,4})))|(([0-9A-Fa-f]{1,4}:){5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4}){0,1}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){0,2}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){0,3}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(([0-9A-Fa-f]{1,4}:)(:[0-9A-Fa-f]{1,4}){0,4}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(:(:[0-9A-Fa-f]{1,4}){0,5}((:((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|((:[0-9A-Fa-f]{1,4}){1,2})))|(((25[0-5]|2[0-4]\d|[01]?\d{1,2})(\.(25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))(%.+)?/[0-9]+ *$"`
for x in ${t6}
do
i=$[i + 1]
t2="${t2} ${x}"
done
if [ ${i} -eq 0 -o -z "${t2}" ]
then
echo >&2
echo >&2
echo >&2 "WARNING "
echo >&2 "The file '${FIREHOL_CONFIG_DIR}/${f}' contains zero IP definitions."
echo >&2 "Using internal default values for variable '${v}' and all inherited ones."
echo >&2
if [ ! -z "${m}" ]
then
echo >&2 "${m}"
echo >&2
fi
eval "export ${v}=\"${d}\""
return 0
fi
eval "export ${v}=\"${t2}\""
return 0
}
# ------------------------------------------------------------------------------
# IP definitions
# IANA Reserved IPv4 address space.
load_ips RESERVED_IPV4 RESERVED_IPS "${RESERVED_IPV4}" 0
load_ips RESERVED_IPV6 RESERVED_IPV6 "${RESERVED_IPV6}" 0
# Make the original name a context-dependent function
RESERVED_IPS="reserved_ips()"
reserved_ips() {
if running_both; then
error "Cannot be called in 'both' mode"
return 1
fi
if running_ipv6; then
echo "${RESERVED_IPV6}"
else
echo "${RESERVED_IPV4}"
fi
return 0
}
# private IP address space
load_ips PRIVATE_IPV4 PRIVATE_IPS "${PRIVATE_IPV4}" 0
load_ips PRIVATE_IPV6 PRIVATE_IPV6 "${PRIVATE_IPV6}" 0
PRIVATE_IPS="private_ips()"
private_ips() {
if running_both; then
error "Cannot be called in 'both' mode"
return 1
fi
if running_ipv6; then
echo "${PRIVATE_IPV6}"
else
echo "${PRIVATE_IPV4}"
fi
return 0
}
# The multicast address space
load_ips MULTICAST_IPV4 MULTICAST_IPS "${MULTICAST_IPV4}" 0
load_ips MULTICAST_IPV6 MULTICAST_IPV6 "${MULTICAST_IPV6}" 0
MULTICAST_IPS="multicast_ips()"
multicast_ips() {
if running_both; then
error "Cannot be called in 'both' mode"
return 1
fi
if running_ipv6; then
echo "${MULTICAST_IPV6}"
else
echo "${MULTICAST_IPV4}"
fi
return 0
}
2002-09-05 20:57:59 +00:00
# A shortcut to have all the Internet unroutable addresses in one
# variable
UNROUTABLE_IPV4="${RESERVED_IPV4} ${PRIVATE_IPV4}"
load_ips UNROUTABLE_IPV4 UNROUTABLE_IPS "${UNROUTABLE_IPV4}" 0
UNROUTABLE_IPV6="${RESERVED_IPV6} ${PRIVATE_IPV6}"
load_ips UNROUTABLE_IPV6 UNROUTABLE_IPV6 "${UNROUTABLE_IPV6}" 0
UNROUTABLE_IPS="unroutable_ips()"
unroutable_ips() {
if running_both; then
error "Cannot be called in 'both' mode"
return 1
fi
if running_ipv6; then
echo "${UNROUTABLE_IPV6}"
else
echo "${UNROUTABLE_IPV4}"
fi
return 0
}
2002-09-05 20:57:59 +00:00
if [ $ENABLE_IPV4 -eq 1 -a $ENABLE_IPV6 -eq 1 ]
then
2015-01-10 19:50:20 +00:00
FIREHOL_DEFAULT_NAMESPACE=both
elif [ $ENABLE_IPV4 -eq 1 ]
then
2015-01-10 19:50:20 +00:00
FIREHOL_DEFAULT_NAMESPACE=ipv4
else
2015-01-10 19:50:20 +00:00
FIREHOL_DEFAULT_NAMESPACE=ipv6
fi
# Get the default client ports from the kernel configuration.
# This is formed to a range of ports to be used for all "default"
# client ports when the client specified is the localhost.
2013-11-16 15:33:24 +00:00
#
# According to http://tldp.org/HOWTO/Linux+IPv6-HOWTO/proc-sys-net-ipv4..html
# the ipv4 values are also used for ipv6, so no needed change here
LOCAL_CLIENT_PORTS_LOW=`${SYSCTL_CMD} net.ipv4.ip_local_port_range | ${CUT_CMD} -d '=' -f 2 | ${CUT_CMD} -f 1`
LOCAL_CLIENT_PORTS_HIGH=`${SYSCTL_CMD} net.ipv4.ip_local_port_range | ${CUT_CMD} -d '=' -f 2 | ${CUT_CMD} -f 2`
LOCAL_CLIENT_PORTS="${LOCAL_CLIENT_PORTS_LOW}:${LOCAL_CLIENT_PORTS_HIGH}"
# ----------------------------------------------------------------------
# This is our version number. It is increased when the configuration
# file commands and arguments change their meaning and usage, so that
# the user will have to review it more precisely.
FIREHOL_VERSION=6
# ----------------------------------------------------------------------
# The initial line number of the configuration file.
FIREHOL_LINEID="INIT"
# Variable kernel module requirements.
# Suggested by Fco.Felix Belmonte <ffelix@gescosoft.com>
# Note that each of the complex services
# may add to this variable the kernel modules it requires.
FIREHOL_KERNEL_MODULES=
#
# In the configuration file you can write:
#
# require_kernel_module <module_name>
#
# to have FireHOL require a specific module for the configurarion.
# Services may add themeselves to this variable so that the service "all" will
# also call them.
# By default it is empty - only rules programmers should change this.
ALL_SHOULD_ALSO_RUN=
2002-09-05 20:57:59 +00:00
2002-12-23 14:39:19 +00:00
# ------------------------------------------------------------------------------
# Various Defaults
# Valid modes:
# START, DEBUG, EXPLAIN, WIZARD, STOP, PANIC
FIREHOL_MODE="NONE"
2002-09-05 20:57:59 +00:00
# If set to 1, the firewall will be saved for normal iptables processing.
# Valid only for FIREHOL_MODE="START"
2002-09-05 20:57:59 +00:00
FIREHOL_SAVE=0
# If set to 1, the firewall will be restored if you don't commit it.
# Valid only for FIREHOL_MODE="START"
2009-10-01 10:25:23 +00:00
FIREHOL_TRY=0
2002-09-05 20:57:59 +00:00
# If set to 1, firehol will output the commands of the configuration file
# with variables expanded.
FIREHOL_CONF_SHOW=1
# ------------------------------------------------------------------------------
# Keep information about the current namespace: ipv4, ipv6 or both
FIREHOL_NS_STACK=
FIREHOL_NS_CURR=$FIREHOL_DEFAULT_NAMESPACE
FIREHOL_NS_PREP=
peek_namespace() {
local ns=$(echo $FIREHOL_NS_STACK | cut -f1 -d:)
if [ "$ns" = "" ]
then
echo $FIREHOL_DEFAULT_NAMESPACE
else
echo $ns
fi
}
push_namespace() {
if [ "$1" != "ipv4" -a "$1" != "ipv6" -a "$1" != "both" ]
then
error "Bad namespace: $1 (must be ipv4/ipv6/both)"
return 1
fi
if [ "${FIREHOL_NS_CURR}" != "both" -a "$1" != "${FIREHOL_NS_CURR}" ]
then
error "Cannot use namespace $1 within ${FIREHOL_NS_CURR}"
return 1
fi
FIREHOL_NS_STACK="$1:$FIREHOL_NS_STACK"
FIREHOL_NS_CURR=$(peek_namespace)
return 0
}
pop_namespace() {
FIREHOL_NS_STACK=$(echo $FIREHOL_NS_STACK | cut -f2- -d:)
FIREHOL_NS_CURR=$(peek_namespace)
return 0
}
running_ipv4() {
if [ "${FIREHOL_NS_CURR}" = "ipv4" -o "${FIREHOL_NS_CURR}" = "both" ]
then
return 0;
fi
return 1
}
running_ipv6() {
if [ "${FIREHOL_NS_CURR}" = "ipv6" -o "${FIREHOL_NS_CURR}" = "both" ]
then
return 0;
fi
return 1
}
running_both() {
if [ "${FIREHOL_NS_CURR}" = "both" ]
then
return 0;
fi
return 1
}
ipv4() {
local command="$1"
shift
if [ "${command}" = "interface" -o "${command}" = "router" ]
then
# Old rules must be closed before these apply
FIREHOL_NS_PREP=ipv4
$command "$@"
status=$?
else
if ! push_namespace ipv4; then return 1; fi
$command "$@"
status=$?
pop_namespace
fi
return $status
}
ipv6() {
local command="$1"
shift
if [ "${command}" = "interface" -o "${command}" = "router" ]
then
# Old rules must be closed before these apply
FIREHOL_NS_PREP=ipv6
$command "$@"
status=$?
else
if ! push_namespace ipv6; then return 1; fi
$command "$@"
status=$?
pop_namespace
fi
return $status
}
both() {
local command="$1"
shift
if [ "${command}" = "interface" -o "${command}" = "router" ]
then
# Old rules must be closed before these apply
FIREHOL_NS_PREP=$FIREHOL_DEFAULT_NAMESPACE
$command "$@"
status=$?
else
if ! push_namespace both; then return 1; fi
$command "$@"
status=$?
pop_namespace
fi
return $status
}
2002-09-05 20:57:59 +00:00
expression_list() {
# Clean up a complex expression expressions, such that e.g.
# fun0 ( x ) word word2 fun1(a b) ...
# Becomes:
# fun0(x)
# word
# word2
# fun(a,b)
# ....
# 1st sed: remove excess space, insert newlines at end of functions
# 2nd sed: insert newlines before functions that are preceded by words
# 3nd sed: function? no: split words to lines. yes: comma parameters.
echo "$@" | tr '\t' ' ' | \
sed -e 's/ */ /g' \
-e 's/ *\([()]\) */\1/g' \
-e 's/)/)\
/g' | \
sed -e 's/ \([^ ][^ ]*\)(/\
\1(/' | \
sed -e '/[()]/bfun' -e 's/ /\n/g' -e ':fun' -e 's/ /,/g'
}
eval_param() {
expression_list "$@" | \
while read exp
do
case "$exp" in
*"("*)
$(echo "$exp" | tr '(),' ' ')
;;
*)
echo "$exp"
;;
esac
done
}
# ------------------------------------------------------------------------------
# Keep information about the current primary command
# Primary commands are: interface, router
work_counter4=0
work_counter6=0
work_cmd=
work_realcmd=("(unset)")
work_name=
work_inface=
work_outface=
work_policy=
work_error=0
work_function="Initializing"
2013-11-03 21:08:26 +00:00
get_next_work_counter() {
local var="$1"
if running_both
then
if [ $work_counter4 -gt $work_counter6 ]
then
work_counter4=$[work_counter4 + 1]
work_counter6=$[work_counter4]
else
work_counter6=$[work_counter6 + 1]
work_counter4=$[work_counter6]
fi
2013-11-03 21:08:26 +00:00
eval ${var}=${work_counter4}
elif running_ipv6
then
work_counter6=$[work_counter6 + 1]
2013-11-03 21:08:26 +00:00
eval ${var}=${work_counter6}
else
work_counter4=$[work_counter4 + 1]
2013-11-03 21:08:26 +00:00
eval ${var}=${work_counter4}
fi
}
2002-09-05 20:57:59 +00:00
# ------------------------------------------------------------------------------
# Keep status information
# 0 = no errors, >0 = there were errors in the script
work_runtime_error=0
2002-09-05 20:57:59 +00:00
2013-11-03 21:08:26 +00:00
# This function is used for generating dynamic chains when needed for
2002-12-23 14:39:19 +00:00
# combined negative statements (AND) implied by the "not" parameter
# to many FireHOL directives.
# What FireHOL is doing to accomplish this, is to produce dynamically
# a linked list of iptables chains with just one condition each, making
# the packets to traverse from chain to chain when matched, to reach
# their final destination.
2013-11-03 21:08:26 +00:00
dynamic_counter4=0
dynamic_counter6=0
get_next_dynamic_counter() {
local var="$1"
if running_both
then
if [ $dynamic_counter4 -gt $dynamic_counter6 ]
then
dynamic_counter4=$[dynamic_counter4 + 1]
dynamic_counter6=$[dynamic_counter4]
else
dynamic_counter6=$[dynamic_counter6 + 1]
dynamic_counter4=$[dynamic_counter6]
fi
eval ${var}=${dynamic_counter4}
elif running_ipv6
then
dynamic_counter6=$[dynamic_counter6 + 1]
eval ${var}=${dynamic_counter6}
else
dynamic_counter4=$[dynamic_counter4 + 1]
eval ${var}=${dynamic_counter4}
fi
}
2002-12-23 14:39:19 +00:00
# Services API version
FIREHOL_SERVICES_API="1"
2002-09-05 20:57:59 +00:00
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# SIMPLE SERVICES DEFINITIONS
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
# The following are definitions for simple services.
# We define as "simple" the services that are implemented using a single socket,
# initiated by the client and used by the server.
#
# The following list is sorted by service name.
server_AH_ports="51/any"
client_AH_ports="any"
server_amanda_ports="udp/10080"
client_amanda_ports="default"
helper_amanda="amanda"
server_aptproxy_ports="tcp/9999"
client_aptproxy_ports="default"
server_apcupsd_ports="tcp/6544"
client_apcupsd_ports="default"
2002-12-22 14:02:54 +00:00
server_apcupsdnis_ports="tcp/3551"
client_apcupsdnis_ports="default"
server_asterisk_ports="tcp/5038"
client_asterisk_ports="default"
server_cups_ports="tcp/631 udp/631"
client_cups_ports="any"
server_cvspserver_ports="tcp/2401"
client_cvspserver_ports="default"
server_darkstat_ports="tcp/666"
client_darkstat_ports="default"
server_daytime_ports="tcp/13"
client_daytime_ports="default"
server_dcc_ports="udp/6277"
client_dcc_ports="default"
2003-06-10 21:27:46 +00:00
server_dcpp_ports="tcp/1412 udp/1412"
client_dcpp_ports="default"
server_dns_ports="udp/53 tcp/53"
client_dns_ports="any"
server_dhcprelay_ports="udp/67"
client_dhcprelay_ports="67"
server_dict_ports="tcp/2628"
client_dict_ports="default"
server_distcc_ports="tcp/3632"
client_distcc_ports="default"
server_eserver_ports="tcp/4661 udp/4661 udp/4665"
client_eserver_ports="any"
server_ESP_ports="50/any"
client_ESP_ports="any"
server_echo_ports="tcp/7"
client_echo_ports="default"
server_finger_ports="tcp/79"
client_finger_ports="default"
server_ftp_ports="tcp/21"
client_ftp_ports="default"
helper_ftp="ftp"
ALL_SHOULD_ALSO_RUN="${ALL_SHOULD_ALSO_RUN} ftp"
server_gift_ports="tcp/4302 tcp/1214 tcp/2182 tcp/2472"
client_gift_ports="any"
server_giftui_ports="tcp/1213"
client_giftui_ports="default"
2003-10-13 18:50:30 +00:00
server_gkrellmd_ports="tcp/19150"
client_gkrellmd_ports="default"
server_GRE_ports="47/any"
client_GRE_ports="any"
helper_GRE="proto_gre"
server_h323_ports="tcp/1720"
client_h323_ports="default"
helper_h323="h323"
server_heartbeat_ports="udp/690:699"
client_heartbeat_ports="default"
server_http_ports="tcp/80"
client_http_ports="default"
server_https_ports="tcp/443"
client_https_ports="default"
2002-09-05 20:57:59 +00:00
server_httpalt_ports="tcp/8080"
client_httpalt_ports="default"
server_iax_ports="udp/5036"
client_iax_ports="default"
server_iax2_ports="udp/5469 udp/4569"
client_iax2_ports="default"
server_ICMP_ports="icmp/any"
client_ICMP_ports="any"
2013-11-16 15:49:16 +00:00
server_icmp_ports="${server_ICMP_ports}"
client_icmp_ports="${client_ICMP_ports}"
# ALL_SHOULD_ALSO_RUN="${ALL_SHOULD_ALSO_RUN} icmp"
2013-11-16 15:49:16 +00:00
server_ICMPV6_ports="icmpv6/any"
client_ICMPV6_ports="any"
server_icmpv6_ports="${server_ICMPV6_ports}"
client_icmpv6_ports="${client_ICMPV6_ports}"
server_icp_ports="udp/3130"
client_icp_ports="3130"
server_ident_ports="tcp/113"
2002-09-05 20:57:59 +00:00
client_ident_ports="default"
server_imap_ports="tcp/143"
2002-09-05 20:57:59 +00:00
client_imap_ports="default"
server_imaps_ports="tcp/993"
client_imaps_ports="default"
2002-09-05 20:57:59 +00:00
server_irc_ports="tcp/6667"
client_irc_ports="default"
helper_irc="irc"
ALL_SHOULD_ALSO_RUN="${ALL_SHOULD_ALSO_RUN} irc"
2002-09-05 20:57:59 +00:00
server_isakmp_ports="udp/500"
client_isakmp_ports="any"
2010-04-08 22:12:35 +00:00
server_ipsecnatt_ports="udp/4500"
client_ipsecnatt_ports="any"
2003-06-30 22:07:01 +00:00
server_jabber_ports="tcp/5222 tcp/5223"
client_jabber_ports="default"
server_jabberd_ports="tcp/5222 tcp/5223 tcp/5269"
client_jabberd_ports="default"
2010-04-08 22:12:35 +00:00
server_l2tp_ports="udp/1701"
client_l2tp_ports="any"
server_ldap_ports="tcp/389"
2002-09-05 20:57:59 +00:00
client_ldap_ports="default"
server_ldaps_ports="tcp/636"
2002-12-05 09:23:36 +00:00
client_ldaps_ports="default"
server_lpd_ports="tcp/515"
client_lpd_ports="any"
2002-09-05 20:57:59 +00:00
server_microsoft_ds_ports="tcp/445"
2003-01-01 04:32:48 +00:00
client_microsoft_ds_ports="default"
server_mms_ports="tcp/1755 udp/1755"
client_mms_ports="default"
helper_mms="mms"
# this will produce warnings on most distribution
# because the mms module is not there:
# ALL_SHOULD_ALSO_RUN="${ALL_SHOULD_ALSO_RUN} mms"
server_ms_ds_ports="${server_microsoft_ds_ports}"
client_ms_ds_ports="${client_microsoft_ds_ports}"
server_msnp_ports="tcp/6891"
client_msnp_ports="default"
server_msn_ports="tcp/1863 udp/1863"
2003-06-10 21:27:46 +00:00
client_msn_ports="default"
server_mysql_ports="tcp/3306"
2002-09-05 20:57:59 +00:00
client_mysql_ports="default"
2003-10-09 10:01:26 +00:00
server_netbackup_ports="tcp/13701 tcp/13711 tcp/13720 tcp/13721 tcp/13724 tcp/13782 tcp/13783"
client_netbackup_ports="any"
server_netbios_ns_ports="udp/137"
client_netbios_ns_ports="any"
server_netbios_dgm_ports="udp/138"
client_netbios_dgm_ports="any"
server_netbios_ssn_ports="tcp/139"
client_netbios_ssn_ports="default"
server_nntp_ports="tcp/119"
client_nntp_ports="default"
server_nntps_ports="tcp/563"
client_nntps_ports="default"
server_ntp_ports="udp/123 tcp/123"
client_ntp_ports="any"
2004-10-28 22:02:43 +00:00
server_nut_ports="tcp/3493 udp/3493"
client_nut_ports="default"
2004-09-12 06:57:47 +00:00
server_nxserver_ports="tcp/5000:5200"
client_nxserver_ports="default"
server_openvpn_ports="tcp/1194 udp/1194"
client_openvpn_ports="default"
2003-10-13 18:50:30 +00:00
server_oracle_ports="tcp/1521"
client_oracle_ports="default"
server_OSPF_ports="89/any"
client_OSPF_ports="any"
server_pop3_ports="tcp/110"
client_pop3_ports="default"
server_pop3s_ports="tcp/995"
2002-12-05 09:23:36 +00:00
client_pop3s_ports="default"
# Portmap clients appear to use ports bellow 1024
server_portmap_ports="udp/111 tcp/111"
client_portmap_ports="any"
2002-09-05 20:57:59 +00:00
2003-07-20 21:52:41 +00:00
server_postgres_ports="tcp/5432"
client_postgres_ports="default"
server_pptp_ports="tcp/1723"
client_pptp_ports="default"
helper_pptp="pptp proto_gre"
# ALL_SHOULD_ALSO_RUN="${ALL_SHOULD_ALSO_RUN} pptp"
2002-12-05 09:23:36 +00:00
server_privoxy_ports="tcp/8118"
client_privoxy_ports="default"
server_radius_ports="udp/1812 udp/1813"
2002-09-05 20:57:59 +00:00
client_radius_ports="default"
server_radiusproxy_ports="udp/1814"
client_radiusproxy_ports="default"
2002-09-05 20:57:59 +00:00
server_radiusold_ports="udp/1645 udp/1646"
client_radiusold_ports="default"
server_radiusoldproxy_ports="udp/1647"
client_radiusoldproxy_ports="default"
2004-09-12 07:24:58 +00:00
server_rdp_ports="tcp/3389"
client_rdp_ports="default"
server_rndc_ports="tcp/953"
client_rndc_ports="default"
2002-09-05 20:57:59 +00:00
server_rsync_ports="tcp/873 udp/873"
client_rsync_ports="default"
2002-09-05 20:57:59 +00:00
server_rtp_ports="udp/10000:20000"
client_rtp_ports="any"
2010-04-08 22:16:03 +00:00
server_sane_ports="tcp/6566"
client_sane_ports="default"
helper_sane="sane"
server_sip_ports="udp/5060"
client_sip_ports="5060 default"
helper_sip="sip"
server_socks_ports="tcp/1080 udp/1080"
2003-03-15 01:24:19 +00:00
client_socks_ports="default"
server_squid_ports="tcp/3128"
2002-12-05 09:23:36 +00:00
client_squid_ports="default"
server_smtp_ports="tcp/25"
client_smtp_ports="default"
server_smtps_ports="tcp/465"
2002-12-05 09:23:36 +00:00
client_smtps_ports="default"
server_snmp_ports="udp/161"
client_snmp_ports="default"
server_snmptrap_ports="udp/162"
client_snmptrap_ports="any"
2002-12-05 09:23:36 +00:00
server_nrpe_ports="tcp/5666"
client_nrpe_ports="default"
server_ssh_ports="tcp/22"
client_ssh_ports="default"
server_stun_ports="udp/3478 udp/3479"
client_stun_ports="any"
server_submission_ports="tcp/587"
client_submission_ports="default"
server_sunrpc_ports="${server_portmap_ports}"
client_sunrpc_ports="${client_portmap_ports}"
2002-09-05 20:57:59 +00:00
server_swat_ports="tcp/901"
2002-12-05 09:23:36 +00:00
client_swat_ports="default"
server_syslog_ports="udp/514"
client_syslog_ports="syslog default"
2002-09-05 20:57:59 +00:00
server_telnet_ports="tcp/23"
client_telnet_ports="default"
2002-09-05 20:57:59 +00:00
server_tftp_ports="udp/69"
client_tftp_ports="default"
helper_tftp="tftp"
2013-03-10 11:19:47 +00:00
server_tomcat_ports="${server_httpalt_ports}"
client_tomcat_ports="${client_httpalt_ports}"
server_time_ports="tcp/37 udp/37"
2003-07-20 21:50:29 +00:00
client_time_ports="default"
server_upnp_ports="udp/1900 tcp/2869"
client_upnp_ports="default"
server_uucp_ports="tcp/540"
client_uucp_ports="default"
2004-04-21 22:23:10 +00:00
server_whois_ports="tcp/43"
client_whois_ports="default"
server_vmware_ports="tcp/902"
client_vmware_ports="default"
server_vmwareauth_ports="tcp/903"
client_vmwareauth_ports="default"
2005-11-19 09:38:25 +00:00
server_vmwareweb_ports="tcp/8222 tcp/8333"
client_vmwareweb_ports="default"
server_vnc_ports="tcp/5900:5903"
client_vnc_ports="default"
server_webcache_ports="${server_httpalt_ports}"
client_webcache_ports="${client_httpalt_ports}"
2002-09-05 20:57:59 +00:00
2003-06-30 22:18:46 +00:00
server_webmin_ports="tcp/10000"
client_webmin_ports="default"
2003-08-31 22:21:49 +00:00
server_xdmcp_ports="udp/177"
client_xdmcp_ports="default"
2002-09-05 20:57:59 +00:00
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# COMPLEX SERVICES DEFINITIONS
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
# The following are definitions for complex services.
# We define as "complex" the services that are implemented using multiple sockets.
# Each function bellow is organized in three parts:
# 1) A Header, common to each and every function
# 2) The rules required for the INPUT of the server
# 3) The rules required for the OUTPUT of the server
#
# The Header part, together with the "reverse" keyword can reverse the rules so
# that if we are implementing a client the INPUT will become OUTPUT and vice versa.
#
# In most the cases the input and output rules are the same with the following
# differences:
#
# a) The output rules begin with the "reverse" keyword, which reverses:
# inface/outface, src/dst, sport/dport
# b) The output rules use ${out}_${mychain} instead of ${in}_${mychain}
# c) The state rules match the client operation, not the server.
# --- ICMP (v4/v6) helper functions --------------------------------------------
add_icmp_rule_pair() {
local mychain="${1}"; shift
local type="${1}"; shift
local request="${1}"; shift
local response="${1}"; shift
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
# allow incoming new and established packets
rule ${in} action "$@" chain "${in}_${mychain}" proto icmp custom "--icmp-type $request" state NEW,ESTABLISHED || return 1
# allow outgoing established packets
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto icmp custom "--icmp-type $response" state ESTABLISHED || return 1
return 0
}
add_icmpv6_rule_pair() {
local mychain="${1}"; shift
local type="${1}"; shift
local request="${1}"; shift
local response="${1}"; shift
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
# allow incoming new and established packets
rule ${in} action "$@" chain "${in}_${mychain}" proto icmpv6 custom "--icmpv6-type $request" state NEW,ESTABLISHED || return 1
# allow outgoing established packets
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto icmpv6 custom "--icmpv6-type $response" state ESTABLISHED || return 1
return 0
}
add_icmpv6_rule_pair_stateless() {
local mychain="${1}"; shift
local type="${1}"; shift
local icmpv6in="${1}"; shift
local icmpv6out="${1}"; shift
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
rule ${in} action "$@" chain "${in}_${mychain}" proto icmpv6 custom "--icmpv6-type $icmpv6in" || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto icmpv6 custom "--icmpv6-type $icmpv6out" || return 1
return 0
}
add_icmpv6_rule_error() {
# Unlike stateful and stateless icmpv6 packets, for a server
# or client we do the same thing:
# ingress error packets allowed if we think we have a connection
# egress error packets allowed whatever
# Possibly we could restrict the whatever to be related also?
local mychain="${1}"; shift
local type="${1}"; shift
local icmpv6error="${1}"; shift
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
rule ${in} reverse action "$@" chain "${in}_${mychain}" proto icmpv6 custom "--icmpv6-type $icmpv6error" state ESTABLISHED,RELATED || return 1
rule ${out} action "$@" chain "${out}_${mychain}" proto icmpv6 custom "--icmpv6-type $icmpv6error" || return 1
return 0
}
# --- XBOX ---------------------------------------------------------------------
# Contributed by andrex@alumni.utexas.net
# Following is the (complex) service definition function for xbox, the Xbox live
# service. With this definition our Xbox connects and plays from behind a NAT
# firewall with no trouble. Andrew.
rules_xbox() {
local mychain="${1}"; shift
local type="${1}"; shift
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
local client_ports="${DEFAULT_CLIENT_PORTS}"
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
then
client_ports="${LOCAL_CLIENT_PORTS}"
fi
# ----------------------------------------------------------------------
set_work_function "Setting up rules for Xbox live"
rule ${in} action "$@" chain "${in}_${mychain}" proto udp dport "88 3074" sport "${client_ports}" state NEW,ESTABLISHED || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto udp dport "88 3074" sport "${client_ports}" state ESTABLISHED || return 1
rule ${in} action "$@" chain "${in}_${mychain}" proto tcp dport 3074 sport "${client_ports}" state NEW,ESTABLISHED || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto tcp dport 3074 sport "${client_ports}" state ESTABLISHED || return 1
rule ${in} action "$@" chain "${in}_${mychain}" proto udp sport 3074 dport "${client_ports}" state NEW,ESTABLISHED || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto udp sport 3074 dport "${client_ports}" state ESTABLISHED || return 1
return 0
}
2002-09-05 20:57:59 +00:00
# --- DHCP --------------------------------------------------------------------
rules_dhcp() {
local mychain="${1}"; shift
local type="${1}"; shift
2013-12-01 17:56:34 +00:00
if ! push_namespace ipv4; then return 1; fi
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
local client_ports="${DEFAULT_CLIENT_PORTS}"
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
then
client_ports="${LOCAL_CLIENT_PORTS}"
fi
# ----------------------------------------------------------------------
set_work_function "Setting up rules for DHCP (${type})"
rule ${in} action "$@" chain "${in}_${mychain}" proto "udp" sport "68" dport "67" || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "udp" sport "68" dport "67" || return 1
2013-12-01 17:56:34 +00:00
pop_namespace
return 0
}
rules_dhcpv6() {
local mychain="${1}"; shift
local type="${1}"; shift
if ! push_namespace ipv6; then return 1; fi
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
local client_ports="${DEFAULT_CLIENT_PORTS}"
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
then
client_ports="${LOCAL_CLIENT_PORTS}"
fi
# ----------------------------------------------------------------------
set_work_function "Setting up rules for DHCPv6 (${type})"
rule ${in} action "$@" chain "${in}_${mychain}" proto "udp" dport "547" || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "udp" sport "546" || return 1
2013-12-01 17:56:34 +00:00
pop_namespace
return 0
}
2003-02-03 23:11:49 +00:00
# --- EMULE --------------------------------------------------------------------
rules_emule() {
local mychain="${1}"; shift
local type="${1}"; shift
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
local client_ports="${DEFAULT_CLIENT_PORTS}"
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
then
client_ports="${LOCAL_CLIENT_PORTS}"
fi
# ----------------------------------------------------------------------
# allow incomming to server tcp/4662
set_work_function "Setting up rules for EMULE/client-to-server tcp/4662 (${type})"
rule ${in} action "$@" chain "${in}_${mychain}" proto "tcp" sport any dport 4662 state NEW,ESTABLISHED || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "tcp" sport any dport 4662 state ESTABLISHED || return 1
2003-02-03 23:11:49 +00:00
# allow outgoing to client tcp/4662
set_work_function "Setting up rules for EMULE/server-to-client tcp/4662 (${type})"
2003-02-03 23:11:49 +00:00
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "tcp" dport any sport 4662 state NEW,ESTABLISHED || return 1
rule ${in} action "$@" chain "${in}_${mychain}" proto "tcp" dport any sport 4662 state ESTABLISHED || return 1
2003-02-03 23:11:49 +00:00
# allow incomming to server udp/4672
set_work_function "Setting up rules for EMULE/client-to-server udp/4672 (${type})"
rule ${in} action "$@" chain "${in}_${mychain}" proto "udp" sport any dport 4672 state NEW,ESTABLISHED || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "udp" sport any dport 4672 state ESTABLISHED || return 1
2003-02-03 23:11:49 +00:00
# allow outgoing to client udp/4672
set_work_function "Setting up rules for EMULE/server-to-client udp/4672 (${type})"
2003-02-03 23:11:49 +00:00
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "udp" dport any sport 4672 state NEW,ESTABLISHED || return 1
rule ${in} action "$@" chain "${in}_${mychain}" proto "udp" dport any sport 4672 state ESTABLISHED || return 1
2003-02-03 23:11:49 +00:00
# allow incomming to server tcp/4661
set_work_function "Setting up rules for EMULE/client-to-server tcp/4661 (${type})"
rule ${in} action "$@" chain "${in}_${mychain}" proto "tcp" sport any dport 4661 state NEW,ESTABLISHED || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "tcp" sport any dport 4661 state ESTABLISHED || return 1
2003-02-03 23:11:49 +00:00
# allow incomming to server udp/4665
set_work_function "Setting up rules for EMULE/client-to-server udp/4665 (${type})"
rule ${in} action "$@" chain "${in}_${mychain}" proto "udp" sport any dport 4665 state NEW,ESTABLISHED || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "udp" sport any dport 4665 state ESTABLISHED || return 1
2003-02-03 23:11:49 +00:00
return 0
}
# --- HYLAFAX ------------------------------------------------------------------
# Written by: Franscisco Javier Felix <ffelix@gescosoft.com>
rules_hylafax() {
local mychain="${1}"; shift
local type="${1}"; shift
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
local client_ports="${DEFAULT_CLIENT_PORTS}"
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
then
client_ports="${LOCAL_CLIENT_PORTS}"
fi
# ----------------------------------------------------------------------
# allow incomming to server tcp/4559
set_work_function "Setting up rules for HYLAFAX/client-to-server tcp/4559 (${type})"
rule ${in} action "$@" chain "${in}_${mychain}" proto "tcp" sport any dport 4559 state NEW,ESTABLISHED || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "tcp" sport any dport 4559 state ESTABLISHED || return 1
# allow outgoing to client from server tcp/4558
set_work_function "Setting up rules for HYLAFAX/server-to-client from server tcp/4558 (${type})"
rule ${out} action "$@" chain "${out}_${mychain}" proto "tcp" sport 4558 dport any state NEW,ESTABLISHED || return 1
rule ${in} reverse action "$@" chain "${in}_${mychain}" proto "tcp" sport 4558 dport any state ESTABLISHED || return 1
return 0
}
2002-09-05 20:57:59 +00:00
# --- SAMBA --------------------------------------------------------------------
rules_samba() {
local mychain="${1}"; shift
2002-09-05 20:57:59 +00:00
local type="${1}"; shift
local in=in
local out=out
if [ "${type}" = "client" ]
2002-09-05 20:57:59 +00:00
then
in=out
out=in
fi
local client_ports="${DEFAULT_CLIENT_PORTS}"
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
2002-09-05 20:57:59 +00:00
then
client_ports="${LOCAL_CLIENT_PORTS}"
fi
# ----------------------------------------------------------------------
set_work_function "Setting up rules for SAMBA/NETBIOS-NS (${type})"
rule ${in} action "$@" chain "${in}_${mychain}" proto "udp" sport "137 ${client_ports}" dport 137 state NEW,ESTABLISHED || return 1
# NETBIOS initiates based on the broadcast address of an interface
# (request goes to broadcast address) but the server responds from
# its own IP address. This makes the server samba accept statement
# drop the server reply.
# Bellow is a hack, that allows a linux samba server to respond
# correctly, as it allows new outgoing connections from the well
# known netbios-ns port to the clients high ports.
# For clients and routers this hack is not applied because it
# would be a huge security hole.
if [ "${type}" = "server" -a "${work_cmd}" = "interface" ]
then
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "udp" sport "137 ${client_ports}" dport 137 state NEW,ESTABLISHED || return 1
else
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "udp" sport "137 ${client_ports}" dport 137 state ESTABLISHED || return 1
fi
2002-09-05 20:57:59 +00:00
set_work_function "Setting up rules for SAMBA/NETBIOS-DGM (${type})"
rule ${in} action "$@" chain "${in}_${mychain}" proto "udp" sport "138 ${client_ports}" dport 138 state NEW,ESTABLISHED || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "udp" sport "138 ${client_ports}" dport 138 state ESTABLISHED || return 1
set_work_function "Setting up rules for SAMBA/NETBIOS-SSN (${type})"
rule ${in} action "$@" chain "${in}_${mychain}" proto "tcp" sport "${client_ports}" dport 139 state NEW,ESTABLISHED || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "tcp" sport "${client_ports}" dport 139 state ESTABLISHED || return 1
set_work_function "Setting up rules for SAMBA/MICROSOFT_DS (${type})"
rule ${in} action "$@" chain "${in}_${mychain}" proto "tcp" sport "${client_ports}" dport 445 state NEW,ESTABLISHED || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "tcp" sport "${client_ports}" dport 445 state ESTABLISHED || return 1
2002-09-05 20:57:59 +00:00
return 0
}
# --- PPTP --------------------------------------------------------------------
# THIS IS OLD
# THE NEW ONE USES THE KERNEL HELPER TO DO IT, AND THEREFORE IS A SIMPLE SERVICE
#
#rules_pptp() {
# local mychain="${1}"; shift
# local type="${1}"; shift
#
# local in=in
# local out=out
# if [ "${type}" = "client" ]
# then
# in=out
# out=in
# fi
#
# local client_ports="${DEFAULT_CLIENT_PORTS}"
# if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
# then
# client_ports="${LOCAL_CLIENT_PORTS}"
# fi
#
# # ----------------------------------------------------------------------
#
# set_work_function "Setting up rules for PPTP/initial connection (${type})"
# rule ${in} action "$@" chain "${in}_${mychain}" proto "tcp" sport "${client_ports}" dport "1723" state NEW,ESTABLISHED || return 1
# rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "tcp" sport "${client_ports}" dport "1723" state ESTABLISHED || return 1
#
# set_work_function "Setting up rules for PPTP/tunnel GRE traffic (${type})"
# rule ${in} action "$@" chain "${in}_${mychain}" proto "47" || return 1
# rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "47" || return 1
#
# return 0
#}
2002-09-05 20:57:59 +00:00
# --- NFS ----------------------------------------------------------------------
rules_nfs() {
local mychain="${1}"; shift
2002-09-05 20:57:59 +00:00
local type="${1}"; shift
local in=in
local out=out
if [ "${type}" = "client" ]
2002-09-05 20:57:59 +00:00
then
in=out
out=in
fi
local client_ports="${DEFAULT_CLIENT_PORTS}"
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
2002-09-05 20:57:59 +00:00
then
client_ports="${LOCAL_CLIENT_PORTS}"
fi
# ----------------------------------------------------------------------
# This command requires in the client or route subcommands,
# the first argument after the policy/action is a dst.
local action="${1}"; shift
local servers="localhost"
2002-12-10 18:10:38 +00:00
if [ "${type}" = "client" -o ! "${work_cmd}" = "interface" ]
2002-09-05 20:57:59 +00:00
then
case "${1}" in
dst|DST|destination|DESTINATION)
shift
local servers="${1}"
2002-09-05 20:57:59 +00:00
shift
;;
*)
error "Please re-phrase to: ${type} nfs ${action} dst <NFS_SERVER> [other rules]"
return 1
;;
esac
fi
local x=
for x in ${servers}
do
local tmp="`${MKTEMP_CMD} ${FIREHOL_DIR}/firehol-rpcinfo-XXXXXXXXXX`"
2002-09-05 20:57:59 +00:00
set_work_function "Getting RPC information from server '${x}'"
2002-09-05 20:57:59 +00:00
rpcinfo -p ${x} >"${tmp}"
if [ $? -gt 0 -o ! -s "${tmp}" ]
then
error "Cannot get rpcinfo from host '${x}' (using the previous firewall rules)"
${RM_CMD} -f "${tmp}"
2002-09-05 20:57:59 +00:00
return 1
fi
2004-09-10 21:36:26 +00:00
local server_rquotad_ports="`${CAT_CMD} "${tmp}" | ${GREP_CMD} " rquotad$" | ( while read a b proto port s; do echo "$proto/$port"; done ) | ${SORT_CMD} | ${UNIQ_CMD}`"
local server_mountd_ports="`${CAT_CMD} "${tmp}" | ${GREP_CMD} " mountd$" | ( while read a b proto port s; do echo "$proto/$port"; done ) | ${SORT_CMD} | ${UNIQ_CMD}`"
local server_lockd_ports="`${CAT_CMD} "${tmp}" | ${GREP_CMD} " nlockmgr$" | ( while read a b proto port s; do echo "$proto/$port"; done ) | ${SORT_CMD} | ${UNIQ_CMD}`"
local server_statd_ports="`${CAT_CMD} "${tmp}" | ${GREP_CMD} " status$" | ( while read a b proto port s; do echo "$proto/$port"; done ) | ${SORT_CMD} | ${UNIQ_CMD}`"
local server_nfsd_ports="`${CAT_CMD} "${tmp}" | ${GREP_CMD} " nfs$" | ( while read a b proto port s; do echo "$proto/$port"; done ) | ${SORT_CMD} | ${UNIQ_CMD}`"
2002-09-05 20:57:59 +00:00
test -z "${server_mountd_ports}" && error "Cannot find mountd ports for nfs server '${x}'" && return 1
test -z "${server_lockd_ports}" && error "Cannot find lockd ports for nfs server '${x}'" && return 1
test -z "${server_statd_ports}" && error "Cannot find statd ports for nfs server '${x}'" && return 1
test -z "${server_nfsd_ports}" && error "Cannot find nfsd ports for nfs server '${x}'" && return 1
2002-09-05 20:57:59 +00:00
local dst=
if [ ! "${x}" = "localhost" ]
then
dst="dst ${x}"
fi
2004-09-10 21:36:26 +00:00
if [ ! -z "${server_rquotad_ports}" ]
then
set_work_function "Processing rquotad rules for server '${x}'"
rules_custom "${mychain}" "${type}" nfs-rquotad "${server_rquotad_ports}" "500:65535" "${action}" $dst "$@"
fi
set_work_function "Processing mountd rules for server '${x}'"
rules_custom "${mychain}" "${type}" nfs-mountd "${server_mountd_ports}" "500:65535" "${action}" $dst "$@"
set_work_function "Processing lockd rules for server '${x}'"
rules_custom "${mychain}" "${type}" nfs-lockd "${server_lockd_ports}" "500:65535" "${action}" $dst "$@"
set_work_function "Processing statd rules for server '${x}'"
rules_custom "${mychain}" "${type}" nfs-statd "${server_statd_ports}" "500:65535" "${action}" $dst "$@"
set_work_function "Processing nfsd rules for server '${x}'"
rules_custom "${mychain}" "${type}" nfs-nfsd "${server_nfsd_ports}" "500:65535" "${action}" $dst "$@"
${RM_CMD} -f "${tmp}"
2002-09-05 20:57:59 +00:00
echo >&2 ""
echo >&2 "WARNING:"
echo >&2 "This firewall must be restarted if NFS server ${x} is restarted!"
2002-09-05 20:57:59 +00:00
echo >&2 ""
done
return 0
}
# --- NIS ----------------------------------------------------------------------
# These rules work for client access only!
#
# Pushing changes to slave servers won't work if these rules are active
# somewhere between the master and its slaves, because it is impossible to
# predict the ports where "yppush" will be listening on each push.
#
# Pulling changes directly on the slaves will work, and could be improved
# performance-wise if these rules are modified to open "fypxfrd". This wasn't
# done because it doesn't make that much sense since pushing changes on the
# master server is the most common, and recommended, way to replicate maps.
#
# Created by Carlos Rodrigues <crlf@users.sourceforge.net>
# Feature Requests item #1050951 <https://sourceforge.net/tracker/?func=detail&atid=487695&aid=1050951&group_id=58425>
rules_nis() {
local mychain="${1}"; shift
local type="${1}"; shift
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
local client_ports="${DEFAULT_CLIENT_PORTS}"
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
then
client_ports="${LOCAL_CLIENT_PORTS}"
fi
# ----------------------------------------------------------------------
# This command requires in the client or route subcommands,
# the first argument after the policy/action is a dst.
local action="${1}"; shift
local servers="localhost"
if [ "${type}" = "client" -o ! "${work_cmd}" = "interface" ]
then
case "${1}" in
dst|DST|destination|DESTINATION)
shift
local servers="${1}"
shift
;;
*)
error "Please re-phrase to: ${type} nis ${action} dst <NIS_SERVER> [other rules]"
return 1
;;
esac
fi
local x=
for x in ${servers}
do
local tmp="`${MKTEMP_CMD} ${FIREHOL_DIR}/firehol-rpcinfo-XXXXXXXXXX)`"
set_work_function "Getting RPC information from server '${x}'"
rpcinfo -p ${x} >"${tmp}"
if [ $? -gt 0 -o ! -s "${tmp}" ]
then
error "Cannot get rpcinfo from host '${x}' (using the previous firewall rules)"
${RM_CMD} -f "${tmp}"
return 1
fi
local server_ypserv_ports="`${CAT_CMD} "${tmp}" | ${GREP_CMD} " ypserv$" | ( while read a b proto port s; do echo "$proto/$port"; done ) | ${SORT_CMD} | ${UNIQ_CMD}`"
local server_yppasswdd_ports="`${CAT_CMD} "${tmp}" | ${GREP_CMD} " yppasswdd$" | ( while read a b proto port s; do echo "$proto/$port"; done ) | ${SORT_CMD} | ${UNIQ_CMD}`"
test -z "${server_ypserv_ports}" && error "Cannot find ypserv ports for nis server '${x}'" && return 1
local dst=
if [ ! "${x}" = "localhost" ]
then
dst="dst ${x}"
fi
if [ ! -z "${server_yppasswdd_ports}" ]
then
set_work_function "Processing yppasswd rules for server '${x}'"
rules_custom "${mychain}" "${type}" nis-yppasswd "${server_yppasswdd_ports}" "500:65535" "${action}" $dst "$@"
fi
set_work_function "Processing ypserv rules for server '${x}'"
rules_custom "${mychain}" "${type}" nis-ypserv "${server_ypserv_ports}" "500:65535" "${action}" $dst "$@"
${RM_CMD} -f "${tmp}"
echo >&2 ""
echo >&2 "WARNING:"
echo >&2 "This firewall must be restarted if NIS server ${x} is restarted!"
echo >&2 ""
done
return 0
}
2002-12-19 22:52:15 +00:00
# --- AMANDA -------------------------------------------------------------------
# THIS IS OLD
# THE NEW ONE USES THE KERNEL HELPER TO DO IT, AND THEREFORE IS A SIMPLE SERVICE
#
#FIREHOL_AMANDA_PORTS="850:859"
#
#rules_amanda() {
# local mychain="${1}"; shift
# local type="${1}"; shift
#
# local in=in
# local out=out
# if [ "${type}" = "client" ]
# then
# in=out
# out=in
# fi
#
# local client_ports="${DEFAULT_CLIENT_PORTS}"
# if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
# then
# client_ports="${LOCAL_CLIENT_PORTS}"
# fi
#
# # ----------------------------------------------------------------------
#
# set_work_function "*** AMANDA: See http://amanda.sourceforge.net/fom-serve/cache/139.html"
#
#
# set_work_function "Setting up rules for initial amanda server-to-client connection"
# rule ${out} action "$@" chain "${out}_${mychain}" proto "udp" dport 10080 state NEW,ESTABLISHED || return 1
# rule ${in} reverse action "$@" chain "${in}_${mychain}" proto "udp" dport 10080 state ESTABLISHED || return 1
#
#
# set_work_function "Setting up rules for amanda data exchange client-to-server"
# rule ${in} action "$@" chain "${in}_${mychain}" proto "tcp udp" dport "${FIREHOL_AMANDA_PORTS}" state NEW,ESTABLISHED || return 1
# rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "tcp udp" dport "${FIREHOL_AMANDA_PORTS}" state ESTABLISHED || return 1
#
# return 0
#}
2002-12-19 22:52:15 +00:00
2002-09-05 20:57:59 +00:00
# --- FTP ----------------------------------------------------------------------
# THIS IS OLD
# THE NEW ONE USES THE KERNEL HELPER TO DO IT, AND THEREFORE IS A SIMPLE SERVICE
#
#ALL_SHOULD_ALSO_RUN="${ALL_SHOULD_ALSO_RUN} ftp"
#
#rules_ftp() {
# local mychain="${1}"; shift
# local type="${1}"; shift
#
# local in=in
# local out=out
# if [ "${type}" = "client" ]
# then
# in=out
# out=in
# fi
#
# local client_ports="${DEFAULT_CLIENT_PORTS}"
# if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
# then
# client_ports="${LOCAL_CLIENT_PORTS}"
# fi
#
# # For an explanation of how FTP connections work, see
# # http://slacksite.com/other/ftp.html
#
# # ----------------------------------------------------------------------
#
# # allow new and established incoming, and established outgoing
# # accept port ftp new connections
# set_work_function "Setting up rules for initial FTP connection ${type}"
# rule ${in} action "$@" chain "${in}_${mychain}" proto tcp sport "${client_ports}" dport ftp state NEW,ESTABLISHED || return 1
# rule ${out} reverse action "$@" chain "${out}_${mychain}" proto tcp sport "${client_ports}" dport ftp state ESTABLISHED || return 1
#
# set_work_function "Match anything related to the kernel ftp helper"
# rule ${in} action "$@" chain "${in}_${mychain}" custom "-m helper --helper ftp" state ESTABLISHED,RELATED || return 1
# rule ${out} reverse action "$@" chain "${out}_${mychain}" custom "-m helper --helper ftp" state ESTABLISHED,RELATED || return 1
#
# this is old code - replaced by the two helper statements above
# # Active FTP
# # send port ftp-data related connections
# set_work_function "Setting up rules for Active FTP ${type}"
# rule ${out} reverse action "$@" chain "${out}_${mychain}" proto tcp sport "${client_ports}" dport ftp-data state ESTABLISHED,RELATED || return 1
# rule ${in} action "$@" chain "${in}_${mychain}" proto tcp sport "${client_ports}" dport ftp-data state ESTABLISHED || return 1
#
# # ----------------------------------------------------------------------
#
# # A hack for Passive FTP only
# local s_client_ports="${DEFAULT_CLIENT_PORTS}"
# local c_client_ports="${DEFAULT_CLIENT_PORTS}"
#
# if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
# then
# c_client_ports="${LOCAL_CLIENT_PORTS}"
# elif [ "${type}" = "server" -a "${work_cmd}" = "interface" ]
# then
# s_client_ports="${LOCAL_CLIENT_PORTS}"
# fi
#
# # Passive FTP
# # accept high-ports related connections
# set_work_function "Setting up rules for Passive FTP ${type}"
# rule ${in} action "$@" chain "${in}_${mychain}" proto tcp sport "${c_client_ports}" dport "${s_client_ports}" state ESTABLISHED,RELATED || return 1
# rule ${out} reverse action "$@" chain "${out}_${mychain}" proto tcp sport "${c_client_ports}" dport "${s_client_ports}" state ESTABLISHED || return 1
#
# require_kernel_module nf_conntrack_ftp
# test ${FIREHOL_NAT} -eq 1 && require_kernel_module nf_nat_ftp
#
# return 0
#}
2002-09-05 20:57:59 +00:00
# --- TFTP ---------------------------------------------------------------------
# THIS IS OLD
# THE NEW ONE USES THE KERNEL HELPER TO DO IT, AND THEREFORE IS A SIMPLE SERVICE
#
# Written by: Goetz Bock <bock@blacknet.de>
#
#rules_tftp() {
# local mychain="${1}"; shift
# local type="${1}"; shift
#
# local in=in
# local out=out
# if [ "${type}" = "client" ]
# then
# in=out
# out=in
# fi
#
# local client_ports="${DEFAULT_CLIENT_PORTS}"
# if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
# then
# client_ports="${LOCAL_CLIENT_PORTS}"
# fi
#
# # ---------------------------------------------------------------------
# # TFTP is a broken protokol. It works like this:
# #
# # 1. The client sends from a high port (a) to the server's tftp port an
# # udp packet with "give me file 'bla'".
# #
# # 2. The server replies from a high port (b) to the highport the client
# # used (a) with "this is part 0 if your file"
# #
# # 3. The client now has to send a reply (from his highport a) to the
# # servers high port (b): "got part 0, send next part 1".
# #
# # 4. repeat 2. and 3. till file transmitted
#
# # allow the initial TFTP connection
# set_work_function "Setting up rules for initial TFTP connection (${type})"
# rule ${in} action "$@" chain "${in}_${mychain}" proto "udp" sport "${client_ports}" dport tftp state NEW,ESTABLISHED || return 1
## rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "udp" sport "${client_ports}" dport tftp state ESTABLISHED || return 1
#
# # We now need both server and client port ranges
# local s_client_ports="${DEFAULT_CLIENT_PORTS}"
# local c_client_ports="${DEFAULT_CLIENT_PORTS}"
#
# if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
# then
# c_client_ports="${LOCAL_CLIENT_PORTS}"
# elif [ "${type}" = "server" -a "${work_cmd}" = "interface" ]
# then
# s_client_ports="${LOCAL_CLIENT_PORTS}"
# fi
#
# # allow the TFTP server to establish a new connection to the client
# set_work_function "Setting up rules for server-to-client TFTP connection (${type})"
# rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "udp" sport "${c_client_ports}" dport "${s_client_ports}" state RELATED,ESTABLISHED || return 1
# rule ${in} action "$@" chain "${in}_${mychain}" proto "udp" sport "${c_client_ports}" dport "${s_client_ports}" state ESTABLISHED || return 1
#
# require_kernel_module nf_conntrack_tftp
# test ${FIREHOL_NAT} -eq 1 && require_kernel_module nf_nat_tftp
#
# return 0
#}
2003-01-05 20:03:07 +00:00
# --- PING ---------------------------------------------------------------------
rules_ping() {
local mychain="${1}"; shift
local type="${1}"; shift
if running_ipv4; then
ipv4 add_icmp_rule_pair $mychain $type echo-request echo-reply "$@" || return 1
2003-01-05 20:03:07 +00:00
fi
if running_ipv6; then
ipv6 add_icmpv6_rule_pair $mychain $type echo-request echo-reply "$@" || return 1
fi
2003-01-05 20:03:07 +00:00
return 0
}
2004-12-03 21:29:41 +00:00
# --- TIMESTAMP ----------------------------------------------------------------
rules_timestamp() {
local mychain="${1}"; shift
local type="${1}"; shift
if ! push_namespace ipv4; then return 1; fi
local status=0
add_icmp_rule_pair $mychain $type timestamp-request timestamp-reply "$@"|| status=1
2004-12-03 21:29:41 +00:00
pop_namespace
return $status
}
# --- IVP6NEIGH ----------------------------------------------------------------
rules_ipv6neigh() {
local mychain="${1}"; shift
local type="${1}"; shift
if ! push_namespace ipv6; then return 1; fi
local status=0
add_icmpv6_rule_pair_stateless $mychain $type neighbour-solicitation neighbour-advertisement "$@" || status=1
pop_namespace
return $status
2004-12-03 21:29:41 +00:00
}
# --- IVP6ROUTER ---------------------------------------------------------------
rules_ipv6router() {
local mychain="${1}"; shift
local type="${1}"; shift
if ! push_namespace ipv6; then return 1; fi
local status=0
add_icmpv6_rule_pair_stateless $mychain $type router-solicitation router-advertisement "$@" || status=1
pop_namespace
return $status
}
# --- IVP6ERROR ----------------------------------------------------------------
rules_ipv6error() {
local mychain="${1}"; shift
local type="${1}"; shift
if ! push_namespace ipv6; then return 1; fi
for icmptype in destination-unreachable \
packet-too-big \
ttl-zero-during-transit \
ttl-zero-during-reassembly \
unknown-header-type \
unknown-option
do
add_icmpv6_rule_error $mychain $type $icmptype "$@"
if [ $? -ne 0 ]
then
pop_namespace
return 1
fi
done
pop_namespace
return 0
}
2003-01-05 20:03:07 +00:00
# --- ALL ----------------------------------------------------------------------
rules_all() {
local mychain="${1}"; shift
local type="${1}"; shift
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
local client_ports="${DEFAULT_CLIENT_PORTS}"
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
then
client_ports="${LOCAL_CLIENT_PORTS}"
fi
# ----------------------------------------------------------------------
# allow new and established incoming packets
rule ${in} action "$@" chain "${in}_${mychain}" state NEW,ESTABLISHED || return 1
# allow outgoing established packets
rule ${out} reverse action "$@" chain "${out}_${mychain}" state ESTABLISHED || return 1
local ser=
for ser in ${ALL_SHOULD_ALSO_RUN}
do
"${type}" ${ser} "$@" || return 1
done
return 0
}
# --- ANY ----------------------------------------------------------------------
rules_any() {
local mychain="${1}"; shift
local type="${1}"; shift
local name="${1}"; shift # a special case: service any gets a name
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
local client_ports="${DEFAULT_CLIENT_PORTS}"
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
then
client_ports="${LOCAL_CLIENT_PORTS}"
fi
# ----------------------------------------------------------------------
# allow new and established incoming packets
rule ${in} action "$@" chain "${in}_${mychain}" state NEW,ESTABLISHED || return 1
# allow outgoing established packets
rule ${out} reverse action "$@" chain "${out}_${mychain}" state ESTABLISHED || return 1
return 0
}
2004-11-02 00:37:15 +00:00
# --- ANYSTATELESS -------------------------------------------------------------
rules_anystateless() {
local mychain="${1}"; shift
local type="${1}"; shift
local name="${1}"; shift # a special case: service any gets a name
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
local client_ports="${DEFAULT_CLIENT_PORTS}"
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
then
client_ports="${LOCAL_CLIENT_PORTS}"
fi
# ----------------------------------------------------------------------
# allow new and established incoming packets
rule ${in} action "$@" chain "${in}_${mychain}" || return 1
# allow outgoing established packets
rule ${out} reverse action "$@" chain "${out}_${mychain}" || return 1
return 0
}
# --- MULTICAST ----------------------------------------------------------------
rules_multicast() {
local mychain="${1}"; shift
local type="${1}"; shift
local in=in
local out=out
if [ "${type}" = "client" ]
then
in=out
out=in
fi
local client_ports="${DEFAULT_CLIENT_PORTS}"
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
then
client_ports="${LOCAL_CLIENT_PORTS}"
fi
# ----------------------------------------------------------------------
# match multicast packets in both directions
rule ${out} action "$@" chain "${out}_${mychain}" dst "${MULTICAST_IPS}" proto 2 || return 1
rule ${in} reverse action "$@" chain "${in}_${mychain}" src "${MULTICAST_IPS}" proto 2 || return 1
rule ${out} action "$@" chain "${out}_${mychain}" dst "${MULTICAST_IPS}" proto udp || return 1
rule ${in} reverse action "$@" chain "${in}_${mychain}" src "${MULTICAST_IPS}" proto udp || return 1
return 0
}
2002-12-23 14:39:19 +00:00
# --- CUSTOM -------------------------------------------------------------------
2002-09-05 20:57:59 +00:00
2002-12-23 14:39:19 +00:00
rules_custom() {
local mychain="${1}"; shift
local type="${1}"; shift
2002-12-23 14:39:19 +00:00
local server="${1}"; shift
local my_server_ports="${1}"; shift
local my_client_ports="${1}"; shift
2002-09-05 20:57:59 +00:00
local helpers=
if [ "$1" = "helpers" ]
then
local helpers="$2"
shift 2
fi
2002-12-23 14:39:19 +00:00
local in=in
local out=out
if [ "${type}" = "client" ]
2002-09-05 20:57:59 +00:00
then
2002-12-23 14:39:19 +00:00
in=out
out=in
2002-09-05 20:57:59 +00:00
fi
2002-12-23 14:39:19 +00:00
local client_ports="${DEFAULT_CLIENT_PORTS}"
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
then
2002-12-23 14:39:19 +00:00
client_ports="${LOCAL_CLIENT_PORTS}"
fi
2002-12-23 14:39:19 +00:00
# ----------------------------------------------------------------------
2002-09-05 20:57:59 +00:00
2002-12-23 14:39:19 +00:00
local sp=
for sp in ${my_server_ports}
do
local proto=
local sport=
2002-09-05 20:57:59 +00:00
2002-12-23 14:39:19 +00:00
IFS="/" read proto sport <<EOF
$sp
EOF
local cp=
for cp in ${my_client_ports}
do
local cport=
case ${cp} in
default)
cport="${client_ports}"
;;
*) cport="${cp}"
;;
esac
2002-09-05 20:57:59 +00:00
set_work_function "Rules for ${server} ${type}, with server port(s) '${sp}' and client port(s) '${cp}'"
2002-12-23 14:39:19 +00:00
# allow new and established incoming packets
rule ${in} action "$@" chain "${in}_${mychain}" proto "${proto}" sport "${cport}" dport "${sport}" state NEW,ESTABLISHED || return 1
2002-12-23 14:39:19 +00:00
# allow outgoing established packets
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "${proto}" sport "${cport}" dport "${sport}" state ESTABLISHED || return 1
2002-12-23 14:39:19 +00:00
done
done
2002-09-05 20:57:59 +00:00
local x=
for x in ${helpers}
do
set_work_function "Rules for ${server} ${type}, with helper '${x}'"
rule ${in} action "$@" chain "${in}_${mychain}" custom "-m helper --helper ${x}" state ESTABLISHED,RELATED || return 1
rule ${out} reverse action "$@" chain "${out}_${mychain}" custom "-m helper --helper ${x}" state ESTABLISHED,RELATED || return 1
done
2002-09-05 20:57:59 +00:00
return 0
}
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# SUPPORT FOR EXTERNAL DEFINITIONS OF SERVICES
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
# Load all the services.
# All these files should start with: #FHVER: 1
cd "${FIREHOL_SERVICES_DIR}" || exit 1
for f in `ls *.conf 2>/dev/null`
do
cd "${FIREHOL_SERVICES_DIR}" || exit 1
if [ ! -O "${f}" ]
then
echo >&2 " >>> Ignoring service in '${FIREHOL_SERVICES_DIR}/${f}' because it is not owned by root."
continue
fi
n=`"${HEAD_CMD}" -n 1 "${f}" | "${CUT_CMD}" -d ':' -f 2`
"${EXPR_CMD}" ${n} + 0 >/dev/null 2>&1
if [ $? -ne 0 ]
then
echo >&2 " >>> Ignoring service in '${FIREHOL_SERVICES_DIR}/${f}' due to malformed header."
elif [ ${n} -ne ${FIREHOL_SERVICES_API} ]
then
echo >&2 " >>> Ignoring service '${FIREHOL_SERVICES_DIR}/${f}' due to incompatible API version."
else
n=`"${HEAD_CMD}" -n 1 "${f}" | "${CUT_CMD}" -d ':' -f 3`
"${EXPR_CMD}" ${n} + 0 >/dev/null 2>&1
if [ $? -ne 0 ]
then
echo >&2 " >>> Ignoring service in '${FIREHOL_SERVICES_DIR}/${f}' due to malformed API minor number."
else
source ${f}
ret=$?
if [ ${ret} -ne 0 ]
then
echo >&2 " >>> Service in '${FIREHOL_SERVICES_DIR}/${f}' returned code ${ret}."
continue
fi
fi
fi
done
cd "${FIREHOL_DEFAULT_WORKING_DIRECTORY}" || exit 1
2013-09-25 23:50:30 +00:00
# ------------------------------------------------------------------------------
# The caller may need just our services definitions
if [ "$1" = "gimme-the-services-defs" ]
then
return 0
exit 1
fi
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# HELPER FUNCTIONS BELLOW THIS POINT
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
ecn_shame() {
softwarning "ECN_SHAME IP list no longer available, helper is ignored."
return 0
}
Added helper action: action [chain <name> <action>] The action helper creates an iptables chain which can be used to control the action of other firewall rules during runtime. For example, you can setup the custom action ACT1, which by default is ACCEPT, but under certain cases it can be changed to DROP, REJECT or RETURN without restarting the firewall. The first argument must always be the word 'chain', for the moment. name can be any chain name accepted by iptables. It is suggested to keep it between 5 to 10 letters. action can be any action supported by FireHOL, although ony ACCEPT, REJECT, DROP, RETURN may have any meaning under this use. Example 1: At the top of firehol.conf, create the action ACT1: action chain ACT1 accept later, in interfaces and routers, create rules that use the ACT1 action: server smtp ACT1 client imap ACT1 Please note that actions created this way are case sensitive. At some point, and while the firewall is running, the action ACT1 can be changed to DROP, with this linux command (this is not FireHOL specific): iptables -t filter -I ACT1 -j DROP The above command inserts (-I) the new action DROP above the default action ACCEPT, and therefore all the traffic matching the FireHOL rules that have the action ACT1 will now be dropped. To return to the default action (ACCEPT), run the following linux command: iptables -t filter -D ACT1 -j DROP This command deletes (-D) the DROP action that was inserted above the default action. If you delete all actions in the chain ACT1, the default action will be RETURN, in which case all rules with action ACT1 will be nutralized (it will be the same as they were not specified at all in firehol.conf). Example 2: action chain "ACT1 ACT2 ACT3" accept chain "ACT4 ACT5 ACT6" drop will create 6 actions, ACT1, ACT2, ACT3 with ACCEPT, and ACT4, ACT5, ACT6 with DROP.
2007-07-20 19:58:38 +00:00
# define custom actions
action() {
work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
require_work clear || ( error "$FUNCNAME cannot be used in '${work_cmd}'. Put it before any '${work_cmd}' definition."; return 1 )
while [ ! -z "${1}" ]
do
local what="${1}"; shift
case "${what}" in
chain) local name="${1}"; shift
local act="${1}"; shift
if [ -z "${name}" ]
then
error "Cannot create an action chain without a name."
return 1
fi
if [ -z "${act}" ]
then
error "Cannot create the action chain(s) '$name' without a default action."
return 1
fi
local nm=
for nm in $name
do
create_chain filter ${nm}
rule table filter chain ${nm} action "${act}"
done
;;
*) error "Cannot understand $FUNCNAME '${what}'."
return 1
;;
esac
done
return 0
}
masquerade() {
work_realcmd_helper ${FUNCNAME} "$@"
set_work_function -ne "Initializing $FUNCNAME"
local f="${work_outface}"
test "${1}" = "reverse" && f="${work_inface}" && shift
test -z "${f}" && local f="${1}" && shift
test -z "${f}" && error "masquerade requires an interface set or as argument" && return 1
set_work_function "Initializing masquerade on interface '${f}'"
rule noowner table nat chain POSTROUTING "$@" inface any outface "${f}" action MASQUERADE || return 1
FIREHOL_NAT=1
FIREHOL_ROUTING=1
return 0
}
transparent_proxy_count=0
transparent_proxy() {
work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
require_work clear || ( error "$FUNCNAME cannot be used in '${work_cmd}'. Put it before any '${work_cmd}' definition."; return 1 )
local ports="${1}"; shift
local redirect="${1}"; shift
local user="${1}"; shift
test -z "${redirect}" && error "Proxy listening port is empty" && return 1
transparent_proxy_count=$[transparent_proxy_count + 1]
set_work_function "Setting up rules for catching routed tcp/${ports} traffic"
create_chain nat "in_trproxy.${transparent_proxy_count}" PREROUTING noowner "$@" outface any proto tcp sport "${DEFAULT_CLIENT_PORTS}" dport "${ports}" || return 1
# rule table nat chain "in_trproxy.${transparent_proxy_count}" proto tcp dport "${ports}" action REDIRECT to-port ${redirect} || return 1
rule table nat chain "in_trproxy.${transparent_proxy_count}" proto tcp action REDIRECT to-port ${redirect} || return 1
if [ ! -z "${user}" ]
then
set_work_function "Setting up rules for catching outgoing tcp/${ports} traffic"
create_chain nat "out_trproxy.${transparent_proxy_count}" OUTPUT "$@" uid not "${user}" nosoftwarnings inface any outface any src any proto tcp sport "${LOCAL_CLIENT_PORTS}" dport "${ports}" || return 1
# do not catch traffic for localhost servers
rule table nat chain "out_trproxy.${transparent_proxy_count}" dst "127.0.0.1" action RETURN || return 1
# rule table nat chain "out_trproxy.${transparent_proxy_count}" proto tcp dport "${ports}" action REDIRECT to-port ${redirect} || return 1
rule table nat chain "out_trproxy.${transparent_proxy_count}" proto tcp action REDIRECT to-port ${redirect} || return 1
fi
FIREHOL_NAT=1
FIREHOL_ROUTING=1
return 0
}
transparent_squid() {
transparent_proxy 80 "$@"
}
FIREHOL_TPROXY_MARK="0xffff/0xffff"
FIREHOL_TPROXY_IP_ROUTE_TABLE="999"
FIREHOL_TPROXY_ROUTE_DEVICE="lo"
tproxy_setup_ip_route() {
local x=
for x in inet inet6
do
# remove the existing ip rules for this mark
postprocess -ne ${IP_CMD} -f $x rule del from all fwmark $FIREHOL_TPROXY_MARK
# remove the existing rules from the ip route table
postprocess -ne ${IP_CMD} -f $x route flush table $FIREHOL_TPROXY_IP_ROUTE_TABLE
# add the ip rule to match the mask and forward it to the proper ip route table for tproxy
postprocess -warn ${IP_CMD} -f $x rule add from all fwmark $FIREHOL_TPROXY_MARK lookup $FIREHOL_TPROXY_IP_ROUTE_TABLE
# add the route to forward all traffic to lo, on the ip route table for tproxy
postprocess -warn ${IP_CMD} -f $x route add local default dev $FIREHOL_TPROXY_ROUTE_DEVICE table $FIREHOL_TPROXY_IP_ROUTE_TABLE
done
# disable the reverse path discovery for lo
postprocess -warn ${SYSCTL_CMD} -w net.ipv4.conf.default.rp_filter=0
postprocess -warn ${SYSCTL_CMD} -w net.ipv4.conf.all.rp_filter=0
postprocess -warn ${SYSCTL_CMD} -w net.ipv4.conf.$FIREHOL_TPROXY_ROUTE_DEVICE.rp_filter=0
}
tproxy_count=0
tproxy() {
work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
require_work clear || ( error "$FUNCNAME cannot be used in '${work_cmd}'. Put it before any '${work_cmd}' definition."; return 1 )
local ports="${1}"; shift
local tproxy_action_options="tproxy-mark $FIREHOL_TPROXY_MARK"
local tport=
local tip=
if [ "$1" = "port" ]
then
local tproxy_action_options="$tproxy_action_options on-port ${2}"
local tport="${2}"
shift 2
else
error "TPROXY needs at least the port the proxy is listening at."
return 1
fi
if [ "$1" = "ip" ]
then
local tproxy_action_options="$tproxy_action_options on-ip ${2}"
local tip="${2}"
shift 2
fi
tproxy_count=$[tproxy_count + 1]
set_work_function "Setting up rules for catching routed tcp/${ports} traffic"
create_chain mangle "in_tproxy.${tproxy_count}" PREROUTING "$@" outface any proto tcp dport "${ports}" || return 1
create_chain mangle "in_tproxy.${tproxy_count}.divert" "in_tproxy.${tproxy_count}" proto tcp custom '-m socket' || return 1
rule table mangle chain "in_tproxy.${tproxy_count}.divert" action MARK to $FIREHOL_TPROXY_MARK
rule table mangle chain "in_tproxy.${tproxy_count}.divert" action ACCEPT
rule table mangle chain "in_tproxy.${tproxy_count}" proto tcp action TPROXY ${tproxy_action_options} || return 1
FIREHOL_NAT=1
FIREHOL_ROUTING=1
if [ $tproxy_count -eq 1 ]
then
tproxy_setup_ip_route
fi
return 0
}
nat_count=0
nat_helper() {
# work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
require_work clear || ( error "NAT cannot be used in '${work_cmd}'. Put all NAT related commands before any '${work_cmd}' definition."; return 1 )
local type="${1}"; shift
local to="${1}"; shift
nat_count=$[nat_count + 1]
set_work_function -ne "Setting up rules for NAT type: '${type}'"
case ${type} in
to-source)
create_chain nat "nat.${nat_count}" POSTROUTING nolog "$@" inface any || return 1
local action=snat
;;
to-destination)
create_chain nat "nat.${nat_count}" PREROUTING noowner nolog "$@" outface any || return 1
local action=dnat
;;
redirect-to)
create_chain nat "nat.${nat_count}" PREROUTING noowner nolog "$@" outface any || return 1
local action=redirect
;;
*)
error "$FUNCNAME requires a type (i.e. to-source, to-destination, redirect-to, etc) as its first argument. '${type}' is not understood."
return 1
;;
esac
set_work_function "Taking the NAT action: '${action}'"
# we now need to keep the protocol
rule table nat chain "nat.${nat_count}" noowner "$@" action "${action}" to "${to}" nosoftwarnings src any dst any inface any outface any sport any dport any || return 1
FIREHOL_NAT=1
FIREHOL_ROUTING=1
return 0
}
nat() {
work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
nat_helper "$@"
}
snat() {
work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
local to="${1}"; shift
2003-01-30 21:39:36 +00:00
test "${to}" = "to" && local to="${1}" && shift
nat_helper "to-source" "${to}" "$@"
}
dnat() {
work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
local to="${1}"; shift
2003-01-30 21:39:36 +00:00
test "${to}" = "to" && local to="${1}" && shift
nat_helper "to-destination" "${to}" "$@"
}
redirect() {
work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
local to="${1}"; shift
2003-01-30 21:39:36 +00:00
test "${to}" = "to" -o "${to}" = "to-port" && local to="${1}" && shift
nat_helper "redirect-to" "${to}" "$@"
}
wrongmac_chain=0
wrongmac6_chain=0
mac() {
work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
require_work clear || ( error "$FUNCNAME cannot be used in '${work_cmd}'. Put it before any '${work_cmd}' definition."; return 1 )
if running_ipv4; then
if [ ${wrongmac_chain} -eq 0 ]
then
set_work_function "Creating the MAC-MISSMATCH chain (only once)"
2003-06-28 23:22:49 +00:00
iptables -t filter -N WRONGMAC
rule table filter chain WRONGMAC loglimit "MAC MISSMATCH" action DROP || return 1
wrongmac_chain=1
fi
fi
if running_ipv6; then
if [ ${wrongmac6_chain} -eq 0 ]
then
set_work_function "Creating the MAC-MISSMATCH chain (only once)"
ip6tables -t filter -N WRONGMAC
rule table filter chain WRONGMAC loglimit "MAC MISSMATCH" action DROP || return 1
wrongmac6_chain=1
fi
fi
2003-06-28 23:22:49 +00:00
set_work_function "If the source IP ${1} does not match MAC ${2}, drop the packet"
iptables_both -t filter -A INPUT -s "${1}" -m mac ! --mac-source "${2}" -j WRONGMAC
iptables_both -t filter -A FORWARD -s "${1}" -m mac ! --mac-source "${2}" -j WRONGMAC
return 0
}
# blacklist creates two types of blacklists: unidirectional or bidirectional
blacklist_chain=0
blacklist() {
work_realcmd_helper ${FUNCNAME} "$@"
set_work_function -ne "Initializing $FUNCNAME"
require_work clear || ( error "$FUNCNAME cannot be used in '${work_cmd}'. Put it before any '${work_cmd}' definition."; return 1 )
local full=1
if [ "${1}" = "them" -o "${1}" = "him" -o "${1}" = "her" -o "${1}" = "it" -o "${1}" = "this" -o "${1}" = "these" -o "${1}" = "input" ]
then
shift
full=0
elif [ "${1}" = "all" -o "${1}" = "full" ]
then
shift
full=1
fi
if [ ${blacklist_chain} -eq 0 ]
then
set_work_function "Generating blacklist chains"
# Blacklist INPUT unidirectional
iptables_both -t filter -N BL_IN_UNI # INPUT
iptables_both -A BL_IN_UNI -m conntrack --ctstate NEW -j DROP
iptables_both -A BL_IN_UNI -j DROP
# No need for OUTPUT/FORWARD unidirectional
# Blacklist INPUT bidirectional
iptables_both -t filter -N BL_IN_BI # INPUT
iptables_both -A BL_IN_BI -m conntrack --ctstate NEW -j DROP
iptables_both -A BL_IN_BI -j DROP
# Blacklist OUTPUT/FORWARD bidirectional
iptables_both -t filter -N BL_OUT_BI # OUTPUT and FORWARD
iptables_both -A BL_OUT_BI -m conntrack --ctstate NEW -p tcp -j REJECT #--reject-with tcp-reset
if running_ipv4; then
iptables -A BL_OUT_BI -m conntrack --ctstate NEW -j REJECT --reject-with icmp-host-unreachable
fi
if running_ipv6; then
iptables -A BL_OUT_BI -m conntrack --ctstate NEW -j REJECT --reject-with icmp6-addr-unreachable
fi
iptables_both -A BL_OUT_BI -j REJECT
blacklist_chain=1
fi
set_work_function "Generating blacklist rules"
local z=
for z in $@
do
local x=
for x in ${z}
do
set_work_function "Blacklisting '${x}'"
if [ ${full} -eq 1 ]
then
iptables_both -I INPUT -s ${x} -j BL_IN_BI
iptables_both -I FORWARD -s ${x} -j BL_IN_BI
iptables_both -I OUTPUT -d ${x} -j BL_OUT_BI
iptables_both -I FORWARD -d ${x} -j BL_OUT_BI
else
iptables_both -I INPUT -s ${x} -j BL_IN_UNI
iptables_both -I FORWARD -s ${x} -j BL_IN_UNI
fi
done
done
return 0
}
classify_count=0
classify() {
work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
require_work clear || ( error "$FUNCNAME cannot be used in '${work_cmd}'. Put it before any '${work_cmd}' definition."; return 1 )
local class="${1}"; shift
classify_count=$[classify_count + 1]
set_work_function "Setting up rules for CLASSIFY"
create_chain mangle "classify.${classify_count}" POSTROUTING "$@" || return 1
iptables_both -t mangle -A "classify.${classify_count}" -j CLASSIFY --set-class ${class}
return 0
}
connmark_count=0
connmark() {
work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
require_work clear || ( error "$FUNCNAME cannot be used in '${work_cmd}'. Put it before any '${work_cmd}' definition."; return 1 )
2014-03-13 01:08:34 +00:00
local num="${1}"; shift
local where="${1}"; shift
test -z "${where}" && where="OUTPUT POSTROUTING"
if [ $mark_count -gt 0 -a $connmark_count -eq 0 ]
then
softwarning "MARK and CONNMARK can interfere with each other. Check https://github.com/ktsaou/firehol/issues/23#issuecomment-37599754"
fi
2014-03-13 01:08:34 +00:00
connmark_count=$[connmark_count + 1]
if [ "${num}" = "save" ]
2014-03-13 00:35:12 +00:00
then
# save MARK to CONNMARK
shift 1
rule table mangle chain INPUT custom '-m conntrack --ctstate NEW' "$@" action CONNMARK save
rule table mangle chain POSTROUTING custom '-m conntrack --ctstate NEW' "$@" action CONNMARK save
2014-03-13 00:35:12 +00:00
return 0
2014-03-13 01:08:34 +00:00
fi
if [ "${num}" = "restore" ]
2014-03-13 00:35:12 +00:00
then
2014-03-13 01:08:34 +00:00
# backward compatibility - nothing to be done here
2014-03-13 00:35:12 +00:00
return 0
fi
2014-03-12 22:20:00 +00:00
set_work_function "Setting up rules for CONNMARK rule No $connmark_count"
2014-03-12 22:20:00 +00:00
local chain=
for chain in ${where}
do
case "${chain}" in
interface)
create_chain mangle "connmark.inface.${connmark_count}" "PREROUTING" custom '-m conntrack --ctstate NEW' inface "$@" || return 1
rule table mangle chain "connmark.inface.${connmark_count}" action CONNMARK to ${num}
rule table mangle chain "connmark.inface.${connmark_count}" action CONNMARK restore
create_chain mangle "connmark.outface.${connmark_count}" "POSTROUTING" custom '-m conntrack --ctstate NEW' outface "$@" || return 1
rule table mangle chain "connmark.outface.${connmark_count}" action CONNMARK to ${num}
rule table mangle chain "connmark.outface.${connmark_count}" action CONNMARK restore
2014-03-12 22:20:00 +00:00
;;
*)
create_chain mangle "connmark.${chain}.${connmark_count}" "${chain}" custom '-m conntrack --ctstate NEW' "$@" || return 1
rule table mangle chain "connmark.${chain}.${connmark_count}" action CONNMARK to ${num}
rule table mangle chain "connmark.${chain}.${connmark_count}" action CONNMARK restore
2014-03-12 22:20:00 +00:00
;;
esac
done
return 0
}
mark_count=0
mark() {
work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
require_work clear || ( error "$FUNCNAME cannot be used in '${work_cmd}'. Put it before any '${work_cmd}' definition."; return 1 )
local num="${1}"; shift
local where="${1}"; shift
test -z "${where}" && where=OUTPUT
if [ $mark_count -eq 0 -a $connmark_count -gt 0 ]
then
softwarning "MARK and CONNMARK can interfere with each other. Check https://github.com/ktsaou/firehol/issues/23#issuecomment-37599754"
fi
mark_count=$[mark_count + 1]
set_work_function "Setting up rules for MARK"
create_chain mangle "mark.${mark_count}" "${where}" "$@" || return 1
iptables_both -t mangle -A "mark.${mark_count}" -j MARK --set-mark ${num}
return 0
}
tos_count=0
tos() {
work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
require_work clear || ( error "$FUNCNAME cannot be used in '${work_cmd}'. Put it before any '${work_cmd}' definition."; return 1 )
local num="${1}"; shift
local where="${1}"; shift
test -z "${where}" && where=OUTPUT
tos_count=$[tos_count + 1]
set_work_function "Setting up rules for TOS"
create_chain mangle "tos.${tos_count}" "${where}" "$@" || return 1
iptables_both -t mangle -A "tos.${tos_count}" -j TOS --set-tos ${num}
return 0
}
# from http://blog.edseek.com/~jasonb/articles/traffic_shaping/scenarios.html
tosfix() {
work_realcmd=($FUNCNAME "$@")
set_work_function -ne "Initializing $FUNCNAME"
require_work clear || ( error "$FUNCNAME cannot be used in '${work_cmd}'. Put it before any '${work_cmd}' definition."; return 1 )
set_work_function "Fixing TOS for TCP ACK packets"
iptables_both -t mangle -N ackfix
iptables_both -t mangle -A ackfix -m tos ! --tos Normal-Service -j RETURN
iptables_both -t mangle -A ackfix -p tcp -m length --length 0:128 -j TOS --set-tos Minimize-Delay
iptables_both -t mangle -A ackfix -p tcp -m length --length 128: -j TOS --set-tos Maximize-Throughput
iptables_both -t mangle -A ackfix -j RETURN
iptables_both -t mangle -I POSTROUTING -p tcp -m tcp --tcp-flags SYN,RST,ACK ACK -j ackfix
set_work_function "Fixing TOS for Minimize-Delay packets"
iptables_both -t mangle -N tosfix
iptables_both -t mangle -A tosfix -p tcp -m length --length 0:512 -j RETURN
iptables_both -t mangle -A tosfix -m limit --limit 2/s --limit-burst 10 -j RETURN
iptables_both -t mangle -A tosfix -j TOS --set-tos Maximize-Throughput
iptables_both -t mangle -A tosfix -j RETURN
iptables_both -t mangle -I POSTROUTING -p tcp -m tos --tos Minimize-Delay -j tosfix
return 0
}
dscp_count=0
dscp() {
work_realcmd=($FUNCNAME "$@")
set_work_function -ne "Initializing $FUNCNAME"
require_work clear || ( error "$FUNCNAME cannot be used in '${work_cmd}'. Put it before any '${work_cmd}' definition."; return 1 )
local value="${1}"; shift
local class=""
if [ "${value}" = "class" ]
then
local value=""
local class="${1}"; shift
fi
local where="${1}"; shift
test -z "${where}" && where=OUTPUT
dscp_count=$[dscp_count + 1]
set_work_function "Setting up rules for setting DSCP"
create_chain mangle "dscp.${dscp_count}" "${where}" "$@" || return 1
if [ ! -z "${class}" ]
then
iptables_both -t mangle -A "dscp.${dscp_count}" -j DSCP --set-dscp-class ${class}
else
iptables_both -t mangle -A "dscp.${dscp_count}" -j DSCP --set-dscp ${value}
fi
return 0
}
tcpmss() {
work_realcmd_helper $FUNCNAME "$@"
set_work_function -ne "Initializing $FUNCNAME"
local value="$1"
local iface="$2"
if [ -z "$iface" ]
then
if [ ! -z "${work_cmd}" ]
then
local iface="${work_outface}"
if [ -z "$iface" ]
then
error "$FUNCNAME cannot find the interfaces to setup. Did you set an outface?"
return 1
fi
else
local iface="all"
fi
fi
local target=
case "$value" in
auto)
local target="-j TCPMSS --clamp-mss-to-pmtu"
;;
[0-9]*)
local target="-j TCPMSS --set-mss $value"
;;
*)
;;
esac
if [ -z "$target" ]
then
error "$FUNCNAME requires either the word 'auto' or a numeric argument for mss."
return 1
fi
if [ "$iface" = "all" ]
then
set_work_function "Initializing tcpmss for all interfaces"
iptables_both -t mangle -A POSTROUTING -p tcp --tcp-flags SYN,RST SYN $target
else
local f=
for f in $iface
do
set_work_function "Initializing tcpmss for interface '${f}'"
iptables_both -t mangle -A POSTROUTING -o ${f} -p tcp --tcp-flags SYN,RST SYN $target
done
fi
return 0
}
2002-09-05 20:57:59 +00:00
# ------------------------------------------------------------------------------
2002-12-23 14:39:19 +00:00
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# INTERNAL FUNCTIONS BELLOW THIS POINT - Primary commands
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
2002-09-05 20:57:59 +00:00
2002-12-23 14:39:19 +00:00
# ------------------------------------------------------------------------------
# Check the version required by the configuration file
# WHY:
# We have to make sure the configuration file has been written for this version
# of FireHOL. Note that the version command does not actually check the version
# of firehol.sh. It checks only its config version number.
2002-09-05 20:57:59 +00:00
2002-12-23 14:39:19 +00:00
version() {
work_realcmd_helper ${FUNCNAME} "$@"
2002-12-23 14:39:19 +00:00
if [ ${1} -gt ${FIREHOL_VERSION} ]
then
error "Wrong version. FireHOL is v${FIREHOL_VERSION}, your script requires v${1}. See http://firehol.org/upgrade/#config-version-${FIREHOL_VERSION}"
fi
if [ ${1} -eq 5 ]
then
ENABLE_IPV6=0
FIREHOL_DEFAULT_NAMESPACE=ipv4
FIREHOL_NS_CURR=$FIREHOL_DEFAULT_NAMESPACE
FIREHOL_NS_STACK=
FIREHOL_NS_PREP=
warning "Running version 5 config. Update configuration to version 6 for IPv6 support. See http://firehol.org/upgrade/#config-version-${FIREHOL_VERSION}"
2002-12-23 14:39:19 +00:00
fi
}
2002-12-23 14:39:19 +00:00
2002-09-05 20:57:59 +00:00
# ------------------------------------------------------------------------------
# PRIMARY COMMAND: interface
# Setup rules specific to an interface (physical or logical)
interface() {
work_realcmd_primary ${FUNCNAME} "$@"
2002-09-05 20:57:59 +00:00
# --- close any open command ---
close_cmd || return 1
# --- reset namespace
if [ "${FIREHOL_NS_PREP}" != "" ]
then
FIREHOL_NS_STACK=${FIREHOL_NS_PREP}
FIREHOL_NS_CURR=${FIREHOL_NS_PREP}
else
FIREHOL_NS_STACK=${FIREHOL_DEFAULT_NAMESPACE}
FIREHOL_NS_CURR=${FIREHOL_DEFAULT_NAMESPACE}
fi
FIREHOL_NS_PREP=
2002-09-05 20:57:59 +00:00
# --- test prerequisites ---
require_work clear || return 1
set_work_function -ne "Initializing $FUNCNAME"
2002-09-05 20:57:59 +00:00
# --- get paramaters and validate them ---
# Get the interface
local inface="${1}"; shift
test -z "${inface}" && error "real interface is not set" && return 1
2002-09-05 20:57:59 +00:00
# Get the name for this interface
local name="${1}"; shift
test -z "${name}" && error "$FUNCNAME name is not set" && return 1
2002-09-05 20:57:59 +00:00
# --- do the job ---
work_cmd="${FUNCNAME}"
work_name="${name}"
work_realcmd=("(unset)")
2002-09-05 20:57:59 +00:00
set_work_function -ne "Initializing $FUNCNAME '${work_name}'"
create_chain filter "in_${work_name}" INPUT in set_work_inface "$@" inface "${inface}" outface any || return 1
create_chain filter "out_${work_name}" OUTPUT out set_work_outface reverse "$@" inface "${inface}" outface any || return 1
2002-09-05 20:57:59 +00:00
return 0
}
interface4() {
ipv4 interface "$@"
}
interface6() {
ipv6 interface "$@"
}
interface46() {
both interface "$@"
}
2002-09-05 20:57:59 +00:00
2002-12-23 14:39:19 +00:00
router() {
work_realcmd_helper ${FUNCNAME} "$@"
2002-09-05 20:57:59 +00:00
2002-12-23 14:39:19 +00:00
# --- close any open command ---
2002-09-05 20:57:59 +00:00
close_cmd || return 1
2002-09-05 20:57:59 +00:00
# --- reset namespace
if [ "${FIREHOL_NS_PREP}" != "" ]
then
FIREHOL_NS_STACK=${FIREHOL_NS_PREP}
FIREHOL_NS_CURR=${FIREHOL_NS_PREP}
else
FIREHOL_NS_STACK=${FIREHOL_DEFAULT_NAMESPACE}
FIREHOL_NS_CURR=${FIREHOL_DEFAULT_NAMESPACE}
fi
FIREHOL_NS_PREP=
2002-09-05 20:57:59 +00:00
# --- test prerequisites ---
require_work clear || return 1
set_work_function -ne "Initializing $FUNCNAME"
2002-09-05 20:57:59 +00:00
# --- get paramaters and validate them ---
# Get the name for this router
local name="${1}"; shift
test -z "${name}" && error "$FUNCNAME name is not set" && return 1
2002-09-05 20:57:59 +00:00
# --- do the job ---
work_cmd="${FUNCNAME}"
work_name="${name}"
work_realcmd=("(unset)")
2002-09-05 20:57:59 +00:00
set_work_function -ne "Initializing $FUNCNAME '${work_name}'"
create_chain filter "in_${work_name}" FORWARD in set_work_inface set_work_outface "$@" || return 1
create_chain filter "out_${work_name}" FORWARD out reverse "$@" || return 1
2002-09-05 20:57:59 +00:00
FIREHOL_ROUTING=1
2002-09-05 20:57:59 +00:00
return 0
}
router4() {
ipv4 router "$@"
}
router6() {
ipv6 router "$@"
}
router46() {
both router "$@"
}
save_for_restore() {
local check="$1"; shift
printf "%q " "$@" >>"${FIREHOL_DIR}/firewall_restore_commands.sh"
if [ "${check}" = "none" -o "${check}" = "warn" ]
then
printf " || echo >/dev/null\n" >>"${FIREHOL_DIR}/firewall_restore_commands.sh"
else
printf " || exit 1\n" >>"${FIREHOL_DIR}/firewall_restore_commands.sh"
fi
}
2002-12-23 14:39:19 +00:00
postprocess() {
# work_realcmd_helper ${FUNCNAME} "$@"
local check="error"
local save=1
while [ ! "A${1}" = "A" ]
do
case "A${1}" in
A-ne) shift; local check="none";;
A-warn) shift; local check="warn";;
A-ns) shift; local save=0;;
*) break;;
esac
done
test "${FIREHOL_MODE}" = "DEBUG" && local check="none"
test "${FIREHOL_MODE}" = "EXPLAIN" && local check="none"
if [ ! ${check} = "none" ]
then
printf "runcmd '${check}' '${FIREHOL_LINEID}' " >>${FIREHOL_OUTPUT}
fi
2002-12-23 14:39:19 +00:00
printf "%q " "$@" >>${FIREHOL_OUTPUT}
if [ ${check} = "none" ]
then
printf " >/dev/null 2>&1 || echo >/dev/null\n" >>${FIREHOL_OUTPUT}
else
printf "\n" >>${FIREHOL_OUTPUT}
fi
if [ "${FIREHOL_MODE}" = "EXPLAIN" ]
2002-12-23 14:39:19 +00:00
then
${CAT_CMD} ${FIREHOL_OUTPUT}
${RM_CMD} -f ${FIREHOL_OUTPUT}
2002-12-23 14:39:19 +00:00
fi
test $save -eq 1 && save_for_restore ${check} "${@}"
return 0
}
runcmd() {
local check="${1}"; shift
local line="${1}"; shift
local cmd="${1}"; shift
"${cmd}" "$@" >${FIREHOL_OUTPUT}.log 2>&1
local r=$?
if [ "$FIREHOL_DEBUGGING" ]
then
"${CAT_CMD}" ${FIREHOL_OUTPUT}.log 1>&2
fi
test ${r} -gt 0 && runtime_error ${check} ${r} ${line} "${cmd}" "$@"
2002-12-23 14:39:19 +00:00
return 0
}
2013-09-28 09:03:57 +00:00
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# fast activation
# in fast activation mode we catch all /sbin/iptables commands and instead of
# executing them, we generate an iptables-restore compatible file.
2013-12-01 14:59:44 +00:00
run_fast() {
2013-09-28 09:03:57 +00:00
local t=filter
2013-12-01 14:59:44 +00:00
local cmd="$1"
shift
if [ "${cmd}" = "ip6tables" ]
then
n=table6
else
n=table
fi
2013-09-28 09:03:57 +00:00
if [ "z$1" = "z-t" ]
then
local t="$2"
shift 2
fi
case "$1" in
2013-12-01 14:59:44 +00:00
-P) echo ":$2 $3 [0:0]" >>$FIREHOL_DIR/fast/${n}.${t}.policy
2013-09-28 09:03:57 +00:00
;;
2013-12-01 14:59:44 +00:00
-N) echo ":$2 - [0:0]" >>$FIREHOL_DIR/fast/${n}.${t}.chains
2013-09-28 09:03:57 +00:00
;;
2013-12-01 14:59:44 +00:00
-A) ${CAT_CMD} <<EOFA >>$FIREHOL_DIR/fast/${n}.${t}.rules
2013-09-28 09:03:57 +00:00
${@}
EOFA
;;
2013-12-01 14:59:44 +00:00
-I) ${CAT_CMD} <<EOFI >>$FIREHOL_DIR/fast/${n}.${t}.rules
2013-09-28 09:03:57 +00:00
${@}
EOFI
;;
# if it is none of the above, we execute it normally.
2013-12-01 14:59:44 +00:00
*) echo "Ignoring command '${cmd} -t ${t} ${@}'"
2013-09-28 09:03:57 +00:00
;;
esac
2013-12-01 14:59:44 +00:00
test ! -f $FIREHOL_DIR/fast/${n}s/${t} && ${TOUCH_CMD} $FIREHOL_DIR/fast/${n}s/${t}
2013-09-28 09:03:57 +00:00
return 0
}
FIREHOL_COMMAND_COUNTER=0
2002-12-23 14:39:19 +00:00
iptables() {
2013-09-28 09:03:57 +00:00
[ $firewall_policy_applied -eq 0 ] && firewall_policy
2013-09-28 09:03:57 +00:00
if [ $FIREHOL_FAST_ACTIVATION -eq 1 ]
then
2013-12-01 14:59:44 +00:00
run_fast iptables "${@}"
2013-09-28 09:03:57 +00:00
else
postprocess -ns "${IPTABLES_CMD}" "$@"
2013-09-28 09:03:57 +00:00
FIREHOL_COMMAND_COUNTER=$[FIREHOL_COMMAND_COUNTER + 1]
fi
2002-12-23 14:39:19 +00:00
return 0
}
FIREHOL_COMMAND6_COUNTER=0
ip6tables() {
[ $firewall_policy6_applied -eq 0 ] && firewall_policy6
if [ $FIREHOL_FAST_ACTIVATION -eq 1 ]
then
2013-12-01 14:59:44 +00:00
run_fast ip6tables "${@}"
else
postprocess -ns "${IP6TABLES_CMD}" "$@"
FIREHOL_COMMAND6_COUNTER=$[FIREHOL_COMMAND6_COUNTER + 1]
fi
return 0
}
iptables_both() {
if running_ipv4; then iptables "${@}" || return; fi
if running_ipv6; then ip6tables "${@}" || return; fi
return 0
}
2002-12-23 14:39:19 +00:00
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# INTERNAL FUNCTIONS BELLOW THIS POINT - Sub-commands
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Change the policy of an interface
# WHY:
# Not all interfaces have the same policy. The admin must have control over it.
# Here we just set what the admin wants. At the interface finalization we
# produce the iptables rules.
policy() {
work_realcmd_secondary ${FUNCNAME} "$@"
2002-12-23 14:39:19 +00:00
require_work set any || return 1
2002-12-23 14:39:19 +00:00
set_work_function "Setting policy of ${work_name} to ${1}"
work_policy="$*"
2002-12-23 14:39:19 +00:00
return 0
}
server() {
work_realcmd_secondary ${FUNCNAME} "$@"
2002-12-23 14:39:19 +00:00
require_work set any || return 1
smart_function server "$@"
return $?
}
server4() {
ipv4 server "$@"
}
server6() {
ipv6 server "$@"
}
server46() {
both server "$@"
}
2002-12-23 14:39:19 +00:00
client() {
work_realcmd_secondary ${FUNCNAME} "$@"
2002-12-23 14:39:19 +00:00
require_work set any || return 1
smart_function client "$@"
return $?
}
client4() {
ipv4 client "$@"
}
client6() {
ipv6 client "$@"
}
client46() {
both client "$@"
}
2002-12-23 14:39:19 +00:00
route() {
work_realcmd_secondary ${FUNCNAME} "$@"
2002-12-23 14:39:19 +00:00
require_work set router || return 1
smart_function server "$@"
return $?
}
route4() {
ipv4 route "$@"
}
route6() {
ipv6 route "$@"
}
route46() {
both route "$@"
}
2002-12-23 14:39:19 +00:00
# --- protection ---------------------------------------------------------------
protection() {
work_realcmd_secondary ${FUNCNAME} "$@"
2002-12-23 14:39:19 +00:00
require_work set any || return 1
local in="in"
local prface="${work_inface}"
local pre="pr"
local reverse=
2002-12-23 14:39:19 +00:00
if [ "${1}" = "reverse" ]
then
local reverse="reverse" # needed to recursion
local pre="prr" # in case a router has protections
# both ways, the second needs to
# have different chain names
local in="out" # reverse the interface
prface="${work_outface}"
shift
fi
local type="${1}"
local rate="${2}"
local burst="${3}"
test -z "${rate}" && rate="100/s"
test -z "${burst}" && burst="50"
set_work_function -ne "Generating protections on '${prface}' for ${work_cmd} '${work_name}'"
local x=
for x in ${type}
do
case "${x}" in
none|NONE)
return 0
;;
2006-04-22 17:26:18 +00:00
bad-packets|BAD-PACKETS)
protection ${reverse} "fragments new-tcp-w/o-syn malformed-xmas malformed-null malformed-bad invalid" "${rate}" "${burst}"
2006-04-22 17:26:18 +00:00
return $?
;;
2002-12-23 14:39:19 +00:00
strong|STRONG|full|FULL|all|ALL)
protection ${reverse} "fragments new-tcp-w/o-syn icmp-floods syn-floods malformed-xmas malformed-null malformed-bad invalid" "${rate}" "${burst}"
2002-12-23 14:39:19 +00:00
return $?
;;
invalid|INVALID)
iptables_both -A "${in}_${work_name}" -m conntrack --ctstate INVALID -j DROP || return 1
;;
2002-12-23 14:39:19 +00:00
fragments|FRAGMENTS)
if running_ipv4; then
push_namespace ipv4
local frag_status=0
local mychain="${pre}_${work_name}_fragments"
create_chain filter "${mychain}" "${in}_${work_name}" in custom "-f" || frag_status=$[frag_status+1]
set_work_function "Generating rules to be protected from packet fragments on '${prface}' for ${work_cmd} '${work_name}'"
rule in chain "${mychain}" loglimit "PACKET FRAGMENTS" action drop || frag_status=$[frag_status+1]
pop_namespace
if [ $frag_status -gt 0 ]
then
return 1
fi
fi
# IPv6 packet fragments can be used to
# evade stateless firewalls. FireHOL
# creates a stateful firewall with connection
# tracking, so fragments will be reassembled
# before checking.
2002-12-23 14:39:19 +00:00
;;
new-tcp-w/o-syn|NEW-TCP-W/O-SYN)
local mychain="${pre}_${work_name}_nosyn"
create_chain filter "${mychain}" "${in}_${work_name}" in proto tcp state NEW custom "! --syn" || return 1
2002-12-23 14:39:19 +00:00
set_work_function "Generating rules to be protected from new TCP connections without the SYN flag set on '${prface}' for ${work_cmd} '${work_name}'"
rule in chain "${mychain}" loglimit "NEW TCP w/o SYN" action drop || return 1
2002-12-23 14:39:19 +00:00
;;
icmp-floods|ICMP-FLOODS)
local mychain="${pre}_${work_name}_icmpflood"
if running_ipv4; then
ipv4 create_chain filter "${mychain}" "${in}_${work_name}" in proto icmp custom "--icmp-type echo-request" || return 1
fi
if running_ipv6; then
ipv6 create_chain filter "${mychain}" "${in}_${work_name}" in proto icmpv6 custom "--icmpv6-type echo-request" || return 1
fi
2002-12-23 14:39:19 +00:00
set_work_function "Generating rules to be protected from ICMP floods on '${prface}' for ${work_cmd} '${work_name}'"
rule in chain "${mychain}" limit "${rate}" "${burst}" action return || return 1
rule in chain "${mychain}" loglimit "ICMP FLOOD" action drop || return 1
2002-12-23 14:39:19 +00:00
;;
syn-floods|SYN-FLOODS)
local mychain="${pre}_${work_name}_synflood"
create_chain filter "${mychain}" "${in}_${work_name}" in proto tcp custom "--syn" || return 1
2002-12-23 14:39:19 +00:00
set_work_function "Generating rules to be protected from TCP SYN floods on '${prface}' for ${work_cmd} '${work_name}'"
rule in chain "${mychain}" limit "${rate}" "${burst}" action return || return 1
rule in chain "${mychain}" loglimit "SYN FLOOD" action drop || return 1
2002-12-23 14:39:19 +00:00
;;
all-floods|ALL-FLOODS)
local mychain="${pre}_${work_name}_allflood"
create_chain filter "${mychain}" "${in}_${work_name}" in state NEW || return 1
set_work_function "Generating rules to be protected from ALL floods on '${prface}' for ${work_cmd} '${work_name}'"
rule in chain "${mychain}" limit "${rate}" "${burst}" action return || return 1
rule in chain "${mychain}" loglimit "ALL FLOOD" action drop || return 1
;;
2002-12-23 14:39:19 +00:00
malformed-xmas|MALFORMED-XMAS)
local mychain="${pre}_${work_name}_malxmas"
create_chain filter "${mychain}" "${in}_${work_name}" in proto tcp custom "--tcp-flags ALL ALL" || return 1
2002-12-23 14:39:19 +00:00
set_work_function "Generating rules to be protected from packets with all TCP flags set on '${prface}' for ${work_cmd} '${work_name}'"
rule in chain "${mychain}" loglimit "MALFORMED XMAS" action drop || return 1
2002-12-23 14:39:19 +00:00
;;
malformed-null|MALFORMED-NULL)
local mychain="${pre}_${work_name}_malnull"
create_chain filter "${mychain}" "${in}_${work_name}" in proto tcp custom "--tcp-flags ALL NONE" || return 1
2002-12-23 14:39:19 +00:00
set_work_function "Generating rules to be protected from packets with all TCP flags unset on '${prface}' for ${work_cmd} '${work_name}'"
rule in chain "${mychain}" loglimit "MALFORMED NULL" action drop || return 1
2002-12-23 14:39:19 +00:00
;;
malformed-bad|MALFORMED-BAD)
local mychain="${pre}_${work_name}_malbad"
create_chain filter "${mychain}" "${in}_${work_name}" in proto tcp custom "--tcp-flags SYN,FIN SYN,FIN" || return 1
2002-12-23 14:39:19 +00:00
set_work_function "Generating rules to be protected from packets with illegal TCP flags on '${prface}' for ${work_cmd} '${work_name}'"
rule in chain "${in}_${work_name}" action "${mychain}" proto tcp custom "--tcp-flags SYN,RST SYN,RST" || return 1
rule in chain "${in}_${work_name}" action "${mychain}" proto tcp custom "--tcp-flags ALL SYN,RST,ACK,FIN,URG" || return 1
rule in chain "${in}_${work_name}" action "${mychain}" proto tcp custom "--tcp-flags ALL FIN,URG,PSH" || return 1
2002-12-23 14:39:19 +00:00
rule in chain "${mychain}" loglimit "MALFORMED BAD" action drop || return 1
2002-12-23 14:39:19 +00:00
;;
*)
error "Protection '${x}' does not exists."
return 1
;;
esac
done
return 0
}
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# KERNEL MODULE MANAGEMENT
2002-12-23 14:39:19 +00:00
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Manage kernel modules
# WHY:
# We need to load a set of kernel modules during postprocessing, and after the
# new firewall has been activated.
# The whole point of the following code, is not to attempt loading modules when
# they are compiled into the kernel.
# Try to find the current kernel configuration
KERNEL_CONFIG=
if [ -f "/proc/config" ]
then
KERNEL_CONFIG="/proc/config"
${CAT_CMD} /proc/config >"${FIREHOL_DIR}/kcfg" || KERNEL_CONFIG=
fi
if [ -z "${KERNEL_CONFIG}" -a -f "/proc/config.gz" ]
then
KERNEL_CONFIG="/proc/config.gz"
zcat_cmd /proc/config.gz >"${FIREHOL_DIR}/kcfg" || KERNEL_CONFIG=
fi
if [ -z "${KERNEL_CONFIG}" -a -f "/lib/modules/`${UNAME_CMD} -r`/build/.config" ]
then
KERNEL_CONFIG="/lib/modules/`${UNAME_CMD} -r`/build/.config"
"${CAT_CMD}" "${KERNEL_CONFIG}" >"${FIREHOL_DIR}/kcfg" || KERNEL_CONFIG=
fi
if [ -z "${KERNEL_CONFIG}" -a -f "/boot/config-`${UNAME_CMD} -r`" ]
then
KERNEL_CONFIG="/boot/config-`${UNAME_CMD} -r`"
"${CAT_CMD}" "${KERNEL_CONFIG}" >"${FIREHOL_DIR}/kcfg" || KERNEL_CONFIG=
fi
if [ -z "${KERNEL_CONFIG}" -a -f "/usr/src/linux/.config" ]
then
KERNEL_CONFIG="/usr/src/linux/.config"
"${CAT_CMD}" "${KERNEL_CONFIG}" >"${FIREHOL_DIR}/kcfg" || KERNEL_CONFIG=
fi
# Did we managed to find the kernel configuration?
if [ ! -z "{$KERNEL_CONFIG}" -a -s "${FIREHOL_DIR}/kcfg" ]
then
# We found a kernel configuration
# Load all the definitions for CONFIG_*_NF_* variables
# We grep what we care for, to make sure there is no garbage or malicious code
# in the file we will run.
"${CAT_CMD}" "${FIREHOL_DIR}/kcfg" | ${GREP_CMD} -e "^CONFIG_[A-Z0-9_]\+_NF_[A-Z0-9_]\+=[ynm]$" >"${FIREHOL_DIR}/kcfg.nf"
# run it to get the variables
source "${FIREHOL_DIR}/kcfg.nf"
else
# We could not find a kernel configuration
KERNEL_CONFIG=
if [ ! ${FIREHOL_LOAD_KERNEL_MODULES} -eq 0 ]
then
echo >&2 " "
echo >&2 " IMPORTANT WARNING:"
echo >&2 " ------------------"
echo >&2 " FireHOL cannot find your current kernel configuration."
echo >&2 " Please, either compile your kernel with /proc/config,"
echo >&2 " or make sure there is a valid kernel config in:"
echo >&2 " /usr/src/linux/.config"
echo >&2 " "
echo >&2 " Because of this, FireHOL will simply attempt to load"
echo >&2 " all kernel modules for the services used, without"
echo >&2 " being able to detect failures."
echo >&2 " "
sleep 2
fi
fi
# activation-phase command to check for the existance of
# a kernel configuration directive. It returns:
# 0 = module is already in the kernel
# 1 = module can be loaded with modprobe
# 2 = no info about this module in the kernel
check_kernel_config() {
# In kernels 2.6.20+ _IP_ was removed from kernel iptables config names.
# A few kernels have _CONNTRACT_ replaced with _CT_ for certain modules.
# Try all versions.
# the original way
eval local kcfg1="\$${1}"
# without _IP_
local t=`echo ${1} | ${SED_CMD} "s/_IP_//g"`
eval local kcfg2="\$${t}"
# _CONNTRACK_ as _CT_
local t=`echo ${1} | ${SED_CMD} "s/_CONNTRACK_/_CT_/g"`
eval local kcfg3="\$${t}"
# prefer the kernel 2.6.20+ way
if [ ! -z "${kcfg2}" ]
then
kcfg="${kcfg2}"
elif [ ! -z "${kcfg3}" ]
then
kcfg="${kcfg3}"
else
kcfg="${kcfg1}"
fi
case ${kcfg} in
y) return 0
;;
m) return 1
;;
*) return 2
;;
esac
return 2
}
# activation-phase command to check for the existance of
# a kernel module. It returns:
# 0 = module is already in the kernel
# 1 = module can be loaded with modprobe
# 2 = no info about this module in the kernel
check_kernel_module() {
local mod="${1}"
case ${mod} in
ip_tables)
test -f /proc/net/ip_tables_names && return 0
check_kernel_config CONFIG_IP_NF_IPTABLES
test $? -ne 0 && check_kernel_config CONFIG_NF_TABLES_IPV4
return $?
;;
ip6_tables)
test -f /proc/net/ip6_tables_names && return 0
check_kernel_config CONFIG_NF_TABLES_IPV6
return $?
;;
ip_conntrack|nf_conntrack)
test -f /proc/net/ip_conntrack -o -f /proc/net/nf_conntrack && return 0
check_kernel_config CONFIG_IP_NF_CONNTRACK
test $? -ne 0 && check_kernel_config CONFIG_NF_CONNTRACK_IPV4
return $?
;;
ip_conntrack_*|nf_conntrack_*)
local mnam="CONFIG_IP_NF_`echo ${mod} | ${CUT_CMD} -d '_' -f 3- | ${TR_CMD} a-z A-Z`"
check_kernel_config ${mnam}
return $?
;;
ip_nat_*|nf_nat_*)
local mnam="CONFIG_IP_NF_NAT_`echo ${mod} | ${CUT_CMD} -d '_' -f 3- | ${TR_CMD} a-z A-Z`"
check_kernel_config ${mnam}
return $?
;;
*)
return 2
;;
esac
return 2
}
# activation-phase command to load a kernel module.
LOADED_KERNEL_MODULES=
load_kernel_module() {
local mod="${1}"
if [ ! ${FIREHOL_LOAD_KERNEL_MODULES} -eq 0 ]
then
local m=
for m in ${LOADED_KERNEL_MODULES}
do
test "${m}" = "${mod}" && return 0
done
LOADED_KERNEL_MODULES="${LOADED_KERNEL_MODULES} ${mod}"
modprobe_cmd ${mod} -q
if [ $? -gt 0 ]
then
check_kernel_module ${mod} || runtime_error warn 1 ${FIREHOL_LINEID} "${MODPROBE_CMD}" ${mod} -q
fi
fi
return 0
}
# Processing-phase command to tell FireHOL to find one or more
# kernel modules to load, during activation-phase.
2002-12-23 14:39:19 +00:00
require_kernel_module() {
local new="${1}"
local m=
for m in ${FIREHOL_KERNEL_MODULES}
do
test "${m}" = "${new}" && return 0
done
set_work_function "Adding kernel module '${new}' in the list of kernel modules to load"
2002-12-23 14:39:19 +00:00
FIREHOL_KERNEL_MODULES="${FIREHOL_KERNEL_MODULES} ${new}"
return 0
}
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# INTERNAL FUNCTIONS BELLOW THIS POINT - FireHOL internals
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
set_work_function() {
local show_explain=1
test "$1" = "-ne" && shift && local show_explain=0
work_function="$*"
if [ "${FIREHOL_MODE}" = "EXPLAIN" ]
then
test ${show_explain} -eq 1 && printf "\n# %s\n" "$*"
elif [ ${FIREHOL_CONF_SHOW} -eq 1 ]
then
test ${show_explain} -eq 1 && printf "\n# INFO>>> %s\n" "$*" >>${FIREHOL_OUTPUT}
fi
}
2002-12-23 14:39:19 +00:00
# ------------------------------------------------------------------------------
# Check the status of the current primary command.
# WHY:
# Some sanity check for the order of commands in the configuration file.
# Each function has a "require_work type command" in order to check that it is
# placed in a valid point. This means that if you place a "route" command in an
# interface section (and many other combinations) it will fail.
2002-12-23 14:39:19 +00:00
require_work() {
local type="${1}"
local cmd="${2}"
case "${type}" in
clear)
test ! -z "${work_cmd}" && error "Previous work was not applied." && return 1
;;
set)
test -z "${work_cmd}" && error "The command used requires that a primary command is set." && return 1
test ! "${work_cmd}" = "${cmd}" -a ! "${cmd}" = "any" && error "Primary command is '${work_cmd}' but '${cmd}' is required." && return 1
;;
*)
error "Unknown work status '${type}'."
return 1
;;
esac
return 0
}
# ------------------------------------------------------------------------------
# Finalizes the rules of the last primary command.
# WHY:
# At the end of an interface or router we need to add some code to apply its
# policy, accept all related packets, etc.
# Finalization occures automatically when a new primary command is executed and
# when the configuration file finishes.
close_cmd() {
set_work_function -ne "Closing last open primary command (${work_cmd}/${work_name})"
case "${work_cmd}" in
interface)
close_interface || return 1
;;
router)
close_router || return 1
;;
'')
;;
*)
error "Unknown work '${work_cmd}'."
return 1
;;
esac
# Reset the current status variables to empty/default
work_counter4=0
work_counter6=0
2002-12-23 14:39:19 +00:00
work_cmd=
work_realcmd=("(unset)")
2002-12-23 14:39:19 +00:00
work_name=
work_inface=
work_outface=
work_policy=
2002-12-23 14:39:19 +00:00
return 0
}
2002-12-23 14:39:19 +00:00
# ------------------------------------------------------------------------------
# close_interface
# WHY:
# Finalizes the rules for the last interface().
close_interface() {
require_work set interface || return 1
close_all_groups
2002-12-23 14:39:19 +00:00
set_work_function "Finilizing interface '${work_name}'"
# Accept all related traffic to the established connections
rule chain "in_${work_name}" state RELATED action ACCEPT || return 1
rule chain "out_${work_name}" state RELATED action ACCEPT || return 1
# make sure we have a policy
test -z "${work_policy}" && work_policy="${DEFAULT_INTERFACE_POLICY}"
2002-12-23 14:39:19 +00:00
case "${work_policy}" in
return|RETURN)
return 0
;;
accept|ACCEPT)
;;
*)
if [ "${FIREHOL_DROP_ORPHAN_TCP_ACK_FIN}" = "1" ]
then
# Silently drop orphan TCP/ACK FIN packets
rule chain "in_${work_name}" proto tcp custom "--tcp-flags ALL ACK,FIN" action DROP || return 1
rule reverse chain "out_${work_name}" proto tcp custom "--tcp-flags ALL ACK,FIN" action DROP || return 1
fi
2010-04-08 22:27:18 +00:00
local -a inlog=(loglimit "IN-${work_name}")
local -a outlog=(loglimit "OUT-${work_name}")
2002-12-23 14:39:19 +00:00
;;
esac
rule chain "in_${work_name}" "${inlog[@]}" action ${work_policy} || return 1
rule reverse chain "out_${work_name}" "${outlog[@]}" action ${work_policy} || return 1
2002-12-23 14:39:19 +00:00
return 0
}
# ------------------------------------------------------------------------------
# close_router
# WHY:
# Finalizes the rules for the last router().
close_router() {
2002-09-05 20:57:59 +00:00
require_work set router || return 1
close_all_groups
set_work_function "Finilizing router '${work_name}'"
# Accept all related traffic to the established connections
rule chain "in_${work_name}" state RELATED action ACCEPT || return 1
rule chain "out_${work_name}" state RELATED action ACCEPT || return 1
# make sure we have a policy
test -z "${work_policy}" && work_policy="${DEFAULT_ROUTER_POLICY}"
case "${work_policy}" in
return|RETURN)
return 0
;;
accept|ACCEPT)
;;
*)
if [ "${FIREHOL_DROP_ORPHAN_TCP_ACK_FIN}" = "1" ]
then
# Silently drop orphan TCP/ACK FIN packets
rule chain "in_${work_name}" proto tcp custom "--tcp-flags ALL ACK,FIN" action DROP || return 1
rule reverse chain "out_${work_name}" proto tcp custom "--tcp-flags ALL ACK,FIN" action DROP || return 1
fi
2010-04-08 22:27:18 +00:00
local -a inlog=(loglimit "PASS-${work_name}")
local -a outlog=(loglimit "PASS-${work_name}")
;;
esac
rule chain "in_${work_name}" "${inlog[@]}" action ${work_policy} || return 1
rule reverse chain "out_${work_name}" "${outlog[@]}" action ${work_policy} || return 1
2002-09-05 20:57:59 +00:00
return 0
}
2002-12-23 14:39:19 +00:00
# ------------------------------------------------------------------------------
# close_master
# WHY:
# Finalizes the rules for the whole firewall.
# It assummes there is not primary command open.
2002-09-05 20:57:59 +00:00
close_master() {
set_work_function "Finilizing firewall policies"
2014-03-13 01:08:34 +00:00
if [ $connmark_count -gt 0 ]
then
# if connmark has been used, add MARK restoration from CONNMARK, at the top of mangle
2014-03-13 01:08:34 +00:00
# copy CONNMARK to MARK
iptables_both -t mangle -I OUTPUT 1 -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark
iptables_both -t mangle -I PREROUTING 1 -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark
2014-03-13 01:08:34 +00:00
fi
# Accept all related traffic to the established connections
rule chain INPUT state RELATED action ACCEPT || return 1
rule chain OUTPUT state RELATED action ACCEPT || return 1
rule chain FORWARD state RELATED action ACCEPT || return 1
if [ "${FIREHOL_DROP_ORPHAN_TCP_ACK_FIN}" = "1" ]
then
# Silently drop orphan TCP/ACK FIN packets
rule chain INPUT proto tcp custom "--tcp-flags ALL ACK,FIN" action DROP || return 1
rule chain OUTPUT proto tcp custom "--tcp-flags ALL ACK,FIN" action DROP || return 1
rule chain FORWARD proto tcp custom "--tcp-flags ALL ACK,FIN" action DROP || return 1
fi
rule chain INPUT loglimit "IN-unknown" action ${UNMATCHED_INPUT_POLICY} || return 1
rule chain OUTPUT loglimit "OUT-unknown" action ${UNMATCHED_OUTPUT_POLICY} || return 1
rule chain FORWARD loglimit "PASS-unknown" action ${UNMATCHED_ROUTER_POLICY} || return 1
2002-09-05 20:57:59 +00:00
return 0
}
2002-12-23 14:39:19 +00:00
FIREHOL_GROUP_COUNTER=0
FIREHOL_GROUP_DEPTH=0
FIREHOL_GROUP_STACK=()
group() {
work_realcmd_primary ${FUNCNAME} "$@"
require_work set any || return 1
local type="${1}"; shift
case $type in
with|start|begin)
2014-02-03 22:49:51 +00:00
push_namespace "${FIREHOL_NS_CURR}"
# increase the counter
FIREHOL_GROUP_COUNTER=$[FIREHOL_GROUP_COUNTER + 1]
set_work_function "Starting new group No ${FIREHOL_GROUP_COUNTER}, under '${work_name}'"
# put the current name in the stack
FIREHOL_GROUP_STACK[$FIREHOL_GROUP_DEPTH]=${work_name}
FIREHOL_GROUP_DEPTH=$[FIREHOL_GROUP_DEPTH + 1]
# name for the new chain
mychain="group${FIREHOL_GROUP_COUNTER}"
# create the new chain
create_chain filter "in_${mychain}" "in_${work_name}" in "$@" || return 1
create_chain filter "out_${mychain}" "out_${work_name}" out reverse "$@" || return 1
# set a new name for new rules
work_name=${mychain}
;;
end|stop|close)
if [ ${FIREHOL_GROUP_DEPTH} -eq 0 ]
then
error "There is no group open to close."
return 1
fi
# pop one name from the stack
FIREHOL_GROUP_DEPTH=$[FIREHOL_GROUP_DEPTH - 1]
set_work_function "Closing group '${work_name}'. Now working under '${FIREHOL_GROUP_STACK[$FIREHOL_GROUP_DEPTH]}'"
work_name=${FIREHOL_GROUP_STACK[$FIREHOL_GROUP_DEPTH]}
2014-02-03 22:49:51 +00:00
pop_namespace
;;
*)
error "Statement 'group' requires the first argument to be one of with, start, begin, end, stop, close."
return 1
;;
esac
return 0
}
2014-02-03 22:49:51 +00:00
group4() {
ipv4 group "$@"
}
group6() {
ipv6 group "$@"
}
group46() {
both group "$@"
}
close_all_groups() {
while [ ${FIREHOL_GROUP_DEPTH} -gt 0 ]
do
group close || return 1
done
return 0
}
2002-12-23 14:39:19 +00:00
# ------------------------------------------------------------------------------
# rule - the heart of FireHOL - iptables commands generation
# WHY:
# This is the function that gives all the magic to FireHOL. Actually it is a
# wrapper for iptables, producing multiple iptables commands based on its
# arguments. The rest of FireHOL is simply a "driver" for this function.
2002-09-05 20:57:59 +00:00
# rule_action_param() is a function - part of rule() - to create the final iptables cmd
# taking into account the "action_param" parameter of the action.
# rule_action_param() should only be used within rule() - no other place
FIREHOL_ACCEPT_CHAIN_COUNT=0
rule_action_param() {
local iptables_cmd="${1}"; shift
local action="${1}"; shift
local protocol="${1}"; shift
local statenot="${1}"; shift
local state="${1}"; shift
local table="${1}"; shift
local -a action_param=()
# All arguments until the separator are the parameters of the action
local count=0
while [ ! -z "${1}" -a ! "A${1}" = "A--" ]
do
local -a action_param[$count]="${1}"
shift
local count=$[count + 1]
done
# If we don't have a seperator, generate an error
local sep="${1}"; shift
if [ ! "A${sep}" = "A--" ]
then
error "Internal Error, in parsing action_param parameters ($FUNCNAME '${action}' '${protocol}' '${statenot}' '${state}' '${table}' '${action_param[@]}' ${sep} '$@')."
return 1
fi
# Do the rule
case "${action}" in
NONE)
return 0
;;
ACCEPT)
# do we have any options for this accept?
if [ ! -z "${action_param[0]}" ]
then
# find the options we have
case "${action_param[0]}" in
"limit")
# limit NEW connections to the specified rate
local freq="${action_param[1]}"
local burst="${action_param[2]}"
local overflow="REJECT"
# if we have a custom overflow action, parse it.
test "${action_param[3]}" = "overflow" && local overflow="`echo "${action_param[4]}" | tr "a-z" "A-Z"`"
# unset the action_param, so that if this rule does not include NEW connections,
# we will not append anything to the generated iptables statements.
local -a action_param=()
# find is this rule matches NEW connections
local has_new=`echo "${state}" | grep -i NEW`
local do_accept_limit=0
if [ -z "${statenot}" ]
then
test ! -z "${has_new}" && local do_accept_limit=1
else
test -z "${has_new}" && local do_accept_limit=1
fi
# we have a match for NEW connections.
# redirect the traffic to a new chain, which will control
# the NEW connections while allowing all the other traffic
# to pass.
if [ "${do_accept_limit}" = "1" ]
then
local accept_limit_chain="`echo "ACC LIM ${freq} ${burst} ${overflow}" | tr " /." "___"`"
# does the chain we need already exist?
#if [ ! -f "${FIREHOL_CHAINS_DIR}/${accept_limit_chain}.${iptables_cmd}" ]
if [ -z "${FIREHOL_CHAINS[${accept_limit_chain}.${iptables_cmd}]}" ]
then
# the chain does not exist. create it.
$iptables_cmd ${table} -N "${accept_limit_chain}"
FIREHOL_CHAINS[${accept_limit_chain}.${iptables_cmd}]="1"
#touch "${FIREHOL_CHAINS_DIR}/${accept_limit_chain}.${iptables_cmd}"
# first, if the traffic is not a NEW connection, allow it.
# doing this first will speed up normal traffic.
$iptables_cmd ${table} -A "${accept_limit_chain}" -m conntrack ! --ctstate NEW -j ACCEPT
# accept NEW connections within the given limits.
$iptables_cmd ${table} -A "${accept_limit_chain}" -m limit --limit "${freq}" --limit-burst "${burst}" -j ACCEPT
# log the overflow NEW connections reaching this step within the new chain
local -a logopts_arg=()
if [ "${FIREHOL_LOG_MODE}" = "ULOG" ]
then
2010-04-08 22:27:18 +00:00
local -a logopts_arg=("--ulog-prefix=${FIREHOL_LOG_PREFIX}LIMIT_OVERFLOW:")
elif [ "${FIREHOL_LOG_MODE}" = "NFLOG" ]
then
2010-04-08 22:27:18 +00:00
local -a logopts_arg=("--nflog-prefix=${FIREHOL_LOG_PREFIX}LIMIT_OVERFLOW:")
else
2010-04-08 22:27:18 +00:00
local -a logopts_arg=("--log-level" "${FIREHOL_LOG_LEVEL}" "--log-prefix=${FIREHOL_LOG_PREFIX}LIMIT_OVERFLOW:")
fi
$iptables_cmd ${table} -A "${accept_limit_chain}" -m limit --limit "${FIREHOL_LOG_FREQUENCY}" --limit-burst "${FIREHOL_LOG_BURST}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}"
# if the overflow is to be rejected is tcp, reject it with TCP-RESET
if [ "${overflow}" = "REJECT" ]
then
$iptables_cmd ${table} -A "${accept_limit_chain}" -p tcp -j REJECT --reject-with tcp-reset
fi
# do the specified action on the overflow
$iptables_cmd ${table} -A "${accept_limit_chain}" -j ${overflow}
fi
# send the rule to be generated to this chain
local action=${accept_limit_chain}
fi
;;
"recent")
# limit NEW connections to the specified rate
local name="${action_param[1]}"
local seconds="${action_param[2]}"
local hits="${action_param[3]}"
# unset the action_param, so that if this rule does not include NEW connections,
# we will not append anything to the generated iptables statements.
local -a action_param=()
# find is this rule matches NEW connections
local has_new=`echo "${state}" | grep -i NEW`
local do_accept_recent=0
if [ -z "${statenot}" ]
then
test ! -z "${has_new}" && local do_accept_recent=1
else
test -z "${has_new}" && local do_accept_recent=1
fi
# we have a match for NEW connections.
# redirect the traffic to a new chain, which will control
# the NEW connections while allowing all the other traffic
# to pass.
if [ "${do_accept_recent}" = "1" ]
then
local accept_recent_chain="`echo "ACC REC $name $seconds $hits" | tr " /." "___"`"
# does the chain we need already exist?
#if [ ! -f "${FIREHOL_CHAINS_DIR}/${accept_recent_chain}.${iptables_cmd}" ]
if [ -z "${FIREHOL_CHAINS[${accept_recent_chain}.${iptables_cmd}]}" ]
then
# the chain does not exist. create it.
$iptables_cmd ${table} -N "${accept_recent_chain}"
FIREHOL_CHAINS[${accept_recent_chain}.${iptables_cmd}]="1"
#touch "${FIREHOL_CHAINS_DIR}/${accept_recent_chain}.${iptables_cmd}"
# first, if the traffic is not a NEW connection, allow it.
# doing this first will speed up normal traffic.
$iptables_cmd ${table} -A "${accept_recent_chain}" -m conntrack ! --ctstate NEW -j ACCEPT
# accept NEW connections within the given limits.
$iptables_cmd ${table} -A "${accept_recent_chain}" -m recent --set --name "${name}"
local t1=
test ! -z $seconds && local t1="--seconds ${seconds}"
local t2=
test ! -z $hits && local t2="--hitcount ${hits}"
$iptables_cmd ${table} -A "${accept_recent_chain}" -m recent --update ${t1} ${t2} --name "${name}" -j RETURN
$iptables_cmd ${table} -A "${accept_recent_chain}" -j ACCEPT
fi
# send the rule to be generated to this chain
local action=${accept_recent_chain}
fi
;;
'knock')
# the name of the knock
local name="knock_${action_param[1]}"
# unset the action_param, so that if this rule does not include NEW connections,
# we will not append anything to the generated iptables statements.
local -a action_param=()
# does the knock chain exists?
#if [ ! -f "${FIREHOL_CHAINS_DIR}/${name}.${iptables_cmd}" ]
if [ -z "${FIREHOL_CHAINS[${name}.${iptables_cmd}]}" ]
then
# the chain does not exist. create it.
$iptables_cmd ${table} -N "${name}"
FIREHOL_CHAINS[${name}.${iptables_cmd}]="1"
#touch "${FIREHOL_CHAINS_DIR}/${name}.${iptables_cmd}"
$iptables_cmd -A "${name}" -m conntrack --ctstate ESTABLISHED -j ACCEPT
# knockd (http://www.zeroflux.org/knock/)
# will create more rules inside this chain to match NEW packets.
fi
# send the rule to be generated to this knock chain
local action=${name}
;;
*)
error "Internal error. Cannot understand action ${action} with parameter '${action_param[0]}'."
return 1
;;
esac
fi
;;
REJECT)
if [ "${action_param[1]}" = "auto" ]
then
if [ "${protocol}" = "tcp" -o "${protocol}" = "TCP" ]
then
local -a action_param=("--reject-with" "tcp-reset")
else
local -a action_param=()
fi
fi
;;
esac
$iptables_cmd "$@" -j "${action}" "${action_param[@]}"
}
2002-09-05 20:57:59 +00:00
rule() {
local table=
2002-09-05 20:57:59 +00:00
local chain=
local inface=any
local infacenot=
local outface=any
local outfacenot=
local physin=any
local physinnot=
local physout=any
local physoutnot=
local mac=any
local macnot=
local src4=default
local src4not=
local dst4=default
local dst4not=
local src6=default
local src6not=
local dst6=default
local dst6not=
2002-09-05 20:57:59 +00:00
local srctype=
local srctypenot=
local dsttype=
local dsttypenot=
2002-09-05 20:57:59 +00:00
local sport=any
local sportnot=
local dport=any
local dportnot=
local proto=any
local protonot=
local uid=any
local uidnot=
local gid=any
local gidnot=
local pid=any
local pidnot=
local sid=any
local sidnot=
2003-07-20 21:46:41 +00:00
local cmd=any
local cmdnot=
local mark=any
local marknot=
local dscp=any
local dscptype=
local despnot=
local tos=any
local tosnot=
2002-09-05 20:57:59 +00:00
local log=
local logtxt=
local loglevel=
2002-09-05 20:57:59 +00:00
local limit=
local burst=
local iplimit=
local iplimit_mask=
local action=
local state=
local statenot=
local failed=0
local reverse=0
local swi=0
local swo=0
local custom=
# if set to 1, all owner module options will be ignored
local noowner=0
# if set to 1, all mac options will be ignored
local nomac=0
# if set to 1, MIRROR will be converted to REJECT
local nomirror=0
# if set to 1, log and loglimit are ignored.
local nolog=0
# if set to 1, detection algorithm about overwritting optional rule
# parameters will take place.
local softwarnings=1
# set it, in order to be local
local -a action_param=()
# accounting
local accounting=
while [ ! -z "${1}" ]
2002-09-05 20:57:59 +00:00
do
case "${1}" in
2002-09-05 20:57:59 +00:00
reverse|REVERSE)
local reverse=1
2002-09-05 20:57:59 +00:00
shift
;;
table|TABLE)
test ${softwarnings} -eq 1 -a ! -z "${table}" && softwarning "Overwritting param: ${1} '${chain}' becomes '${2}'"
local table="-t ${2}"
shift 2
;;
2002-09-05 20:57:59 +00:00
chain|CHAIN)
test ${softwarnings} -eq 1 -a ! -z "${chain}" && softwarning "Overwritting param: ${1} '${chain}' becomes '${2}'"
local chain="${2}"
2002-09-05 20:57:59 +00:00
shift 2
;;
inface|INFACE)
shift
if [ ${reverse} -eq 0 ]
then
local infacenot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
local infacenot="!"
else
if [ ${swi} -eq 1 ]
then
work_inface="${1}"
fi
2002-09-05 20:57:59 +00:00
fi
test ${softwarnings} -eq 1 -a ! "${inface}" = "any" && softwarning "Overwritting param: inface '${inface}' becomes '${1}'"
local inface="${1}"
2002-09-05 20:57:59 +00:00
else
local outfacenot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
local outfacenot="!"
else
if [ ${swo} -eq 1 ]
then
work_outface="$1"
fi
2002-09-05 20:57:59 +00:00
fi
test ${softwarnings} -eq 1 -a ! "${outface}" = "any" && softwarning "Overwritting param: outface '${outface}' becomes '${1}'"
local outface="${1}"
2002-09-05 20:57:59 +00:00
fi
shift
;;
outface|OUTFACE)
shift
if [ ${reverse} -eq 0 ]
then
local outfacenot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
local outfacenot="!"
else
if [ ${swo} -eq 1 ]
then
work_outface="${1}"
fi
2002-09-05 20:57:59 +00:00
fi
test ${softwarnings} -eq 1 -a ! "${outface}" = "any" && softwarning "Overwritting param: outface '${outface}' becomes '${1}'"
local outface="${1}"
2002-09-05 20:57:59 +00:00
else
local infacenot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
local infacenot="!"
else
if [ ${swi} -eq 1 ]
then
work_inface="${1}"
fi
2002-09-05 20:57:59 +00:00
fi
test ${softwarnings} -eq 1 -a ! "${inface}" = "any" && softwarning "Overwritting param: inface '${inface}' becomes '${1}'"
local inface="${1}"
2002-09-05 20:57:59 +00:00
fi
shift
;;
physin|PHYSIN)
shift
if [ ${reverse} -eq 0 ]
then
local physinnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
local physinnot="!"
fi
test ${softwarnings} -eq 1 -a ! "${physin}" = "any" && softwarning "Overwritting param: physin '${physin}' becomes '${1}'"
local physin="${1}"
else
local physoutnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
local physoutnot="!"
fi
test ${softwarnings} -eq 1 -a ! "${physout}" = "any" && softwarning "Overwritting param: physout '${physout}' becomes '${1}'"
local physout="${1}"
fi
shift
;;
physout|PHYSOUT)
shift
if [ ${reverse} -eq 0 ]
then
local physoutnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
local physoutnot="!"
fi
test ${softwarnings} -eq 1 -a ! "${physout}" = "any" && softwarning "Overwritting param: physout '${physout}' becomes '${1}'"
local physout="${1}"
else
local physinnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
local physinnot="!"
fi
test ${softwarnings} -eq 1 -a ! "${physin}" = "any" && softwarning "Overwritting param: physin '${physin}' becomes '${1}'"
local physin="${1}"
fi
shift
;;
mac|MAC)
shift
local macnot=
2004-03-03 21:24:41 +00:00
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
2004-03-03 21:24:41 +00:00
shift
test ${nomac} -eq 0 && local macnot="!"
fi
2004-03-03 21:24:41 +00:00
test ${softwarnings} -eq 1 -a ! "${mac}" = "any" && softwarning "Overwritting param: mac '${mac}' becomes '${1}'"
test ${nomac} -eq 0 && local mac="${1}"
shift
;;
src|SRC|source|SOURCE|src4|src6)
if [ "${1}" = "src4" ]
then
if ! push_namespace ipv4; then return 1; fi
elif [ "${1}" = "src6" ]
then
if ! push_namespace ipv6; then return 1; fi
else
push_namespace "${FIREHOL_NS_CURR}"
fi
2002-09-05 20:57:59 +00:00
shift
if [ ${reverse} -eq 0 ]
then
if running_ipv4; then
local src4not=
fi
if running_ipv6; then
local src6not=
fi
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
if running_ipv4; then
local src4not="!"
fi
if running_ipv6; then
local src6not="!"
fi
fi
if running_ipv4; then
test ${softwarnings} -eq 1 -a ! "${src4}" = "default" && softwarning "Overwritting param: src4 '${src4}' becomes '${1}'"
local src4=$(ipv4 eval_param "${1}")
fi
if running_ipv6; then
test ${softwarnings} -eq 1 -a ! "${src6}" = "default" && softwarning "Overwritting param: src6 '${src6}' becomes '${1}'"
local src6=$(ipv6 eval_param "${1}")
2002-09-05 20:57:59 +00:00
fi
else
if running_ipv4; then
local dst4not=
fi
if running_ipv6; then
local dst6not=
fi
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
if running_ipv4; then
local dst4not="!"
fi
if running_ipv6; then
local dst6not="!"
fi
fi
if running_ipv4; then
test ${softwarnings} -eq 1 -a ! "${dst4}" = "default" && softwarning "Overwritting param: dst4 '${dst4}' becomes '${1}'"
local dst4=$(ipv4 eval_param "${1}")
fi
if running_ipv6; then
test ${softwarnings} -eq 1 -a ! "${dst6}" = "default" && softwarning "Overwritting param: dst6 '${dst6}' becomes '${1}'"
local dst6=$(ipv6 eval_param "${1}")
2002-09-05 20:57:59 +00:00
fi
fi
pop_namespace
2002-09-05 20:57:59 +00:00
shift
;;
dst|DST|destination|DESTINATION|dst4|dst6)
if [ "${1}" = "dst4" ]
then
if ! push_namespace ipv4; then return 1; fi
elif [ "${1}" = "dst6" ]
then
if ! push_namespace ipv6; then return 1; fi
else
push_namespace "${FIREHOL_NS_CURR}"
fi
2002-09-05 20:57:59 +00:00
shift
if [ ${reverse} -eq 0 ]
then
if running_ipv4; then
local dst4not=
fi
if running_ipv6; then
local dst6not=
fi
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
if running_ipv4; then
local dst4not="!"
fi
if running_ipv6; then
local dst6not="!"
fi
fi
if running_ipv4; then
test ${softwarnings} -eq 1 -a ! "${dst4}" = "default" && softwarning "Overwritting param: dst4 '${dst4}' becomes '${1}'"
local dst4=$(ipv4 eval_param "${1}")
fi
if running_ipv6; then
test ${softwarnings} -eq 1 -a ! "${dst6}" = "default" && softwarning "Overwritting param: dst6 '${dst6}' becomes '${1}'"
local dst6=$(ipv6 eval_param "${1}")
2002-09-05 20:57:59 +00:00
fi
else
if running_ipv4; then
local src4not=
fi
if running_ipv6; then
local src6not=
fi
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
if running_ipv4; then
local src4not="!"
fi
if running_ipv6; then
local src6not="!"
fi
fi
if running_ipv4; then
test ${softwarnings} -eq 1 -a ! "${src4}" = "default" && softwarning "Overwritting param: src6 '${src4}' becomes '${1}'"
local src4=$(ipv4 eval_param "${1}")
fi
if running_ipv6; then
test ${softwarnings} -eq 1 -a ! "${src6}" = "default" && softwarning "Overwritting param: src6 '${src6}' becomes '${1}'"
local src6=$(ipv6 eval_param "${1}")
2002-09-05 20:57:59 +00:00
fi
fi
pop_namespace
2002-09-05 20:57:59 +00:00
shift
;;
srctype|SRCTYPE|sourcetype|SOURCETYPE)
shift
if [ ${reverse} -eq 0 ]
then
local srctypenot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
local srctypenot="!"
fi
test ${softwarnings} -eq 1 -a ! "${srctype}" = "" && softwarning "Overwritting param: srctype '${srctype}' becomes '${1}'"
local srctype="`echo ${1} | ${SED_CMD} -e "s|^ \+||" -e "s| \+\$||" -e "s| \+|,|g" | ${TR_CMD} a-z A-Z`"
else
local dsttypenot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
local dsttypenot="!"
fi
test ${softwarnings} -eq 1 -a ! "${dsttype}" = "" && softwarning "Overwritting param: dsttype '${dsttype}' becomes '${1}'"
local dsttype="`echo ${1} | ${SED_CMD} -e "s|^ \+||" -e "s| \+\$||" -e "s| \+|,|g" | ${TR_CMD} a-z A-Z`"
fi
shift
;;
dsttype|DSTTYPE|destinationtype|DESTINATIONTYPE)
shift
if [ ${reverse} -eq 0 ]
then
local dsttypenot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
local dsttypenot="!"
fi
test ${softwarnings} -eq 1 -a ! "${dsttype}" = "" && softwarning "Overwritting param: dsttype '${dsttype}' becomes '${1}'"
local dsttype="`echo ${1} | ${SED_CMD} -e "s|^ \+||" -e "s| \+\$||" -e "s| \+|,|g" | ${TR_CMD} a-z A-Z`"
else
local srctypenot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
local srctypenot="!"
fi
test ${softwarnings} -eq 1 -a ! "${srctype}" = "" && softwarning "Overwritting param: srctype '${srctype}' becomes '${1}'"
local srctype="`echo ${1} | ${SED_CMD} -e "s|^ \+||" -e "s| \+\$||" -e "s| \+|,|g" | ${TR_CMD} a-z A-Z`"
fi
shift
;;
2002-09-05 20:57:59 +00:00
sport|SPORT|sourceport|SOURCEPORT)
shift
if [ ${reverse} -eq 0 ]
then
local sportnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
local sportnot="!"
2002-09-05 20:57:59 +00:00
fi
test ${softwarnings} -eq 1 -a ! "${sport}" = "any" && softwarning "Overwritting param: sport '${sport}' becomes '${1}'"
local sport="${1}"
2002-09-05 20:57:59 +00:00
else
local dportnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
local dportnot="!"
2002-09-05 20:57:59 +00:00
fi
test ${softwarnings} -eq 1 -a ! "${dport}" = "any" && softwarning "Overwritting param: dport '${dport}' becomes '${1}'"
local dport="${1}"
2002-09-05 20:57:59 +00:00
fi
shift
;;
dport|DPORT|destinationport|DESTINATIONPORT)
shift
if [ ${reverse} -eq 0 ]
then
local dportnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
local dportnot="!"
2002-09-05 20:57:59 +00:00
fi
test ${softwarnings} -eq 1 -a ! "${dport}" = "any" && softwarning "Overwritting param: dport '${dport}' becomes '${1}'"
local dport="${1}"
2002-09-05 20:57:59 +00:00
else
local sportnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
local sportnot="!"
2002-09-05 20:57:59 +00:00
fi
test ${softwarnings} -eq 1 -a ! "${sport}" = "any" && softwarning "Overwritting param: sport '${sport}' becomes '${1}'"
local sport="${1}"
2002-09-05 20:57:59 +00:00
fi
shift
;;
proto|PROTO|protocol|PROTOCOL)
shift
local protonot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
local protonot="!"
2002-09-05 20:57:59 +00:00
fi
test ${softwarnings} -eq 1 -a ! "${proto}" = "any" && softwarning "Overwritting param: proto '${proto}' becomes '${1}'"
local proto="${1}"
2002-09-05 20:57:59 +00:00
shift
;;
mark|MARK)
shift
local marknot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
local marknot="!"
fi
test ${softwarnings} -eq 1 -a ! "${mark}" = "any" && softwarning "Overwritting param: mark '${mark}' becomes '${1}'"
local mark="${1}"
shift
;;
tos|TOS)
shift
local tosnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
local tosnot="!"
fi
test ${softwarnings} -eq 1 -a ! "${tos}" = "any" && softwarning "Overwritting param: tos '${tos}' becomes '${1}'"
local tos="${1}"
shift
;;
dscp|DSCP)
shift
local dscpnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
local dscpnot="!"
fi
test ${softwarnings} -eq 1 -a ! "${dscp}" = "any" && softwarning "Overwritting param: dscp '${dscp}' becomes '${1}'"
local dscp="${1}"
shift
if [ "${dscp}" = "class" ]
then
local dscptype="-class"
local dscp="${1}"
shift
fi
;;
2002-09-05 20:57:59 +00:00
action|ACTION)
test ${softwarnings} -eq 1 -a ! -z "${action}" && softwarning "Overwritting param: action '${action}' becomes '${2}'"
local action="${2}"
2002-09-05 20:57:59 +00:00
shift 2
local -a action_param=()
local action_is_chain=0
case "${action}" in
accept|ACCEPT)
local action="ACCEPT"
if [ "${1}" = "with" ]
then
shift
case "${1}" in
limit|LIMIT)
local -a action_param=("limit" "${2}" "${3}")
shift 3
if [ "${1}" = "overflow" ]
then
local -a action_param[3]="overflow"
local -a action_param[4]="${2}"
shift 2
fi
;;
recent|RECENT)
local -a action_param=("recent" "${2}" "${3}" "${4}")
shift 4
;;
knock|KNOCK)
local -a action_param=("knock" "${2}")
shift 2
;;
*)
error "Cannot understand action's '${action}' directive '${1}'"
return 1
;;
esac
fi
;;
deny|DENY|drop|DROP)
local action="DROP"
;;
reject|REJECT)
local action="REJECT"
if [ "${1}" = "with" ]
then
local -a action_param=("--reject-with" "${2}")
shift 2
else
local -a action_param=("--reject-with" "auto")
fi
;;
return|RETURN)
local action="RETURN"
;;
mirror|MIRROR)
local action="MIRROR"
test $nomirror -eq 1 && action="REJECT"
;;
none|NONE)
local action="NONE"
;;
snat|SNAT)
local action="SNAT"
if [ "${1}" = "to" ]
then
local -a action_param=()
local x=
for x in ${2}
do
local -a action_param=("${action_param[@]}" "--to-source" "${x}")
done
shift 2
else
error "${action} requires a 'to' argument."
return 1
fi
if [ ! "A${table}" = "A-t nat" ]
then
error "${action} must on a the 'nat' table."
return 1
fi
;;
dnat|DNAT)
local action="DNAT"
if [ "${1}" = "to" ]
then
local -a action_param=()
local x=
for x in ${2}
do
local -a action_param=("${action_param[@]}" "--to-destination" "${x}")
done
shift 2
else
error "${action} requires a 'to' argument"
return 1
fi
if [ ! "A${table}" = "A-t nat" ]
then
error "${action} must on a the 'nat' table."
return 1
fi
;;
redirect|REDIRECT)
local action="REDIRECT"
if [ "${1}" = "on-port" -o "${1}" = "to-port" -o "${1}" = "to" ]
then
local -a action_param=("--to-ports" "${2}")
shift 2
else
error "${action} requires a 'to-port' or 'to' argument."
return 1
fi
if [ ! "A${table}" = "A-t nat" ]
then
error "${action} must on a the 'nat' table."
return 1
fi
;;
tproxy|TPROXY)
local action="TPROXY"
local -a action_param=()
if [ "${1}" = "mark" -o "${1}" = "tproxy-mark" ]
then
local -a action_param=("--tproxy-mark" "${2}")
shift 2
fi
if [ "${1}" = "on-port" -o "${1}" = "to-port" -o "${1}" = "to" ]
then
local -a action_param=("${action_param[@]}" "--on-port" "${2}")
shift 2
else
error "${action} requires a 'on-port' or 'on-ip' argument."
return 1
fi
if [ "${1}" = "on-ip" -o "${1}" = "to-ip" ]
then
local -a action_param=("${action_param[@]}" "--on-ip" "${2}")
shift 2
fi
if [ ! "A${table}" = "A-t mangle" ]
then
error "${action} cannot be on '$table', only on a the 'mangle' table."
return 1
fi
;;
tos|TOS)
local action="TOS"
if [ "${1}" = "to" ]
then
local -a action_param=("--set-tos" "${2}")
shift 2
else
error "${action} requires a 'to' argument"
return 1
fi
if [ ! "A${table}" = "A-t mangle" ]
then
error "${action} must on a the 'mangle' table."
return 1
fi
;;
mark|MARK)
local action="MARK"
if [ "${1}" = "to" ]
then
local -a action_param=("--set-mark" "${2}")
shift 2
else
error "${action} requires a 'to' argument"
return 1
fi
if [ ! "A${table}" = "A-t mangle" ]
then
error "${action} must on a the 'mangle' table."
return 1
fi
;;
2014-03-12 22:20:00 +00:00
connmark|CONNMARK)
local action="CONNMARK"
2014-03-12 22:20:00 +00:00
case "${1}" in
to)
local -a action_param=("--set-mark" "${2}")
shift 2
;;
save)
if [ "${2}" = "mask" ]
then
local -a action_param=("--save-mark" "--mask" "${3}")
shift 3
else
local -a action_param=("--save-mark")
shift 1
fi
;;
restore)
if [ "${2}" = "mask" ]
then
local -a action_param=("--restore-mark" "--mask" "${3}")
shift 3
else
local -a action_param=("--restore-mark")
shift 1
fi
;;
*)
error "${action} requires a either 'to', 'save' or 'restore' argument"
return 1
;;
esac
if [ ! "A${table}" = "A-t mangle" ]
then
error "${action} must on a the 'mangle' table."
return 1
fi
;;
dscp|DSCP)
local action="DSCP"
if [ "${1}" = "to" ]
then
if [ "${2}" = "class" ]
then
local -a action_param=("--set-dscp-class" "${2}")
shift
else
local -a action_param=("--set-dscp" "${2}")
fi
shift 2
else
error "${action} requires a 'to' argument"
return 1
fi
if [ ! "A${table}" = "A-t mangle" ]
then
error "${action} must on a the 'mangle' table."
return 1
fi
;;
tarpit|TARPIT)
local action="TARPIT"
;;
*)
chain_exists "${action}"
local action_is_chain=$?
;;
esac
;;
2002-09-05 20:57:59 +00:00
state|STATE)
shift
local statenot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
2002-09-05 20:57:59 +00:00
then
shift
local statenot="!"
2002-09-05 20:57:59 +00:00
fi
test ${softwarnings} -eq 1 -a ! -z "${state}" && softwarning "Overwritting param: state '${state}' becomes '${1}'"
local state="${1}"
2002-09-05 20:57:59 +00:00
shift
;;
user|USER|uid|UID)
shift
local uidnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
test ${noowner} -eq 0 && local uidnot="!"
fi
test ${softwarnings} -eq 1 -a ! "${uid}" = "any" && softwarning "Overwritting param: uid '${uid}' becomes '${1}'"
test ${noowner} -eq 0 && local uid="${1}"
shift
;;
group|GROUP|gid|GID)
shift
local gidnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
test ${noowner} -eq 0 && local gidnot="!"
fi
test ${softwarnings} -eq 1 -a ! "${gid}" = "any" && softwarning "Overwritting param: gid '${gid}' becomes '${1}'"
test ${noowner} -eq 0 && local gid="${1}"
shift
;;
process|PROCESS|pid|PID)
shift
local pidnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
test ${noowner} -eq 0 && local pidnot="!"
fi
test ${softwarnings} -eq 1 -a ! "${pid}" = "any" && softwarning "Overwritting param: pid '${pid}' becomes '${1}'"
test ${noowner} -eq 0 && local pid="${1}"
shift
;;
session|SESSION|sid|SID)
shift
local sidnot=
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
test ${noowner} -eq 0 && local sidnot="!"
fi
test ${softwarnings} -eq 1 -a ! "${sid}" = "any" && softwarning "Overwritting param: sid '${sid}' becomes '${1}'"
test ${noowner} -eq 0 && local sid="${1}"
shift
;;
2003-07-20 21:46:41 +00:00
command|COMMAND|cmd|CMD)
shift
local cmdnot=
2003-07-20 21:46:41 +00:00
if [ "${1}" = "not" -o "${1}" = "NOT" ]
then
shift
test ${noowner} -eq 0 && local cmdnot="!"
2003-07-20 21:46:41 +00:00
fi
test ${softwarnings} -eq 1 -a ! "${cmd}" = "any" && softwarning "Overwritting param: cmd '${cmd}' becomes '${1}'"
test ${noowner} -eq 0 && local cmd="${1}"
2003-07-20 21:46:41 +00:00
shift
;;
custom|CUSTOM)
test ${softwarnings} -eq 1 -a ! -z "${custom}" && softwarning "Overwritting param: custom '${custom}' becomes '${2}'"
local custom="${2}"
shift 2
;;
log|LOG)
if [ ${nolog} -eq 0 ]
then
test ${softwarnings} -eq 1 -a ! -z "${log}" && softwarning "Overwritting param: log '${log}/${logtxt}' becomes 'normal/${2}'"
local log=normal
local logtxt="`echo ${2} | ${TR_CMD} " " "_"`"
fi
shift 2
if [ "${1}" = "level" ]
then
local loglevel="${2}"
shift 2
else
local loglevel="${FIREHOL_LOG_LEVEL}"
fi
;;
loglimit|LOGLIMIT)
if [ ${nolog} -eq 0 ]
then
test ${softwarnings} -eq 1 -a ! -z "${log}" && softwarning "Overwritting param: log '${log}/${logtxt}' becomes 'limit/${2}'"
local log=limit
local logtxt="`echo ${2} | ${TR_CMD} " " "_"`"
fi
shift 2
if [ "${1}" = "level" ]
then
local loglevel="${2}"
shift 2
else
local loglevel="${FIREHOL_LOG_LEVEL}"
fi
;;
limit|LIMIT)
test ${softwarnings} -eq 1 -a ! -z "${limit}" && softwarning "Overwritting param: limit '${limit}' becomes '${2}'"
local limit="${2}"
local burst="${3}"
shift 3
;;
iplimit|IPLIMIT)
test ${softwarnings} -eq 1 -a ! -z "${iplimit}" && softwarning "Overwritting param: iplimit '${iplimit}' becomes '${2}'"
local iplimit="${2}"
local iplimit_mask="${3}"
shift 3
;;
in) # this is incoming traffic - ignore packet ownership
local noowner=1
local nomirror=0
local nomac=0
shift
;;
out) # this is outgoing traffic - ignore packet ownership if not in an interface
if [ ! "${work_cmd}" = "interface" ]
then
local noowner=1
else
local nomirror=1
fi
2004-03-03 21:24:41 +00:00
local nomac=1
shift
;;
nolog)
local nolog=1
shift
;;
noowner)
local noowner=1
shift
;;
softwarnings)
local softwarnings=1
shift
;;
nosoftwarnings)
local softwarnings=0
shift
;;
set_work_inface|SET_WORK_INFACE)
local swi=1
shift
;;
set_work_outface|SET_WORK_OUTFACE)
local swo=1
shift
;;
acct|accounting)
if [ ${ENABLE_ACCOUNTING} -eq 1 ]
then
local accounting="$2"
FIREHOL_NFACCT[$accounting]="1"
elif [ ${ACCOUNTING_WARNING} -eq 1 ]
then
softwarning "Accounting is requested, but accounting is disabled. Is nfacct installed?"
ACCOUNTING_WARNING=0
fi
shift 2
;;
2002-09-05 20:57:59 +00:00
*)
error "Cannot understand directive '${1}'."
2002-09-05 20:57:59 +00:00
return 1
;;
esac
done
test -z "${table}" && local table="-t filter"
# If the user did not specified a rejection message,
# we have to be smart and produce a tcp-reset if the protocol
# is TCP and an ICMP port unreachable in all other cases.
# The special case here is the protocol "any".
# To accomplish the differentiation based on protocol we have
# to change the protocol "any" to "tcp any"
test "${action}" = "REJECT" -a "${action_param[1]}" = "auto" -a "${proto}" = "any" && local proto="tcp any"
# we cannot accept empty strings to a few parameters, since this
# will prevent us from generating a rule (due to nested BASH loops).
test -z "${inface}" && error "Cannot accept an empty 'inface'." && return 1
test -z "${outface}" && error "Cannot accept an empty 'outface'." && return 1
test -z "${physin}" && error "Cannot accept an empty 'physin'." && return 1
test -z "${physout}" && error "Cannot accept an empty 'physout'." && return 1
test -z "${mac}" && error "Cannot accept an empty 'mac'." && return 1
test -z "${src4}" && error "Cannot accept an empty 'src4'." && return 1
test -z "${dst4}" && error "Cannot accept an empty 'dst4'." && return 1
test -z "${src6}" && error "Cannot accept an empty 'src6'." && return 1
test -z "${dst6}" && error "Cannot accept an empty 'dst6'." && return 1
test -z "${sport}" && error "Cannot accept an empty 'sport'." && return 1
test -z "${dport}" && error "Cannot accept an empty 'dport'." && return 1
test -z "${proto}" && error "Cannot accept an empty 'proto'." && return 1
test -z "${uid}" && error "Cannot accept an empty 'uid'." && return 1
test -z "${gid}" && error "Cannot accept an empty 'gid'." && return 1
test -z "${pid}" && error "Cannot accept an empty 'pid'." && return 1
test -z "${sid}" && error "Cannot accept an empty 'sid'." && return 1
2003-07-20 21:46:41 +00:00
test -z "${cmd}" && error "Cannot accept an empty 'cmd'." && return 1
local physbridge="--physdev-is-bridged"
if [ ! "${work_cmd}" = "router" -a ! "${physin}${physout}" = "anyany" ]
then
if [ ! "${physin}" = "any" -a "${physout}" = "any" ]
then
local physbridge="--physdev-is-in"
elif [ "${physin}" = "any" -a ! "${physout}" = "any" ]
then
local physbridge="--physdev-is-out"
fi
fi
local srcnot=
local dstnot=
if running_both; then
if [ "${src4not}" != "${src6not}" ]
then
error "Mixed use of 'not' with src4 and src6." && return 1
else
local srcnot="${src4not}"
fi
if [ "${dst4not}" != "${dst6not}" ]
then
error "Mixed use of 'not' with dst4 and dst6." && return 1
else
local dstnot="${dst4not}"
fi
if [ "${src4}" = "default" -a "${src6}" != "default" ]
then
error "Must specify src4 when specifying src6" && return 1
fi
if [ "${dst4}" = "default" -a "${dst6}" != "default" ]
then
error "Must specify dst4 when specifying dst6" && return 1
fi
if [ "${src6}" = "default" -a "${src4}" != "default" ]
then
error "Must specify src6 when specifying src4" && return 1
fi
if [ "${dst6}" = "default" -a "${dst4}" != "default" ]
then
error "Must specify dst6 when specifying dst4" && return 1
fi
elif running_ipv6; then
local srcnot="${src6not}"
local dstnot="${dst6not}"
else
local srcnot="${src4not}"
local dstnot="${dst4not}"
fi
test "${src4}" = "default" && local src4="any"
test "${dst4}" = "default" && local dst4="any"
test "${src6}" = "default" && local src6="any"
test "${dst6}" = "default" && local dst6="any"
# ----------------------------------------------------------------------------------
# Do we have negative contitions?
# If yes, we have to:
#
# case 1: If the action is a chain.
# Add to this chain positive RETURN statements matching all the negatives.
# The positive rules will be added bellow to the same chain and will be
# matched only if all RETURNs have not been matched.
#
# case 2: If the action is not a chain.
# Create a temporary chain, then add to this chain positive RETURN rules
# matching the negatives, and append at its end the final action (which is
# not a chain), then change the action of the positive rules to jump to
# this temporary chain.
# ignore 'statenot', 'srctypenot', 'dsttypenot' since it is negated in the positive rules
if [ ! -z "${infacenot}${outfacenot}${physinnot}${physoutnot}${macnot}${srcnot}${dstnot}${sportnot}${dportnot}${protonot}${uidnot}${gidnot}${pidnot}${sidnot}${cmdnot}${marknot}${tosnot}${dscpnot}" ]
then
if [ ${action_is_chain} -eq 1 ]
then
# if the action is a chain name, then just add the negative
# expressions to this chain. Nothing more.
local negative_chain="${action}"
local negative_action=
else
# if the action is a native iptables action, then create
# an intermidiate chain to store the negative expression,
# and change the action of the rule to point to this action.
# In this case, bellow we add after all negatives, the original
# action of the rule.
2013-11-03 21:08:26 +00:00
local DYNAMIC_CHAIN_COUNTER
get_next_dynamic_counter DYNAMIC_CHAIN_COUNTER
local negative_chain="${chain}.${DYNAMIC_CHAIN_COUNTER}"
iptables_both ${table} -N "${negative_chain}"
local negative_action="${action}"
local action="${negative_chain}"
fi
if [ ! -z "${infacenot}" ]
then
local inf=
for inf in ${inface}
do
iptables_both ${table} -A "${negative_chain}" -i "${inf}" -j RETURN
done
local infacenot=
local inface=any
fi
if [ ! -z "${outfacenot}" ]
then
local outf=
for outf in ${outface}
do
iptables_both ${table} -A "${negative_chain}" -o "${outf}" -j RETURN
done
local outfacenot=
local outface=any
fi
if [ ! -z "${physinnot}" ]
then
local inph=
for inph in ${physin}
do
iptables_both ${table} -A "${negative_chain}" -m physdev ${physbridge} --physdev-in "${inph}" -j RETURN
done
local physinnot=
local physin=any
fi
if [ ! -z "${physoutnot}" ]
then
local outph=
for outph in ${physout}
do
iptables_both ${table} -A "${negative_chain}" -m physdev ${physbridge} --physdev-out "${outph}" -j RETURN
done
local physoutnot=
local physout=any
fi
if [ ! -z "${macnot}" ]
then
local m=
for m in ${mac}
do
iptables_both ${table} -A "${negative_chain}" -m mac --mac-source "${m}" -j RETURN
done
local macnot=
local mac=any
fi
if [ ! -z "${srcnot}" ]
then
local s=
if running_ipv4; then
for s in ${src4}
do
iptables ${table} -A "${negative_chain}" -s "${s}" -j RETURN
done
fi
if running_ipv6; then
for s in ${src6}
do
ip6tables ${table} -A "${negative_chain}" -s "${s}" -j RETURN
done
fi
local srcnot=
local src4=any
local src6=any
fi
if [ ! -z "${dstnot}" ]
then
local d=
if running_ipv4; then
for d in ${dst4}
do
iptables ${table} -A "${negative_chain}" -d "${d}" -j RETURN
done
fi
if running_ipv6; then
for d in ${dst6}
do
ip6tables ${table} -A "${negative_chain}" -d "${d}" -j RETURN
done
fi
local dstnot=
local dst4=any
local dst6=any
fi
if [ ! -z "${protonot}" ]
then
if [ ! -z "${sportnot}" -o ! -z "${dportnot}" ]
then
error "Cannot have negative protocol(s) and source/destination port(s)."
return 1
fi
local pr=
for pr in ${proto}
do
iptables_both ${table} -A "${negative_chain}" -p "${pr}" -j RETURN
done
local protonot=
local proto=any
fi
if [ ! -z "${sportnot}" ]
then
if [ "${proto}" = "any" ]
then
error "Cannot have negative source port specification without protocol."
return 1
fi
local sp=
for sp in ${sport}
do
local pr=
for pr in ${proto}
do
iptables_both ${table} -A "${negative_chain}" -p "${pr}" --sport "${sp}" -j RETURN
done
done
local sportnot=
local sport=any
fi
if [ ! -z "${dportnot}" ]
then
if [ "${proto}" = "any" ]
then
error "Cannot have negative destination port specification without protocol."
return 1
fi
local dp=
for dp in ${dport}
do
local pr=
for pr in ${proto}
do
iptables_both ${table} -A "${negative_chain}" -p "${pr}" --dport "${dp}" -j RETURN
done
done
local dportnot=
local dport=any
fi
if [ ! -z "${uidnot}" ]
then
local tuid=
for tuid in ${uid}
do
iptables_both ${table} -A "${negative_chain}" -m owner --uid-owner "${tuid}" -j RETURN
done
local uidnot=
local uid=any
fi
if [ ! -z "${gidnot}" ]
then
local tgid=
for tgid in ${gid}
do
iptables_both ${table} -A "${negative_chain}" -m owner --gid-owner "${tgid}" -j RETURN
done
local gidnot=
local gid=any
fi
if [ ! -z "${pidnot}" ]
then
local tpid=
for tpid in ${pid}
do
iptables_both ${table} -A "${negative_chain}" -m owner --pid-owner "${tpid}" -j RETURN
done
local pidnot=
local pid=any
fi
if [ ! -z "${sidnot}" ]
then
local tsid=
for tsid in ${sid}
do
iptables_both ${table} -A "${negative_chain}" -m owner --sid-owner "${tsid}" -j RETURN
done
local sidnot=
local sid=any
fi
2003-07-20 21:46:41 +00:00
if [ ! -z "${cmdnot}" ]
then
local tcmd=
for tcmd in ${cmd}
do
iptables_both ${table} -A "${negative_chain}" -m owner --cmd-owner "${tcmd}" -j RETURN
2003-07-20 21:46:41 +00:00
done
local cmdnot=
local cmd=any
2003-07-20 21:46:41 +00:00
fi
if [ ! -z "${marknot}" ]
then
local tmark=
for tmark in ${mark}
do
iptables_both ${table} -A "${negative_chain}" -m mark --mark "${tmark}" -j RETURN
done
local marknot=
local mark=any
fi
if [ ! -z "${tosnot}" ]
then
local ttos=
for ttos in ${tos}
do
iptables_both ${table} -A "${negative_chain}" -m tos --tos "${ttos}" -j RETURN
done
local tosnot=
local tos=any
fi
if [ ! -z "${dscpnot}" ]
then
local tdscp=
for tdscp in ${dscp}
do
iptables_both ${table} -A "${negative_chain}" -m dscp --dscp${dscptype} "${tdscp}" -j RETURN
done
local dscp=any
local dscpnot=
fi
# in case this is temporary chain we created for the negative expression,
# just make it have the final action of the rule.
if [ ! -z "${negative_action}" ]
then
local pr=
for pr in ${proto}
do
local -a proto_arg=()
case ${pr} in
any|ANY)
;;
*)
local -a proto_arg=("-p" "${pr}")
;;
esac
if running_ipv4; then
rule_action_param iptables "${negative_action}" "${pr}" "" "" "${table}" "${action_param[@]}" -- ${table} -A "${negative_chain}" "${proto_arg[@]}" || local failed=$[failed + 1]
fi
if running_ipv6; then
rule_action_param ip6tables "${negative_action}" "${pr}" "" "" "${table}" "${action_param[@]}" -- ${table} -A "${negative_chain}" "${proto_arg[@]}" || local failed=$[failed + 1]
fi
local -a action_param=()
done
fi
fi
# ----------------------------------------------------------------------------------
# Process the positive rules
# addrtype (srctype, dsttype)
local -a addrtype_arg=()
local -a stp_arg=()
local -a dtp_arg=()
if [ ! -z "${srctype}${dsttype}" ]
then
local -a addrtype_arg=("-m" "addrtype")
if [ ! -z "${srctype}" ]
then
local -a stp_arg=(${srctypenot} "--src-type" "${srctype}")
fi
if [ ! -z "${dsttype}" ]
then
local -a dtp_arg=(${dsttypenot} "--dst-type" "${dsttype}")
fi
fi
# state
local -a state_arg=()
if [ ! -z "${state}" ]
then
# local -a state_arg=("-m" "state" ${statenot} "--state" "${state}")
local -a state_arg=("-m" "conntrack" ${statenot} "--ctstate" "${state}")
fi
# limit
local -a limit_arg=()
if [ ! -z "${limit}" ]
then
local -a limit_arg=("-m" "limit" "--limit" "${limit}" "--limit-burst" "${burst}")
fi
# iplimit
local -a iplimit_arg=()
if [ ! -z "${iplimit}" ]
then
local -a iplimit_arg=("-m" "iplimit" "--iplimit-above" "${iplimit}" "--iplimit-mask" "${iplimit_mask}")
fi
# log mode selection
local -a logopts_arg=()
if [ "${FIREHOL_LOG_MODE}" = "ULOG" ]
then
local -a logopts_arg=("--ulog-prefix=${FIREHOL_LOG_PREFIX}${logtxt}:")
elif [ "${FIREHOL_LOG_MODE}" = "NFLOG" ]
then
local -a logopts_arg=("--nflog-prefix=${FIREHOL_LOG_PREFIX}${logtxt}:")
else
local -a logopts_arg=("--log-level" "${loglevel}" "--log-prefix=${FIREHOL_LOG_PREFIX}${logtxt}:")
fi
# log / loglimit
local logrule=
case "${log}" in
'')
local logrule=none
;;
limit)
local logrule=limit
;;
normal)
local logrule=normal
;;
*)
error "Unknown log value '${log}'."
;;
esac
# uid
local tuid=
for tuid in ${uid}
2002-09-05 20:57:59 +00:00
do
local -a uid_arg=()
local -a owner_arg=()
case ${tuid} in
2002-09-05 20:57:59 +00:00
any|ANY)
;;
*)
local -a owner_arg=("-m" "owner")
local -a uid_arg=("--uid-owner" "${tuid}")
2002-09-05 20:57:59 +00:00
;;
esac
# gid
local tgid=
for tgid in ${gid}
do
local -a gid_arg=()
case ${tgid} in
any|ANY)
;;
*)
local -a owner_arg=("-m" "owner")
local -a gid_arg=("--gid-owner" "${tgid}")
;;
esac
# pid
local tpid=
for tpid in ${pid}
do
local -a pid_arg=()
case ${tpid} in
any|ANY)
;;
*)
local -a owner_arg=("-m" "owner")
local -a pid_arg=("--pid-owner" "${tpid}")
;;
esac
# sid
local tsid=
for tsid in ${sid}
do
local -a sid_arg=()
case ${tsid} in
any|ANY)
;;
*)
local -a owner_arg=("-m" "owner")
local -a sid_arg=("--sid-owner" "${tsid}")
;;
esac
# cmd
local tcmd=
for tcmd in ${cmd}
do
local -a cmd_arg=()
case ${tcmd} in
any|ANY)
;;
*)
local -a owner_arg=("-m" "owner")
local -a cmd_arg=("--cmd-owner" "${tcmd}")
;;
esac
# mark
local tmark=
for tmark in ${mark}
do
local -a mark_arg=()
case ${tmark} in
any|ANY)
;;
*)
local -a mark_arg=("-m" "mark" "--mark" "${tmark}")
;;
esac
# tos
local ttos=
for ttos in ${tos}
do
local -a tos_arg=()
case ${ttos} in
any|ANY)
;;
*)
local -a tos_arg=("-m" "tos" "--tos" "${ttos}")
;;
esac
# dscp
local tdscp=
for tdscp in ${dscp}
do
local -a dscp_arg=()
case ${tdscp} in
any|ANY)
;;
*)
local -a dscp_arg=("-m" "dscp" "--dscp${dscptype}" "${tdscp}")
;;
esac
# proto
local pr=
for pr in ${proto}
do
local -a proto_arg=()
case ${pr} in
any|ANY)
;;
*)
local -a proto_arg=("-p" "${pr}")
;;
esac
# inface
local inf=
for inf in ${inface}
do
local -a inf_arg=()
case ${inf} in
any|ANY)
;;
*)
local -a inf_arg=("-i" "${inf}")
;;
esac
# outface
local outf=
for outf in ${outface}
do
local -a outf_arg=()
case ${outf} in
any|ANY)
;;
*)
local -a outf_arg=("-o" "${outf}")
;;
esac
# physin
local inph=
for inph in ${physin}
do
local -a inph_arg=()
case ${inph} in
any|ANY)
;;
*)
local -a physdev_arg=("-m" "physdev" ${physbridge})
local -a inph_arg=("--physdev-in" "${inph}")
;;
esac
# physout
local outph=
for outph in ${physout}
do
local -a outph_arg=()
case ${outph} in
any|ANY)
;;
*)
local -a physdev_arg=("-m" "physdev" ${physbridge})
local -a outph_arg=("--physdev-out" "${outph}")
;;
esac
# sport
local sp=
for sp in ${sport}
do
local -a sp_arg=()
case ${sp} in
any|ANY)
;;
*)
local -a sp_arg=("--sport" "${sp}")
;;
esac
# dport
local dp=
for dp in ${dport}
do
local -a dp_arg=()
case ${dp} in
any|ANY)
;;
*)
local -a dp_arg=("--dport" "${dp}")
;;
esac
# mac
local mc=
for mc in ${mac}
do
local -a mc_arg=()
case ${mc} in
any|ANY)
;;
*)
local -a mc_arg=("-m" "mac" "--mac-source" "${mc}")
;;
esac
if running_ipv4; then
# src4
local s=
for s in ${src4}
do
local -a s_arg=()
case ${s} in
any|ANY)
;;
*)
local -a s_arg=("-s" "${s}")
;;
esac
# dst4
local d=
for d in ${dst4}
do
local -a d_arg=()
case ${d} in
any|ANY)
;;
*)
local -a d_arg=("-d" "${d}")
;;
esac
# build the command
declare -a basecmd=("${inf_arg[@]}" "${outf_arg[@]}" "${physdev_arg[@]}" "${inph_arg[@]}" "${outph_arg[@]}" "${limit_arg[@]}" "${iplimit_arg[@]}" "${proto_arg[@]}" "${s_arg[@]}" "${sp_arg[@]}" "${d_arg[@]}" "${dp_arg[@]}" "${owner_arg[@]}" "${uid_arg[@]}" "${gid_arg[@]}" "${pid_arg[@]}" "${sid_arg[@]}" "${cmd_arg[@]}" "${addrtype_arg[@]}" "${stp_arg[@]}" "${dtp_arg[@]}" "${state_arg[@]}" "${mc_arg[@]}" "${mark_arg[@]}" "${tos_arg[@]}" "${dscp_arg[@]}")
if [ "$logrule" = "limit" ]
then
iptables ${table} -A "${chain}" "${basecmd[@]}" ${custom} -m limit --limit "${FIREHOL_LOG_FREQUENCY}" --limit-burst "${FIREHOL_LOG_BURST}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}"
elif [ "$logrule" = "normal" ]
then
iptables ${table} -A "${chain}" "${basecmd[@]}" ${custom} -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}"
fi
if [ ! -z "${accounting}" ]
then
iptables ${table} -A "${chain}" "${basecmd[@]}" ${custom} -m nfacct --nfacct-name "${accounting}"
fi
# do it!
rule_action_param iptables "${action}" "${pr}" "${statenot}" "${state}" "${table}" "${action_param[@]}" -- ${table} -A "${chain}" "${basecmd[@]}" ${custom} || local failed=$[failed + 1]
done # dst4
done # src4
fi
if running_ipv6; then
# src6
local s=
for s in ${src6}
do
local -a s_arg=()
case ${s} in
any|ANY)
;;
*)
local -a s_arg=("-s" "${s}")
;;
esac
# dst6
local d=
for d in ${dst6}
do
local -a d_arg=()
case ${d} in
any|ANY)
;;
*)
local -a d_arg=("-d" "${d}")
;;
esac
# build the command
declare -a basecmd=("${inf_arg[@]}" "${outf_arg[@]}" "${physdev_arg[@]}" "${inph_arg[@]}" "${outph_arg[@]}" "${limit_arg[@]}" "${iplimit_arg[@]}" "${proto_arg[@]}" "${s_arg[@]}" "${sp_arg[@]}" "${d_arg[@]}" "${dp_arg[@]}" "${owner_arg[@]}" "${uid_arg[@]}" "${gid_arg[@]}" "${pid_arg[@]}" "${sid_arg[@]}" "${cmd_arg[@]}" "${addrtype_arg[@]}" "${stp_arg[@]}" "${dtp_arg[@]}" "${state_arg[@]}" "${mc_arg[@]}" "${mark_arg[@]}" "${tos_arg[@]}" "${dscp_arg[@]}")
if [ "$logrule" = "limit" ]
then
ip6tables ${table} -A "${chain}" "${basecmd[@]}" ${custom} -m limit --limit "${FIREHOL_LOG_FREQUENCY}" --limit-burst "${FIREHOL_LOG_BURST}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}"
elif [ "$logrule" = "normal" ]
then
ip6tables ${table} -A "${chain}" "${basecmd[@]}" ${custom} -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}"
fi
if [ ! -z "${accounting}" ]
then
iptables ${table} -A "${chain}" "${basecmd[@]}" ${custom} -m nfacct --nfacct-name "${accounting}"
fi
# do it!
rule_action_param ip6tables "${action}" "${pr}" "${statenot}" "${state}" "${table}" "${action_param[@]}" -- ${table} -A "${chain}" "${basecmd[@]}" ${custom} || local failed=$[failed + 1]
done # dst6
done # src6
fi
done # mac
done # dport
done # sport
done # physout
done # physin
done # outface
done # inface
done # proto
done # dscp
done # tos
done # mark
done # cmd
done # sid
done # pid
done # gid
done # uid
2002-09-05 20:57:59 +00:00
test ${failed} -gt 0 && error "There are ${failed} failed commands." && return 1
return 0
}
warning() {
echo >&2
echo >&2 "WARNING: " "$@"
echo >&2
return 0
}
2002-09-05 20:57:59 +00:00
softwarning() {
echo >&2
echo >&2 "--------------------------------------------------------------------------------"
echo >&2 "WARNING"
echo >&2 "WHAT : ${work_function}"
echo >&2 "WHY :" "$@"
printf >&2 "COMMAND: "; printf >&2 "%q " "${work_realcmd[@]}"; echo >&2
echo >&2 "MODE :" "${FIREHOL_NS_CURR}"
echo >&2 "SOURCE : line ${FIREHOL_LINEID} of ${FIREHOL_CONFIG}"
echo >&2
return 0
}
2002-12-23 14:39:19 +00:00
# ------------------------------------------------------------------------------
# error - error reporting while still parsing the configuration file
# WHY:
# This is the error handler that presents to the user detected errors during
# processing FireHOL's configuration file.
# This command is directly called by other functions of FireHOL.
error() {
test "${FIREHOL_MODE}" = "START" && syslog err "Error '${@}' when '${work_function}' at ${FIREHOL_CONFIG} line ${FIREHOL_LINEID}"
2002-12-23 14:39:19 +00:00
work_error=$[work_error + 1]
echo >&2
echo >&2 "--------------------------------------------------------------------------------"
echo >&2 "ERROR #: ${work_error}"
echo >&2 "WHAT : ${work_function}"
echo >&2 "WHY :" "$@"
printf >&2 "COMMAND: "; printf >&2 "%q " "${work_realcmd[@]}"; echo >&2
echo >&2 "MODE :" "${FIREHOL_NS_CURR}"
echo >&2 "SOURCE : line ${FIREHOL_LINEID} of ${FIREHOL_CONFIG}"
2002-12-23 14:39:19 +00:00
echo >&2
2002-09-05 20:57:59 +00:00
return 0
}
2002-12-23 14:39:19 +00:00
# ------------------------------------------------------------------------------
# runtime_error - postprocessing evaluation of commands run
2002-12-23 14:39:19 +00:00
# WHY:
# The generated iptables commands must be checked for errors in case they fail.
# This command is executed after every postprocessing command to find out
# if it has been successfull or failed.
runtime_error() {
local type="ERROR"
local id=
case "${1}" in
error)
local type="ERROR "
work_runtime_error=$[work_runtime_error + 1]
local id="# ${work_runtime_error}."
;;
warn)
local type="WARNING"
local id="This might or might not affect the operation of your firewall."
;;
*)
work_runtime_error=$[work_runtime_error + 1]
local id="# ${work_runtime_error}."
echo >&2
echo >&2
echo >&2 "*** unsupported final status type '${1}'. Assuming it is 'ERROR'"
echo >&2
echo >&2
;;
esac
shift
local ret="${1}"; shift
local line="${1}"; shift
syslog err "Runtime ${type} '${id}'. Source ${FIREHOL_CONFIG} line ${line}"
echo >&2
echo >&2
echo >&2 "--------------------------------------------------------------------------------"
echo >&2 "${type} : ${id}"
echo >&2 "WHAT : A runtime command failed to execute (returned error ${ret})."
echo >&2 "SOURCE : line ${line} of ${FIREHOL_CONFIG}"
printf >&2 "COMMAND : "
printf >&2 "%q " "$@"
printf >&2 "\n"
echo >&2 "OUTPUT : "
echo >&2
${CAT_CMD} ${FIREHOL_OUTPUT}.log >&2
echo >&2
2002-09-05 20:57:59 +00:00
return 0
}
2002-12-23 14:39:19 +00:00
# ------------------------------------------------------------------------------
# chain_exists - find if chain name has already being specified
# WHY:
# We have to make sure each service gets its own chain.
# Although FireHOL chain naming makes chains with unique names, this is just
# an extra sanity check.
declare -A FIREHOL_NFACCT=()
declare -A FIREHOL_CHAINS=()
chain_exists() {
local chain="${1}"
if running_ipv4; then
test ! -z "${FIREHOL_CHAINS[${chain}.4]}" && return 1
# test -f "${FIREHOL_CHAINS_DIR}/${chain}.4" && return 1
fi
if running_ipv6; then
test ! -z "${FIREHOL_CHAINS[${chain}.6]}" && return 1
# test -f "${FIREHOL_CHAINS_DIR}/${chain}.6" && return 1
fi
return 0
}
2002-12-23 14:39:19 +00:00
# ------------------------------------------------------------------------------
# create_chain - create a chain and link it to the firewall
# WHY:
# When a chain is created it must somehow to be linked to the rest of the
# firewall apropriately. This function first creates the chain and then
# it links it to its final position within the generated firewall.
2002-09-05 20:57:59 +00:00
create_chain() {
local table="${1}"
local newchain="${2}"
local oldchain="${3}"
shift 3
2002-09-05 20:57:59 +00:00
set_work_function "Creating chain '${newchain}' under '${oldchain}' in table '${table}'"
chain_exists "${newchain}"
test $? -eq 1 && error "Chain '${newchain}' already exists." && return 1
2002-09-05 20:57:59 +00:00
iptables_both -t ${table} -N "${newchain}" || return 1
if running_ipv4; then
FIREHOL_CHAINS[${newchain}.4]="1"
#${TOUCH_CMD} "${FIREHOL_CHAINS_DIR}/${newchain}.4"
fi
if running_ipv6; then
FIREHOL_CHAINS[${newchain}.6]="1"
#${TOUCH_CMD} "${FIREHOL_CHAINS_DIR}/${newchain}.6"
fi
Added helper action: action [chain <name> <action>] The action helper creates an iptables chain which can be used to control the action of other firewall rules during runtime. For example, you can setup the custom action ACT1, which by default is ACCEPT, but under certain cases it can be changed to DROP, REJECT or RETURN without restarting the firewall. The first argument must always be the word 'chain', for the moment. name can be any chain name accepted by iptables. It is suggested to keep it between 5 to 10 letters. action can be any action supported by FireHOL, although ony ACCEPT, REJECT, DROP, RETURN may have any meaning under this use. Example 1: At the top of firehol.conf, create the action ACT1: action chain ACT1 accept later, in interfaces and routers, create rules that use the ACT1 action: server smtp ACT1 client imap ACT1 Please note that actions created this way are case sensitive. At some point, and while the firewall is running, the action ACT1 can be changed to DROP, with this linux command (this is not FireHOL specific): iptables -t filter -I ACT1 -j DROP The above command inserts (-I) the new action DROP above the default action ACCEPT, and therefore all the traffic matching the FireHOL rules that have the action ACT1 will now be dropped. To return to the default action (ACCEPT), run the following linux command: iptables -t filter -D ACT1 -j DROP This command deletes (-D) the DROP action that was inserted above the default action. If you delete all actions in the chain ACT1, the default action will be RETURN, in which case all rules with action ACT1 will be nutralized (it will be the same as they were not specified at all in firehol.conf). Example 2: action chain "ACT1 ACT2 ACT3" accept chain "ACT4 ACT5 ACT6" drop will create 6 actions, ACT1, ACT2, ACT3 with ACCEPT, and ACT4, ACT5, ACT6 with DROP.
2007-07-20 19:58:38 +00:00
if [ ! -z "${oldchain}" ]
then
rule table ${table} chain "${oldchain}" action "${newchain}" "$@" || return 1
fi
return 0
}
# ------------------------------------------------------------------------------
# smart_function - find the valid service definition for a service
# WHY:
# FireHOL supports simple and complex services. This function first tries to
# detect if there are the proper variables set for a simple service, and if
# they do not exist, it then tries to find the complex function definition for
# the service.
#
# Additionally, it creates a chain for the subcommand.
smart_function() {
local type="${1}" # The current subcommand: server/client/route
local services="${2}" # The services to implement
shift 2
local service=
for service in $services
do
local servname="${service}"
test "${service}" = "custom" && local servname="${1}"
set_work_function "Preparing for service '${service}' of type '${type}' under interface '${work_name}'"
# Increase the command counter, to make all chains within a primary
# command, unique.
2013-11-03 21:08:26 +00:00
local work_counter
get_next_work_counter work_counter
local suffix="u${work_counter}"
case "${type}" in
client)
suffix="c${work_counter}"
;;
server)
suffix="s${work_counter}"
;;
route)
suffix="r${work_counter}"
;;
*) error "Cannot understand type '${type}'."
return 1
;;
esac
local mychain="${work_name}_${servname}_${suffix}"
create_chain filter "in_${mychain}" "in_${work_name}" || return 1
create_chain filter "out_${mychain}" "out_${work_name}" || return 1
# Try the simple services first
simple_service "${mychain}" "${type}" "${service}" "$@"
local ret=$?
# simple service completed succesfully.
test $ret -eq 0 && continue
# simple service exists but failed.
if [ $ret -ne 127 ]
then
error "Simple service '${service}' returned an error ($ret)."
return 1
fi
# Try the custom services
local fn="rules_${service}"
set_work_function "Running complex rules function ${fn}() for ${type} '${service}'"
"${fn}" "${mychain}" "${type}" "$@"
local ret=$?
test $ret -eq 0 && continue
if [ $ret -eq 127 ]
then
error "There is no service '${service}' defined."
else
error "Complex service '${service}' returned an error ($ret)."
fi
return 1
done
return 0
}
# ------------------------------------------------------------------------------
# simple_service - convert a service definition to an inline service definition
# WHY:
# When a simple service is detected, there must be someone to call
# rules_custom() with the appropriate service definition parameters.
simple_service() {
local mychain="${1}"; shift
local type="${1}"; shift
local server="${1}"; shift
local server_varname="server_${server}_ports"
eval local server_ports="\$${server_varname}"
local client_varname="client_${server}_ports"
eval local client_ports="\$${client_varname}"
if [ ! -z "${server_ports}" -a -z "${client_ports}" ]
then
error "Simple service '${service}' has server ports, but no client ports defined."
return 1
elif [ -z "${server_ports}" -a ! -z "${client_ports}" ]
then
error "Simple service '${service}' has client ports, but no server ports defined."
return 1
elif [ -z "${server_ports}" -a -z "${client_ports}" ]
then
# this will make the caller attempt to find a complex service
return 127
fi
local varname="helper_${server}"
eval local helpers="\$${varname}"
local x=
local varname="require_${server}_modules"
eval local value="\$${varname}"
for x in ${value}
do
require_kernel_module $x || return 1
done
if [ ${FIREHOL_NAT} -eq 1 ]
then
local varname="require_${server}_nat_modules"
eval local value="\$${varname}"
for x in ${value}
do
require_kernel_module $x || return 1
done
fi
# load the helper modules
for x in ${helpers}
do
case "${x}" in
snmp_basic) # this does not exist in conntrack
;;
*) require_kernel_module nf_conntrack_$x
;;
esac
if [ ${FIREHOL_NAT} -eq 1 ]
then
case "${x}" in
netbios_ns|netlink|sane)
# these do not exist in nat
;;
*) require_kernel_module nf_nat_$x
;;
esac
fi
done
set_work_function "Running simple rules for ${type} '${service}'"
rules_custom "${mychain}" "${type}" "${server}" "${server_ports}" "${client_ports}" helpers "${helpers}" "$@"
return $?
}
show_work_realcmd() {
test "${FIREHOL_MODE}" = "EXPLAIN" && return 0
(
printf "\n\n"
printf "# === CONFIGURATION STATEMENT =================================================\n"
printf "# CONF:%3s>>> " ${FIREHOL_LINEID}
case $1 in
2) printf " "
;;
*) ;;
esac
printf "%q " "${work_realcmd[@]}"
printf "\n\n"
) >>${FIREHOL_OUTPUT}
}
work_realcmd_primary() {
work_realcmd=("$@")
test ${FIREHOL_CONF_SHOW} -eq 1 && show_work_realcmd 1
}
work_realcmd_secondary() {
work_realcmd=("$@")
test ${FIREHOL_CONF_SHOW} -eq 1 && show_work_realcmd 2
}
work_realcmd_helper() {
work_realcmd=("$@")
test ${FIREHOL_CONF_SHOW} -eq 1 && show_work_realcmd 3
}
wait_for_interface() {
local iface=$1; shift
local timeout=60
if [ -n "$1" ]; then
timeout=$1
fi
local start=`date +%s`
local found=0
while [ "`date +%s`" -lt $(($start+$timeout)) -a $found -eq 0 ]
do
local addr=`ip addr show $iface 2> /dev/null | awk '$1 ~ /^inet$/ {print $2}'`
if [ -n "$addr" ]
then
found=1
fi
if [ $found -eq 0 ]
then
sleep 0.5
fi
done
if [ $found -eq 1 ]
then
# the interface is up
return 0
else
return 1
fi
}
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# keep a copy of the running firewall on disk for fast restoration
fixed_save() {
local command="$1"
local tmp="${FIREHOL_DIR}/iptables-save-$$"
local err=
load_kernel_module ip_tables
${command} -c >$tmp
err=$?
if [ ! $err -eq 0 ]
then
${RM_CMD} -f $tmp >/dev/null 2>&1
return $err
fi
${CAT_CMD} ${tmp} |\
${SED_CMD} \
-e "s/--uid-owner !/! --uid-owner /g" \
-e "s/--gid-owner !/! --gid-owner /g" \
-e "s/--pid-owner !/! --pid-owner /g" \
-e "s/--sid-owner !/! --sid-owner /g" \
-e "s/--cmd-owner !/! --cmd-owner /g"
err=$?
${RM_CMD} -f $tmp >/dev/null 2>&1
return $err
}
FIREHOL_LAST_SUCCESSFUL_COMMAND="${FIREHOL_SPOOL_DIR}/firehol-last-ok-command"
firehol_save_activated_firewall() {
echo -n $"FireHOL: Saving activated firewall to ${FIREHOL_SPOOL_DIR}:"
if [ -f "${FIREHOL_SPOOL_DIR}/ipv4.enable" ]
then
fixed_save ${IPTABLES_SAVE_CMD} >"${FIREHOL_SPOOL_DIR}/ipv4.rules"
if [ ! $? -eq 0 ]
then
failure $"FireHOL: Saving activated firewall to ${FIREHOL_SPOOL_DIR}:"
echo
return 1
fi
"${CHOWN_CMD}" root:root "${FIREHOL_SPOOL_DIR}/ipv4.rules"
"${CHMOD_CMD}" 600 "${FIREHOL_SPOOL_DIR}/ipv4.rules"
else
test -f "${FIREHOL_SPOOL_DIR}/ipv4.rules" && rm "${FIREHOL_SPOOL_DIR}/ipv4.rules"
fi
if [ -f "${FIREHOL_SPOOL_DIR}/ipv6.enable" ]
then
fixed_save ${IP6TABLES_SAVE_CMD} >"${FIREHOL_SPOOL_DIR}/ipv6.rules"
if [ ! $? -eq 0 ]
then
failure $"FireHOL: Saving activated firewall to ${FIREHOL_SPOOL_DIR}:"
echo
return 1
fi
"${CHOWN_CMD}" root:root "${FIREHOL_SPOOL_DIR}/ipv6.rules"
"${CHMOD_CMD}" 600 "${FIREHOL_SPOOL_DIR}/ipv6.rules"
else
test -f "${FIREHOL_SPOOL_DIR}/ipv6.rules" && rm "${FIREHOL_SPOOL_DIR}/ipv6.rules"
fi
printf "%q " "${FIREHOL_ARGS[@]}" >"${FIREHOL_LAST_SUCCESSFUL_COMMAND}"
printf "\n" >>"${FIREHOL_LAST_SUCCESSFUL_COMMAND}"
success $"FireHOL: Saving activated firewall to ${FIREHOL_SPOOL_DIR}:"
echo
return 0
}
firehol_can_restore_saved_firewall() {
test ! -f "${FIREHOL_LAST_SUCCESSFUL_COMMAND}" \
&& warning "No saved firewall found to restore." \
&& return 1
local args="`printf "%q " "${FIREHOL_ARGS[@]}"`"
local old_args="`cat "${FIREHOL_LAST_SUCCESSFUL_COMMAND}"`"
test ! "${args}" = "${old_args}" \
&& warning "Saved firewall cannot be restored because it was run with different parameters." \
&& return 2
local do_ipv4=0
local do_ipv6=0
test -f "${FIREHOL_SPOOL_DIR}/ipv4.enable" -a -f "${FIREHOL_SPOOL_DIR}/ipv4.rules" && local do_ipv4=1
test -f "${FIREHOL_SPOOL_DIR}/ipv6.enable" -a -f "${FIREHOL_SPOOL_DIR}/ipv6.rules" && local do_ipv6=1
test "${do_ipv4}${do_ipv6}" = "00" \
&& warning "Saved firewall includes neither IPv4 nor IPv6 rules to restore." \
&& return 1
test "${FIREHOL_CONFIG}" -nt "${FIREHOL_LAST_SUCCESSFUL_COMMAND}" \
&& warning "${FIREHOL_CONFIG} is newer than saved firewall. Cannot restore saved firewall." \
&& return 3
test ! -z "`${FIND_CMD} "${FIREHOL_CONFIG_DIR}" -newer "${FIREHOL_LAST_SUCCESSFUL_COMMAND}"`" \
&& warning "${FIREHOL_CONFIG_DIR} has updated files. Cannot restore saved firewall." \
&& return 4
test ! -z "`${FIND_CMD} "${FIREHOL_SERVICES_DIR}" -newer "${FIREHOL_LAST_SUCCESSFUL_COMMAND}"`" \
&& warning "${FIREHOL_SERVICES_DIR} has updated files. Cannot restore saved firewall." \
&& return 5
return 0
}
firehol_restore_last_activated_firewall() {
firehol_can_restore_saved_firewall || return 2
echo -n $"FireHOL: Restoring last activated firewall from ${FIREHOL_SPOOL_DIR}:"
if [ -x "${FIREHOL_SPOOL_DIR}/firewall_restore_commands.sh" ]
then
"${FIREHOL_SPOOL_DIR}/firewall_restore_commands.sh" >/dev/null
if [ $? -ne 0 ]
then
warning "Failed to execute restoration script."
failure $"FireHOL: Restoring last activated firewall from ${FIREHOL_SPOOL_DIR}:"
return 3
fi
fi
if [ -f "${FIREHOL_SPOOL_DIR}/ipv4.enable" -a -f "${FIREHOL_SPOOL_DIR}/ipv4.rules" ]
then
${IPTABLES_RESTORE_CMD} <"${FIREHOL_SPOOL_DIR}/ipv4.rules"
if [ $? -ne 0 ]
then
warning "Failed to restore IPv4 rules."
failure $"FireHOL: Restoring last activated firewall from ${FIREHOL_SPOOL_DIR}:"
return 3
fi
fi
if [ -f "${FIREHOL_SPOOL_DIR}/ipv6.enable" -a -f "${FIREHOL_SPOOL_DIR}/ipv6.rules" ]
then
${IP6TABLES_RESTORE_CMD} <"${FIREHOL_SPOOL_DIR}/ipv6.rules"
if [ $? -ne 0 ]
then
warning "Failed to restore IPv6 rules."
failure $"FireHOL: Restoring last activated firewall from ${FIREHOL_SPOOL_DIR}:"
return 3
fi
fi
success $"FireHOL: Saving activated firewall to ${FIREHOL_SPOOL_DIR}:"
echo
return 0
}
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# START UP SCRIPT PROCESSING
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# On non RedHat machines we need success() and failure()
success() {
printf " OK"
}
failure() {
echo " FAILED"
}
# ------------------------------------------------------------------------------
# A small part bellow is copied from /etc/init.d/iptables
# On RedHat systems this will define success() and failure()
test -f /etc/init.d/functions && . /etc/init.d/functions
kernel_maj_min() {
local kmaj kmin IFS=.-
kmaj=$1
kmin=$2
set -- $(uname -r)
eval $kmaj=\$1 $kmin=\$2
}
kernel_maj_min KERNELMAJ KERNELMIN
if [ "$KERNELMAJ" -lt 2 ] ; then
echo >&2 "FireHOL requires a kernel version higher than 2.3."
exit 0
fi
if [ "$KERNELMAJ" -eq 2 -a "$KERNELMIN" -lt 3 ] ; then
echo >&2 "FireHOL requires a kernel version higher than 2.3."
exit 0
fi
if ${LSMOD_CMD} 2>/dev/null | ${GREP_CMD} -q ipchains ; then
# Don't do both
echo >&2 "ipchains is loaded in the kernel. Please remove ipchains to run iptables."
exit 0
fi
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# COMMAND LINE ARGUMENTS PROCESSING
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
arg="${1}"
shift
if [ "${arg}" = "nofast" ]
then
FIREHOL_FAST_ACTIVATION=0
arg="${1}"
shift
fi
case "${arg}" in
explain)
test ! -z "${1}" && warning "Arguments after parameter '${arg}' are ignored."
firewall_policy_applied=1
2015-01-18 19:38:38 +00:00
firewall_policy6_applied=1
FIREHOL_FAST_ACTIVATION=0
FIREHOL_MODE="EXPLAIN"
;;
helpme|wizard)
test ! -z "${1}" && warning "Arguments after parameter '${arg}' are ignored."
FIREHOL_MODE="WIZARD"
;;
try)
FIREHOL_MODE="START"
FIREHOL_TRY=1
;;
start)
FIREHOL_MODE="START"
2009-10-01 10:25:23 +00:00
FIREHOL_TRY=0
;;
stop)
FIREHOL_MODE="STOP"
test ! -z "${1}" && warning "Arguments after parameter '${arg}' are ignored."
echo -n $"FireHOL: Clearing Firewall:"
if [ $ENABLE_IPV4 -eq 1 ]; then
load_kernel_module ip_tables
tables=`${CAT_CMD} /proc/net/ip_tables_names`
for t in ${tables}
do
${IPTABLES_CMD} -t "${t}" -F
${IPTABLES_CMD} -t "${t}" -X
${IPTABLES_CMD} -t "${t}" -Z
# Find all default chains in this table.
chains=`${IPTABLES_CMD} -t "${t}" -nL | ${GREP_CMD} "^Chain " | ${CUT_CMD} -d ' ' -f 2`
for c in ${chains}
do
${IPTABLES_CMD} -t "${t}" -P "${c}" ACCEPT
done
done
fi
if [ $ENABLE_IPV6 -eq 1 ]; then
load_kernel_module ip6_tables
tables6=`${CAT_CMD} /proc/net/ip6_tables_names`
for t in ${tables6}
do
${IP6TABLES_CMD} -t "${t}" -F
${IP6TABLES_CMD} -t "${t}" -X
${IP6TABLES_CMD} -t "${t}" -Z
# Find all default chains in this table.
chains=`${IP6TABLES_CMD} -t "${t}" -nL | ${GREP_CMD} "^Chain " | ${CUT_CMD} -d ' ' -f 2`
for c in ${chains}
do
${IP6TABLES_CMD} -t "${t}" -P "${c}" ACCEPT
done
done
fi
success $"FireHOL: Clearing Firewall:"
echo
exit 0
;;
restore|condrestart)
FIREHOL_RESTORE_INSTEAD_OF_START=1
FIREHOL_MODE="START"
FIREHOL_TRY=0
;;
restart|force-reload)
FIREHOL_MODE="START"
FIREHOL_TRY=0
;;
status)
test ! -z "${1}" && warning "Arguments after parameter '${arg}' are ignored."
(
if [ $ENABLE_IPV4 -eq 1 ]; then
echo
echo
echo "--- MANGLE IPv4 ----------------------------------------------------------------"
echo
${IPTABLES_CMD} -t mangle -nxvL
fi
if [ $ENABLE_IPV6 -eq 1 ]; then
echo
echo
echo "--- MANGLE IPv6 ----------------------------------------------------------------"
echo
${IP6TABLES_CMD} -t mangle -nxvL
fi
if [ $ENABLE_IPV4 -eq 1 ]; then
echo
echo
echo "--- NAT IPv4 -------------------------------------------------------------------"
echo
${IPTABLES_CMD} -t nat -nxvL
fi
if [ $ENABLE_IPV6 -eq 1 ]; then
echo
echo
echo "--- NAT IPv6 -------------------------------------------------------------------"
2013-11-03 18:11:16 +00:00
echo
if grep -q '^nat$' /proc/net/ip6_tables_names
then
${IP6TABLES_CMD} -t nat -nxvL
else
echo "IPv6 NAT not available"
fi
fi
if [ $ENABLE_IPV4 -eq 1 ]; then
echo
echo
echo "--- FILTER IPv4 ----------------------------------------------------------------"
echo
${IPTABLES_CMD} -nxvL
fi
if [ $ENABLE_IPV6 -eq 1 ]; then
echo
echo
echo "--- FILTER IPv6 ----------------------------------------------------------------"
echo
${IP6TABLES_CMD} -nxvL
fi
) | pager_cmd
exit $?
;;
panic)
FIREHOL_MODE="PANIC"
ssh_src=
ssh_sport="0:65535"
ssh_dport="0:65535"
if [ ! -z "${SSH_CLIENT}" ]
then
set -- ${SSH_CLIENT}
ssh_src="${1}"
ssh_sport="${2}"
ssh_dport="${3}"
elif [ ! -z "${1}" ]
then
ssh_src="${1}"
fi
syslog info "Starting PANIC mode (SSH SOURCE_IP=${ssh_src} SOURCE_PORTS=${ssh_sport} DESTINATION_PORTS=${ssh_dport})"
echo -n $"FireHOL: Blocking all communications:"
if [ $ENABLE_IPV4 -eq 1 ]; then
load_kernel_module ip_tables
tables=`${CAT_CMD} /proc/net/ip_tables_names`
for t in ${tables}
do
${IPTABLES_CMD} -t "${t}" -F
${IPTABLES_CMD} -t "${t}" -X
${IPTABLES_CMD} -t "${t}" -Z
# Find all default chains in this table.
chains=`${IPTABLES_CMD} -t "${t}" -nL | ${GREP_CMD} "^Chain " | ${CUT_CMD} -d ' ' -f 2`
for c in ${chains}
do
${IPTABLES_CMD} -t "${t}" -P "${c}" ACCEPT
if [ ! -z "${ssh_src}" ]
then
${IPTABLES_CMD} -t "${t}" -A "${c}" -p tcp -s "${ssh_src}" --sport "${ssh_sport}" --dport "${ssh_dport}" -m conntrack --ctstate ESTABLISHED -j ACCEPT
${IPTABLES_CMD} -t "${t}" -A "${c}" -p tcp -d "${ssh_src}" --dport "${ssh_sport}" --sport "${ssh_dport}" -m conntrack --ctstate ESTABLISHED -j ACCEPT
fi
if [ "${t}" != "nat" ] ; then
${IPTABLES_CMD} -t "${t}" -A "${c}" -j DROP
fi
done
done
fi
if [ $ENABLE_IPV6 -eq 1 ]; then
load_kernel_module ip6_tables
tables6=`${CAT_CMD} /proc/net/ip6_tables_names`
for t in ${tables6}
do
${IP6TABLES_CMD} -t "${t}" -F
${IP6TABLES_CMD} -t "${t}" -X
${IP6TABLES_CMD} -t "${t}" -Z
# Find all default chains in this table.
chains=`${IP6TABLES_CMD} -t "${t}" -nL | ${GREP_CMD} "^Chain " | ${CUT_CMD} -d ' ' -f 2`
for c in ${chains}
do
${IP6TABLES_CMD} -t "${t}" -P "${c}" ACCEPT
if [ ! -z "${ssh_src}" ]
then
${IP6TABLES_CMD} -t "${t}" -A "${c}" -p tcp -s "${ssh_src}" --sport "${ssh_sport}" --dport "${ssh_dport}" -m conntrack --ctstate ESTABLISHED -j ACCEPT
${IP6TABLES_CMD} -t "${t}" -A "${c}" -p tcp -d "${ssh_src}" --dport "${ssh_sport}" --sport "${ssh_dport}" -m conntrack --ctstate ESTABLISHED -j ACCEPT
fi
if [ "${t}" != "nat" ] ; then
${IP6TABLES_CMD} -t "${t}" -A "${c}" -j DROP
fi
done
done
fi
success $"FireHOL: Blocking all communications:"
echo
exit 0
;;
save)
test ! -z "${1}" && test ${1} != "--" && softwarning "Arguments after parameter '${arg}' are ignored."
FIREHOL_MODE="START"
FIREHOL_SAVE=1
;;
debug)
test ! -z "${1}" && test ${1} != "--" && softwarning "Arguments after parameter '${arg}' are ignored."
FIREHOL_MODE="DEBUG"
;;
*) if [ ! -z "${arg}" -a -f "${arg}" ]
then
FIREHOL_MODE="START"
FIREHOL_TRY=1
FIREHOL_CONFIG="${arg}"
arg="${1}"
test -z "${arg}" && arg="try"
case "${arg}" in
start)
FIREHOL_TRY=0
shift
;;
try)
FIREHOL_TRY=1
shift
;;
debug)
FIREHOL_MODE="DEBUG"
FIREHOL_TRY=0
shift
;;
--)
FIREHOL_TRY=1
;;
*)
echo "Cannot accept command line argument '${1}' here."
exit 1
;;
esac
else
emit_version
${CAT_CMD} <<EOF
FireHOL supports the following command line arguments (only one of them):
start to activate the firewall configuration.
The configuration is expected to be found in
${FIREHOL_CONFIG_DIR}/firehol.conf
try to activate the firewall, but wait until
the user types the word "commit". If this word
is not typed within 30 seconds, the previous
firewall is restored.
stop to stop a running iptables firewall.
This will allow all traffic to pass unchecked.
restart this is an alias for start and is given for
compatibility with /etc/init.d/iptables.
status will show the running firewall, as in:
${IPTABLES_CMD} -nxvL
and
${IP6TABLES_CMD} -nxvL
panic will block all IP communication.
2002-09-05 20:57:59 +00:00
restore will restore the last activated firewall.
Useful for quickly restoring at boot the last
successfully activated FireHOL firewall.
save to start the firewall and then save it to the
place where /etc/init.d/iptables looks for it.
Note that not all firewalls will work if
restored with:
/etc/init.d/iptables start
The fastest way to restore a FireHOL firewall
at boot is the 'restore' feature.
debug to parse the configuration file but instead of
activating it, to show the generated iptables
statements.
explain to enter interactive mode and accept configuration
directives. It also gives the iptables commands
for each directive together with reasoning.
helpme or to enter a wizard mode where FireHOL will try
wizard to figure out the configuration you need.
You can redirect the standard output of FireHOL to
a file to get the config to this file.
<a filename> a different configuration file.
If not other argument is given, the configuration
will be "tried" (default = try).
Otherwise the argument next to the filename can
be one of 'start', 'debug' and 'try'.
2002-09-05 20:57:59 +00:00
-------------------------------------------------------------------------
FireHOL supports the following services (sorted by name):
EOF
(
# The simple services
${CAT_CMD} "${FIREHOL_FILE}" |\
${GREP_CMD} -e "^server_.*_ports=" |\
${CUT_CMD} -d '=' -f 1 |\
${SED_CMD} "s/^server_//" |\
${SED_CMD} "s/_ports\$//"
# The complex services
${CAT_CMD} "${FIREHOL_FILE}" |\
${GREP_CMD} -e "^rules_.*()" |\
${CUT_CMD} -d '(' -f 1 |\
${SED_CMD} "s/^rules_/(*) /"
) | ${SORT_CMD} | ${UNIQ_CMD} |\
(
x=0
while read
do
x=$[x + 1]
if [ $x -gt 4 ]
then
printf "\n"
x=1
fi
printf "% 16s |" "$REPLY"
done
printf "\n\n"
)
${CAT_CMD} <<EOF
Services marked with (*) are "smart" or complex services.
All the others are simple single socket services.
Please note that the service:
all matches all packets and all protocols, while ensuring that
required kernel modules are loaded. Packets "untracked" by
iptables (e.g. ICMPv6 neighbour discovery packets) are not
included in "all" and must be handled separately.
any allows the matching of packets with unusual rules, like
only protocol but no ports. If service any is used
without other parameters, it does what service all does
but it does not handle kernel modules.
For example, to match GRE traffic use:
server any mygre accept proto 47
Service any does not handle kernel modules.
custom allows the definition of a custom service.
The template is:
server custom name protocol/sport cport accept
where name is just a name, protocol is the protocol the
service uses (tcp, udp, etc), sport is server port,
cport is the client port. For example, IMAP4 is:
server custom imap tcp/143 default accept
YOU DO NOT KNOW WHAT TO DO? FireHOL can help you! Just run it with the
argument 'helpme' and it will generate its configuration file for this
machine. Your running firewall will not be altered or stopped, and no
systems settings will be modified. Just run:
${FIREHOL_FILE} helpme >/tmp/firehol.conf
and you will get the configuration written to /tmp/firehol.conf
EOF
exit 1
fi
;;
esac
2002-09-05 20:57:59 +00:00
# Remove all parameters until --
while [ ! -z "${1}" ]
do
if [ "${1}" = "--" ]
then
shift
break
fi
warning "Parameter '${1}' is ignored."
shift
done
2002-09-05 20:57:59 +00:00
if [ "${FIREHOL_MODE}" = "START" -o "${FIREHOL_MODE}" = "DEBUG" ]
then
if [ ! -f "${FIREHOL_CONFIG}" ]
then
echo -n $"FireHOL config ${FIREHOL_CONFIG} not found:"
failure $"FireHOL config ${FIREHOL_CONFIG} not found:"
echo
exit 1
fi
fi
2002-09-05 20:57:59 +00:00
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
2002-12-23 14:39:19 +00:00
# MAIN PROCESSING - Interactive mode
2002-09-05 20:57:59 +00:00
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
if [ "${FIREHOL_MODE}" = "EXPLAIN" ]
then
FIREHOL_CONFIG="Interactive User Input"
FIREHOL_LINEID="1"
FIREHOL_TEMP_CONFIG="${FIREHOL_DIR}/firehol.conf"
echo "version ${FIREHOL_VERSION}" >"${FIREHOL_TEMP_CONFIG}"
version ${FIREHOL_VERSION}
emit_version
${CAT_CMD} <<EOF
You can now start typing firehol configuration directives.
Special interactive commands: help, show, quit
EOF
while [ 1 = 1 ]
do
read -p "# FireHOL [${work_cmd}:${work_name}] > " -e -r
test -z "${REPLY}" && continue
set_work_function -ne "Executing user input"
while [ 1 = 1 ]
do
set -- ${REPLY}
case "${1}" in
help)
${CAT_CMD} <<EOF
You can use anything a FireHOL configuration file accepts, including variables,
loops, etc. Take only care to write loops in one row.
Additionaly, you can use the following commands:
help to print this text on your screen.
show to show all the successfull commands so far.
quit to show the interactively given configuration file
and quit.
in same as typing: interface eth0 internet
This is used as a shortcut to get into the server/client
mode in which you can test the rules for certain
services.
EOF
break
;;
show)
echo
${CAT_CMD} "${FIREHOL_TEMP_CONFIG}"
echo
break
;;
quit)
echo
${CAT_CMD} "${FIREHOL_TEMP_CONFIG}"
echo
exit 1
;;
in)
REPLY="interface eth0 internet"
continue
;;
*)
${CAT_CMD} <<EOF
# \/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/
# Cmd Line : ${FIREHOL_LINEID}
# Command : ${REPLY}
EOF
eval "$@"
if [ $? -gt 0 ]
then
printf "\n# > FAILED <\n"
else
if [ "${1}" = "interface" -o "${1}" = "router" ]
then
echo >>"${FIREHOL_TEMP_CONFIG}"
else
printf " " >>"${FIREHOL_TEMP_CONFIG}"
fi
printf "%s\n" "${REPLY}" >>"${FIREHOL_TEMP_CONFIG}"
FIREHOL_LINEID=$[FIREHOL_LINEID + 1]
printf "\n# > OK <\n"
fi
break
;;
esac
break
done
done
exit 0
fi
2002-12-23 14:39:19 +00:00
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# MAIN PROCESSING - help wizard
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
if [ "${FIREHOL_MODE}" = "WIZARD" ]
then
# require commands for wizard mode
require_cmd ip
require_cmd ss
require_cmd date
require_cmd hostname
wizard_ask() {
local prompt="${1}"; shift
local def="${1}"; shift
echo >&2
while [ 1 = 1 ]
do
printf >&2 "%s [%s] > " "${prompt}" "${def}"
read
local ans="${REPLY}"
test -z "${ans}" && ans="${def}"
local c=0
while [ $c -le $# ]
do
eval local t="\${${c}}"
test "${ans}" = "${t}" && break
c=$[c + 1]
done
test $c -le $# && return $c
printf >&2 "*** '${ans}' is not a valid answer. Pick one of "
printf >&2 "%s " "$@"
echo >&2
echo >&2
done
return 0
}
ip_in_net() {
local ip="${1}"; shift
local net="${1}"; shift
if [ -z "${ip}" -o -z "${net}" ]
then
return 1
fi
test "${net}" = "default" && net="0.0.0.0/0"
set -- `echo ${ip} | ${TR_CMD} './' ' '`
local i1=${1}
local i2=${2}
local i3=${3}
local i4=${4}
set -- `echo ${net} | ${TR_CMD} './' ' '`
local n1=${1}
local n2=${2}
local n3=${3}
local n4=${4}
local n5=${5:-32}
local i=$[i1*256*256*256 + i2*256*256 + i3*256 + i4]
local n=$[n1*256*256*256 + n2*256*256 + n3*256 + n4]
# echo "IP : '${i1}' . '${i2}' . '${i3}' . '${i4}'"
# echo "NET: '${n1}' . '${n2}' . '${n3}' . '${n4}' / '${n5}'"
local d=1
local c=${n5}
while [ $c -lt 32 ]
do
c=$[c + 1]
d=$[d * 2]
done
local nm=$[n + d - 1]
printf "# INFO: Is ${ip} part of network ${net}? "
if [ ${i} -ge ${n} -a ${i} -le ${nm} ]
then
echo "yes"
return 0
else
echo "no"
return 1
fi
}
ip_is_net() {
local ip="${1}"; shift
local net="${1}"; shift
if [ -z "${ip}" -o -z "${net}" ]
then
return 1
fi
test "${net}" = "default" && net="0.0.0.0/0"
set -- `echo ${ip} | ${TR_CMD} './' ' '`
local i1=${1}
local i2=${2}
local i3=${3}
local i4=${4}
local i5=${5:-32}
set -- `echo ${net} | ${TR_CMD} './' ' '`
local n1=${1}
local n2=${2}
local n3=${3}
local n4=${4}
local n5=${5:-32}
local i=$[i1*256*256*256 + i2*256*256 + i3*256 + i4]
local n=$[n1*256*256*256 + n2*256*256 + n3*256 + n4]
if [ ${i} -eq ${n} -a ${i5} -eq ${n5} ]
then
return 0
else
return 1
fi
}
ip2net() {
local ip="${1}"; shift
if [ -z "${ip}" ]
then
return 0
fi
if [ "${ip}" = "default" ]
then
echo "default"
return 0
fi
set -- `echo ${ip} | ${TR_CMD} './' ' '`
local i1=${1}
local i2=${2}
local i3=${3}
local i4=${4}
local i5=${5:-32}
if [ "${i5}" = "32" ]
then
echo ${i1}.${i2}.${i3}.${i4}
else
echo ${i1}.${i2}.${i3}.${i4}/${i5}
fi
}
ips2net() {
(
if [ "A${1}" = "A-" ]
then
while read ip
do
ip2net ${ip}
done
else
while [ ! -z "${1}" ]
do
ip2net ${1}
shift
done
fi
) | ${SORT_CMD} | ${UNIQ_CMD} | ${TR_CMD} "\n" " "
}
cd "${FIREHOL_DIR}"
"${MKDIR_CMD}" ports
"${MKDIR_CMD}" keys
cd ports
"${MKDIR_CMD}" tcp
"${MKDIR_CMD}" udp
emit_version >&2
"${CAT_CMD}" >&2 <<EOF
FireHOL will now try to figure out its configuration file on this system.
Please have all the services and network interfaces on this system running.
Your running firewall will not be stopped or altered.
You can re-run the same command with output redirection to get the config
to a file. Example:
EOF
echo >&2 "${FIREHOL_FILE} helpme >/tmp/firehol.conf"
echo >&2
echo >&2
echo >&2
echo >&2 "Building list of known services."
echo >&2 "Please wait..."
${CAT_CMD} /etc/services |\
${TR_CMD} '\t' ' ' |\
${SED_CMD} "s/ \+/ /g" >services
for c in `echo ${!server_*} | ${TR_CMD} ' ' '\n' | ${GREP_CMD} "_ports$"`
do
serv=`echo $c | ${SED_CMD} "s/server_//" | ${SED_CMD} "s/_ports//"`
eval "ret=\${$c}"
for x in ${ret}
do
proto=`echo $x | ${CUT_CMD} -d '/' -f 1`
port=`echo $x | ${CUT_CMD} -d '/' -f 2`
test ! -d "${proto}" && continue
nport=`${EGREP_CMD} "^${port}[[:space:]][0-9]+/${proto}" services | ${CUT_CMD} -d ' ' -f 2 | ${CUT_CMD} -d '/' -f 1`
test -z "${nport}" && nport="${port}"
echo "server ${serv}" >"${proto}/${nport}"
done
done
echo "server ftp" >tcp/21
echo "server nfs" >udp/2049
echo "client amanda" >udp/10080
echo "server dhcp" >udp/67
echo "server dhcp" >tcp/67
echo "client dhcp" >udp/68
echo "client dhcp" >tcp/68
echo "server emule" >tcp/4662
echo "server pptp" >tcp/1723
echo "server samba" >udp/137
echo "server samba" >udp/138
echo "server samba" >tcp/139
wizard_ask "Press RETURN to start." "continue" "continue"
echo >&2
echo >&2 "--- snip --- snip --- snip --- snip ---"
echo >&2
${CAT_CMD} <<EOF
#
# FireHOL configuration (autogenerated)
#
# This config will have the same effect as NO PROTECTION!
# Everything that found to be running, is allowed.
# YOU SHOULD NEVER USE THIS CONFIG AS-IS.
#
# Date: `${DATE_CMD}` on host `${HOSTNAME_CMD}`
#
# IMPORTANT:
# The TODOs bellow, are *YOUR* to-dos!
#
EOF
# globals for routing
set -a found_interfaces=
set -a found_ips=
set -a found_nets=
set -a found_excludes=
helpme_iface() {
local route="${1}"; shift
local i="${1}"; shift
local iface="${1}"; shift
local ifip="${1}"; shift
local ifnets="${1}"; shift
local ifreason="${1}"; shift
2003-03-06 08:18:49 +00:00
# one argument left: ifnets_excluded
if [ "${route}" = "route" ]
then
found_interfaces[$i]="${iface}"
found_ips[$i]="${ifip}"
found_nets[$i]="${ifnets}"
found_excludes[$i]="${1}"
fi
if [ "${ifnets}" = "default" ]
2003-03-06 08:18:49 +00:00
then
ifnets="not \"\${UNROUTABLE_IPS} ${1}\""
else
ifnets="\"${ifnets}\""
fi
# output the interface
echo
echo "# Interface No $i."
echo "# The purpose of this interface is to control the traffic"
if [ ! -z "${ifreason}" ]
then
echo "# ${ifreason}."
else
echo "# on the ${iface} interface with IP ${ifip} (net: ${ifnets})."
fi
echo "# TODO: Change \"if${i}\" to something with meaning to you."
echo "# TODO: Check the optional rule parameters (src/dst)."
echo "# Remove 'dst ${ifip}' if this is dynamically assigned."
echo "# To add IPv6, read http://firehol.org/upgrade/#config-version-6"
echo "interface4 ${iface} if${i} src ${ifnets} dst ${ifip}"
echo
echo " # The default policy is DROP. You can be more polite with REJECT."
echo " # Prefer to be polite on your own clients to prevent timeouts."
echo " policy drop"
echo
echo " # If you don't trust the clients behind ${iface} (net ${ifnets}),"
echo " # add something like this."
echo " # > protection strong"
echo
echo " # Here are the services listening on ${iface}."
echo " # TODO: Normally, you will have to remove those not needed."
(
local x=
local ports=
for x in `${SS_CMD} -tln | ${SED_CMD} "s|:::|\*:|g" | ${SED_CMD} "s|::ffff:||g" | ${EGREP_CMD} " (${ifip}|\*):[0-9]+" | ${CUT_CMD} -d ':' -f 2 | ${CUT_CMD} -d ' ' -f 1 | ${SORT_CMD} -n | ${UNIQ_CMD}`
do
if [ -f "tcp/${x}" ]
then
echo " `${CAT_CMD} tcp/${x}` accept"
else
ports="${ports} tcp/${x}"
fi
done
for x in `${SS_CMD} -uln | ${SED_CMD} "s|:::|\*:|g" | ${SED_CMD} "s|::ffff:||g" | ${EGREP_CMD} " (${ifip}|\*):[0-9]+" | ${CUT_CMD} -d ':' -f 2 | ${CUT_CMD} -d ' ' -f 1 | ${SORT_CMD} -n | ${UNIQ_CMD}`
do
if [ -f "udp/${x}" ]
then
echo " `${CAT_CMD} udp/${x}` accept"
else
ports="${ports} udp/${x}"
fi
done
echo " server ICMP accept"
echo "${ports}" | ${TR_CMD} " " "\n" | ${SORT_CMD} -n | ${UNIQ_CMD} | ${TR_CMD} "\n" " " >unknown.ports
) | ${SORT_CMD} | ${UNIQ_CMD}
echo
echo " # The following ${iface} services are not known by FireHOL:"
${CAT_CMD} unknown.ports | ${FOLD_CMD} -s -w 65 | ${SED_CMD} "s|^ *|\t# |"
echo
echo
echo " # Custom service definitions for the above unknown services."
local ts=
local tscount=0
for ts in `${CAT_CMD} unknown.ports`
do
local tscount=$[tscount + 1]
echo " server custom if${i}_${tscount} ${ts} any accept"
done
echo
echo " # The following means that this machine can REQUEST anything via ${iface}."
echo " # TODO: On production servers, avoid this and allow only the"
echo " # client services you really need."
echo " client all accept"
echo
}
interfaces=`${IP_CMD} link show | ${EGREP_CMD} "^[0-9A-Za-z]+:" | ${CUT_CMD} -d ':' -f 2 | ${SED_CMD} "s/^ //" | ${SED_CMD} "s/@[a-z0-9]*//" | ${GREP_CMD} -v "^lo$" | ${SORT_CMD} | ${UNIQ_CMD} | ${TR_CMD} "\n" " "`
gw_if=`${IP_CMD} route show | ${GREP_CMD} "^default" | ${SED_CMD} "s/dev /dev:/g" | ${TR_CMD} " " "\n" | ${GREP_CMD} "^dev:" | ${CUT_CMD} -d ':' -f 2`
gw_ip=`${IP_CMD} route show | ${GREP_CMD} "^default" | ${SED_CMD} "s/via /via:/g" | ${TR_CMD} " " "\n" | ${GREP_CMD} "^via:" | ${CUT_CMD} -d ':' -f 2 | ips2net -`
i=0
for iface in ${interfaces}
do
echo "# INFO: Processing interface '${iface}'"
ips=`${IP_CMD} addr show dev ${iface} | ${SED_CMD} "s/ \+/ /g" | ${GREP_CMD} "^ inet " | ${CUT_CMD} -d ' ' -f 3 | ${CUT_CMD} -d '/' -f 1 | ips2net -`
peer=`${IP_CMD} addr show dev ${iface} | ${SED_CMD} "s/ \+/ /g" | ${SED_CMD} "s/peer /peer:/g" | ${TR_CMD} " " "\n" | ${GREP_CMD} "^peer:" | ${CUT_CMD} -d ':' -f 2 | ips2net -`
nets=`${IP_CMD} route show dev ${iface} | ${CUT_CMD} -d ' ' -f 1 | ips2net -`
if [ -z "${ips}" -o -z "${nets}" ]
then
echo
echo "# IMPORTANT: "
echo "# Ignoring interface '${iface}' because does not have an IP or route."
echo
continue
fi
for ip in ${ips}
do
echo "# INFO: Processing IP ${ip} of interface '${iface}'"
2003-03-06 08:18:49 +00:00
ifreason=""
# find all the networks this IP can access directly
# or through its peer
netcount=0
ifnets=
ofnets=
for net in ${nets}
do
test "${net}" = "default" && continue
found=1
ip_in_net ${ip} ${net}
found=$?
if [ ${found} -gt 0 -a ! -z "${peer}" ]
then
ip_in_net ${peer} ${net}
found=$?
fi
if [ ${found} -eq 0 ]
then
2003-03-17 23:03:00 +00:00
# Add it to ifnets
f=0; ff=0
while [ $f -lt $netcount ]
do
if ip_in_net ${net} ${ifnets[$f]}
then
2003-03-17 23:03:00 +00:00
# Already satisfied
ff=1
elif ip_in_net ${ifnets[$f]} ${net}
then
2003-03-17 23:03:00 +00:00
# New one is superset of old
ff=1
ifnets[$f]=${net}
fi
f=$[f + 1]
done
if [ $ff -eq 0 ]
then
2003-03-17 23:03:00 +00:00
# Add it
netcount=$[netcount + 1]
ifnets=(${net} "${ifnets[@]}")
fi
else
ofnets=(${net} "${ofnets[@]}")
fi
done
# find all the networks this IP can access through gateways
if [ ! -z "${ofnets[*]}" ]
then
for net in "${ofnets[@]}"
do
test "${net}" = "default" && continue
nn=`echo "${net}" | ${CUT_CMD} -d "/" -f 1`
gw=`${IP_CMD} route show ${nn} dev ${iface} | ${EGREP_CMD} "^${nn}[[:space:]]+via[[:space:]][0-9\.]+" | ${CUT_CMD} -d ' ' -f 3 | ips2net -`
test -z "${gw}" && continue
for nn in "${ifnets[@]}"
do
test "${nn}" = "default" && continue
if ip_in_net ${gw} ${nn}
then
echo "# INFO: Route ${net} is accessed through ${gw}"
2003-03-17 23:03:00 +00:00
# Add it to ifnets
f=0; ff=0
while [ $f -lt $netcount ]
do
if ip_in_net ${net} ${ifnets[$f]}
then
2003-03-17 23:03:00 +00:00
# Already satisfied
ff=1
elif ip_in_net ${ifnets[$f]} ${net}
then
2003-03-17 23:03:00 +00:00
# New one is superset of old
ff=1
ifnets[$f]=${net}
fi
f=$[f + 1]
done
if [ $ff -eq 0 ]
then
2003-03-17 23:03:00 +00:00
# Add it
netcount=$[netcount + 1]
ifnets=(${net} "${ifnets[@]}")
fi
break
fi
done
done
fi
# Don't produce an interface if this is just a peer that is also the default gw
def_ignore_ifnets=0
if (test ${netcount} -eq 1 -a "${gw_if}" = "${iface}" && ip_is_net "${peer}" "${ifnets[*]}" && ip_is_net "${gw_ip}" "${peer}")
then
echo "# INFO: Skipping ${iface} peer ${ifnets[*]} only interface (default gateway)."
echo
def_ignore_ifnets=1
else
i=$[i + 1]
helpme_iface route $i "${iface}" "${ip}" "${ifnets[*]}" "${ifreason}"
fi
# Is this interface the default gateway too?
if [ "${gw_if}" = "${iface}" ]
then
for nn in "${ifnets[@]}"
do
if ip_in_net "${gw_ip}" ${nn}
then
echo "# INFO: Default gateway ${gw_ip} is part of network ${nn}"
i=$[i + 1]
helpme_iface route $i "${iface}" "${ip}" "default" "from/to unknown networks behind the default gateway ${gw_ip}" "`test ${def_ignore_ifnets} -eq 0 && echo "${ifnets[*]}"`"
break
fi
done
fi
done
done
echo
echo "# The above $i interfaces were found active at this moment."
echo "# Add more interfaces that can potentially be activated in the future."
echo "# FireHOL will not complain if you setup a firewall on an interface that is"
echo "# not active when you activate the firewall."
echo "# If you don't setup an interface, FireHOL will drop all traffic from or to"
echo "# this interface, if and when it becomes available."
echo "# Also, if an interface name dynamically changes (i.e. ppp0 may become ppp1)"
echo "# you can use the plus (+) character to match all of them (i.e. ppp+)."
echo
if [ "1" = "`${CAT_CMD} /proc/sys/net/ipv4/ip_forward`" ]
then
x=0
i=0
while [ $i -lt ${#found_interfaces[*]} ]
do
i=$[i + 1]
inface="${found_interfaces[$i]}"
src="${found_nets[$i]}"
case "${src}" in
"default")
src="not \"\${UNROUTABLE_IPS} ${found_excludes[$i]}\""
;;
*)
src="\"${src}\""
;;
esac
j=0
while [ $j -lt ${#found_interfaces[*]} ]
do
j=$[j + 1]
test $j -eq $i && continue
outface="${found_interfaces[$j]}"
dst="${found_nets[$j]}"
dst_ip="${found_ips[$j]}"
case "${dst}" in
"default")
dst="not \"\${UNROUTABLE_IPS} ${found_excludes[$j]}\""
;;
*)
dst="\"${dst}\""
;;
esac
# Make sure we are not routing to the same subnet
test "${inface}" = "${outface}" -a "${src}" = "${dst}" && continue
# Make sure this is not a duplicate router
key="`echo ${inface}/${src}-${outface}/${dst} | ${TR_CMD} "/ \\\$\\\"{}" "______"`"
test -f "${FIREHOL_DIR}/keys/${key}" && continue
${TOUCH_CMD} "${FIREHOL_DIR}/keys/${key}"
x=$[x + 1]
if [ $x -lt 10 ]
then
lx="0$x"
else
lx="$x"
fi
echo
echo "# Router No ${x}."
echo "# Clients on ${inface} (from ${src}) accessing servers on ${outface} (to ${dst})."
echo "# TODO: Change \"router${lx}\" to something with meaning to you."
echo "# TODO: Check the optional rule parameters (src/dst)."
echo "# To add IPv6, read http://firehol.org/upgrade/#config-version-6"
echo "router4 router${lx} inface ${inface} outface ${outface} src ${src} dst ${dst}"
echo
echo " # If you don't trust the clients on ${inface} (from ${src}), or"
echo " # if you want to protect the servers on ${outface} (to ${dst}),"
echo " # uncomment the following line."
echo " # > protection strong"
echo
echo " # To NAT client requests on the output of ${outface}, add this."
echo " # > masquerade"
echo " # Alternatively, you can SNAT them by placing this at the top of this config:"
echo " # > snat to ${dst_ip} outface ${outface} src ${src} dst ${dst}"
echo " # SNAT commands can be enhanced using 'proto', 'sport', 'dport', etc in order to"
echo " # NAT only some specific traffic."
echo
echo " # TODO: This will allow all traffic to pass."
echo " # If you remove it, no REQUEST will pass matching this traffic."
echo " route all accept"
echo
done
done
if [ ${x} -eq 0 ]
then
echo
echo
echo "# No router statements have been produced, because your server"
echo "# does not seem to need any."
echo
fi
else
echo
echo
echo "# No router statements have been produced, because your server"
echo "# is not configured for forwarding traffic."
echo
fi
exit 0
fi
2002-12-23 14:39:19 +00:00
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
#
# MAIN PROCESSING
#
# ------------------------------------------------------------------------------
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# ------------------------------------------------------------------------------
# make sure we are alone
firehol_concurrent_run_lock
2002-09-05 20:57:59 +00:00
# --- Initialization -----------------------------------------------------------
echo -n $"FireHOL: Saving your running firewall to a temporary file:"
if [ $ENABLE_IPV4 -eq 1 ]
then
fixed_save ${IPTABLES_SAVE_CMD} >${FIREHOL_SAVED}.new
status4=$?
else
status4=0
fi
if [ $ENABLE_IPV6 -eq 1 ]
then
fixed_save ${IP6TABLES_SAVE_CMD} >${FIREHOL_SAVED6}.new
status6=$?
else
status6=0
fi
if [ $status4 -eq 0 -a $status6 -eq 0 ]
then
test -f ${FIREHOL_SAVED}.new && mv ${FIREHOL_SAVED}.new ${FIREHOL_SAVED}
test -f ${FIREHOL_SAVED6}.new && mv ${FIREHOL_SAVED6}.new ${FIREHOL_SAVED6}
success $"FireHOL: Saving your running firewall to a temporary file:"
echo
else
${RM_CMD} -f "${FIREHOL_SAVED}" "${FIREHOL_SAVED6}"
failure $"FireHOL: Saving your running firewall to a temporary file:"
echo
exit 1
fi
declare -a FIREHOL_ARGS=("${FIREHOL_CONFIG}" "${FIREHOL_CONFIG_DIR}" "${FIREHOL_SERVICES_DIR}" "${@}")
if [ "${FIREHOL_MODE}" = "START" -a ${FIREHOL_RESTORE_INSTEAD_OF_START} -eq 1 ]
then
firehol_restore_last_activated_firewall
if [ $? -eq 0 ]
then
${RM_CMD} -f "${FIREHOL_SAVED}" "${FIREHOL_SAVED6}" >/dev/null 2>&1
FIREHOL_ACTIVATED_SUCCESSFULLY=1
exit 0
fi
warning "Starting the firewall normally..."
fi
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2013-09-28 09:16:56 +00:00
# clear all chains
2013-09-28 09:03:57 +00:00
firehol_filter_chains=
firehol_filter6_chains=
2013-09-28 09:03:57 +00:00
initialize_firewall() {
load_kernel_module ip_tables
load_kernel_module nf_conntrack
if [ $ENABLE_IPV6 -eq 1 ]
then
load_kernel_module ip6_tables
fi
2013-09-28 09:03:57 +00:00
for m in ${FIREHOL_KERNEL_MODULES}
do
postprocess -ne -ns load_kernel_module $m
2013-09-28 09:03:57 +00:00
done
test $FIREHOL_ROUTING -eq 1 && postprocess -warn ${SYSCTL_CMD} -w "net.ipv4.ip_forward=1"
for m in "${!FIREHOL_NFACCT[@]}"
do
# -ne here because nfacct will generate an error
# if the object already exists.
postprocess -ne "${NFACCT_CMD}" add "${m}"
done
2013-09-28 09:03:57 +00:00
# Find all tables supported
local t=
if [ $ENABLE_IPV4 -eq 1 ]; then
local tables=`${CAT_CMD} /proc/net/ip_tables_names`
for t in ${tables}
do
# Reset/empty this table.
${IPTABLES_CMD} -t "${t}" -F || exit 1
${IPTABLES_CMD} -t "${t}" -X || exit 1
${IPTABLES_CMD} -t "${t}" -Z || exit 1
2013-09-28 09:03:57 +00:00
# Find all default chains in this table.
local chains=`${IPTABLES_CMD} -t "${t}" -nL | ${GREP_CMD} "^Chain " | ${CUT_CMD} -d ' ' -f 2`
2013-09-28 09:03:57 +00:00
# If this is the 'filter' table, remember the default chains.
# This will be used at the end to make it DROP all packets.
test "${t}" = "filter" && firehol_filter_chains="${chains}"
2013-09-28 09:03:57 +00:00
# Set the policy to ACCEPT on all default chains.
local c=
for c in ${chains}
do
local policy=ACCEPT
if [ "${t}" = "filter" ]
then
eval "policy=\${FIREHOL_${c}_ACTIVATION_POLICY}"
fi
${IPTABLES_CMD} -t "${t}" -P "${c}" $policy || exit 1
done
2013-09-28 09:03:57 +00:00
done
# Allow existing traffic to continue:
# insert as the first rule in each chain, making it easy to
# undo once the firewall is completely established
if [ ${FIREHOL_FAST_ACTIVATION} -ne 1 -a \
"${FIREHOL_ESTABLISHED_ACTIVATION_ACCEPT}" = "1" ]
then
${IPTABLES_CMD} -I INPUT 1 \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
${IPTABLES_CMD} -I OUTPUT 1 \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
${IPTABLES_CMD} -I FORWARD 1 \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
fi
fi
if [ $ENABLE_IPV6 -eq 1 ]; then
local tables6=`${CAT_CMD} /proc/net/ip6_tables_names`
for t in ${tables6}
do
# Reset/empty this table.
${IP6TABLES_CMD} -t "${t}" -F || exit 1
${IP6TABLES_CMD} -t "${t}" -X || exit 1
${IP6TABLES_CMD} -t "${t}" -Z || exit 1
# Find all default chains in this table.
local chains=`${IP6TABLES_CMD} -t "${t}" -nL | ${GREP_CMD} "^Chain " | ${CUT_CMD} -d ' ' -f 2`
# If this is the 'filter' table, remember the default chains.
# This will be used at the end to make it DROP all packets.
test "${t}" = "filter" && firehol_filter6_chains="${chains}"
# Set the policy to ACCEPT on all default chains.
local c=
for c in ${chains}
do
local policy=ACCEPT
if [ "${t}" = "filter" ]
then
eval "policy=\${FIREHOL_${c}_ACTIVATION_POLICY}"
fi
${IP6TABLES_CMD} -t "${t}" -P "${c}" $policy || exit 1
done
done
# Allow existing traffic to continue
if [ ${FIREHOL_FAST_ACTIVATION} -ne 1 -a \
"${FIREHOL_ESTABLISHED_ACTIVATION_ACCEPT}" = "1" ]
then
${IP6TABLES_CMD} -I INPUT 1 \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
${IP6TABLES_CMD} -I OUTPUT 1 \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
${IP6TABLES_CMD} -I FORWARD 1 \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
fi
fi
2013-09-28 09:03:57 +00:00
}
2013-09-28 09:16:56 +00:00
# drop everything
finalize_firewall() {
# Make it drop everything on table 'filter'.
local c=
if [ $ENABLE_IPV4 -eq 1 ]; then
for c in ${firehol_filter_chains}
do
${IPTABLES_CMD} -t filter -P "${c}" DROP || exit 1
done
# Remove rules inserted which were to keep existing traffic
# alive during activation
if [ ${FIREHOL_FAST_ACTIVATION} -ne 1 -a \
"${FIREHOL_ESTABLISHED_ACTIVATION_ACCEPT}" = "1" ]
then
${IPTABLES_CMD} -D INPUT \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
${IPTABLES_CMD} -D OUTPUT \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
${IPTABLES_CMD} -D FORWARD \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
fi
fi
if [ $ENABLE_IPV6 -eq 1 ]; then
for c in ${firehol_filter6_chains}
do
${IP6TABLES_CMD} -t filter -P "${c}" DROP || exit 1
done
if [ ${FIREHOL_FAST_ACTIVATION} -ne 1 -a \
"${FIREHOL_ESTABLISHED_ACTIVATION_ACCEPT}" = "1" ]
then
${IP6TABLES_CMD} -D INPUT \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
${IP6TABLES_CMD} -D OUTPUT \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
${IP6TABLES_CMD} -D FORWARD \
-m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
fi
fi
2013-09-28 09:16:56 +00:00
}
2013-09-28 09:16:56 +00:00
# this will be run when the first iptables command get executed in pre-process mode.
# so that its commands are prepended to the other iptables commands of the firewall
2013-09-28 09:03:57 +00:00
firewall_policy_applied=0
firewall_policy() {
firewall_policy_applied=1
iptables -t filter -P INPUT "${FIREHOL_INPUT_ACTIVATION_POLICY}"
iptables -t filter -P OUTPUT "${FIREHOL_OUTPUT_ACTIVATION_POLICY}"
iptables -t filter -P FORWARD "${FIREHOL_FORWARD_ACTIVATION_POLICY}"
2013-09-28 09:03:57 +00:00
# Accept everything in/out the loopback device.
if [ "${FIREHOL_TRUST_LOOPBACK}" = "1" ]
then
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
fi
2013-09-28 09:03:57 +00:00
# Drop all invalid packets.
# Netfilter HOWTO suggests to DROP all INVALID packets.
if [ "${FIREHOL_DROP_INVALID}" = "1" ]
then
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
iptables -A OUTPUT -m conntrack --ctstate INVALID -j DROP
iptables -A FORWARD -m conntrack --ctstate INVALID -j DROP
fi
}
firewall_policy6_applied=0
firewall_policy6() {
firewall_policy6_applied=1
ip6tables -t filter -P INPUT "${FIREHOL_INPUT_ACTIVATION_POLICY}"
ip6tables -t filter -P OUTPUT "${FIREHOL_OUTPUT_ACTIVATION_POLICY}"
ip6tables -t filter -P FORWARD "${FIREHOL_FORWARD_ACTIVATION_POLICY}"
# Accept everything in/out the loopback device.
if [ "${FIREHOL_TRUST_LOOPBACK}" = "1" ]
then
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT
fi
# Drop all invalid packets.
# Netfilter HOWTO suggests to DROP all INVALID packets.
if [ "${FIREHOL_DROP_INVALID}" = "1" ]
then
ip6tables -A INPUT -m conntrack --ctstate INVALID -j DROP
ip6tables -A OUTPUT -m conntrack --ctstate INVALID -j DROP
ip6tables -A FORWARD -m conntrack --ctstate INVALID -j DROP
fi
}
2002-09-05 20:57:59 +00:00
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
echo -n $"FireHOL: Processing file ${FIREHOL_CONFIG}:"
ret=0
# ------------------------------------------------------------------------------
# Create a small awk script that inserts line numbers in the configuration file
# just before each known directive.
# These line numbers will be used for debugging the configuration script.
${CAT_CMD} >"${FIREHOL_TMP}.awk" <<'EOF'
$1 == "action" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "blacklist" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "classify" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "client" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "connmark" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "dnat" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "dscp" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "ecn_shame" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "group" { printf "FIREHOL_LINEID=${LINENO} " }
2014-02-03 22:50:16 +00:00
$1 == "group4" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "group6" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "group46" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "interface" { printf "FIREHOL_LINEID=${LINENO} " }
2014-02-03 22:50:16 +00:00
$1 == "interface4" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "interface6" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "interface46" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "iptables" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "ip6tables" { printf "FIREHOL_LINEID=${LINENO} " }
2014-02-03 22:50:16 +00:00
$1 == "ipv4" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "ipv6" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "mac" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "mark" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "masquerade" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "nat" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "policy" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "postprocess" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "protection" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "redirect" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "require_kernel_module" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "router" { printf "FIREHOL_LINEID=${LINENO} " }
2014-02-03 22:50:16 +00:00
$1 == "router4" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "router6" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "router46" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "route" { printf "FIREHOL_LINEID=${LINENO} " }
2014-02-03 22:50:16 +00:00
$1 == "route4" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "route6" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "route46" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "server" { printf "FIREHOL_LINEID=${LINENO} " }
2014-02-03 22:50:16 +00:00
$1 == "server4" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "server6" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "server46" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "snat" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "tcpmss" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "tos" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "transparent_squid" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "transparent_proxy" { printf "FIREHOL_LINEID=${LINENO} " }
$1 == "version" { printf "FIREHOL_LINEID=${LINENO} " }
2002-09-05 20:57:59 +00:00
{ print }
EOF
# at the same time, replace all ${IPTABLES_CMD} references with just
# the word 'iptables' to protect the currently running firewall
SED_RULES=
if [ $ENABLE_IPV4 -eq 1 ]; then
SED_RULES="${SED_RULES} -e s|${IPTABLES_CMD}|iptables|g"
fi
if [ $ENABLE_IPV6 -eq 1 ]; then
SED_RULES="${SED_RULES} -e s|${IP6TABLES_CMD}|ip6tables|g"
fi
${CAT_CMD} ${FIREHOL_CONFIG} | ${SED_CMD} ${SED_RULES} | gawk_cmd -f "${FIREHOL_TMP}.awk" >${FIREHOL_TMP}
${RM_CMD} -f "${FIREHOL_TMP}.awk"
2002-09-05 20:57:59 +00:00
# ------------------------------------------------------------------------------
# Run the configuration file.
if [ -n "$WAIT_FOR_IFACE" ]
then
for i in "$WAIT_FOR_IFACE"
do
wait_for_interface $i
done
fi
2002-09-05 20:57:59 +00:00
enable -n trap # Disable the trap buildin shell command.
enable -n exit # Disable the exit buildin shell command.
{ source ${FIREHOL_TMP} "$@"; } # Run the configuration as a normal script.
source_status=$?
[ $source_status -ne 0 ] && ret=$[ret + 1]
[ $source_status -ne 0 ] && FIREHOL_CLEAN_TMP=0
2002-09-05 20:57:59 +00:00
FIREHOL_LINEID="FIN"
enable trap # Enable the trap buildin shell command.
enable exit # Enable the exit buildin shell command.
close_cmd || ret=$[ret + 1]
close_master || ret=$[ret + 1]
2002-09-05 20:57:59 +00:00
if [ ${work_error} -gt 0 -o $ret -gt 0 ]
then
echo >&2
echo >&2 "NOTICE: No changes made to your firewall."
failure $"FireHOL: Processing file ${FIREHOL_CONFIG}:"
echo
exit 1
fi
success $"FireHOL: Processing file ${FIREHOL_CONFIG}:"
echo
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
2002-09-05 20:57:59 +00:00
2013-09-25 23:50:30 +00:00
if [ ${FIREHOL_FAST_ACTIVATION} -eq 1 ]
2002-09-05 20:57:59 +00:00
then
2013-09-25 23:50:30 +00:00
# construct the iptables-restore file from the splitted ones.
cd $FIREHOL_DIR/fast/tables || exit 1
for firehol_table in `ls`
do
(
echo "*${firehol_table}"
test -f $FIREHOL_DIR/fast/table.${firehol_table}.policy && ${CAT_CMD} $FIREHOL_DIR/fast/table.${firehol_table}.policy
test -f $FIREHOL_DIR/fast/table.${firehol_table}.chains && ${CAT_CMD} $FIREHOL_DIR/fast/table.${firehol_table}.chains
test -f $FIREHOL_DIR/fast/table.${firehol_table}.rules && ${CAT_CMD} $FIREHOL_DIR/fast/table.${firehol_table}.rules
echo "COMMIT"
) >>${FIREHOL_OUTPUT}.fast
done
2013-12-01 14:59:44 +00:00
cd $FIREHOL_DIR/fast/table6s || exit 1
for firehol_table in `ls`
do
(
echo "*${firehol_table}"
test -f $FIREHOL_DIR/fast/table6.${firehol_table}.policy && ${CAT_CMD} $FIREHOL_DIR/fast/table6.${firehol_table}.policy
test -f $FIREHOL_DIR/fast/table6.${firehol_table}.chains && ${CAT_CMD} $FIREHOL_DIR/fast/table6.${firehol_table}.chains
test -f $FIREHOL_DIR/fast/table6.${firehol_table}.rules && ${CAT_CMD} $FIREHOL_DIR/fast/table6.${firehol_table}.rules
echo "COMMIT"
) >>${FIREHOL_OUTPUT}.fast6
done
2013-09-28 09:16:56 +00:00
if [ "${FIREHOL_MODE}" = "DEBUG" ]
then
test $ENABLE_IPV4 -eq 1 && ${CAT_CMD} ${FIREHOL_OUTPUT}.fast
test $ENABLE_IPV6 -eq 1 && ${CAT_CMD} ${FIREHOL_OUTPUT}.fast6
2013-09-28 09:16:56 +00:00
exit 1
fi
2013-09-25 23:50:30 +00:00
syslog info "Activating new firewall from ${FIREHOL_CONFIG} (translated to ${FIREHOL_COMMAND_COUNTER} iptables rules)."
2013-09-28 09:03:57 +00:00
echo -n $"FireHOL: Fast activating new firewall:"
initialize_firewall
2013-09-25 23:50:30 +00:00
# execute any postprocessing commands
# in FAST_ACTIVATION the output file does not have any iptables commands
# it might have kernel modules management, activation of routing, etc.
source ${FIREHOL_OUTPUT} "${@}"
2013-09-25 23:50:30 +00:00
if [ $? -ne 0 ]
then
work_runtime_error=$[work_runtime_error+1]
2013-09-28 09:03:57 +00:00
else
# attempt to restore this firewall from the generated commands
if [ $ENABLE_IPV4 -eq 1 ]
then
${IPTABLES_RESTORE_CMD} <${FIREHOL_OUTPUT}.fast >${FIREHOL_OUTPUT}.log 2>&1
status4=$?
else
status4=0
fi
if [ $status4 -ne 0 ]
then
# it failed
runtime_error error "CANNOT APPLY IN FAST MODE" FIN "${IPTABLES_RESTORE_CMD}" "<${FIREHOL_OUTPUT}.fast"
2014-03-12 22:20:00 +00:00
work_runtime_error=$[work_runtime_error+1]
2014-03-12 22:20:00 +00:00
# find the line
echo >&2 "Offending line:"
line=`cat "${FIREHOL_OUTPUT}.log" | grep "Error occurred at line: " | cut -d ':' -f 2`
test -z "$line" && line=`cat "${FIREHOL_OUTPUT}.log" | grep "iptables-restore: line " | grep failed | cut -d ' ' -f 3`
2014-03-12 22:20:00 +00:00
${CAT_CMD} "${FIREHOL_OUTPUT}.fast" | ${HEAD_CMD} -n $line | ${TAIL_CMD} -n 1 >&2
echo >&2
# the rest of the script will restore the original firewall
else
if [ $ENABLE_IPV6 -eq 1 ]
then
${IP6TABLES_RESTORE_CMD} <${FIREHOL_OUTPUT}.fast6 >>${FIREHOL_OUTPUT}.log 2>&1
status6=$?
else
status6=0
fi
if [ $status6 -ne 0 ]
2013-12-01 14:59:44 +00:00
then
# it failed
runtime_error error "CANNOT APPLY IN FAST MODE" FIN "${IP6TABLES_RESTORE_CMD}" "<${FIREHOL_OUTPUT}.fast6"
work_runtime_error=$[work_runtime_error+1]
2014-03-12 22:20:00 +00:00
# find the line
echo >&2 "Offending line:"
line=`cat "${FIREHOL_OUTPUT}.log" | grep "Error occurred at line: " | cut -d ':' -f 2`
test -z "$line" && line=`cat "${FIREHOL_OUTPUT}.log" | grep "ip6tables-restore: line " | grep failed | cut -d ' ' -f 3`
2014-03-12 22:20:00 +00:00
${CAT_CMD} "${FIREHOL_OUTPUT}.fast6" | ${HEAD_CMD} -n $line | ${TAIL_CMD} -n 1 >&2
echo >&2
2013-12-01 14:59:44 +00:00
# the rest of the script will restore the original firewall
else
finalize_firewall
fi
fi
2013-09-25 23:50:30 +00:00
fi
else
2013-09-28 09:16:56 +00:00
if [ "${FIREHOL_MODE}" = "DEBUG" ]
then
${CAT_CMD} ${FIREHOL_OUTPUT}
exit 1
fi
2013-09-25 23:50:30 +00:00
syslog info "Activating new firewall from ${FIREHOL_CONFIG} (translated to ${FIREHOL_COMMAND_COUNTER} iptables rules)."
echo -n $"FireHOL: Activating new firewall (${FIREHOL_COMMAND_COUNTER} rules):"
2013-09-28 09:03:57 +00:00
initialize_firewall
2013-09-25 23:50:30 +00:00
2013-09-28 09:03:57 +00:00
source ${FIREHOL_OUTPUT} "$@"
if [ $? -ne 0 ]
then
work_runtime_error=$[work_runtime_error+1]
else
finalize_firewall
fi
2002-09-05 20:57:59 +00:00
fi
if [ ${work_runtime_error} -gt 0 ]
2002-09-05 20:57:59 +00:00
then
failure $"FireHOL: Activating new firewall:"
echo
syslog err "Activation of new firewall failed."
# The trap will restore the firewall we saved above.
if [ ${FIREHOL_FAST_ACTIVATION} -eq 1 ]
then
echo >&2
echo >&2 "To get a more detailed report of the offending command,"
echo >&2 "you can quickly re-apply the same firewall with fast"
echo >&2 "activation disabled, like this:"
echo >&2
printf >&2 "${FIREHOL_FILE} nofast "
printf >&2 "%q " "${FIREHOL_ORIGINAL_ARGS[@]}"
printf >&2 "\n"
fi
2002-09-05 20:57:59 +00:00
exit 1
fi
2003-12-29 22:46:00 +00:00
success $"FireHOL: Activating new firewall (${FIREHOL_COMMAND_COUNTER} rules):"
2002-09-05 20:57:59 +00:00
echo
syslog info "Activation of new firewall succeeded."
if [ ${FIREHOL_TRY} -eq 1 ]
then
syslog info "Waiting user to commit the new firewall."
read -p "Keep the firewall? (type 'commit' to accept - 30 seconds timeout) : " -t 30 -e
ret=$?
echo
if [ ! $ret -eq 0 -o ! "${REPLY}" = "commit" ]
then
syslog err "User did not confirm the new firewall."
# The trap will restore the firewall.
exit 1
else
echo "Successfull activation of FireHOL firewall."
syslog info "User committed new firewall."
fi
fi
# Remove the saved firewall, so that the trap will not restore it.
${RM_CMD} -f "${FIREHOL_SAVED}" "${FIREHOL_SAVED6}" >/dev/null 2>&1
FIREHOL_ACTIVATED_SUCCESSFULLY=1
2002-09-05 20:57:59 +00:00
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# last, keep a copy of the firewall we activated, on disk
mv "${FIREHOL_DIR}/firewall_restore_commands.sh" "${FIREHOL_SPOOL_DIR}/firewall_restore_commands.sh"
chown root:root "${FIREHOL_SPOOL_DIR}/firewall_restore_commands.sh"
chmod 700 "${FIREHOL_SPOOL_DIR}/firewall_restore_commands.sh"
# keep track if we do ipv4
if [ $ENABLE_IPV4 -eq 1 ]
then
touch "${FIREHOL_SPOOL_DIR}/ipv4.enable"
chown root:root "${FIREHOL_SPOOL_DIR}/ipv4.enable"
chmod 600 "${FIREHOL_SPOOL_DIR}/ipv4.enable"
else
test -f "${FIREHOL_SPOOL_DIR}/ipv4.enable" && rm "${FIREHOL_SPOOL_DIR}/ipv4.enable"
fi
# keep track if we do ipv6
if [ $ENABLE_IPV6 -eq 1 ]
then
touch "${FIREHOL_SPOOL_DIR}/ipv6.enable"
chown root:root "${FIREHOL_SPOOL_DIR}/ipv6.enable"
chmod 600 "${FIREHOL_SPOOL_DIR}/ipv6.enable"
else
test -f "${FIREHOL_SPOOL_DIR}/ipv6.enable" && rm "${FIREHOL_SPOOL_DIR}/ipv6.enable"
fi
# save the rules to ${FIREHOL_SPOOL_DIR}
firehol_save_activated_firewall
2002-09-05 20:57:59 +00:00
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
if [ ${FIREHOL_SAVE} -eq 1 ]
then
if [ $ENABLE_IPV4 -eq 1 -a -z "${FIREHOL_AUTOSAVE}" ]
then
if [ -d "/etc/sysconfig" ]
then
# RedHat
FIREHOL_AUTOSAVE="/etc/sysconfig/iptables"
elif [ -d "/var/lib/iptables" ]
then
if [ -f /etc/conf.d/iptables ]
then
# Gentoo
IPTABLES_SAVE=
. /etc/conf.d/iptables
FIREHOL_AUTOSAVE="${IPTABLES_SAVE}"
fi
if [ -z "${FIREHOL_AUTOSAVE}" ]
then
# Debian
FIREHOL_AUTOSAVE="/var/lib/iptables/autosave"
fi
else
error "Cannot find where to save iptables file. Please set FIREHOL_AUTOSAVE."
echo
exit 1
fi
fi
if [ $ENABLE_IPV6 -eq 1 -a -z "${FIREHOL_AUTOSAVE6}" ]
2013-11-03 17:43:40 +00:00
then
error "Cannot find where to save ip6tables file. Please set FIREHOL_AUTOSAVE6."
echo
exit 1
fi
if [ $ENABLE_IPV4 -eq 1 ]
then
echo -n $"FireHOL: Saving firewall to ${FIREHOL_AUTOSAVE}:"
cat "${FIREHOL_SPOOL_DIR}/ipv4.rules" >${FIREHOL_AUTOSAVE}
if [ ! $? -eq 0 ]
then
syslog err "Failed to save new firewall to '${FIREHOL_AUTOSAVE}'."
failure $"FireHOL: Saving firewall to ${FIREHOL_AUTOSAVE}:"
echo
exit 1
fi
syslog info "New firewall saved to '${FIREHOL_AUTOSAVE}'."
success $"FireHOL: Saving firewall to ${FIREHOL_AUTOSAVE}:"
echo
fi
if [ $ENABLE_IPV6 -eq 1 ]
then
echo -n $"FireHOL: Saving IPv6 firewall to ${FIREHOL_AUTOSAVE6}:"
cat "${FIREHOL_SPOOL_DIR}/ipv6.rules" >${FIREHOL_AUTOSAVE6}
if [ ! $? -eq 0 ]
then
syslog err "Failed to save new IPv6 firewall to '${FIREHOL_AUTOSAVE6}'."
failure $"FireHOL: Saving IPv6 firewall to ${FIREHOL_AUTOSAVE6}:"
echo
exit 1
fi
syslog info "New IPv6 firewall saved to '${FIREHOL_AUTOSAVE6}'."
success $"FireHOL: Saving IPv6 firewall to ${FIREHOL_AUTOSAVE6}:"
2013-11-03 17:43:40 +00:00
echo
fi
exit 0
2002-09-05 20:57:59 +00:00
fi