mirror of
https://github.com/firehol/firehol.git
synced 2024-06-30 19:02:21 +00:00
9878 lines
260 KiB
Bash
Executable File
9878 lines
260 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# FireHOL - A firewall for humans...
|
|
#
|
|
# Copyright
|
|
#
|
|
# Copyright (C) 2003-2014 Costa Tsaousis <costa@tsaousis.gr>
|
|
# Copyright (C) 2012-2014 Phil Whineray <phil@sanewall.org>
|
|
#
|
|
# License
|
|
#
|
|
# 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.
|
|
#
|
|
|
|
if [ $(( ${BASH_VERSINFO[0]} )) -lt 4 ]
|
|
then
|
|
echo >&2
|
|
echo >&2 "ERROR:"
|
|
echo >&2 "FireHOL requires BASH version 4 or later."
|
|
echo >&2 "You are running version: ${BASH_VERSION}"
|
|
echo >&2 "Please upgrade."
|
|
echo >&2
|
|
exit 1
|
|
fi
|
|
|
|
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)
|
|
|
|
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 "ERROR:"
|
|
echo >&2 "Only user root can run FireHOL."
|
|
echo >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Remember who you are.
|
|
PROGRAM_FILE="${0}"
|
|
declare -a FIREHOL_ORIGINAL_ARGS=("${@}")
|
|
FIREHOL_DEFAULT_WORKING_DIRECTORY="${PWD}"
|
|
|
|
# Make sure we don't get localized results
|
|
export LC_ALL=C
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
#
|
|
# BITMASKED MARKS
|
|
#
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
|
|
declare -A MARKS_BITS=()
|
|
declare -A MARKS_MASKS=()
|
|
declare -A MARKS_MAX=()
|
|
declare -A MARKS_SHIFT=()
|
|
declare -A MARKS_SAVERESTORE=()
|
|
declare -A MARKS_STATEFUL=()
|
|
MARKS_SAVERESTORE_STATEFUL_MASK="0x00000000"
|
|
MARKS_SAVERESTORE_STATELESS_MASK="0x00000000"
|
|
MARKS_TOTAL_BITS=0
|
|
|
|
# taken from http://stackoverflow.com/questions/109023/how-to-count-the-number-of-set-bits-in-a-32-bit-integer
|
|
# and http://books.google.gr/books?id=iBNKMspIlqEC&pg=PA66&redir_esc=y#v=onepage&q&f=false
|
|
# counts the number of bits set in a number
|
|
numberofbits() {
|
|
local x="${1}"
|
|
|
|
x=$[ x - ( (x >> 1) & 0x55555555) ]
|
|
x=$[ (x & 0x33333333) + ( (x >> 2) & 0x33333333) ]
|
|
x=$[ (x + (x >> 4) ) & 0x0F0F0F0F ]
|
|
x=$[ x + (x >> 8) ]
|
|
x=$[ x + (x >> 16) ];
|
|
bits=$[ x & 0x0000003F ]
|
|
echo $bits
|
|
}
|
|
|
|
# taken from http://www.skorks.com/2010/10/write-a-function-to-determine-if-a-number-is-a-power-of-2/
|
|
# checks if a number is power of 2
|
|
ispoweroftwo() {
|
|
local num="${1}"
|
|
|
|
test ! $num = 0 -a $[num & (num - 1)] = 0 && return 0
|
|
return 1
|
|
}
|
|
|
|
marksreset() { markdef clear; }
|
|
markdef() {
|
|
if [ "$1" = "reset" -o "$1" = "clear" ]
|
|
then
|
|
MARKS_BITS=()
|
|
MARKS_MASKS=()
|
|
MARKS_MAX=()
|
|
MARKS_SHIFT=()
|
|
MARKS_SAVERESTORE=()
|
|
MARKS_STATEFUL=()
|
|
MARKS_SAVERESTORE_STATEFUL_MASK="0x00000000"
|
|
MARKS_SAVERESTORE_STATELESS_MASK="0x00000000"
|
|
MARKS_TOTAL_BITS=0
|
|
return 0
|
|
fi
|
|
|
|
local saverestore=1
|
|
local stateful=1
|
|
local mask=
|
|
local name="${1}"
|
|
local max="${2}"
|
|
shift 2
|
|
|
|
while [ ! -z "${1}" ]
|
|
do
|
|
case "${1}" in
|
|
default)
|
|
saverestore=1
|
|
stateful=1
|
|
;;
|
|
|
|
classic)
|
|
saverestore=0
|
|
stateful=0
|
|
;;
|
|
|
|
save|restore|permanent)
|
|
saverestore=1
|
|
;;
|
|
|
|
nosave|norestore|temp|temporary)
|
|
saverestore=0
|
|
;;
|
|
|
|
stateless)
|
|
stateful=0
|
|
;;
|
|
|
|
stateful)
|
|
stateful=1
|
|
;;
|
|
|
|
*)
|
|
echo >&2 "ERROR in ${FUNCNAME}: Unknown keyword '${1}'."
|
|
exit 1
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
if [ ! -z "${MARKS_MASKS[$name]}" ]
|
|
then
|
|
echo >&2 "ERROR in ${FUNCNAME}: Mark type '${name}' already exists with mask ${MARKS_MASKS[$name]}. Please use 'marksreset' to reset them before re-defining them."
|
|
exit 1
|
|
fi
|
|
|
|
if [ "${max}" = "rest" ]
|
|
then
|
|
max=$[ 1 << (32 - MARKS_TOTAL_BITS) ]
|
|
fi
|
|
|
|
if ! ispoweroftwo $max
|
|
then
|
|
echo >&2 "ERROR in ${FUNCNAME}: Max value $max of mark '$name' is not a power of 2."
|
|
exit 1
|
|
fi
|
|
|
|
# it will be from 0 to max - 1
|
|
max=$[ max - 1 ]
|
|
|
|
if [ $max -lt 1 -o $max -gt $[ 0xffffffff ] ]
|
|
then
|
|
echo >&2 "ERROR in ${FUNCNAME}: Max value $max of mark '$name' is out of bounds."
|
|
exit 1
|
|
fi
|
|
|
|
# prevent a fork
|
|
bits=$(numberofbits $max)
|
|
|
|
if [ $bits -eq 0 ]
|
|
then
|
|
echo >&2 "ERROR in ${FUNCNAME}: INTERNAL ERROR: Cannot figure out the bits set of value $max."
|
|
exit 1
|
|
fi
|
|
|
|
if [ $[ bits + MARKS_TOTAL_BITS ] -gt 32 ]
|
|
then
|
|
echo >&2 "ERROR in ${FUNCNAME}: Too many masks were requested. Cannot proceed. Please use fewer."
|
|
exit 1
|
|
fi
|
|
|
|
# find its mask
|
|
# we have all the bits we need set in $mark
|
|
# just shift it to the right position.
|
|
mask=$[ max << MARKS_TOTAL_BITS ]
|
|
|
|
MARKS_SHIFT[$name]=${MARKS_TOTAL_BITS}
|
|
MARKS_MAX[$name]=$max
|
|
MARKS_BITS[$name]=$bits
|
|
MARKS_MASKS[$name]=$(printf "0x%08x" $mask)
|
|
MARKS_STATEFUL[$name]=$stateful
|
|
MARKS_SAVERESTORE[$name]=$saverestore
|
|
|
|
if [ $saverestore -eq 1 ]
|
|
then
|
|
if [ $stateful -eq 1 ]
|
|
then
|
|
MARKS_SAVERESTORE_STATEFUL_MASK=$(printf "0x%08x" $[MARKS_SAVERESTORE_STATEFUL_MASK | mask])
|
|
else
|
|
MARKS_SAVERESTORE_STATELESS_MASK=$(printf "0x%08x" $[MARKS_SAVERESTORE_STATELESS_MASK | mask])
|
|
fi
|
|
fi
|
|
MARKS_TOTAL_BITS=$[ MARKS_TOTAL_BITS + bits ]
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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}"
|
|
|
|
# Only when FIREHOL_FAST_ACTIVATION=1, this value is the time in
|
|
# seconds, to wait for just an ENTER before trying the new firewall.
|
|
FIREHOL_WAIT_USER_BEFORE_TRY=600
|
|
|
|
# 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 MARKS BITMASKING
|
|
|
|
# FireHOL allows multiple independent MARKs.
|
|
# By default FireHOL requires 'connmark' and 'usermark'.
|
|
|
|
# Mark types may be defined with this template:
|
|
#
|
|
# markdef NAME VALUES [stateful|stateless] [permanent|temporary]
|
|
#
|
|
# NAME = a name for this mark type
|
|
# connmark and usermark should always be defined.
|
|
#
|
|
# VALUES = max number of marks to support (0 to VALUES - 1)
|
|
# VALUES must be a power of two.
|
|
#
|
|
# stateful = all statements that assign this mark should
|
|
# only apply it on NEW packets.
|
|
#
|
|
# stateless = all statements that assign this mark type should
|
|
# only apply it only to traffic matched by the
|
|
# optional rule parameters given.
|
|
#
|
|
# temporary = do not save/restore to/from connection marks.
|
|
# This means RESPONSES to the matched packets
|
|
# will not get the mark.
|
|
#
|
|
# permanent = save/restore to/from connection marks
|
|
# This means that RESPONSES will get the mark.
|
|
#
|
|
# NOTES ABOUT markdef OPTIONS
|
|
#
|
|
# default is : stateful permanent or default
|
|
# in this mode, only NEW packets of connections need
|
|
# to be marked. ESTABLISHED and RELATED packets
|
|
# will automatically get the same mark too.
|
|
# So, in FireHOL mark helpers (connmark, mark, custommark)
|
|
# you will only need to match a REQUEST packet and
|
|
# automatically all the packets of the connection will
|
|
# get the mark.
|
|
#
|
|
# - stateful temporary
|
|
# In this mode, only NEW packets will be marked for each
|
|
# connection. ESTABLISHED and RELATED packets will NOT
|
|
# get the mark.
|
|
#
|
|
# - stateless permanent
|
|
# In this mode, whatever the helper statement matches
|
|
# will get the mark. This mark will also be applied to
|
|
# all the packets that are encountered after the marked
|
|
# packet and are part of the same socket.
|
|
#
|
|
# - stateless temporary or classic
|
|
# In this mode, only whatever the helper statement matches
|
|
# will get the mark. Nothing else.
|
|
#
|
|
|
|
# clear the internal marks - do not remove this line
|
|
markdef clear
|
|
|
|
# connmarks are used by the connmark helper
|
|
markdef connmark 64
|
|
|
|
# usermark are used by the mark helper
|
|
markdef usermark 128
|
|
|
|
# Custom mark example:
|
|
#
|
|
# markdef qosmark 8
|
|
#
|
|
# To use it use 'custommark' helper and optional rule parameter.
|
|
# The first argument to both should the mark name (qosmark in this case)
|
|
|
|
# ----------------------------------------------------------------------
|
|
# 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
|
|
|
|
# ----------------------------------------------------------------------
|
|
# IPSET OPTIONS
|
|
|
|
# A recent ipset command uses these:
|
|
IPSET_CREATE_OPTION="create"
|
|
IPSET_FLUSH_OPTION="flush"
|
|
IPSET_ADD_OPTION="add"
|
|
IPSET_SAVE_OPTION="save"
|
|
IPSET_RESTORE_OPTION="restore"
|
|
IPSET_CREATE_IPV6_OPTION="family inet6"
|
|
IPSET_LIST_NAMES_EVAL="list -n"
|
|
|
|
# older versions use these
|
|
#IPSET_CREATE_OPTION="-N"
|
|
#IPSET_FLUSH_OPTION="-F"
|
|
#IPSET_ADD_OPTION="-A"
|
|
#IPSET_SAVE_OPTION="--save"
|
|
#IPSET_RESTORE_OPTION="--restore"
|
|
#IPSET_CREATE_IPV6_OPTION="" # No ipv6 support
|
|
#IPSET_LIST_NAMES_EVAL="--list | grep Name: | cut -d: -f 2"
|
|
|
|
# ----------------------------------------------------------------------
|
|
# 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 ---
|
|
|
|
# load the defaults if they exist
|
|
if [ -f "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" ]
|
|
then
|
|
source "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" || exit 1
|
|
fi
|
|
|
|
# default config file
|
|
FIREHOL_CONFIG="${FIREHOL_CONFIG_DIR}/firehol.conf"
|
|
|
|
# Concurrent run control
|
|
FIREHOL_LOCK_FILE="${FIREHOL_RUN_DIR}/firehol.lck"
|
|
|
|
# make sure the defaults include a connmark
|
|
if [ -z "${MARKS_MASKS[connmark]}" ]
|
|
then
|
|
echo >&2 "ERROR: File ${FIREHOL_CONFIG_DIR}/marks.conf does not define a 'connmark' definition."
|
|
exit 1
|
|
fi
|
|
|
|
# make sure the defaults include a usermark
|
|
if [ -z "${MARKS_MASKS[usermark]}" ]
|
|
then
|
|
echo >&2 "ERROR: File ${FIREHOL_CONFIG_DIR}/marks.conf does not define a 'usermark' definition."
|
|
exit 1
|
|
fi
|
|
|
|
# save the information for the other tools
|
|
declare -p MARKS_BITS MARKS_MASKS MARKS_MAX MARKS_SHIFT MARKS_STATEFUL MARKS_SAVERESTORE MARKS_SAVERESTORE_STATEFUL_MASK MARKS_SAVERESTORE_STATELESS_MASK >"${FIREHOL_SPOOL_DIR}/marks.conf"
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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 cmd= block=1
|
|
if [ "a${1}" = "a-n" ]
|
|
then
|
|
block=0
|
|
shift
|
|
fi
|
|
|
|
unalias $2 >/dev/null 2>&1
|
|
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 var= val= block=1
|
|
|
|
if [ "a$1" = "a-n" ]
|
|
then
|
|
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
|
|
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
|
|
|
|
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
|
|
|
|
ENABLE_IPSET=1
|
|
IPSET_WARNING=0
|
|
require_cmd -n ipset
|
|
if [ -z "${IPSET_CMD}" ]
|
|
then
|
|
# silently disable accounting here,
|
|
# the user will get a warning when the first
|
|
# accounting rule is evaluated
|
|
ENABLE_IPSET=0
|
|
IPSET_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 " 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 " 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
|
|
}
|
|
|
|
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 " 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}" "${@}"
|
|
}
|
|
|
|
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 "ERROR: 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
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# ----------------------------------------------------------------------
|
|
# 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 "ERROR: Cannot create temporary directory in ${FIREHOL_RUN_DIR}. Make sure you have a working mktemp."
|
|
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"
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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
|
|
}
|
|
|
|
# Enable colors
|
|
if [ $[$(tput colors 2>/dev/null)] -ge 8 ]
|
|
then
|
|
COLOR_RESET="\e[0m"
|
|
COLOR_BLACK="\e[30m"
|
|
COLOR_RED="\e[31m"
|
|
COLOR_GREEN="\e[32m"
|
|
COLOR_YELLOW="\e[33m"
|
|
COLOR_BLUE="\e[34m"
|
|
COLOR_PURPLE="\e[35m"
|
|
COLOR_CYAN="\e[36m"
|
|
COLOR_WHITE="\e[37m"
|
|
COLOR_BGBLACK="\e[40m"
|
|
COLOR_BGRED="\e[41m"
|
|
COLOR_BGGREEN="\e[42m"
|
|
COLOR_BGYELLOW="\e[43m"
|
|
COLOR_BGBLUE="\e[44m"
|
|
COLOR_BGPURPLE="\e[45m"
|
|
COLOR_BGCYAN="\e[46m"
|
|
COLOR_BGWHITE="\e[47m"
|
|
COLOR_BOLD="\e[1m"
|
|
COLOR_DIM="\e[2m"
|
|
COLOR_UNDERLINED="\e[4m"
|
|
COLOR_BLINK="\e[5m"
|
|
COLOR_INVERTED="\e[7m"
|
|
fi
|
|
|
|
declare -a FIREHOL_PROGRESS_MESSAGES=()
|
|
progress() {
|
|
printf >&2 "FireHOL: ${*}... "
|
|
FIREHOL_PROGRESS_MESSAGES=("${*}" "${FIREHOL_PROGRESS_MESSAGES[@]}")
|
|
syslog info "${*} started"
|
|
}
|
|
|
|
success() {
|
|
if [ ! -z "${1}" ]
|
|
then
|
|
echo >&2 " OK (${*})"
|
|
syslog info "${FIREHOL_PROGRESS_MESSAGES[0]} succeeded with message: ${*}"
|
|
else
|
|
echo >&2 " OK"
|
|
syslog info "${FIREHOL_PROGRESS_MESSAGES[0]} succeeded"
|
|
fi
|
|
unset FIREHOL_PROGRESS_MESSAGES[0]
|
|
FIREHOL_PROGRESS_MESSAGES=("${FIREHOL_PROGRESS_MESSAGES[@]}")
|
|
}
|
|
|
|
failure() {
|
|
if [ ! -z "${1}" ]
|
|
then
|
|
echo >&2 " FAILED (${*})"
|
|
syslog err "${FIREHOL_PROGRESS_MESSAGES[0]} failed with message: ${*}"
|
|
else
|
|
echo >&2 " FAILED"
|
|
syslog err "${FIREHOL_PROGRESS_MESSAGES[0]} failed"
|
|
fi
|
|
unset FIREHOL_PROGRESS_MESSAGES[0]
|
|
FIREHOL_PROGRESS_MESSAGES=("${FIREHOL_PROGRESS_MESSAGES[@]}")
|
|
}
|
|
|
|
firehol_exit() {
|
|
local restored="NO"
|
|
if [ \( -f "${FIREHOL_SAVED}" -o -f "${FIREHOL_SAVED6}" \) -a "${FIREHOL_MODE}" = "START" ]
|
|
then
|
|
echo >&2
|
|
progress "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
|
|
restored="OK"
|
|
success # "Restoring old firewall"
|
|
else
|
|
restored="FAILED"
|
|
failure # "Restoring old firewall"
|
|
fi
|
|
fi
|
|
|
|
# remove the temporary directory created for this session
|
|
if [ ${FIREHOL_ACTIVATED_SUCCESSFULLY} -eq 0 -a ${FIREHOL_CLEAN_TMP} -eq 0 ]
|
|
then
|
|
echo >&2 "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}."
|
|
result="FAILED"
|
|
else
|
|
syslog info "Successfully activated new firewall from ${FIREHOL_CONFIG}."
|
|
result="OK"
|
|
fi
|
|
notify=1
|
|
;;
|
|
|
|
STOP) syslog emerg "Firewall has been stopped. Policy is ACCEPT EVERYTHING!"
|
|
notify=1
|
|
;;
|
|
|
|
PANIC) syslog emerg "PANIC! Machine has been locked. Policy is DROP EVERYTHING!"
|
|
notify=1
|
|
;;
|
|
|
|
*) # do nothing for the rest
|
|
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
|
|
|
|
enable trap
|
|
enable exit
|
|
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
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
#
|
|
# LIBRARY OF COMMON FUNCTIONS
|
|
#
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
# file management for BASH
|
|
|
|
file() {
|
|
local cmd="$1"
|
|
shift
|
|
|
|
case "${cmd}" in
|
|
open)
|
|
local fd="$1" filename="$2" mode="$3"
|
|
|
|
case "$mode" in
|
|
r) # read
|
|
eval "exec $fd<\"$filename\""
|
|
return $?
|
|
;;
|
|
|
|
rw) # read-write
|
|
eval "exec $fd<>\"$filename\""
|
|
return $?
|
|
;;
|
|
|
|
w) # write
|
|
eval "exec $fd>\"$filename\""
|
|
return $?
|
|
;;
|
|
|
|
*)
|
|
echo >&2 "${FUNCNAME}(): unknown mode '$mode'."
|
|
return 1
|
|
;;
|
|
esac
|
|
;;
|
|
|
|
close)
|
|
local fd="$1"
|
|
eval "exec $fd>&-"
|
|
return $?
|
|
;;
|
|
|
|
dup2)
|
|
local fd1="$1" fd2="$2"
|
|
eval "exec $fd2>&$fd1"
|
|
return $?
|
|
;;
|
|
|
|
*)
|
|
echo >&2 "${FUNCNAME}: unknown command '${cmd}'"
|
|
return 1
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
# Given a mark-type and a list of marks, this function
|
|
# calculates the bitmasked equivalent values
|
|
mark_value() {
|
|
local x= name="${1}"; shift
|
|
|
|
if [ -z "${name}" ]
|
|
then
|
|
error "Cannot find the value of mark with name '${name}'."
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "${1}" ]
|
|
then
|
|
error "Empty mark value given for mark ${name}."
|
|
return 1
|
|
fi
|
|
|
|
if [ -z "${MARKS_MASKS[$name]}" ]
|
|
then
|
|
error "Mark $name does not exist."
|
|
return 1
|
|
fi
|
|
|
|
for x in ${@//,/ }
|
|
do
|
|
x=$[ x + 1 - 1 ]
|
|
if [ $x -gt ${MARKS_MAX[$name]} -o $x -lt 0 ]
|
|
then
|
|
error "Cannot get mark $name of value $x. Mark $name is configured to get values from 0 to ${MARKS_MAX[$name]}. Change firehol-defaults.conf to add more."
|
|
return 1
|
|
fi
|
|
|
|
printf "0x%08x/${MARKS_MASKS[$name]}\n" "$[ x << ${MARKS_SHIFT[$name]} ]"
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
# Find in the BASH execution stack, the line and the source file that has called us.
|
|
# Before first use the variable PROGRAM_FILE should be set to the file to be excluded.
|
|
# It also sets the variable LAST_CONFIG_LINE on each run.
|
|
declare -A PROGRAM_CONFIG_FILES=()
|
|
config_line() {
|
|
if [ ! -z "${FORCE_CONFIG_LINEID}" ]
|
|
then
|
|
LAST_CONFIG_LINE="${FORCE_CONFIG_LINEID}"
|
|
else
|
|
# find the config line in the BASH stack
|
|
# start from 2
|
|
# 0 is this line
|
|
# 1 is the caller - our line for sure
|
|
# 2 is the caller's caller - possibly a config file line
|
|
local i= all=${#BASH_SOURCE} cfg=
|
|
for (( i = 2; i < $all; i++ ))
|
|
do
|
|
[ ! "${BASH_SOURCE[$i]}" = "${PROGRAM_FILE}" ] && break
|
|
done
|
|
cfg="${BASH_SOURCE[$i]}"
|
|
if [ ! "${cfg}" = "${PROGRAM_CONFIG}" -a -z "${PROGRAM_CONFIG_FILES[$cfg]}" ]
|
|
then
|
|
syslog info "Processing configuration file '${cfg}'..."
|
|
PROGRAM_CONFIG_FILES[$cfg]=1
|
|
fi
|
|
LAST_CONFIG_LINE="${BASH_LINENO[$[i-1]]}@${cfg}: ${FUNCNAME[$[i-1]]}:"
|
|
fi
|
|
test ! "z$1" = "z-ne" && echo "${LAST_CONFIG_LINE}"
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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.
|
|
file open 20 "${FIREHOL_DIR}/firewall_restore_commands.sh" w || exit 1
|
|
cat >&20 <<EOFMTL
|
|
#!/bin/sh
|
|
# Generated by FireHOL to execute additional actions
|
|
# to restore the generated firewall.
|
|
#
|
|
|
|
EOFMTL
|
|
|
|
# prepare the file that will hold the generated iptables commands
|
|
# when FAST_ACTIVATION is zero
|
|
file open 21 "${FIREHOL_OUTPUT}" w || exit 1
|
|
|
|
# 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
|
|
|
|
# Generate firehol-defaults.conf file
|
|
if [ ! -f "${FIREHOL_CONFIG_DIR}/firehol-defaults.conf" ]
|
|
then
|
|
"${EGREP_CMD}" "^# --- BEGIN OF FIREHOL DEFAULTS ---" -A 1000 "${PROGRAM_FILE}" |\
|
|
"${EGREP_CMD}" "^# --- END OF FIREHOL DEFAULTS ---" -B 1000 >"${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
|
|
|
|
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
|
|
}
|
|
|
|
# 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
|
|
}
|
|
|
|
|
|
if [ $ENABLE_IPV4 -eq 1 -a $ENABLE_IPV6 -eq 1 ]
|
|
then
|
|
FIREHOL_DEFAULT_NAMESPACE=both
|
|
elif [ $ENABLE_IPV4 -eq 1 ]
|
|
then
|
|
FIREHOL_DEFAULT_NAMESPACE=ipv4
|
|
else
|
|
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.
|
|
#
|
|
# 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.
|
|
FORCE_CONFIG_LINEID="INIT"
|
|
LAST_CONFIG_LINE="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.
|
|
declare -A 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=
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Various Defaults
|
|
|
|
# Valid modes:
|
|
# START, DEBUG, EXPLAIN, WIZARD, STOP, PANIC
|
|
FIREHOL_MODE="NONE"
|
|
|
|
# If set to 1, the firewall will be saved for normal iptables processing.
|
|
# Valid only for FIREHOL_MODE="START"
|
|
FIREHOL_SAVE=0
|
|
|
|
# If set to 1, the firewall will be restored if you don't commit it.
|
|
# Valid only for FIREHOL_MODE="START"
|
|
FIREHOL_TRY=0
|
|
|
|
# If set to 1, firehol will output the commands of the configuration file
|
|
# with variables expanded.
|
|
FIREHOL_CONF_SHOW=0
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Keep information about the current namespace: ipv4, ipv6 or both
|
|
declare -a FIREHOL_NS_STACK=()
|
|
FIREHOL_NS_CURR=${FIREHOL_DEFAULT_NAMESPACE}
|
|
FIREHOL_NS_PREP=
|
|
|
|
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="$1"
|
|
return 0
|
|
}
|
|
pop_namespace() {
|
|
FIREHOL_NS_STACK=(${FIREHOL_NS_STACK[@]:1})
|
|
FIREHOL_NS_CURR=${FIREHOL_NS_STACK[0]-${FIREHOL_DEFAULT_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
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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"
|
|
|
|
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
|
|
eval ${var}=${work_counter4}
|
|
elif running_ipv6
|
|
then
|
|
work_counter6=$[work_counter6 + 1]
|
|
eval ${var}=${work_counter6}
|
|
else
|
|
work_counter4=$[work_counter4 + 1]
|
|
eval ${var}=${work_counter4}
|
|
fi
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Keep status information
|
|
|
|
# 0 = no errors, >0 = there were errors in the script
|
|
work_runtime_error=0
|
|
|
|
# This function is used for generating dynamic chains when needed for
|
|
# 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.
|
|
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
|
|
}
|
|
|
|
# Services API version
|
|
FIREHOL_SERVICES_API="1"
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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"
|
|
|
|
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"
|
|
|
|
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"
|
|
|
|
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"
|
|
|
|
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"
|
|
|
|
server_icmp_ports="${server_ICMP_ports}"
|
|
client_icmp_ports="${client_ICMP_ports}"
|
|
# ALL_SHOULD_ALSO_RUN="${ALL_SHOULD_ALSO_RUN} icmp"
|
|
|
|
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"
|
|
client_ident_ports="default"
|
|
|
|
server_imap_ports="tcp/143"
|
|
client_imap_ports="default"
|
|
|
|
server_imaps_ports="tcp/993"
|
|
client_imaps_ports="default"
|
|
|
|
server_irc_ports="tcp/6667"
|
|
client_irc_ports="default"
|
|
helper_irc="irc"
|
|
ALL_SHOULD_ALSO_RUN="${ALL_SHOULD_ALSO_RUN} irc"
|
|
|
|
server_isakmp_ports="udp/500"
|
|
client_isakmp_ports="any"
|
|
|
|
server_ipsecnatt_ports="udp/4500"
|
|
client_ipsecnatt_ports="any"
|
|
|
|
server_jabber_ports="tcp/5222 tcp/5223"
|
|
client_jabber_ports="default"
|
|
|
|
server_jabberd_ports="tcp/5222 tcp/5223 tcp/5269"
|
|
client_jabberd_ports="default"
|
|
|
|
server_l2tp_ports="udp/1701"
|
|
client_l2tp_ports="any"
|
|
|
|
server_ldap_ports="tcp/389"
|
|
client_ldap_ports="default"
|
|
|
|
server_ldaps_ports="tcp/636"
|
|
client_ldaps_ports="default"
|
|
|
|
server_lpd_ports="tcp/515"
|
|
client_lpd_ports="any"
|
|
|
|
server_microsoft_ds_ports="tcp/445"
|
|
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"
|
|
client_msn_ports="default"
|
|
|
|
server_mysql_ports="tcp/3306"
|
|
client_mysql_ports="default"
|
|
|
|
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"
|
|
|
|
server_nut_ports="tcp/3493 udp/3493"
|
|
client_nut_ports="default"
|
|
|
|
server_nxserver_ports="tcp/5000:5200"
|
|
client_nxserver_ports="default"
|
|
|
|
server_openvpn_ports="tcp/1194 udp/1194"
|
|
client_openvpn_ports="default"
|
|
|
|
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"
|
|
client_pop3s_ports="default"
|
|
|
|
# Portmap clients appear to use ports bellow 1024
|
|
server_portmap_ports="udp/111 tcp/111"
|
|
client_portmap_ports="any"
|
|
|
|
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"
|
|
|
|
server_privoxy_ports="tcp/8118"
|
|
client_privoxy_ports="default"
|
|
|
|
server_radius_ports="udp/1812 udp/1813"
|
|
client_radius_ports="default"
|
|
|
|
server_radiusproxy_ports="udp/1814"
|
|
client_radiusproxy_ports="default"
|
|
|
|
server_radiusold_ports="udp/1645 udp/1646"
|
|
client_radiusold_ports="default"
|
|
|
|
server_radiusoldproxy_ports="udp/1647"
|
|
client_radiusoldproxy_ports="default"
|
|
|
|
server_rdp_ports="tcp/3389"
|
|
client_rdp_ports="default"
|
|
|
|
server_rndc_ports="tcp/953"
|
|
client_rndc_ports="default"
|
|
|
|
server_rsync_ports="tcp/873 udp/873"
|
|
client_rsync_ports="default"
|
|
|
|
server_rtp_ports="udp/10000:20000"
|
|
client_rtp_ports="any"
|
|
|
|
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"
|
|
client_socks_ports="default"
|
|
|
|
server_squid_ports="tcp/3128"
|
|
client_squid_ports="default"
|
|
|
|
server_smtp_ports="tcp/25"
|
|
client_smtp_ports="default"
|
|
|
|
server_smtps_ports="tcp/465"
|
|
client_smtps_ports="default"
|
|
|
|
server_snmp_ports="udp/161"
|
|
client_snmp_ports="default"
|
|
|
|
server_snmptrap_ports="udp/162"
|
|
client_snmptrap_ports="any"
|
|
|
|
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}"
|
|
|
|
server_swat_ports="tcp/901"
|
|
client_swat_ports="default"
|
|
|
|
server_syslog_ports="udp/514"
|
|
client_syslog_ports="syslog default"
|
|
|
|
server_telnet_ports="tcp/23"
|
|
client_telnet_ports="default"
|
|
|
|
server_tftp_ports="udp/69"
|
|
client_tftp_ports="default"
|
|
helper_tftp="tftp"
|
|
|
|
server_tomcat_ports="${server_httpalt_ports}"
|
|
client_tomcat_ports="${client_httpalt_ports}"
|
|
|
|
server_time_ports="tcp/37 udp/37"
|
|
client_time_ports="default"
|
|
|
|
server_upnp_ports="udp/1900 tcp/2869"
|
|
client_upnp_ports="default"
|
|
|
|
server_uucp_ports="tcp/540"
|
|
client_uucp_ports="default"
|
|
|
|
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"
|
|
|
|
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}"
|
|
|
|
server_webmin_ports="tcp/10000"
|
|
client_webmin_ports="default"
|
|
|
|
server_xdmcp_ports="udp/177"
|
|
client_xdmcp_ports="default"
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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 in=in out=out \
|
|
mychain="${1}" \
|
|
type="${2}" \
|
|
request="${3}" \
|
|
response="${4}"
|
|
shift 4
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
rule ${in} action "$@" chain "${in}_${mychain}" proto icmp custom "--icmp-type $request" state NEW,ESTABLISHED || return 1
|
|
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto icmp custom "--icmp-type $response" state ESTABLISHED || return 1
|
|
|
|
return 0
|
|
}
|
|
|
|
add_icmpv6_rule_pair() {
|
|
local in=in out=out \
|
|
mychain="${1}" \
|
|
type="${2}" \
|
|
request="${3}" \
|
|
response="${4}"
|
|
shift 4
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
rule ${in} action "$@" chain "${in}_${mychain}" proto icmpv6 custom "--icmpv6-type $request" state NEW,ESTABLISHED || return 1
|
|
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 in=in out=out \
|
|
mychain="${1}" \
|
|
type="${2}" \
|
|
icmpv6in="${3}" \
|
|
icmpv6out="${4}"
|
|
shift 4
|
|
|
|
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}" \
|
|
type="${2}" \
|
|
icmpv6error="${3}"
|
|
shift 3
|
|
|
|
local in=in 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 in=in out=out \
|
|
client_ports="${DEFAULT_CLIENT_PORTS}" \
|
|
mychain="${1}" \
|
|
type="${2}"
|
|
shift 2
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
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
|
|
}
|
|
|
|
|
|
# --- DHCP --------------------------------------------------------------------
|
|
|
|
rules_dhcp() {
|
|
local in=in out=out \
|
|
client_ports="${DEFAULT_CLIENT_PORTS}" \
|
|
mychain="${1}" \
|
|
type="${2}"
|
|
shift 2
|
|
|
|
if ! push_namespace ipv4; then return 1; fi
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
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
|
|
|
|
pop_namespace
|
|
return 0
|
|
}
|
|
|
|
rules_dhcpv6() {
|
|
local in=in out=out \
|
|
client_ports="${DEFAULT_CLIENT_PORTS}" \
|
|
mychain="${1}" \
|
|
type="${2}"
|
|
shift 2
|
|
|
|
if ! push_namespace ipv6; then return 1; fi
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
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
|
|
|
|
pop_namespace
|
|
return 0
|
|
}
|
|
|
|
|
|
# --- EMULE --------------------------------------------------------------------
|
|
|
|
rules_emule() {
|
|
local in=in out=out \
|
|
client_ports="${DEFAULT_CLIENT_PORTS}" \
|
|
mychain="${1}" \
|
|
type="${2}"
|
|
shift 2
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
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
|
|
|
|
# allow outgoing to client tcp/4662
|
|
set_work_function "Setting up rules for EMULE/server-to-client tcp/4662 (${type})"
|
|
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
|
|
|
|
# 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
|
|
|
|
# allow outgoing to client udp/4672
|
|
set_work_function "Setting up rules for EMULE/server-to-client udp/4672 (${type})"
|
|
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
|
|
|
|
# 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
|
|
|
|
# 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
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# --- HYLAFAX ------------------------------------------------------------------
|
|
# Written by: Franscisco Javier Felix <ffelix@gescosoft.com>
|
|
|
|
rules_hylafax() {
|
|
local in=in out=out \
|
|
client_ports="${DEFAULT_CLIENT_PORTS}" \
|
|
mychain="${1}" \
|
|
type="${2}"
|
|
shift 2
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
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
|
|
}
|
|
|
|
|
|
# --- SAMBA --------------------------------------------------------------------
|
|
|
|
rules_samba() {
|
|
local in=in out=out \
|
|
client_ports="${DEFAULT_CLIENT_PORTS}" \
|
|
mychain="${1}" \
|
|
type="${2}"
|
|
shift 2
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
|
|
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
|
|
|
|
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
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# --- NFS ----------------------------------------------------------------------
|
|
|
|
rules_nfs() {
|
|
local in=in out=out \
|
|
client_ports="${DEFAULT_CLIENT_PORTS}" \
|
|
mychain="${1}" \
|
|
type="${2}"
|
|
shift 2
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
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 servers="localhost" \
|
|
action="${1}"
|
|
shift
|
|
|
|
if [ "${type}" = "client" -o ! "${work_cmd}" = "interface" ]
|
|
then
|
|
case "${1}" in
|
|
dst|DST|destination|DESTINATION)
|
|
shift
|
|
servers="${1}"
|
|
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`"
|
|
|
|
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_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}`"
|
|
|
|
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
|
|
|
|
local dst=
|
|
if [ ! "${x}" = "localhost" ]
|
|
then
|
|
dst="dst ${x}"
|
|
fi
|
|
|
|
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}"
|
|
|
|
echo >&2 ""
|
|
echo >&2 "WARNING:"
|
|
echo >&2 "This firewall must be restarted if NFS server ${x} is restarted!"
|
|
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 in=in out=out \
|
|
client_ports="${DEFAULT_CLIENT_PORTS}" \
|
|
mychain="${1}" \
|
|
type="${2}"
|
|
shift 2
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
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 servers="localhost" \
|
|
action="${1}"
|
|
shift
|
|
|
|
if [ "${type}" = "client" -o ! "${work_cmd}" = "interface" ]
|
|
then
|
|
case "${1}" in
|
|
dst|DST|destination|DESTINATION)
|
|
shift
|
|
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
|
|
}
|
|
|
|
|
|
# --- PING ---------------------------------------------------------------------
|
|
|
|
rules_ping() {
|
|
local mychain="${1}" \
|
|
type="${2}"
|
|
shift 2
|
|
|
|
if running_ipv4; then
|
|
ipv4 add_icmp_rule_pair $mychain $type echo-request echo-reply "$@" || return 1
|
|
fi
|
|
if running_ipv6; then
|
|
ipv6 add_icmpv6_rule_pair $mychain $type echo-request echo-reply "$@" || return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# --- TIMESTAMP ----------------------------------------------------------------
|
|
|
|
rules_timestamp() {
|
|
local mychain="${1}" \
|
|
type="${2}" \
|
|
status=0
|
|
shift 2
|
|
|
|
if ! push_namespace ipv4; then return 1; fi
|
|
|
|
add_icmp_rule_pair $mychain $type timestamp-request timestamp-reply "$@" || status=1
|
|
|
|
pop_namespace
|
|
return $status
|
|
}
|
|
|
|
# --- IVP6NEIGH ----------------------------------------------------------------
|
|
|
|
rules_ipv6neigh() {
|
|
local mychain="${1}" \
|
|
type="${2}" \
|
|
status=0
|
|
shift 2
|
|
|
|
if ! push_namespace ipv6; then return 1; fi
|
|
|
|
add_icmpv6_rule_pair_stateless $mychain $type neighbour-solicitation neighbour-advertisement "$@" || status=1
|
|
|
|
pop_namespace
|
|
return $status
|
|
}
|
|
|
|
# --- IVP6ROUTER ---------------------------------------------------------------
|
|
|
|
rules_ipv6router() {
|
|
local mychain="${1}" \
|
|
type="${2}" \
|
|
status=0
|
|
shift 2
|
|
|
|
if ! push_namespace ipv6; then return 1; fi
|
|
|
|
add_icmpv6_rule_pair_stateless $mychain $type router-solicitation router-advertisement "$@" || status=1
|
|
|
|
pop_namespace
|
|
return $status
|
|
}
|
|
|
|
# --- IVP6ERROR ----------------------------------------------------------------
|
|
|
|
rules_ipv6error() {
|
|
local mychain="${1}" \
|
|
type="${2}"
|
|
shift 2
|
|
|
|
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
|
|
}
|
|
|
|
# --- ALL ----------------------------------------------------------------------
|
|
|
|
rules_all() {
|
|
local in=in out=out \
|
|
client_ports="${DEFAULT_CLIENT_PORTS}" \
|
|
mychain="${1}" \
|
|
type="${2}"
|
|
shift 2
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
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 in=in out=out \
|
|
client_ports="${DEFAULT_CLIENT_PORTS}" \
|
|
mychain="${1}" \
|
|
type="${2}" \
|
|
name="${3}"
|
|
shift 3
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
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
|
|
}
|
|
|
|
|
|
# --- ANYSTATELESS -------------------------------------------------------------
|
|
|
|
rules_anystateless() {
|
|
local in=in out=out \
|
|
client_ports="${DEFAULT_CLIENT_PORTS}" \
|
|
mychain="${1}" \
|
|
type="${2}" \
|
|
name="${3}"
|
|
shift 3
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
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 in=in out=out \
|
|
client_ports="${DEFAULT_CLIENT_PORTS}" \
|
|
mychain="${1}" \
|
|
type="${2}"
|
|
shift 2
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
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
|
|
}
|
|
|
|
|
|
# --- CUSTOM -------------------------------------------------------------------
|
|
|
|
rules_custom() {
|
|
local in=in out=out \
|
|
client_ports="${DEFAULT_CLIENT_PORTS}" \
|
|
mychain="${1}" \
|
|
type="${2}" \
|
|
server="${3}" \
|
|
my_server_ports="${4}" \
|
|
my_client_ports="${5}" \
|
|
helpers=
|
|
shift 5
|
|
|
|
if [ "$1" = "helpers" ]
|
|
then
|
|
helpers="$2"
|
|
shift 2
|
|
fi
|
|
|
|
if [ "${type}" = "client" ]
|
|
then
|
|
in=out
|
|
out=in
|
|
fi
|
|
|
|
if [ "${type}" = "client" -a "${work_cmd}" = "interface" ]
|
|
then
|
|
client_ports="${LOCAL_CLIENT_PORTS}"
|
|
fi
|
|
|
|
# ----------------------------------------------------------------------
|
|
|
|
local x= sp= proto= sport= cp= cport= oIFS="${IFS}"
|
|
local -a args=()
|
|
for sp in ${my_server_ports}
|
|
do
|
|
IFS="/" args=( $sp )
|
|
IFS="${oIFS}"
|
|
proto=${args[0]}
|
|
sport=${args[1]}
|
|
|
|
for cp in ${my_client_ports}
|
|
do
|
|
cport="${cp}"
|
|
[ "${cport}" = "default" ] && cport="${client_ports}"
|
|
|
|
set_work_function "Rules for ${server} ${type}, with server port(s) '${sp}' and client port(s) '${cp}'"
|
|
|
|
# allow new and established incoming packets
|
|
rule ${in} action "$@" chain "${in}_${mychain}" proto "${proto}" sport "${cport}" dport "${sport}" state NEW,ESTABLISHED || return 1
|
|
|
|
# allow outgoing established packets
|
|
rule ${out} reverse action "$@" chain "${out}_${mychain}" proto "${proto}" sport "${cport}" dport "${sport}" state ESTABLISHED || return 1
|
|
done
|
|
done
|
|
|
|
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
|
|
|
|
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 " WARNING >>> 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 " WARNING >>> Ignoring service in '${FIREHOL_SERVICES_DIR}/${f}' due to malformed header."
|
|
elif [ ${n} -ne ${FIREHOL_SERVICES_API} ]
|
|
then
|
|
echo >&2 " WARNING >>> 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 " WARNING >>> Ignoring service in '${FIREHOL_SERVICES_DIR}/${f}' due to malformed API minor number."
|
|
else
|
|
source ${f}
|
|
ret=$?
|
|
if [ ${ret} -ne 0 ]
|
|
then
|
|
echo >&2 " WARNING >>> Service in '${FIREHOL_SERVICES_DIR}/${f}' returned code ${ret}."
|
|
continue
|
|
fi
|
|
fi
|
|
fi
|
|
done
|
|
cd "${FIREHOL_DEFAULT_WORKING_DIRECTORY}" || exit 1
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# 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() {
|
|
work_realcmd_helper $FUNCNAME "$@"
|
|
softwarning "ECN_SHAME IP list no longer available, helper is ignored."
|
|
return 0
|
|
}
|
|
|
|
# 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
|
|
}
|
|
|
|
masquerade4() { ipv4 masquerade "${@}"; }
|
|
masquerade6() { ipv6 masquerade "${@}"; }
|
|
masquerade46() { both masquerade "${@}"; }
|
|
masquerade() {
|
|
work_realcmd_helper ${FUNCNAME} "$@"
|
|
|
|
set_work_function -ne "Initializing $FUNCNAME"
|
|
|
|
local f="${work_outface}" ports= random=
|
|
|
|
while [ ! -z "${1}" ]
|
|
do
|
|
case "${1}" in
|
|
reverse)
|
|
f="${work_inface}"
|
|
shift
|
|
;;
|
|
|
|
ports|to-ports|--to-ports)
|
|
ports="to-ports ${2}"
|
|
shift 2
|
|
;;
|
|
|
|
random|--random)
|
|
random="random"
|
|
shift
|
|
;;
|
|
|
|
*)
|
|
test -z "${f}" && f="${1}" && shift
|
|
break
|
|
;;
|
|
esac
|
|
done
|
|
|
|
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 ${ports} ${random} || return 1
|
|
|
|
FIREHOL_NAT=1
|
|
FIREHOL_ROUTING=1
|
|
|
|
return 0
|
|
}
|
|
|
|
transparent_proxy_count=0
|
|
transparent_proxy4() { ipv4 transparent_proxy "${@}"; }
|
|
transparent_proxy6() { ipv6 transparent_proxy "${@}"; }
|
|
transparent_proxy46() { both transparent_proxy "${@}"; }
|
|
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}" \
|
|
redirect="${2}" \
|
|
user="${3}"
|
|
shift 3
|
|
|
|
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_squid4() { ipv4 transparent_squid "${@}"; }
|
|
transparent_squid6() { ipv6 transparent_squid "${@}"; }
|
|
transparent_squid46() { both transparent_squid "${@}"; }
|
|
transparent_squid() {
|
|
transparent_proxy 80 "$@"
|
|
}
|
|
|
|
FIREHOL_TPROXY_MARK=
|
|
FIREHOL_TPROXY_IP_ROUTE_TABLE="241"
|
|
FIREHOL_TPROXY_ROUTE_DEVICE="lo"
|
|
|
|
tproxy_setup_ip_route() {
|
|
require_cmd ip
|
|
|
|
local x=
|
|
for x in inet inet6
|
|
do
|
|
# remove the existing ip rules for this mark
|
|
postprocess -ne ${IP_CMD} -f $x rule del lookup $FIREHOL_TPROXY_IP_ROUTE_TABLE
|
|
|
|
# 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
|
|
|
|
tproxy4() { ipv4 tproxy "${@}"; }
|
|
tproxy6() { ipv6 tproxy "${@}"; }
|
|
tproxy46() { both tproxy "${@}"; }
|
|
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
|
|
|
|
if [ -z "${FIREHOL_TPROXY_MARK}" ]
|
|
then
|
|
#FIREHOL_TPROXY_MARK="$[ MARKS_MAX[usermark] << MARKS_SHIFT[usermark] ]/${MARKS_MASKS[usermark]}"
|
|
FIREHOL_TPROXY_MARK="$(mark_value usermark MARKS_MAX[usermark])"
|
|
fi
|
|
|
|
local tproxy_action_options="tproxy-mark $FIREHOL_TPROXY_MARK" \
|
|
tport= \
|
|
tip=
|
|
|
|
if [ "$1" = "port" ]
|
|
then
|
|
tproxy_action_options="$tproxy_action_options on-port ${2}"
|
|
tport="${2}"
|
|
shift 2
|
|
else
|
|
error "TPROXY needs at least the port the proxy is listening at."
|
|
return 1
|
|
fi
|
|
|
|
if [ "$1" = "ip" ]
|
|
then
|
|
tproxy_action_options="$tproxy_action_options on-ip ${2}"
|
|
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}" \
|
|
to="${2}" \
|
|
action=
|
|
shift 2
|
|
|
|
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
|
|
action=snat
|
|
;;
|
|
|
|
to-destination)
|
|
create_chain nat "nat.${nat_count}" PREROUTING noowner nolog "$@" outface any || return 1
|
|
action=dnat
|
|
;;
|
|
|
|
redirect-to)
|
|
create_chain nat "nat.${nat_count}" PREROUTING noowner nolog "$@" outface any || return 1
|
|
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
|
|
}
|
|
|
|
nat4() { ipv4 nat "${@}"; }
|
|
nat6() { ipv6 nat "${@}"; }
|
|
nat46() { both nat "${@}"; }
|
|
nat() {
|
|
work_realcmd_helper $FUNCNAME "$@"
|
|
|
|
set_work_function -ne "Initializing $FUNCNAME"
|
|
|
|
nat_helper "$@"
|
|
}
|
|
|
|
snat4() { ipv4 snat "${@}"; }
|
|
snat6() { ipv6 snat "${@}"; }
|
|
snat46() { both snat "${@}"; }
|
|
snat() {
|
|
work_realcmd_helper $FUNCNAME "$@"
|
|
|
|
set_work_function -ne "Initializing $FUNCNAME"
|
|
|
|
local to="${1}"; shift
|
|
test "${to}" = "to" && to="${1}" && shift
|
|
|
|
nat_helper "to-source" "${to}" "$@"
|
|
}
|
|
|
|
dnat4() { ipv4 dnat "${@}"; }
|
|
dnat6() { ipv6 dnat "${@}"; }
|
|
dnat46() { both dnat "${@}"; }
|
|
dnat() {
|
|
work_realcmd_helper $FUNCNAME "$@"
|
|
|
|
set_work_function -ne "Initializing $FUNCNAME"
|
|
|
|
local to="${1}"; shift
|
|
test "${to}" = "to" && to="${1}" && shift
|
|
|
|
nat_helper "to-destination" "${to}" "$@"
|
|
}
|
|
|
|
redirect4() { ipv4 redirect "${@}"; }
|
|
redirect6() { ipv6 redirect "${@}"; }
|
|
redirect46() { both redirect "${@}"; }
|
|
redirect() {
|
|
work_realcmd_helper $FUNCNAME "$@"
|
|
|
|
set_work_function -ne "Initializing $FUNCNAME"
|
|
|
|
local to="${1}"; shift
|
|
test "${to}" = "to" -o "${to}" = "to-port" && to="${1}" && shift
|
|
|
|
nat_helper "redirect-to" "${to}" "$@"
|
|
}
|
|
|
|
wrongmac_chain=0
|
|
wrongmac6_chain=0
|
|
mac4() { ipv4 mac "${@}"; }
|
|
mac6() { ipv6 mac "${@}"; }
|
|
mac46() { both mac "${@}"; }
|
|
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)"
|
|
|
|
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
|
|
|
|
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
|
|
blacklist4() { ipv4 blacklist "${@}"; }
|
|
blacklist6() { ipv6 blacklist "${@}"; }
|
|
blacklist46() { both blacklist "${@}"; }
|
|
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"
|
|
|
|
local -a logopts_arg=()
|
|
if [ "${FIREHOL_LOG_MODE}" = "ULOG" ]
|
|
then
|
|
logopts_arg=("--ulog-prefix=${FIREHOL_LOG_PREFIX}BLACKLIST:")
|
|
elif [ "${FIREHOL_LOG_MODE}" = "NFLOG" ]
|
|
then
|
|
logopts_arg=("--nflog-prefix=${FIREHOL_LOG_PREFIX}BLACKLIST:")
|
|
else
|
|
logopts_arg=("--log-level" "${FIREHOL_LOG_LEVEL}" "--log-prefix=${FIREHOL_LOG_PREFIX}BLACKLIST:")
|
|
fi
|
|
|
|
# Blacklist INPUT unidirectional
|
|
iptables_both -t filter -N BL_IN_UNI # INPUT
|
|
iptables_both -A BL_IN_UNI -m limit --limit "${FIREHOL_LOG_FREQUENCY}" --limit-burst "${FIREHOL_LOG_BURST}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}"
|
|
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 limit --limit "${FIREHOL_LOG_FREQUENCY}" --limit-burst "${FIREHOL_LOG_BURST}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}"
|
|
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 limit --limit "${FIREHOL_LOG_FREQUENCY}" --limit-burst "${FIREHOL_LOG_BURST}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}"
|
|
iptables_both -A BL_OUT_BI -m conntrack --ctstate NEW -p tcp -j REJECT #--reject-with tcp-reset
|
|
running_ipv4 && iptables -A BL_OUT_BI -m conntrack --ctstate NEW -j REJECT --reject-with icmp-host-unreachable
|
|
running_ipv6 && ip6tables -A BL_OUT_BI -m conntrack --ctstate NEW -j REJECT --reject-with icmp6-addr-unreachable
|
|
iptables_both -A BL_OUT_BI -j REJECT
|
|
|
|
blacklist_chain=1
|
|
fi
|
|
|
|
set_work_function "Generating blacklist rules"
|
|
|
|
local x=
|
|
for x in ${@//,/ }
|
|
do
|
|
set_work_function "Blacklisting '${x}'"
|
|
|
|
case "${x}" in
|
|
ipset:*)
|
|
[ ${IPSET_WARNING} -eq 1 ] && ipset_warning
|
|
|
|
x="${x/ipset:/}"
|
|
test -z "${FIREHOL_IPSETS_USED[$x]}" && FIREHOL_IPSETS_USED[$x]="USED"
|
|
if [ ${full} -eq 1 ]
|
|
then
|
|
iptables_both -I INPUT -m set --match-set ${x} src -j BL_IN_BI
|
|
iptables_both -I FORWARD -m set --match-set ${x} src -j BL_IN_BI
|
|
|
|
iptables_both -I OUTPUT -m set --match-set ${x} dst -j BL_OUT_BI
|
|
iptables_both -I FORWARD -m set --match-set ${x} dst -j BL_OUT_BI
|
|
else
|
|
iptables_both -I INPUT -m set --match-set ${x} src -j BL_IN_UNI
|
|
iptables_both -I FORWARD -m set --match-set ${x} src -j BL_IN_UNI
|
|
fi
|
|
;;
|
|
*)
|
|
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
|
|
;;
|
|
esac
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
classify_count=0
|
|
classify4() { ipv4 classify "${@}"; }
|
|
classify6() { ipv6 classify "${@}"; }
|
|
classify46() { both classify "${@}"; }
|
|
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
|
|
}
|
|
|
|
connmark4() { ipv4 connmark "${@}"; }
|
|
connmark6() { ipv6 connmark "${@}"; }
|
|
connmark46() { both connmark "${@}"; }
|
|
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 )
|
|
|
|
local num="${1}" \
|
|
where="${2}"
|
|
shift 2
|
|
|
|
test -z "${where}" && where="OUTPUT POSTROUTING"
|
|
|
|
if [ "${num}" = "save" ]
|
|
then
|
|
# backward compatibility - nothing to be done here
|
|
return 0
|
|
fi
|
|
|
|
if [ "${num}" = "restore" ]
|
|
then
|
|
# backward compatibility - nothing to be done here
|
|
return 0
|
|
fi
|
|
|
|
set_work_function "Setting up rules for CONNMARK"
|
|
|
|
local mark="$(mark_value connmark $num)"
|
|
test -z "${mark}" && work_error=$[work_error + 1] && return 1
|
|
|
|
local chain=
|
|
for chain in ${where}
|
|
do
|
|
case "${chain}" in
|
|
interface)
|
|
if [ ${MARKS_STATEFUL[connmark]} -eq 1 ]
|
|
then
|
|
rule table mangle chain PREROUTING custom '-m conntrack --ctstate NEW' inface "${@}" action MARK to "${mark}" || return 1
|
|
rule table mangle chain POSTROUTING custom '-m conntrack --ctstate NEW' outface "${@}" action MARK to "${mark}" || return 1
|
|
else
|
|
rule table mangle chain PREROUTING inface "${@}" action MARK to "${mark}" || return 1
|
|
rule table mangle chain POSTROUTING outface "${@}" action MARK to "${mark}" || return 1
|
|
fi
|
|
;;
|
|
|
|
*)
|
|
if [ ${MARKS_STATEFUL[connmark]} -eq 1 ]
|
|
then
|
|
rule table mangle chain "${chain}" custom '-m conntrack --ctstate NEW' "${@}" action MARK to "${mark}" || return 1
|
|
else
|
|
rule table mangle chain "${chain}" action MARK to "${mark}" || return 1
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
custommark4() { ipv4 custommark "${@}"; }
|
|
custommark6() { ipv6 custommark "${@}"; }
|
|
custommark46() { both custommark "${@}"; }
|
|
custommark() {
|
|
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 name="${1}" \
|
|
num="${2}" \
|
|
where="${3}"
|
|
shift 3
|
|
|
|
test -z "${where}" && where=OUTPUT
|
|
|
|
set_work_function "Setting up rules for MARK"
|
|
|
|
local mark="$(mark_value $name $num)"
|
|
test -z "${mark}" && work_error=$[work_error + 1] && return 1
|
|
|
|
if [ ${MARKS_STATEFUL[$name]} -eq 1 ]
|
|
then
|
|
rule table mangle chain "${where}" custom '-m conntrack --ctstate NEW' "${@}" action MARK to "${mark}" || return 1
|
|
else
|
|
rule table mangle chain "${where}" "${@}" action MARK to "${mark}" || return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
mark4() { ipv4 mark "${@}"; }
|
|
mark6() { ipv6 mark "${@}"; }
|
|
mark46() { both mark "${@}"; }
|
|
mark() {
|
|
custommark usermark "${@}"
|
|
}
|
|
|
|
tos_count=0
|
|
tos4() { ipv4 tos "${@}"; }
|
|
tos6() { ipv6 tos "${@}"; }
|
|
tos46() { both tos "${@}"; }
|
|
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}" \
|
|
where="${2}"
|
|
shift 2
|
|
|
|
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
|
|
tosfix4() { ipv4 tosfix "${@}"; }
|
|
tosfix6() { ipv6 tosfix "${@}"; }
|
|
tosfix46() { both tosfix "${@}"; }
|
|
tosfix() {
|
|
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 )
|
|
|
|
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
|
|
dscp4() { ipv4 dscp "${@}"; }
|
|
dscp6() { ipv6 dscp "${@}"; }
|
|
dscp46() { both dscp "${@}"; }
|
|
dscp() {
|
|
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 value="${1}" \
|
|
class= \
|
|
where=
|
|
shift
|
|
|
|
if [ "${value}" = "class" ]
|
|
then
|
|
value=
|
|
class="${1}"
|
|
shift
|
|
fi
|
|
|
|
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
|
|
}
|
|
|
|
tcpmss4() { ipv4 tcpmss "${@}"; }
|
|
tcpmss6() { ipv6 tcpmss "${@}"; }
|
|
tcpmss46() { both tcpmss "${@}"; }
|
|
tcpmss() {
|
|
work_realcmd_helper $FUNCNAME "$@"
|
|
|
|
set_work_function -ne "Initializing $FUNCNAME"
|
|
|
|
local value="${1}" \
|
|
iface="$2" \
|
|
target=
|
|
|
|
if [ -z "$iface" ]
|
|
then
|
|
if [ ! -z "${work_cmd}" ]
|
|
then
|
|
iface="${work_outface}"
|
|
if [ -z "$iface" ]
|
|
then
|
|
error "$FUNCNAME cannot find the interfaces to setup. Did you set an outface?"
|
|
return 1
|
|
fi
|
|
|
|
else
|
|
iface="all"
|
|
fi
|
|
fi
|
|
|
|
case "$value" in
|
|
auto)
|
|
target="-j TCPMSS --clamp-mss-to-pmtu"
|
|
;;
|
|
|
|
[0-9]*)
|
|
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
|
|
}
|
|
|
|
ipset_addfile() {
|
|
local name="${1}" file= opts= final_cmd="${CAT_CMD}"
|
|
shift
|
|
|
|
[ "${1}" = "ip" -o "${1}" = "ips" ] && final_cmd="${GREP_CMD} -v /" && shift
|
|
[ "${1}" = "net" -o "${1}" = "nets" ] && final_cmd="${GREP_CMD} /" && shift
|
|
|
|
file="${1}"
|
|
shift
|
|
opts="${*}"
|
|
|
|
[ ! -f "${file}" ] && file="${FIREHOL_CONFIG_DIR}/${file}"
|
|
if [ ! -f "${file}" ]
|
|
then
|
|
error "${FUNCNAME}: Cannot find file '${file}'."
|
|
return 1
|
|
fi
|
|
|
|
# cleanup the file
|
|
${CAT_CMD} "${file}" |\
|
|
${SED_CMD} -e "s/#.*$//g" -e "s/[\t\\ ]\+/ /g" -e "s/ \+$//g" -e "s/^ \+//g" |\
|
|
${EGREP_CMD} "^[0-9\.:/\-]+$" |\
|
|
${final_cmd} |\
|
|
${SORT_CMD} -u |\
|
|
while read
|
|
do
|
|
echo "${IPSET_ADD_OPTION} ${name} ${REPLY} ${opts}"
|
|
done
|
|
}
|
|
|
|
ipset_warning() {
|
|
if [ ${IPSET_WARNING} -eq 1 ]
|
|
then
|
|
warning "ipset is requested, but ipset is not installed. Firewall may not be able to be activated."
|
|
IPSET_WARNING=0
|
|
fi
|
|
}
|
|
|
|
ipset_list_active_names() {
|
|
eval "${IPSET_CMD} ${IPSET_LIST_NAMES_EVAL}"
|
|
}
|
|
|
|
ipset_save_active_to_spool() {
|
|
${IPSET_CMD} ${IPSET_SAVE_OPTION} >"${FIREHOL_SPOOL_DIR}/last.ipset.save"
|
|
}
|
|
|
|
# keep track of all the ipsets the firewall uses
|
|
declare -A FIREHOL_IPSETS_USED=()
|
|
|
|
# this is a wrapper around ipset
|
|
# it has the same syntax
|
|
ipset4() { ipv4 ipset "${@}"; }
|
|
ipset6() { ipv6 ipset "${@}"; }
|
|
ipset46() { both ipset "${@}"; }
|
|
ipset() {
|
|
work_realcmd_helper $FUNCNAME "$@"
|
|
|
|
if [ ${ENABLE_IPSET} -ne 1 ]
|
|
then
|
|
error "ipset is not enabled. Do you have ipset installed?"
|
|
return 1
|
|
fi
|
|
|
|
local cmd="${1}" name="${2}"
|
|
shift 2
|
|
|
|
case "${cmd}" in
|
|
create|-N|--create)
|
|
local type="${1}" inet=
|
|
shift
|
|
|
|
if [ ! -z "${FIREHOL_IPSETS_USED[$name]}" ]
|
|
then
|
|
error "ipset ${name} already exists."
|
|
return 1
|
|
fi
|
|
|
|
if running_both
|
|
then
|
|
error "Cannot run ipset for both IPv4 and IPv6 at the same time."
|
|
return 1
|
|
elif running_ipv6
|
|
then
|
|
inet="${IPSET_CREATE_IPV6_OPTION}"
|
|
fi
|
|
|
|
local opts="${*}"
|
|
|
|
echo "${IPSET_CREATE_OPTION} ${name} ${type} ${inet} ${opts}" >"${FIREHOL_DIR}/ipset.${name}.rules"
|
|
echo "${IPSET_FLUSH_OPTION} ${name}" >>"${FIREHOL_DIR}/ipset.${name}.rules"
|
|
FIREHOL_IPSETS_USED[$name]="CREATED"
|
|
;;
|
|
|
|
add|-A|--add)
|
|
if [ ! "${FIREHOL_IPSETS_USED[$name]}" = "CREATED" ]
|
|
then
|
|
error "${FUNCNAME}: Cannot add IPs to ipset '${name}'. The ipset must be created first."
|
|
return 1
|
|
fi
|
|
|
|
local ip="${1}" x=
|
|
shift
|
|
|
|
for x in ${ip//,/ }
|
|
do
|
|
echo "${IPSET_ADD_OPTION} ${name} ${x} ${*}" >>"${FIREHOL_DIR}/ipset.${name}.rules"
|
|
done
|
|
;;
|
|
|
|
addfile|--addfile)
|
|
if [ ! "${FIREHOL_IPSETS_USED[$name]}" = "CREATED" ]
|
|
then
|
|
error "${FUNCNAME}: Cannot add IPs to ipset '${name}'. The ipset must be created first."
|
|
return 1
|
|
fi
|
|
|
|
ipset_addfile "${name}" "${@}" >>"${FIREHOL_DIR}/ipset.${name}.rules" || return 1
|
|
;;
|
|
|
|
*)
|
|
test -z "${FIREHOL_IPSETS_USED[$name]}" && FIREHOL_IPSETS_USED[$name]="USED"
|
|
|
|
postprocess ${IPSET_CMD} ${cmd} ${name} "${@}"
|
|
;;
|
|
esac
|
|
}
|
|
|
|
ipsets_apply() {
|
|
local from="${1}" base="${FIREHOL_DIR}" only_non_existing=0 x=
|
|
|
|
# if we have nothing to do, return
|
|
# if we are called with 'spool', FIREHOL_IPSETS_USED is empty and we will load it from the spool file
|
|
# otherwise, if FIREHOL_IPSETS_USED is empty and the mode is START, there is nothing to be done
|
|
[ ! "${from}" = "spool" -a "${#FIREHOL_IPSETS_USED[@]}" -eq 0 ] && return 0
|
|
|
|
if [ ${ENABLE_IPSET} -ne 1 ]
|
|
then
|
|
error "ipset is not enabled. Do you have ipset installed?"
|
|
return 1
|
|
fi
|
|
|
|
if [ "${from}" = "spool" ]
|
|
then
|
|
base="${FIREHOL_SPOOL_DIR}"
|
|
only_non_existing=1
|
|
|
|
if [ -f "${FIREHOL_SPOOL_DIR}/ipsets.conf" ]
|
|
then
|
|
source "${FIREHOL_SPOOL_DIR}/ipsets.conf"
|
|
if [ $? -ne 0 ]
|
|
then
|
|
warning "Cannot load ${FIREHOL_SPOOL_DIR}/ipsets.conf"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
progress "Restoring ipsets from '${FIREHOL_SPOOL_DIR}'"
|
|
else
|
|
progress "Activating ipsets"
|
|
fi
|
|
|
|
# take a list of all active ipsets
|
|
# and mark each one that we have too as existing
|
|
for x in $( ipset_list_active_names )
|
|
do
|
|
if [ "${FIREHOL_IPSETS_USED[$x]}" = "CREATED" ]
|
|
then
|
|
FIREHOL_IPSETS_USED[$x]="EXISTS"
|
|
fi
|
|
done
|
|
|
|
for x in ${!FIREHOL_IPSETS_USED[@]}
|
|
do
|
|
# did we had an ipset helper for this ipset?
|
|
[ ! -s "${base}/ipset.${x}.rules" ] && continue
|
|
|
|
# shall we restore this ipset?
|
|
if [ "${FIREHOL_IPSETS_USED[$x]}" = "EXISTS" ]
|
|
then
|
|
test $only_non_existing -eq 1 && continue
|
|
|
|
# add all the rules except the create one
|
|
${CAT_CMD} "${base}/ipset.${x}.rules" |\
|
|
${GREP_CMD} -v "^${IPSET_CREATE_OPTION} ${x} " >>"${FIREHOL_DIR}/ipsets.restore"
|
|
else
|
|
# copy the generated rules to the ipset restoration file
|
|
${CAT_CMD} "${base}/ipset.${x}.rules" >>"${FIREHOL_DIR}/ipsets.restore"
|
|
fi
|
|
|
|
FIREHOL_IPSETS_USED[$x]="RESTORED"
|
|
done
|
|
|
|
if [ -s "${FIREHOL_DIR}/ipsets.restore" ]
|
|
then
|
|
${IPSET_CMD} ${IPSET_RESTORE_OPTION} <"${FIREHOL_DIR}/ipsets.restore"
|
|
if [ $? -ne 0 ]
|
|
then
|
|
error "${FUNCNAME}: Cannot apply generated ipset rules."
|
|
return 1
|
|
else
|
|
success
|
|
fi
|
|
|
|
if [ ! "${from}" = "spool" ]
|
|
then
|
|
# save the list and the new rules to our spool directory
|
|
for x in ${!FIREHOL_IPSETS_USED[@]}
|
|
do
|
|
if [ "${FIREHOL_IPSETS_USED[$x]}" = "RESTORED" ]
|
|
then
|
|
cp -p "${base}/ipset.${x}.rules" "${FIREHOL_SPOOL_DIR}/ipset.${x}.rules"
|
|
FIREHOL_IPSETS_USED[$x]="CREATED"
|
|
|
|
elif [ "${FIREHOL_IPSETS_USED[$x]}" = "EXISTS" ]
|
|
then
|
|
FIREHOL_IPSETS_USED[$x]="CREATED"
|
|
fi
|
|
done
|
|
|
|
declare -p FIREHOL_IPSETS_USED >"${FIREHOL_SPOOL_DIR}/ipsets.conf"
|
|
cp "${FIREHOL_DIR}/ipsets.restore" "${FIREHOL_SPOOL_DIR}/last.ipsets.restore"
|
|
ipset_save_active_to_spool
|
|
fi
|
|
else
|
|
success "sets already exist, not updated IPs"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
#
|
|
# INTERNAL FUNCTIONS BELLOW THIS POINT - Primary commands
|
|
#
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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.
|
|
|
|
version() {
|
|
work_realcmd_helper ${FUNCNAME} "$@"
|
|
|
|
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}"
|
|
fi
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# PRIMARY COMMAND: interface
|
|
# Setup rules specific to an interface (physical or logical)
|
|
|
|
interface() {
|
|
work_realcmd_primary ${FUNCNAME} "$@"
|
|
|
|
# --- 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=
|
|
|
|
# --- test prerequisites ---
|
|
|
|
require_work clear || return 1
|
|
set_work_function -ne "Initializing $FUNCNAME"
|
|
|
|
|
|
# --- get paramaters and validate them ---
|
|
|
|
# Get the interface
|
|
local inface="${1}" \
|
|
name="${2}"
|
|
shift 2
|
|
|
|
test -z "${inface}" && error "real interface is not set" && return 1
|
|
test -z "${name}" && error "$FUNCNAME name is not set" && return 1
|
|
|
|
# --- do the job ---
|
|
|
|
work_cmd="${FUNCNAME}"
|
|
work_name="${name}"
|
|
work_realcmd=("(unset)")
|
|
|
|
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
|
|
|
|
return 0
|
|
}
|
|
|
|
interface4() {
|
|
ipv4 interface "$@"
|
|
}
|
|
|
|
interface6() {
|
|
ipv6 interface "$@"
|
|
}
|
|
|
|
interface46() {
|
|
both interface "$@"
|
|
}
|
|
|
|
|
|
router() {
|
|
work_realcmd_primary ${FUNCNAME} "$@"
|
|
|
|
# --- 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=
|
|
|
|
# --- test prerequisites ---
|
|
|
|
require_work clear || return 1
|
|
set_work_function -ne "Initializing $FUNCNAME"
|
|
|
|
|
|
# --- 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
|
|
|
|
|
|
# --- do the job ---
|
|
|
|
work_cmd="${FUNCNAME}"
|
|
work_name="${name}"
|
|
work_realcmd=("(unset)")
|
|
|
|
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
|
|
|
|
FIREHOL_ROUTING=1
|
|
|
|
return 0
|
|
}
|
|
|
|
router4() {
|
|
ipv4 router "$@"
|
|
}
|
|
|
|
router6() {
|
|
ipv6 router "$@"
|
|
}
|
|
|
|
router46() {
|
|
both router "$@"
|
|
}
|
|
|
|
save_for_restore() {
|
|
local check="$1"; shift
|
|
|
|
printf "%q " "$@" >&20
|
|
|
|
if [ "${check}" = "none" -o "${check}" = "warn" ]
|
|
then
|
|
printf " || echo >/dev/null\n" >&20
|
|
else
|
|
printf " || exit 1\n" >&20
|
|
fi
|
|
}
|
|
|
|
postprocess() {
|
|
# if the caller is not from the program file, get the config line calling us
|
|
[ ! "${BASH_SOURCE[1]}" = "${PROGRAM_FILE}" ] && work_realcmd_helper ${FUNCNAME} "$@"
|
|
|
|
local check="error" save=1
|
|
while [ ! "A${1}" = "A" ]
|
|
do
|
|
case "A${1}" in
|
|
A-ne) shift; check="none";;
|
|
A-warn) shift; check="warn";;
|
|
A-ns) shift; save=0;;
|
|
*) break;;
|
|
esac
|
|
done
|
|
|
|
test "${FIREHOL_MODE}" = "DEBUG" && check="debug"
|
|
test "${FIREHOL_MODE}" = "EXPLAIN" && check="none"
|
|
|
|
printf "%q " "$@" >&21
|
|
case "${check}" in
|
|
debug) printf "\n" >&21
|
|
;;
|
|
|
|
none) printf " >/dev/null 2>&1 || echo >/dev/null\n" >&21
|
|
;;
|
|
|
|
warn|error)
|
|
# do not run config_line here, it is very slow
|
|
# config_line -ne
|
|
printf " >${FIREHOL_OUTPUT}.log 2>&1 || runtime_error ${check} \$? '${LAST_CONFIG_LINE}' " >&21
|
|
printf "%q " "$@" >&21
|
|
printf "\n" >&21
|
|
;;
|
|
esac
|
|
|
|
if [ "${FIREHOL_MODE}" = "EXPLAIN" ]
|
|
then
|
|
file close 21
|
|
${CAT_CMD} "${FIREHOL_OUTPUT}"
|
|
${RM_CMD} "${FIREHOL_OUTPUT}"
|
|
file open 21 "${FIREHOL_OUTPUT}" w || exit 1
|
|
fi
|
|
|
|
test $save -eq 1 && save_for_restore ${check} "${@}"
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# 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.
|
|
|
|
run_fast() {
|
|
local n=table \
|
|
t=filter \
|
|
cmd="$1"
|
|
shift
|
|
|
|
[ "${cmd}" = "ip6tables" ] && n=table6
|
|
|
|
if [ "z${1}" = "z-t" ]
|
|
then
|
|
t="${2}"
|
|
shift 2
|
|
fi
|
|
|
|
case "$1" in
|
|
-P) echo ":$2 $3 [0:0]" >>"${FIREHOL_DIR}/fast/${n}.${t}.policy"
|
|
;;
|
|
|
|
-N) echo ":$2 - [0:0]" >>"${FIREHOL_DIR}/fast/${n}.${t}.chains"
|
|
;;
|
|
|
|
-A|-I) echo "${*}" >>"${FIREHOL_DIR}/fast/${n}.${t}.rules"
|
|
;;
|
|
|
|
# if it is none of the above, we execute it normally.
|
|
*) echo >&2 "WARNING: Ignoring command '${cmd} -t ${t} ${@}'"
|
|
;;
|
|
esac
|
|
|
|
test ! -f "${FIREHOL_DIR}/fast/${n}s/${t}" && ${TOUCH_CMD} "${FIREHOL_DIR}/fast/${n}s/${t}"
|
|
return 0
|
|
}
|
|
|
|
FIREHOL_COMMAND_COUNTER=0
|
|
iptables() {
|
|
|
|
# if the caller is not from the program file, get the config line calling us
|
|
[ ! "${BASH_SOURCE[1]}" = "${PROGRAM_FILE}" ] && work_realcmd_helper ${FUNCNAME} "$@"
|
|
|
|
[ $firewall_policy_applied -eq 0 ] && firewall_policy
|
|
|
|
if [ $FIREHOL_FAST_ACTIVATION -eq 1 ]
|
|
then
|
|
run_fast iptables "${@}"
|
|
else
|
|
postprocess -ns "${IPTABLES_CMD}" "$@"
|
|
FIREHOL_COMMAND_COUNTER=$[FIREHOL_COMMAND_COUNTER + 1]
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
FIREHOL_COMMAND6_COUNTER=0
|
|
ip6tables() {
|
|
# if the caller is not from the program file, get the config line calling us
|
|
[ ! "${BASH_SOURCE[1]}" = "${PROGRAM_FILE}" ] && work_realcmd_helper ${FUNCNAME} "$@"
|
|
|
|
[ $firewall_policy6_applied -eq 0 ] && firewall_policy6
|
|
|
|
if [ $FIREHOL_FAST_ACTIVATION -eq 1 ]
|
|
then
|
|
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
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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} "$@"
|
|
|
|
require_work set any || return 1
|
|
|
|
set_work_function "Setting policy of ${work_name} to ${1}"
|
|
work_policy="$*"
|
|
|
|
return 0
|
|
}
|
|
|
|
server() {
|
|
work_realcmd_secondary ${FUNCNAME} "$@"
|
|
|
|
require_work set any || return 1
|
|
smart_function server "$@"
|
|
return $?
|
|
}
|
|
|
|
server4() {
|
|
ipv4 server "$@"
|
|
}
|
|
|
|
server6() {
|
|
ipv6 server "$@"
|
|
}
|
|
|
|
server46() {
|
|
both server "$@"
|
|
}
|
|
|
|
client() {
|
|
work_realcmd_secondary ${FUNCNAME} "$@"
|
|
|
|
require_work set any || return 1
|
|
smart_function client "$@"
|
|
return $?
|
|
}
|
|
|
|
client4() {
|
|
ipv4 client "$@"
|
|
}
|
|
|
|
client6() {
|
|
ipv6 client "$@"
|
|
}
|
|
|
|
client46() {
|
|
both client "$@"
|
|
}
|
|
|
|
route() {
|
|
work_realcmd_secondary ${FUNCNAME} "$@"
|
|
|
|
require_work set router || return 1
|
|
smart_function server "$@"
|
|
return $?
|
|
}
|
|
|
|
route4() {
|
|
ipv4 route "$@"
|
|
}
|
|
|
|
route6() {
|
|
ipv6 route "$@"
|
|
}
|
|
|
|
route46() {
|
|
both route "$@"
|
|
}
|
|
|
|
|
|
# --- protection ---------------------------------------------------------------
|
|
|
|
protection() {
|
|
work_realcmd_secondary ${FUNCNAME} "$@"
|
|
|
|
require_work set any || return 1
|
|
|
|
local in="in" \
|
|
prface="${work_inface}" \
|
|
pre="pr" \
|
|
reverse= \
|
|
x=
|
|
|
|
if [ "${1}" = "reverse" ]
|
|
then
|
|
reverse="reverse" # needed to recursion
|
|
pre="prr" # in case a router has protections
|
|
# both ways, the second needs to
|
|
# have different chain names
|
|
|
|
in="out" # reverse the interface
|
|
|
|
prface="${work_outface}"
|
|
shift
|
|
fi
|
|
|
|
local type="${1}" \
|
|
rate="${2}" \
|
|
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}'"
|
|
|
|
for x in ${type}
|
|
do
|
|
case "${x}" in
|
|
none|NONE)
|
|
return 0
|
|
;;
|
|
|
|
bad-packets|BAD-PACKETS)
|
|
protection ${reverse} "fragments new-tcp-w/o-syn malformed-xmas malformed-null malformed-bad invalid" "${rate}" "${burst}"
|
|
return $?
|
|
;;
|
|
|
|
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}"
|
|
return $?
|
|
;;
|
|
|
|
invalid|INVALID)
|
|
iptables_both -A "${in}_${work_name}" -m conntrack --ctstate INVALID -j DROP || return 1
|
|
;;
|
|
|
|
fragments|FRAGMENTS)
|
|
if running_ipv4; then
|
|
push_namespace ipv4
|
|
local frag_status=0 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.
|
|
;;
|
|
|
|
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
|
|
|
|
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
|
|
;;
|
|
|
|
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
|
|
|
|
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
|
|
;;
|
|
|
|
syn-floods|SYN-FLOODS)
|
|
local mychain="${pre}_${work_name}_synflood"
|
|
create_chain filter "${mychain}" "${in}_${work_name}" in proto tcp custom "--syn" || return 1
|
|
|
|
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
|
|
;;
|
|
|
|
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
|
|
;;
|
|
|
|
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
|
|
|
|
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
|
|
;;
|
|
|
|
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
|
|
|
|
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
|
|
;;
|
|
|
|
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
|
|
|
|
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
|
|
|
|
rule in chain "${mychain}" loglimit "MALFORMED BAD" action drop || return 1
|
|
;;
|
|
|
|
*)
|
|
error "Protection '${x}' does not exists."
|
|
return 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
#
|
|
# KERNEL MODULE MANAGEMENT
|
|
#
|
|
# ------------------------------------------------------------------------------
|
|
# 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 " 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 "$(config_line)" "${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.
|
|
require_kernel_module() {
|
|
[ ! "${BASH_SOURCE[1]}" = "${PROGRAM_FILE}" ] && work_realcmd_helper ${FUNCNAME} "$@"
|
|
|
|
local new="${1}"
|
|
|
|
if [ -z "${FIREHOL_KERNEL_MODULES[$new]}" ]
|
|
then
|
|
set_work_function "Adding kernel module '${new}' in the list of kernel modules to load"
|
|
FIREHOL_KERNEL_MODULES[$new]="1"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
#
|
|
# INTERNAL FUNCTIONS BELLOW THIS POINT - FireHOL internals
|
|
#
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
set_work_function() {
|
|
local show_explain=1
|
|
test "$1" = "-ne" && shift && 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" "$*" >&21
|
|
fi
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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.
|
|
|
|
require_work() {
|
|
local type="${1}" \
|
|
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
|
|
work_cmd=
|
|
work_realcmd=("(unset)")
|
|
work_name=
|
|
work_inface=
|
|
work_outface=
|
|
work_policy=
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# close_interface
|
|
# WHY:
|
|
# Finalizes the rules for the last interface().
|
|
|
|
close_interface() {
|
|
require_work set interface || return 1
|
|
|
|
close_all_groups
|
|
|
|
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}"
|
|
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
|
|
|
|
local -a inlog=(loglimit "IN-${work_name}")
|
|
local -a outlog=(loglimit "OUT-${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
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# close_router
|
|
# WHY:
|
|
# Finalizes the rules for the last router().
|
|
|
|
close_router() {
|
|
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
|
|
|
|
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
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# close_master
|
|
# WHY:
|
|
# Finalizes the rules for the whole firewall.
|
|
# It assummes there is not primary command open.
|
|
|
|
close_master() {
|
|
set_work_function "Finilizing firewall policies"
|
|
|
|
if [ ! "${MARKS_SAVERESTORE_STATEFUL_MASK}" = "0x00000000" ]
|
|
then
|
|
# copy CONNMARK to MARK at the top of mangle, on entry points
|
|
iptables_both -t mangle -I OUTPUT 1 -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark --mask ${MARKS_SAVERESTORE_STATEFUL_MASK}
|
|
iptables_both -t mangle -I PREROUTING 1 -m conntrack --ctstate ESTABLISHED,RELATED -j CONNMARK --restore-mark --mask ${MARKS_SAVERESTORE_STATEFUL_MASK}
|
|
|
|
# save MARK to CONNMARK at the end of mangle, on exit points
|
|
iptables_both -t mangle -A INPUT -m conntrack --ctstate NEW -j CONNMARK --save-mark --mask ${MARKS_SAVERESTORE_STATEFUL_MASK}
|
|
iptables_both -t mangle -A POSTROUTING -m conntrack --ctstate NEW -j CONNMARK --save-mark --mask ${MARKS_SAVERESTORE_STATEFUL_MASK}
|
|
fi
|
|
|
|
if [ ! "${MARKS_SAVERESTORE_STATELESS_MASK}" = "0x00000000" ]
|
|
then
|
|
# copy CONNMARK to MARK at the top of mangle, on entry points
|
|
iptables_both -t mangle -I OUTPUT 1 -j CONNMARK --restore-mark --mask ${MARKS_SAVERESTORE_STATELESS_MASK}
|
|
iptables_both -t mangle -I PREROUTING 1 -j CONNMARK --restore-mark --mask ${MARKS_SAVERESTORE_STATELESS_MASK}
|
|
|
|
# save MARK to CONNMARK at the end of mangle, on exit points
|
|
iptables_both -t mangle -A INPUT -j CONNMARK --save-mark --mask ${MARKS_SAVERESTORE_STATELESS_MASK}
|
|
iptables_both -t mangle -A POSTROUTING -j CONNMARK --save-mark --mask ${MARKS_SAVERESTORE_STATELESS_MASK}
|
|
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
|
|
return 0
|
|
}
|
|
|
|
|
|
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)
|
|
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]}
|
|
pop_namespace
|
|
;;
|
|
|
|
*)
|
|
error "Statement 'group' requires the first argument to be one of with, start, begin, end, stop, close."
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
return 0
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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.
|
|
|
|
|
|
# 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}" \
|
|
action="${2}" \
|
|
protocol="${3}" \
|
|
statenot="${4}" \
|
|
state="${5}" \
|
|
table="${6}" \
|
|
count=0 val=
|
|
shift 6
|
|
local -a action_param=()
|
|
|
|
# All arguments until the separator are the parameters of the action
|
|
for val in "${@}"
|
|
do
|
|
[ "A${val}" = "A--" ] && break
|
|
|
|
action_param[$count]="${val}"
|
|
((count += 1))
|
|
done
|
|
shift $[count + 1]
|
|
|
|
# If we don't have a seperator, generate an error
|
|
if [ ! "A${val}" = "A--" ]
|
|
then
|
|
error "Internal Error, in parsing action_param parameters ($FUNCNAME '${action}' '${protocol}' '${statenot}' '${state}' '${table}' '${action_param[@]}' '$@')."
|
|
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]}" \
|
|
burst="${action_param[2]}" \
|
|
overflow="REJECT"
|
|
|
|
# if we have a custom overflow action, parse it.
|
|
test "${action_param[3]}" = "overflow" && 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.
|
|
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}" && do_accept_limit=1
|
|
else
|
|
test -z "${has_new}" && 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
|
|
logopts_arg=("--ulog-prefix=${FIREHOL_LOG_PREFIX}LIMIT_OVERFLOW:")
|
|
elif [ "${FIREHOL_LOG_MODE}" = "NFLOG" ]
|
|
then
|
|
logopts_arg=("--nflog-prefix=${FIREHOL_LOG_PREFIX}LIMIT_OVERFLOW:")
|
|
else
|
|
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
|
|
action=${accept_limit_chain}
|
|
fi
|
|
;;
|
|
|
|
"recent")
|
|
# limit NEW connections to the specified rate
|
|
local name="${action_param[1]}" \
|
|
seconds="${action_param[2]}" \
|
|
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.
|
|
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}" && do_accept_recent=1
|
|
else
|
|
test -z "${has_new}" && 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= t2=
|
|
test ! -z $seconds && t1="--seconds ${seconds}"
|
|
test ! -z $hits && 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
|
|
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.
|
|
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
|
|
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
|
|
action_param=("--reject-with" "tcp-reset")
|
|
else
|
|
action_param=()
|
|
fi
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
$iptables_cmd "$@" -j "${action}" "${action_param[@]}"
|
|
}
|
|
|
|
rule() {
|
|
# defining these local variables together speeds FireHOL up by 4%
|
|
local failed=0 \
|
|
table= chain= \
|
|
inface=any infacenot= outface=any outfacenot= \
|
|
physin=any physinnot= physout=any physoutnot= \
|
|
mac=any macnot= \
|
|
src4=default src4not= dst4=default dst4not= \
|
|
src6=default src6not= dst6=default dst6not= \
|
|
srctype= srctypenot= dsttype= dsttypenot= \
|
|
sport=any sportnot= dport=any dportnot= \
|
|
proto=any protonot= \
|
|
uid=any uidnot= gid=any gidnot= \
|
|
pid=any pidnot= sid=any sidnot= \
|
|
cmd=any cmdnot= \
|
|
mark=any marknot= markname= \
|
|
dscp=any dscptype= dscpnot= \
|
|
tos=any tosnot= \
|
|
log= logtxt= loglevel= \
|
|
limit= burst= iplimit= iplimit_mask= \
|
|
action= state= statenot= \
|
|
failed=0 reverse=0 \
|
|
swi=0 swo=0 \
|
|
custom= \
|
|
accounting=
|
|
|
|
# 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=()
|
|
|
|
while [ ! -z "${1}" ]
|
|
do
|
|
case "${1}" in
|
|
reverse|REVERSE)
|
|
reverse=1
|
|
shift
|
|
;;
|
|
|
|
table|TABLE)
|
|
test ${softwarnings} -eq 1 -a ! -z "${table}" && softwarning "Overwritting param: ${1} '${chain}' becomes '${2}'"
|
|
table="-t ${2}"
|
|
shift 2
|
|
;;
|
|
|
|
chain|CHAIN)
|
|
test ${softwarnings} -eq 1 -a ! -z "${chain}" && softwarning "Overwritting param: ${1} '${chain}' becomes '${2}'"
|
|
chain="${2}"
|
|
shift 2
|
|
;;
|
|
|
|
inface|INFACE)
|
|
shift
|
|
if [ ${reverse} -eq 0 ]
|
|
then
|
|
infacenot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
infacenot="!"
|
|
else
|
|
if [ ${swi} -eq 1 ]
|
|
then
|
|
work_inface="${1}"
|
|
fi
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${inface}" = "any" && softwarning "Overwritting param: inface '${inface}' becomes '${1}'"
|
|
inface="${1//,/ }"
|
|
else
|
|
outfacenot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
outfacenot="!"
|
|
else
|
|
if [ ${swo} -eq 1 ]
|
|
then
|
|
work_outface="$1"
|
|
fi
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${outface}" = "any" && softwarning "Overwritting param: outface '${outface}' becomes '${1}'"
|
|
outface="${1//,/ }"
|
|
fi
|
|
shift
|
|
;;
|
|
|
|
outface|OUTFACE)
|
|
shift
|
|
if [ ${reverse} -eq 0 ]
|
|
then
|
|
outfacenot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
outfacenot="!"
|
|
else
|
|
if [ ${swo} -eq 1 ]
|
|
then
|
|
work_outface="${1}"
|
|
fi
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${outface}" = "any" && softwarning "Overwritting param: outface '${outface}' becomes '${1}'"
|
|
outface="${1//,/ }"
|
|
else
|
|
infacenot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
infacenot="!"
|
|
else
|
|
if [ ${swi} -eq 1 ]
|
|
then
|
|
work_inface="${1}"
|
|
fi
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${inface}" = "any" && softwarning "Overwritting param: inface '${inface}' becomes '${1}'"
|
|
inface="${1//,/ }"
|
|
fi
|
|
shift
|
|
;;
|
|
|
|
physin|PHYSIN)
|
|
shift
|
|
if [ ${reverse} -eq 0 ]
|
|
then
|
|
physinnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
physinnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${physin}" = "any" && softwarning "Overwritting param: physin '${physin}' becomes '${1}'"
|
|
physin="${1//,/ }"
|
|
else
|
|
physoutnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
physoutnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${physout}" = "any" && softwarning "Overwritting param: physout '${physout}' becomes '${1}'"
|
|
physout="${1//,/ }"
|
|
fi
|
|
shift
|
|
;;
|
|
|
|
physout|PHYSOUT)
|
|
shift
|
|
if [ ${reverse} -eq 0 ]
|
|
then
|
|
physoutnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
physoutnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${physout}" = "any" && softwarning "Overwritting param: physout '${physout}' becomes '${1}'"
|
|
physout="${1//,/ }"
|
|
else
|
|
physinnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
physinnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${physin}" = "any" && softwarning "Overwritting param: physin '${physin}' becomes '${1}'"
|
|
physin="${1//,/ }"
|
|
fi
|
|
shift
|
|
;;
|
|
|
|
mac|MAC)
|
|
shift
|
|
macnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
test ${nomac} -eq 0 && macnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${mac}" = "any" && softwarning "Overwritting param: mac '${mac}' becomes '${1}'"
|
|
test ${nomac} -eq 0 && 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
|
|
shift
|
|
if [ ${reverse} -eq 0 ]
|
|
then
|
|
running_ipv4 && src4not=
|
|
running_ipv6 && src6not=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
running_ipv4 && src4not="!"
|
|
running_ipv6 && src6not="!"
|
|
fi
|
|
if running_ipv4; then
|
|
test ${softwarnings} -eq 1 -a ! "${src4}" = "default" && softwarning "Overwritting param: src4 '${src4}' becomes '${1}'"
|
|
src4="${1//,/ }"
|
|
fi
|
|
if running_ipv6; then
|
|
test ${softwarnings} -eq 1 -a ! "${src6}" = "default" && softwarning "Overwritting param: src6 '${src6}' becomes '${1}'"
|
|
src6="${1//,/ }"
|
|
fi
|
|
else
|
|
running_ipv4 && dst4not=
|
|
running_ipv6 && dst6not=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
running_ipv4 && dst4not="!"
|
|
running_ipv6 && dst6not="!"
|
|
fi
|
|
if running_ipv4; then
|
|
test ${softwarnings} -eq 1 -a ! "${dst4}" = "default" && softwarning "Overwritting param: dst4 '${dst4}' becomes '${1}'"
|
|
dst4="${1//,/ }"
|
|
fi
|
|
if running_ipv6; then
|
|
test ${softwarnings} -eq 1 -a ! "${dst6}" = "default" && softwarning "Overwritting param: dst6 '${dst6}' becomes '${1}'"
|
|
dst6="${1//,/ }"
|
|
fi
|
|
fi
|
|
pop_namespace
|
|
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
|
|
shift
|
|
if [ ${reverse} -eq 0 ]
|
|
then
|
|
running_ipv4 && dst4not=
|
|
running_ipv6 && dst6not=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
running_ipv4 && dst4not="!"
|
|
running_ipv6 && dst6not="!"
|
|
fi
|
|
if running_ipv4; then
|
|
test ${softwarnings} -eq 1 -a ! "${dst4}" = "default" && softwarning "Overwritting param: dst4 '${dst4}' becomes '${1}'"
|
|
dst4="${1//,/ }"
|
|
fi
|
|
if running_ipv6; then
|
|
test ${softwarnings} -eq 1 -a ! "${dst6}" = "default" && softwarning "Overwritting param: dst6 '${dst6}' becomes '${1}'"
|
|
dst6="${1//,/ }"
|
|
fi
|
|
else
|
|
running_ipv4 && src4not=
|
|
running_ipv6 && src6not=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
running_ipv4 && src4not="!"
|
|
running_ipv6 && src6not="!"
|
|
fi
|
|
if running_ipv4; then
|
|
test ${softwarnings} -eq 1 -a ! "${src4}" = "default" && softwarning "Overwritting param: src6 '${src4}' becomes '${1}'"
|
|
src4="${1//,/ }"
|
|
fi
|
|
if running_ipv6; then
|
|
test ${softwarnings} -eq 1 -a ! "${src6}" = "default" && softwarning "Overwritting param: src6 '${src6}' becomes '${1}'"
|
|
src6="${1//,/ }"
|
|
fi
|
|
fi
|
|
pop_namespace
|
|
shift
|
|
;;
|
|
|
|
srctype|SRCTYPE|sourcetype|SOURCETYPE)
|
|
shift
|
|
if [ ${reverse} -eq 0 ]
|
|
then
|
|
srctypenot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
srctypenot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${srctype}" = "" && softwarning "Overwritting param: srctype '${srctype}' becomes '${1}'"
|
|
srctype="`echo ${1} | ${SED_CMD} -e "s|^ \+||" -e "s| \+\$||" -e "s| \+|,|g" | ${TR_CMD} a-z A-Z`"
|
|
else
|
|
dsttypenot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
dsttypenot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${dsttype}" = "" && softwarning "Overwritting param: dsttype '${dsttype}' becomes '${1}'"
|
|
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
|
|
dsttypenot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
dsttypenot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${dsttype}" = "" && softwarning "Overwritting param: dsttype '${dsttype}' becomes '${1}'"
|
|
dsttype="`echo ${1} | ${SED_CMD} -e "s|^ \+||" -e "s| \+\$||" -e "s| \+|,|g" | ${TR_CMD} a-z A-Z`"
|
|
else
|
|
srctypenot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
srctypenot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${srctype}" = "" && softwarning "Overwritting param: srctype '${srctype}' becomes '${1}'"
|
|
srctype="`echo ${1} | ${SED_CMD} -e "s|^ \+||" -e "s| \+\$||" -e "s| \+|,|g" | ${TR_CMD} a-z A-Z`"
|
|
fi
|
|
shift
|
|
;;
|
|
|
|
sport|SPORT|sourceport|SOURCEPORT)
|
|
shift
|
|
if [ ${reverse} -eq 0 ]
|
|
then
|
|
sportnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
sportnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${sport}" = "any" && softwarning "Overwritting param: sport '${sport}' becomes '${1}'"
|
|
sport="${1//,/ }"
|
|
else
|
|
dportnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
dportnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${dport}" = "any" && softwarning "Overwritting param: dport '${dport}' becomes '${1}'"
|
|
dport="${1//,/ }"
|
|
fi
|
|
shift
|
|
;;
|
|
|
|
dport|DPORT|destinationport|DESTINATIONPORT)
|
|
shift
|
|
if [ ${reverse} -eq 0 ]
|
|
then
|
|
dportnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
dportnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${dport}" = "any" && softwarning "Overwritting param: dport '${dport}' becomes '${1}'"
|
|
dport="${1//,/ }"
|
|
else
|
|
sportnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
sportnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${sport}" = "any" && softwarning "Overwritting param: sport '${sport}' becomes '${1}'"
|
|
sport="${1//,/ }"
|
|
fi
|
|
shift
|
|
;;
|
|
|
|
proto|PROTO|protocol|PROTOCOL)
|
|
shift
|
|
protonot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
protonot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${proto}" = "any" && softwarning "Overwritting param: proto '${proto}' becomes '${1}'"
|
|
proto="${1//,/ }"
|
|
shift
|
|
;;
|
|
|
|
custommark|CUSTOMMARK)
|
|
shift
|
|
markname="${1}"
|
|
shift
|
|
marknot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
marknot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${mark}" = "any" && softwarning "Overwritting param: mark '${mark}' becomes ${markname} '${1}'"
|
|
mark="${mark} $(mark_value $markname ${1//,/ })"
|
|
shift
|
|
;;
|
|
|
|
mark|MARK)
|
|
shift
|
|
marknot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
marknot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${mark}" = "any" && softwarning "Overwritting param: mark '${mark}' becomes usermark '${1}'"
|
|
mark="${mark} $(mark_value usermark ${1//,/ })"
|
|
shift
|
|
;;
|
|
|
|
connmark|CONNMARK)
|
|
shift
|
|
marknot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
marknot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${mark}" = "any" && softwarning "Overwritting param: mark '${mark}' becomes connmark '${1}'"
|
|
mark="${mark} $(mark_value connmark ${1//,/ })"
|
|
shift
|
|
;;
|
|
|
|
rawmark|RAWMARK)
|
|
shift
|
|
marknot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
marknot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${mark}" = "any" && softwarning "Overwritting param: mark '${mark}' becomes '${1}'"
|
|
mark="${1//,/ }"
|
|
shift
|
|
;;
|
|
|
|
tos|TOS)
|
|
shift
|
|
tosnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
tosnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${tos}" = "any" && softwarning "Overwritting param: tos '${tos}' becomes '${1}'"
|
|
tos="${1//,/ }"
|
|
shift
|
|
;;
|
|
|
|
dscp|DSCP)
|
|
shift
|
|
dscpnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
dscpnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${dscp}" = "any" && softwarning "Overwritting param: dscp '${dscp}' becomes '${1}'"
|
|
dscp="${1//,/ }"
|
|
shift
|
|
|
|
if [ "${dscp}" = "class" ]
|
|
then
|
|
dscptype="-class"
|
|
dscp="${1//,/ }"
|
|
shift
|
|
fi
|
|
;;
|
|
|
|
action|ACTION)
|
|
test ${softwarnings} -eq 1 -a ! -z "${action}" && softwarning "Overwritting param: action '${action}' becomes '${2}'"
|
|
action="${2}"
|
|
shift 2
|
|
|
|
action_param=()
|
|
local action_is_chain=0
|
|
case "${action}" in
|
|
accept|ACCEPT)
|
|
action="ACCEPT"
|
|
|
|
if [ "${1}" = "with" ]
|
|
then
|
|
shift
|
|
|
|
case "${1}" in
|
|
limit|LIMIT)
|
|
action_param=("limit" "${2}" "${3}")
|
|
shift 3
|
|
|
|
if [ "${1}" = "overflow" ]
|
|
then
|
|
action_param[3]="overflow"
|
|
action_param[4]="${2}"
|
|
shift 2
|
|
fi
|
|
;;
|
|
|
|
recent|RECENT)
|
|
action_param=("recent" "${2}" "${3}" "${4}")
|
|
shift 4
|
|
;;
|
|
|
|
knock|KNOCK)
|
|
action_param=("knock" "${2}")
|
|
shift 2
|
|
;;
|
|
|
|
*)
|
|
error "Cannot understand action's '${action}' directive '${1}'"
|
|
return 1
|
|
;;
|
|
esac
|
|
fi
|
|
;;
|
|
|
|
deny|DENY|drop|DROP)
|
|
action="DROP"
|
|
;;
|
|
|
|
reject|REJECT)
|
|
action="REJECT"
|
|
if [ "${1}" = "with" ]
|
|
then
|
|
action_param=("--reject-with" "${2}")
|
|
shift 2
|
|
else
|
|
action_param=("--reject-with" "auto")
|
|
fi
|
|
;;
|
|
|
|
return|RETURN)
|
|
action="RETURN"
|
|
;;
|
|
|
|
mirror|MIRROR)
|
|
action="MIRROR"
|
|
test $nomirror -eq 1 && action="REJECT"
|
|
;;
|
|
|
|
none|NONE)
|
|
action="NONE"
|
|
;;
|
|
|
|
masquerade|MASQUERADE)
|
|
action="MASQUERADE"
|
|
if [ ! "A${table}" = "A-t nat" ]
|
|
then
|
|
error "${action} must on a the 'nat' table."
|
|
return 1
|
|
fi
|
|
while [ ! -z "${1}" ]
|
|
do
|
|
case "${1}" in
|
|
ports|to-ports|--to-ports)
|
|
action_param=( "${action_param[@]}" "--to-ports" "${2//:/-}" )
|
|
# ports need a protocol: either tcp or udp (or both if unset)
|
|
test "${proto}" = "any" && proto="tcp udp"
|
|
shift 2
|
|
;;
|
|
|
|
random|--random)
|
|
action_param=( "${action_param[@]}" "--random" )
|
|
shift
|
|
;;
|
|
|
|
*) break
|
|
;;
|
|
esac
|
|
done
|
|
;;
|
|
|
|
snat|SNAT)
|
|
action="SNAT"
|
|
if [ ! "A${table}" = "A-t nat" ]
|
|
then
|
|
error "${action} must on a the 'nat' table."
|
|
return 1
|
|
fi
|
|
if [ "${1}" = "to" ]
|
|
then
|
|
action_param=()
|
|
local x=
|
|
for x in ${2}
|
|
do
|
|
action_param=("${action_param[@]}" "--to-source" "${x}")
|
|
done
|
|
shift 2
|
|
else
|
|
error "${action} requires a 'to' argument."
|
|
return 1
|
|
fi
|
|
;;
|
|
|
|
dnat|DNAT)
|
|
action="DNAT"
|
|
if [ ! "A${table}" = "A-t nat" ]
|
|
then
|
|
error "${action} must on a the 'nat' table."
|
|
return 1
|
|
fi
|
|
if [ "${1}" = "to" ]
|
|
then
|
|
action_param=()
|
|
local x=
|
|
for x in ${2}
|
|
do
|
|
action_param=("${action_param[@]}" "--to-destination" "${x}")
|
|
done
|
|
shift 2
|
|
else
|
|
error "${action} requires a 'to' argument"
|
|
return 1
|
|
fi
|
|
;;
|
|
|
|
redirect|REDIRECT)
|
|
action="REDIRECT"
|
|
if [ "${1}" = "on-port" -o "${1}" = "to-port" -o "${1}" = "to" ]
|
|
then
|
|
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)
|
|
action="TPROXY"
|
|
action_param=()
|
|
if [ "${1}" = "mark" -o "${1}" = "tproxy-mark" ]
|
|
then
|
|
action_param=("--tproxy-mark" "${2}")
|
|
shift 2
|
|
fi
|
|
|
|
if [ "${1}" = "on-port" -o "${1}" = "to-port" -o "${1}" = "to" ]
|
|
then
|
|
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
|
|
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)
|
|
action="TOS"
|
|
if [ "${1}" = "to" ]
|
|
then
|
|
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)
|
|
action="MARK"
|
|
if [ "${1}" = "to" ]
|
|
then
|
|
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
|
|
;;
|
|
|
|
connmark|CONNMARK)
|
|
action="CONNMARK"
|
|
case "${1}" in
|
|
to)
|
|
action_param=("--set-mark" "${2}")
|
|
shift 2
|
|
;;
|
|
save)
|
|
if [ "${2}" = "mask" ]
|
|
then
|
|
action_param=("--save-mark" "--mask" "${3}")
|
|
shift 3
|
|
else
|
|
action_param=("--save-mark")
|
|
shift 1
|
|
fi
|
|
;;
|
|
restore)
|
|
if [ "${2}" = "mask" ]
|
|
then
|
|
action_param=("--restore-mark" "--mask" "${3}")
|
|
shift 3
|
|
else
|
|
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)
|
|
action="DSCP"
|
|
if [ "${1}" = "to" ]
|
|
then
|
|
if [ "${2}" = "class" ]
|
|
then
|
|
action_param=("--set-dscp-class" "${2}")
|
|
shift
|
|
else
|
|
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)
|
|
action="TARPIT"
|
|
;;
|
|
|
|
*)
|
|
chain_exists "${action}"
|
|
local action_is_chain=$?
|
|
;;
|
|
esac
|
|
;;
|
|
|
|
state|STATE)
|
|
shift
|
|
statenot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
statenot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! -z "${state}" && softwarning "Overwritting param: state '${state}' becomes '${1}'"
|
|
state="${1}"
|
|
shift
|
|
;;
|
|
|
|
user|USER|uid|UID)
|
|
shift
|
|
uidnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
test ${noowner} -eq 0 && uidnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${uid}" = "any" && softwarning "Overwritting param: uid '${uid}' becomes '${1}'"
|
|
test ${noowner} -eq 0 && uid="${1//,/ }"
|
|
shift
|
|
;;
|
|
|
|
group|GROUP|gid|GID)
|
|
shift
|
|
gidnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
test ${noowner} -eq 0 && gidnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${gid}" = "any" && softwarning "Overwritting param: gid '${gid}' becomes '${1}'"
|
|
test ${noowner} -eq 0 && gid="${1//,/ }"
|
|
shift
|
|
;;
|
|
|
|
process|PROCESS|pid|PID)
|
|
shift
|
|
pidnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
test ${noowner} -eq 0 && pidnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${pid}" = "any" && softwarning "Overwritting param: pid '${pid}' becomes '${1}'"
|
|
test ${noowner} -eq 0 && pid="${1//,/ }"
|
|
shift
|
|
;;
|
|
|
|
session|SESSION|sid|SID)
|
|
shift
|
|
sidnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
test ${noowner} -eq 0 && sidnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${sid}" = "any" && softwarning "Overwritting param: sid '${sid}' becomes '${1}'"
|
|
test ${noowner} -eq 0 && sid="${1//,/ }"
|
|
shift
|
|
;;
|
|
|
|
command|COMMAND|cmd|CMD)
|
|
shift
|
|
cmdnot=
|
|
if [ "${1}" = "not" -o "${1}" = "NOT" ]
|
|
then
|
|
shift
|
|
test ${noowner} -eq 0 && cmdnot="!"
|
|
fi
|
|
test ${softwarnings} -eq 1 -a ! "${cmd}" = "any" && softwarning "Overwritting param: cmd '${cmd}' becomes '${1}'"
|
|
test ${noowner} -eq 0 && cmd="${1}"
|
|
shift
|
|
;;
|
|
|
|
custom|CUSTOM)
|
|
test ${softwarnings} -eq 1 -a ! -z "${custom}" && softwarning "Overwritting param: custom '${custom}' becomes '${2}'"
|
|
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}'"
|
|
log=normal
|
|
logtxt="${2// /_}"
|
|
fi
|
|
shift 2
|
|
if [ "${1}" = "level" ]
|
|
then
|
|
loglevel="${2}"
|
|
shift 2
|
|
else
|
|
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}'"
|
|
log=limit
|
|
logtxt="${2// /_}"
|
|
fi
|
|
shift 2
|
|
if [ "${1}" = "level" ]
|
|
then
|
|
loglevel="${2}"
|
|
shift 2
|
|
else
|
|
loglevel="${FIREHOL_LOG_LEVEL}"
|
|
fi
|
|
;;
|
|
|
|
limit|LIMIT)
|
|
test ${softwarnings} -eq 1 -a ! -z "${limit}" && softwarning "Overwritting param: limit '${limit}' becomes '${2}'"
|
|
limit="${2}"
|
|
burst="${3}"
|
|
shift 3
|
|
;;
|
|
|
|
iplimit|IPLIMIT)
|
|
test ${softwarnings} -eq 1 -a ! -z "${iplimit}" && softwarning "Overwritting param: iplimit '${iplimit}' becomes '${2}'"
|
|
iplimit="${2}"
|
|
iplimit_mask="${3}"
|
|
shift 3
|
|
;;
|
|
|
|
in) # this is incoming traffic - ignore packet ownership
|
|
noowner=1
|
|
nomirror=0
|
|
nomac=0
|
|
shift
|
|
;;
|
|
|
|
out) # this is outgoing traffic - ignore packet ownership if not in an interface
|
|
if [ ! "${work_cmd}" = "interface" ]
|
|
then
|
|
noowner=1
|
|
else
|
|
nomirror=1
|
|
fi
|
|
nomac=1
|
|
shift
|
|
;;
|
|
|
|
nolog)
|
|
nolog=1
|
|
shift
|
|
;;
|
|
|
|
noowner)
|
|
noowner=1
|
|
shift
|
|
;;
|
|
|
|
softwarnings)
|
|
softwarnings=1
|
|
shift
|
|
;;
|
|
|
|
nosoftwarnings)
|
|
softwarnings=0
|
|
shift
|
|
;;
|
|
|
|
set_work_inface|SET_WORK_INFACE)
|
|
swi=1
|
|
shift
|
|
;;
|
|
|
|
set_work_outface|SET_WORK_OUTFACE)
|
|
swo=1
|
|
shift
|
|
;;
|
|
|
|
acct|accounting)
|
|
if [ ${ENABLE_ACCOUNTING} -eq 1 ]
|
|
then
|
|
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
|
|
;;
|
|
|
|
*)
|
|
error "Cannot understand directive '${1}'."
|
|
return 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
test -z "${table}" && 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" && 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
|
|
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
|
|
physbridge="--physdev-is-in"
|
|
elif [ "${physin}" = "any" -a ! "${physout}" = "any" ]
|
|
then
|
|
physbridge="--physdev-is-out"
|
|
fi
|
|
fi
|
|
|
|
local srcnot= dstnot=
|
|
if running_both; then
|
|
if [ "${src4not}" != "${src6not}" ]
|
|
then
|
|
error "Mixed use of 'not' with src4 and src6." && return 1
|
|
else
|
|
srcnot="${src4not}"
|
|
fi
|
|
if [ "${dst4not}" != "${dst6not}" ]
|
|
then
|
|
error "Mixed use of 'not' with dst4 and dst6." && return 1
|
|
else
|
|
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
|
|
srcnot="${src6not}"
|
|
dstnot="${dst6not}"
|
|
else
|
|
srcnot="${src4not}"
|
|
dstnot="${dst4not}"
|
|
fi
|
|
|
|
test "${src4}" = "default" && src4="any"
|
|
test "${dst4}" = "default" && dst4="any"
|
|
test "${src6}" = "default" && src6="any"
|
|
test "${dst6}" = "default" && dst6="any"
|
|
|
|
if [ ! "${src4}" = "any" ]
|
|
then
|
|
src4=${src4//reserved_ips()/${RESERVED_IPV4}}
|
|
src4=${src4//private_ips()/${PRIVATE_IPV4}}
|
|
src4=${src4//multicast_ips()/${MULTICAST_IPV4}}
|
|
src4=${src4//unroutable_ips()/${UNROUTABLE_IPV4}}
|
|
fi
|
|
if [ ! "${dst4}" = "any" ]
|
|
then
|
|
dst4=${dst4//reserved_ips()/${RESERVED_IPV4}}
|
|
dst4=${dst4//private_ips()/${PRIVATE_IPV4}}
|
|
dst4=${dst4//multicast_ips()/${MULTICAST_IPV4}}
|
|
dst4=${dst4//unroutable_ips()/${UNROUTABLE_IPV4}}
|
|
fi
|
|
|
|
if [ ! "${src6}" = "any" ]
|
|
then
|
|
src6=${src6//reserved_ips()/${RESERVED_IPV6}}
|
|
src6=${src6//private_ips()/${PRIVATE_IPV6}}
|
|
src6=${src6//multicast_ips()/${MULTICAST_IPV6}}
|
|
src6=${src6//unroutable_ips()/${UNROUTABLE_IPV6}}
|
|
fi
|
|
if [ ! "${dst6}" = "any" ]
|
|
then
|
|
dst6=${dst6//reserved_ips()/${RESERVED_IPV6}}
|
|
dst6=${dst6//private_ips()/${PRIVATE_IPV6}}
|
|
dst6=${dst6//multicast_ips()/${MULTICAST_IPV6}}
|
|
dst6=${dst6//unroutable_ips()/${UNROUTABLE_IPV6}}
|
|
fi
|
|
|
|
# ----------------------------------------------------------------------------------
|
|
# 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}" 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.
|
|
local DYNAMIC_CHAIN_COUNTER
|
|
get_next_dynamic_counter DYNAMIC_CHAIN_COUNTER
|
|
negative_chain="${chain}.${DYNAMIC_CHAIN_COUNTER}"
|
|
|
|
iptables_both ${table} -N "${negative_chain}"
|
|
negative_action="${action}"
|
|
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
|
|
infacenot=
|
|
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
|
|
outfacenot=
|
|
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
|
|
physinnot=
|
|
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
|
|
physoutnot=
|
|
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
|
|
macnot=
|
|
mac=any
|
|
fi
|
|
|
|
if [ ! -z "${srcnot}" ]
|
|
then
|
|
local s= src= iptables=
|
|
|
|
running_ipv6 && { iptables="ip6tables"; src="${src6}"; }
|
|
running_ipv4 && { iptables="iptables"; src="${src4}"; }
|
|
|
|
for s in ${src}
|
|
do
|
|
case "${s}" in
|
|
ipset:*)
|
|
[ ${IPSET_WARNING} -eq 1 ] && ipset_warning
|
|
s="${s/ipset:/}"
|
|
test -z "${FIREHOL_IPSETS_USED[$s]}" && FIREHOL_IPSETS_USED[$s]="USED"
|
|
${iptables} ${table} -A "${negative_chain}" -m set --match-set "${s}" src -j RETURN
|
|
;;
|
|
*)
|
|
${iptables} ${table} -A "${negative_chain}" -s "${s}" -j RETURN
|
|
;;
|
|
esac
|
|
done
|
|
srcnot=
|
|
src4=any
|
|
src6=any
|
|
fi
|
|
|
|
if [ ! -z "${dstnot}" ]
|
|
then
|
|
local d= dst= iptables=
|
|
|
|
running_ipv6 && { iptables="ip6tables"; dst="${dst6}"; }
|
|
running_ipv4 && { iptables="iptables"; dst="${dst4}"; }
|
|
|
|
for d in ${dst}
|
|
do
|
|
case "${d}" in
|
|
ipset:*)
|
|
[ ${IPSET_WARNING} -eq 1 ] && ipset_warning
|
|
d="${d/ipset:/}"
|
|
test -z "${FIREHOL_IPSETS_USED[$d]}" && FIREHOL_IPSETS_USED[$d]="USED"
|
|
${iptables} ${table} -A "${negative_chain}" -m set --match-set "${d}" dst -j RETURN
|
|
;;
|
|
*)
|
|
${iptables} ${table} -A "${negative_chain}" -d "${d}" -j RETURN
|
|
;;
|
|
esac
|
|
done
|
|
dstnot=
|
|
dst4=any
|
|
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
|
|
protonot=
|
|
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= pr=
|
|
for sp in ${sport}
|
|
do
|
|
for pr in ${proto}
|
|
do
|
|
iptables_both ${table} -A "${negative_chain}" -p "${pr}" --sport "${sp}" -j RETURN
|
|
done
|
|
done
|
|
sportnot=
|
|
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= pr=
|
|
for dp in ${dport}
|
|
do
|
|
for pr in ${proto}
|
|
do
|
|
iptables_both ${table} -A "${negative_chain}" -p "${pr}" --dport "${dp}" -j RETURN
|
|
done
|
|
done
|
|
dportnot=
|
|
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
|
|
uidnot=
|
|
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
|
|
gidnot=
|
|
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
|
|
pidnot=
|
|
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
|
|
sidnot=
|
|
sid=any
|
|
fi
|
|
|
|
if [ ! -z "${cmdnot}" ]
|
|
then
|
|
local tcmd=
|
|
for tcmd in ${cmd}
|
|
do
|
|
iptables_both ${table} -A "${negative_chain}" -m owner --cmd-owner "${tcmd}" -j RETURN
|
|
done
|
|
cmdnot=
|
|
cmd=any
|
|
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
|
|
marknot=
|
|
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
|
|
tosnot=
|
|
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
|
|
dscp=any
|
|
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=
|
|
local -a proto_arg=()
|
|
for pr in ${proto}
|
|
do
|
|
case ${pr} in
|
|
any|ANY)
|
|
;;
|
|
|
|
*)
|
|
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[@]}" || failed=$[failed + 1]
|
|
fi
|
|
if running_ipv6; then
|
|
rule_action_param ip6tables "${negative_action}" "${pr}" "" "" "${table}" "${action_param[@]}" -- ${table} -A "${negative_chain}" "${proto_arg[@]}" || failed=$[failed + 1]
|
|
fi
|
|
action_param=()
|
|
done
|
|
fi
|
|
fi
|
|
|
|
|
|
# ----------------------------------------------------------------------------------
|
|
# Process the positive rules
|
|
|
|
local -a \
|
|
addrtype_arg=() stp_arg=() dtp_arg=() state_arg=() \
|
|
limit_arg=() iplimit_arg=() logopts_arg=() \
|
|
uid_arg=() owner_arg=() gid_arg=() pid_arg=() sid_arg=() cmd_arg=() \
|
|
mark_arg=() tos_arg=() dscp_arg=() proto_arg=() \
|
|
inf_arg=() outf_arg=() inph_arg=() physdev_arg=() outph_arg=() \
|
|
mc_arg=() s_arg=() d_arg=() sp_arg=() dp_arg=() \
|
|
basecmd=()
|
|
|
|
local logrule= ipvall= \
|
|
tuid= tgid= tpid= tsid= tcmd= \
|
|
tmark= ttos= tdscp= pr= \
|
|
inf= outf= inph= outph= \
|
|
mc= ipv= iptables= src= dst= s= d= sp= dp=
|
|
|
|
# addrtype (srctype, dsttype)
|
|
if [ ! -z "${srctype}${dsttype}" ]
|
|
then
|
|
addrtype_arg=("-m" "addrtype")
|
|
|
|
if [ ! -z "${srctype}" ]
|
|
then
|
|
stp_arg=(${srctypenot} "--src-type" "${srctype}")
|
|
fi
|
|
|
|
if [ ! -z "${dsttype}" ]
|
|
then
|
|
dtp_arg=(${dsttypenot} "--dst-type" "${dsttype}")
|
|
fi
|
|
fi
|
|
|
|
# state
|
|
[ ! -z "${state}" ] && state_arg=("-m" "conntrack" ${statenot} "--ctstate" "${state}")
|
|
|
|
# limit
|
|
[ ! -z "${limit}" ] && limit_arg=("-m" "limit" "--limit" "${limit}" "--limit-burst" "${burst}")
|
|
|
|
# iplimit
|
|
[ ! -z "${iplimit}" ] && iplimit_arg=("-m" "iplimit" "--iplimit-above" "${iplimit}" "--iplimit-mask" "${iplimit_mask}")
|
|
|
|
# log mode selection
|
|
if [ "${FIREHOL_LOG_MODE}" = "ULOG" ]
|
|
then
|
|
logopts_arg=("--ulog-prefix=${FIREHOL_LOG_PREFIX}${logtxt}:")
|
|
elif [ "${FIREHOL_LOG_MODE}" = "NFLOG" ]
|
|
then
|
|
logopts_arg=("--nflog-prefix=${FIREHOL_LOG_PREFIX}${logtxt}:")
|
|
else
|
|
logopts_arg=("--log-level" "${loglevel}" "--log-prefix=${FIREHOL_LOG_PREFIX}${logtxt}:")
|
|
fi
|
|
|
|
# log / loglimit
|
|
case "${log}" in
|
|
'')
|
|
logrule=none
|
|
;;
|
|
|
|
limit)
|
|
logrule=limit
|
|
;;
|
|
|
|
normal)
|
|
logrule=normal
|
|
;;
|
|
|
|
*)
|
|
error "Unknown log value '${log}'."
|
|
;;
|
|
esac
|
|
|
|
# keep a list of all ip versions we need
|
|
running_ipv4 && ipvall="ipv4"
|
|
running_ipv6 && ipvall="${ipvall} ipv6"
|
|
|
|
# uid
|
|
for tuid in ${uid}
|
|
do
|
|
case ${tuid} in
|
|
any|ANY)
|
|
uid_arg=()
|
|
owner_arg=()
|
|
;;
|
|
|
|
*)
|
|
owner_arg=("-m" "owner")
|
|
uid_arg=("--uid-owner" "${tuid}")
|
|
;;
|
|
esac
|
|
|
|
# gid
|
|
for tgid in ${gid}
|
|
do
|
|
case ${tgid} in
|
|
any|ANY)
|
|
gid_arg=()
|
|
# do not reset owner_arg=() here
|
|
;;
|
|
|
|
*)
|
|
owner_arg=("-m" "owner")
|
|
gid_arg=("--gid-owner" "${tgid}")
|
|
;;
|
|
esac
|
|
|
|
# pid
|
|
for tpid in ${pid}
|
|
do
|
|
case ${tpid} in
|
|
any|ANY)
|
|
pid_arg=()
|
|
# do not reset owner_arg=() here
|
|
;;
|
|
|
|
*)
|
|
owner_arg=("-m" "owner")
|
|
pid_arg=("--pid-owner" "${tpid}")
|
|
;;
|
|
esac
|
|
|
|
# sid
|
|
for tsid in ${sid}
|
|
do
|
|
case ${tsid} in
|
|
any|ANY)
|
|
sid_arg=()
|
|
# do not reset owner_arg=() here
|
|
;;
|
|
|
|
*)
|
|
owner_arg=("-m" "owner")
|
|
sid_arg=("--sid-owner" "${tsid}")
|
|
;;
|
|
esac
|
|
|
|
# cmd
|
|
for tcmd in ${cmd}
|
|
do
|
|
case ${tcmd} in
|
|
any|ANY)
|
|
cmd_arg=()
|
|
# do not reset owner_arg=() here
|
|
;;
|
|
|
|
*)
|
|
owner_arg=("-m" "owner")
|
|
cmd_arg=("--cmd-owner" "${tcmd}")
|
|
;;
|
|
esac
|
|
|
|
# mark
|
|
for tmark in ${mark}
|
|
do
|
|
case ${tmark} in
|
|
any|ANY)
|
|
mark_arg=()
|
|
;;
|
|
|
|
*)
|
|
mark_arg=("-m" "mark" "--mark" "${tmark}")
|
|
;;
|
|
esac
|
|
|
|
# tos
|
|
for ttos in ${tos}
|
|
do
|
|
case ${ttos} in
|
|
any|ANY)
|
|
tos_arg=()
|
|
;;
|
|
|
|
*)
|
|
tos_arg=("-m" "tos" "--tos" "${ttos}")
|
|
;;
|
|
esac
|
|
|
|
# dscp
|
|
for tdscp in ${dscp}
|
|
do
|
|
case ${tdscp} in
|
|
any|ANY)
|
|
dscp_arg=()
|
|
;;
|
|
|
|
*)
|
|
dscp_arg=("-m" "dscp" "--dscp${dscptype}" "${tdscp}")
|
|
;;
|
|
esac
|
|
|
|
# proto
|
|
for pr in ${proto}
|
|
do
|
|
case ${pr} in
|
|
any|ANY)
|
|
proto_arg=()
|
|
;;
|
|
|
|
*)
|
|
proto_arg=("-p" "${pr}")
|
|
;;
|
|
esac
|
|
|
|
# inface
|
|
for inf in ${inface}
|
|
do
|
|
case ${inf} in
|
|
any|ANY)
|
|
inf_arg=()
|
|
;;
|
|
|
|
*)
|
|
inf_arg=("-i" "${inf}")
|
|
;;
|
|
esac
|
|
|
|
# outface
|
|
for outf in ${outface}
|
|
do
|
|
case ${outf} in
|
|
any|ANY)
|
|
outf_arg=()
|
|
;;
|
|
|
|
*)
|
|
outf_arg=("-o" "${outf}")
|
|
;;
|
|
esac
|
|
|
|
# physin
|
|
for inph in ${physin}
|
|
do
|
|
case ${inph} in
|
|
any|ANY)
|
|
inph_arg=()
|
|
physdev_arg=()
|
|
;;
|
|
|
|
*)
|
|
physdev_arg=("-m" "physdev" ${physbridge})
|
|
inph_arg=("--physdev-in" "${inph}")
|
|
;;
|
|
esac
|
|
|
|
# physout
|
|
for outph in ${physout}
|
|
do
|
|
case ${outph} in
|
|
any|ANY)
|
|
outph_arg=()
|
|
# do not reset physdev_arg=() here
|
|
;;
|
|
|
|
*)
|
|
physdev_arg=("-m" "physdev" ${physbridge})
|
|
outph_arg=("--physdev-out" "${outph}")
|
|
;;
|
|
esac
|
|
|
|
# mac
|
|
for mc in ${mac}
|
|
do
|
|
case ${mc} in
|
|
any|ANY)
|
|
mc_arg=()
|
|
;;
|
|
|
|
*)
|
|
mc_arg=("-m" "mac" "--mac-source" "${mc}")
|
|
;;
|
|
esac
|
|
|
|
# ipvall
|
|
for ipv in ${ipvall}
|
|
do
|
|
case "${ipv}" in
|
|
ipv4)
|
|
iptables="iptables"
|
|
src="${src4}"
|
|
dst="${dst4}"
|
|
;;
|
|
|
|
ipv6)
|
|
iptables="ip6tables"
|
|
src="${src6}"
|
|
dst="${dst6}"
|
|
;;
|
|
esac
|
|
|
|
# src
|
|
for s in ${src}
|
|
do
|
|
case ${s} in
|
|
any|ANY)
|
|
s_arg=()
|
|
;;
|
|
|
|
ipset:*)
|
|
[ ${IPSET_WARNING} -eq 1 ] && ipset_warning
|
|
s="${s/ipset:/}"
|
|
test -z "${FIREHOL_IPSETS_USED[$s]}" && FIREHOL_IPSETS_USED[$s]="USED"
|
|
s_arg=("-m" "set" "--match-set" "${s}" "src")
|
|
;;
|
|
|
|
*)
|
|
s_arg=("-s" "${s}")
|
|
;;
|
|
esac
|
|
|
|
# dst
|
|
for d in ${dst}
|
|
do
|
|
case ${d} in
|
|
any|ANY)
|
|
d_arg=()
|
|
;;
|
|
|
|
ipset:*)
|
|
[ ${IPSET_WARNING} -eq 1 ] && ipset_warning
|
|
d="${d/ipset:/}"
|
|
test -z "${FIREHOL_IPSETS_USED[$d]}" && FIREHOL_IPSETS_USED[$d]="USED"
|
|
d_arg=("-m" "set" "--match-set" "${d}" "dst")
|
|
;;
|
|
|
|
*)
|
|
d_arg=("-d" "${d}")
|
|
;;
|
|
esac
|
|
|
|
# sport
|
|
for sp in ${sport}
|
|
do
|
|
case ${sp} in
|
|
any|ANY)
|
|
sp_arg=()
|
|
;;
|
|
|
|
*)
|
|
sp_arg=("--sport" "${sp}")
|
|
;;
|
|
esac
|
|
|
|
# dport
|
|
for dp in ${dport}
|
|
do
|
|
case ${dp} in
|
|
any|ANY)
|
|
dp_arg=()
|
|
;;
|
|
|
|
*)
|
|
dp_arg=("--dport" "${dp}")
|
|
;;
|
|
esac
|
|
|
|
# build the command
|
|
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} || failed=$[failed + 1]
|
|
|
|
done # dport
|
|
done # sport
|
|
done # dst
|
|
done # src
|
|
done # ipvall
|
|
done # mac
|
|
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
|
|
|
|
test ${failed} -gt 0 && error "There are ${failed} failed commands." && return 1
|
|
return 0
|
|
}
|
|
|
|
warning() {
|
|
echo >&2
|
|
echo >&2 "WARNING: " "$@"
|
|
echo >&2
|
|
|
|
return 0
|
|
}
|
|
|
|
softwarning() {
|
|
echo >&2
|
|
echo >&2 "--------------------------------------------------------------------------------"
|
|
echo >&2 "WARNING"
|
|
echo >&2 "WHEN : ${work_function}"
|
|
echo >&2 "WHY :" "$@"
|
|
printf >&2 "COMMAND: "; printf >&2 "%q " "${work_realcmd[@]}"; echo >&2
|
|
echo >&2 "MODE :" "${FIREHOL_NS_CURR}"
|
|
echo >&2 "SOURCE : $(config_line)"
|
|
echo >&2
|
|
|
|
return 0
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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}' $(config_line)"
|
|
|
|
work_error=$[work_error + 1]
|
|
echo >&2
|
|
echo >&2 "--------------------------------------------------------------------------------"
|
|
echo >&2 "ERROR #: ${work_error}"
|
|
echo >&2 "WHEN : ${work_function}"
|
|
echo >&2 "WHY :" "$@"
|
|
printf >&2 "COMMAND: "; printf >&2 "%q " "${work_realcmd[@]}"; echo >&2
|
|
echo >&2 "MODE :" "${FIREHOL_NS_CURR}"
|
|
echo >&2 "SOURCE : $(config_line)"
|
|
echo >&2
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# runtime_error - postprocessing evaluation of commands run
|
|
# 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" id=
|
|
|
|
case "${1}" in
|
|
error)
|
|
type="ERROR "
|
|
work_runtime_error=$[work_runtime_error + 1]
|
|
id="# ${work_runtime_error}."
|
|
;;
|
|
|
|
warn)
|
|
type="WARNING"
|
|
id="This might or might not affect the operation of your firewall."
|
|
;;
|
|
|
|
*)
|
|
work_runtime_error=$[work_runtime_error + 1]
|
|
id="# ${work_runtime_error}."
|
|
|
|
echo >&2
|
|
echo >&2
|
|
echo >&2 "WARNING: unsupported final status type '${1}'. Assuming it is 'ERROR'"
|
|
echo >&2
|
|
echo >&2
|
|
;;
|
|
esac
|
|
shift
|
|
|
|
local ret="${1}" line="${2}"
|
|
shift 2
|
|
|
|
syslog err "Runtime ${type} '${id}'. Source ${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}"
|
|
printf >&2 "COMMAND : "
|
|
printf >&2 "%q " "$@"
|
|
printf >&2 "\n"
|
|
echo >&2 "OUTPUT : "
|
|
echo >&2
|
|
${CAT_CMD} ${FIREHOL_OUTPUT}.log >&2
|
|
echo >&2
|
|
|
|
return 0
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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
|
|
}
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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.
|
|
|
|
create_chain() {
|
|
local table="${1}" newchain="${2}" oldchain="${3}"
|
|
shift 3
|
|
|
|
set_work_function "Creating chain '${newchain}' under '${oldchain}' in table '${table}'"
|
|
|
|
chain_exists "${newchain}"
|
|
test $? -eq 1 && error "Chain '${newchain}' already exists." && return 1
|
|
|
|
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
|
|
|
|
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}" services="${2}" service= servname= suffix= mychain= ret= fn=
|
|
# type = the current subcommand: server/client/route
|
|
# services = the services to implement
|
|
shift 2
|
|
|
|
for service in $services
|
|
do
|
|
servname="${service}"
|
|
test "${service}" = "custom" && 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.
|
|
local work_counter
|
|
get_next_work_counter work_counter
|
|
|
|
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
|
|
|
|
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}" "$@"
|
|
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
|
|
fn="rules_${service}"
|
|
|
|
set_work_function "Running complex rules function ${fn}() for ${type} '${service}'"
|
|
|
|
"${fn}" "${mychain}" "${type}" "$@"
|
|
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}" \
|
|
type="${2}" \
|
|
server="${3}" \
|
|
server_varname= server_ports= \
|
|
client_varname= client_ports= \
|
|
varname= helpers= \
|
|
x= value=
|
|
shift 3
|
|
|
|
server_varname="server_${server}_ports"
|
|
eval server_ports="\$${server_varname}"
|
|
|
|
client_varname="client_${server}_ports"
|
|
eval 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
|
|
|
|
varname="helper_${server}"
|
|
eval helpers="\$${varname}"
|
|
|
|
varname="require_${server}_modules"
|
|
eval value="\$${varname}"
|
|
for x in ${value}
|
|
do
|
|
require_kernel_module $x || return 1
|
|
done
|
|
|
|
if [ ${FIREHOL_NAT} -eq 1 ]
|
|
then
|
|
varname="require_${server}_nat_modules"
|
|
eval 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 "# $(config_line)\n"
|
|
printf "# >>> "
|
|
|
|
case $1 in
|
|
2) printf " "
|
|
;;
|
|
*) ;;
|
|
esac
|
|
|
|
printf "%q " "${work_realcmd[@]}"
|
|
printf "\n\n"
|
|
) >&21
|
|
}
|
|
|
|
work_realcmd_primary() {
|
|
config_line -ne
|
|
work_realcmd=("$@")
|
|
test ${FIREHOL_CONF_SHOW} -eq 1 && show_work_realcmd 1
|
|
}
|
|
|
|
work_realcmd_secondary() {
|
|
config_line -ne
|
|
work_realcmd=("$@")
|
|
test ${FIREHOL_CONF_SHOW} -eq 1 && show_work_realcmd 2
|
|
}
|
|
|
|
work_realcmd_helper() {
|
|
config_line -ne
|
|
work_realcmd=("$@")
|
|
test ${FIREHOL_CONF_SHOW} -eq 1 && show_work_realcmd 3
|
|
}
|
|
|
|
wait_for_interface() {
|
|
local iface="${1}" timeout=60 found=0 start=`date +%s` addr=
|
|
shift
|
|
|
|
[ -n "$1" ] && timeout="${1}"
|
|
|
|
while [ "`date +%s`" -lt $(($start+$timeout)) -a $found -eq 0 ]
|
|
do
|
|
addr=`ip addr show $iface 2> /dev/null | awk '$1 ~ /^inet$/ {print $2}'`
|
|
[ -n "$addr" ] && found=1
|
|
[ $found -eq 0 ] && sleep 0.5
|
|
done
|
|
|
|
# the interface is up
|
|
[ $found -eq 1 ] && return 0
|
|
|
|
return 1
|
|
}
|
|
|
|
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# keep a copy of the running firewall on disk for fast restoration
|
|
|
|
fixed_save() {
|
|
local command="$1" tmp="${FIREHOL_DIR}/iptables-save-$$" 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() {
|
|
progress "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 # "Saving activated firewall to '${FIREHOL_SPOOL_DIR}'"
|
|
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 # "Saving activated firewall to '${FIREHOL_SPOOL_DIR}'"
|
|
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 # "Saving activated firewall to '${FIREHOL_SPOOL_DIR}'"
|
|
|
|
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 do_ipv6=0
|
|
test -f "${FIREHOL_SPOOL_DIR}/ipv4.enable" -a -f "${FIREHOL_SPOOL_DIR}/ipv4.rules" && do_ipv4=1
|
|
test -f "${FIREHOL_SPOOL_DIR}/ipv6.enable" -a -f "${FIREHOL_SPOOL_DIR}/ipv6.rules" && 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
|
|
|
|
if [ ${ENABLE_IPSET} -eq 1 ]
|
|
then
|
|
ipsets_apply spool || return 6
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
firehol_restore_last_activated_firewall() {
|
|
firehol_can_restore_saved_firewall || return 2
|
|
|
|
progress "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 # "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 # "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 # "Restoring last activated firewall from '${FIREHOL_SPOOL_DIR}'"
|
|
return 3
|
|
fi
|
|
fi
|
|
|
|
success # "Saving activated firewall to '${FIREHOL_SPOOL_DIR}'"
|
|
return 0
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
#
|
|
# START UP SCRIPT PROCESSING
|
|
#
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
|
|
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
|
|
# ------------------------------------------------------------------------------
|
|
|
|
syslog info "FireHOL started from '$PWD' with: ${0} ${*}"
|
|
|
|
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
|
|
firewall_policy6_applied=1
|
|
FIREHOL_FAST_ACTIVATION=0
|
|
FIREHOL_MODE="EXPLAIN"
|
|
FIREHOL_CONF_SHOW=1
|
|
;;
|
|
|
|
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"
|
|
FIREHOL_TRY=0
|
|
;;
|
|
|
|
stop)
|
|
FIREHOL_MODE="STOP"
|
|
test ! -z "${1}" && warning "Arguments after parameter '${arg}' are ignored."
|
|
|
|
progress "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 # "Clearing firewall"
|
|
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 -------------------------------------------------------------------"
|
|
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})"
|
|
progress "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 # "Blocking all communications"
|
|
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"
|
|
FIREHOL_CONF_SHOW=1
|
|
;;
|
|
|
|
ipset_update_from_file)
|
|
if [ ${ENABLE_IPSET} -ne 1 ]
|
|
then
|
|
echo >&2 "ipset is not enabled. Is ipset installed?"
|
|
exit 1
|
|
fi
|
|
name="${1}"
|
|
shift
|
|
|
|
found=0
|
|
for x in $( ipset_list_active_names )
|
|
do
|
|
[ "$x" = "${name}" ] && found=1 && break
|
|
done
|
|
if [ $found -eq 1 ]
|
|
then
|
|
tmp=$(${MKTEMP_CMD} "${FIREHOL_DIR}/ipset-XXXXXXXXXX") || exit 1
|
|
echo "${IPSET_FLUSH_OPTION} ${name}" >${tmp}
|
|
ipset_addfile "${name}" "${@}" >>${tmp}
|
|
if [ $? -ne 0 ]
|
|
then
|
|
echo >&2 "Cannot update ipset '${name}'."
|
|
exit 1
|
|
fi
|
|
${IPSET_CMD} ${IPSET_RESTORE_OPTION} <${tmp}
|
|
if [ $? -ne 0 ]
|
|
then
|
|
echo >&2 "Failed to update ipset '${name}'"
|
|
exit 1
|
|
fi
|
|
echo >&2 "Updated ipset '${name}' with $(( $(cat ${tmp} | wc -l) - 1 )) IPs."
|
|
|
|
# keep it for restoration
|
|
if [ -f "${FIREHOL_SPOOL_DIR}/ipset.${name}.rules" ]
|
|
then
|
|
${CAT_CMD} "${FIREHOL_SPOOL_DIR}/ipset.${name}.rules" |\
|
|
${GREP_CMD} "^${IPSET_CREATE_OPTION} ${name} " >${tmp}.save
|
|
${CAT_CMD} ${tmp} >>${tmp}.save
|
|
cp ${tmp}.save "${FIREHOL_SPOOL_DIR}/ipset.${name}.rules"
|
|
fi
|
|
|
|
ipset_save_active_to_spool
|
|
|
|
# make the exit handler exit with 0
|
|
FIREHOL_ACTIVATED_SUCCESSFULLY=1
|
|
exit 0
|
|
else
|
|
echo >&2 "There is no ipset collection named '${name}'."
|
|
fi
|
|
exit 1
|
|
;;
|
|
|
|
*) 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 >&2 "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.
|
|
|
|
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'.
|
|
|
|
|
|
-------------------------------------------------------------------------
|
|
|
|
FireHOL supports the following services (sorted by name):
|
|
EOF
|
|
|
|
|
|
(
|
|
# The simple services
|
|
${CAT_CMD} "${PROGRAM_FILE}" |\
|
|
${GREP_CMD} -e "^server_.*_ports=" |\
|
|
${CUT_CMD} -d '=' -f 1 |\
|
|
${SED_CMD} "s/^server_//" |\
|
|
${SED_CMD} "s/_ports\$//"
|
|
|
|
# The complex services
|
|
${CAT_CMD} "${PROGRAM_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:
|
|
|
|
${PROGRAM_FILE} helpme >/tmp/firehol.conf
|
|
|
|
and you will get the configuration written to /tmp/firehol.conf
|
|
|
|
EOF
|
|
exit 1
|
|
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
# Remove all parameters until --
|
|
while [ ! -z "${1}" ]
|
|
do
|
|
if [ "${1}" = "--" ]
|
|
then
|
|
shift
|
|
break
|
|
fi
|
|
|
|
warning "Parameter '${1}' is ignored."
|
|
shift
|
|
done
|
|
|
|
if [ "${FIREHOL_MODE}" = "START" -o "${FIREHOL_MODE}" = "DEBUG" ]
|
|
then
|
|
if [ ! -f "${FIREHOL_CONFIG}" ]
|
|
then
|
|
echo >&2 " ERROR: FireHOL config '${FIREHOL_CONFIG}' not found."
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
#
|
|
# MAIN PROCESSING - Interactive mode
|
|
#
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
|
|
if [ "${FIREHOL_MODE}" = "EXPLAIN" ]
|
|
then
|
|
FIREHOL_CONF_SHOW=1
|
|
FIREHOL_FAST_ACTIVATION=0
|
|
FIREHOL_CONFIG="Interactive User Input"
|
|
lineid="1"
|
|
FORCE_CONFIG_LINEID="${lineid}@${FIREHOL_CONFIG}"
|
|
|
|
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 : ${FORCE_CONFIG_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}"
|
|
|
|
lineid=$[lineid + 1]
|
|
FORCE_CONFIG_LINEID="${lineid}@${FIREHOL_CONFIG}"
|
|
|
|
printf "\n# > OK <\n"
|
|
fi
|
|
break
|
|
;;
|
|
esac
|
|
|
|
break
|
|
done
|
|
done
|
|
|
|
exit 0
|
|
fi
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# 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}" def="${2}" ans= c= t=
|
|
shift 2
|
|
|
|
echo >&2
|
|
|
|
while [ 1 = 1 ]
|
|
do
|
|
printf >&2 "%s [%s] > " "${prompt}" "${def}"
|
|
read
|
|
|
|
ans="${REPLY}"
|
|
|
|
test -z "${ans}" && ans="${def}"
|
|
|
|
c=0
|
|
while [ $c -le $# ]
|
|
do
|
|
eval 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}" net="${2}"
|
|
shift 2
|
|
|
|
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} i2=${2} i3=${3} i4=${4}
|
|
|
|
set -- `echo ${net} | ${TR_CMD} './' ' '`
|
|
local n1=${1} n2=${2} n3=${3} n4=${4} 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 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}" net="${2}"
|
|
shift 2
|
|
|
|
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} i2=${2} i3=${3} i4=${4} i5=${5:-32}
|
|
|
|
set -- `echo ${net} | ${TR_CMD} './' ' '`
|
|
local n1=${1} n2=${2} n3=${3} n4=${4} 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} i2=${2} i3=${3} i4=${4} 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 "${PROGRAM_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
|
|
|
|
# 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" ]
|
|
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}'"
|
|
|
|
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
|
|
# Add it to ifnets
|
|
f=0; ff=0
|
|
while [ $f -lt $netcount ]
|
|
do
|
|
if ip_in_net ${net} ${ifnets[$f]}
|
|
then
|
|
# Already satisfied
|
|
ff=1
|
|
elif ip_in_net ${ifnets[$f]} ${net}
|
|
then
|
|
# New one is superset of old
|
|
ff=1
|
|
ifnets[$f]=${net}
|
|
fi
|
|
|
|
f=$[f + 1]
|
|
done
|
|
|
|
if [ $ff -eq 0 ]
|
|
then
|
|
# 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}"
|
|
|
|
# Add it to ifnets
|
|
f=0; ff=0
|
|
while [ $f -lt $netcount ]
|
|
do
|
|
if ip_in_net ${net} ${ifnets[$f]}
|
|
then
|
|
# Already satisfied
|
|
ff=1
|
|
elif ip_in_net ${ifnets[$f]} ${net}
|
|
then
|
|
# New one is superset of old
|
|
ff=1
|
|
ifnets[$f]=${net}
|
|
fi
|
|
|
|
f=$[f + 1]
|
|
done
|
|
|
|
if [ $ff -eq 0 ]
|
|
then
|
|
# 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
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
#
|
|
# MAIN PROCESSING
|
|
#
|
|
# ------------------------------------------------------------------------------
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# ------------------------------------------------------------------------------
|
|
|
|
# make sure we are alone
|
|
firehol_concurrent_run_lock
|
|
|
|
|
|
# --- Initialization -----------------------------------------------------------
|
|
|
|
# let the config_line know our main configuration file
|
|
PROGRAM_CONFIG="${FIREHOL_CONFIG}"
|
|
|
|
progress "Saving active 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 # "Saving active firewall to a temporary file"
|
|
else
|
|
${RM_CMD} -f "${FIREHOL_SAVED}" "${FIREHOL_SAVED6}"
|
|
failure # "Saving active firewall to a temporary file"
|
|
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
|
|
|
|
# clear all chains
|
|
firehol_filter_chains=
|
|
firehol_filter6_chains=
|
|
initialize_firewall() {
|
|
load_kernel_module ip_tables
|
|
load_kernel_module nf_conntrack
|
|
if [ $ENABLE_IPV6 -eq 1 ]
|
|
then
|
|
load_kernel_module ip6_tables
|
|
fi
|
|
|
|
for m in ${!FIREHOL_KERNEL_MODULES[*]}
|
|
do
|
|
postprocess -ne -ns load_kernel_module $m
|
|
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
|
|
|
|
# Find all tables supported
|
|
local t= tables= tables6= chains= c= policy=
|
|
if [ $ENABLE_IPV4 -eq 1 ]; then
|
|
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
|
|
|
|
# Find all default chains in this table.
|
|
chains=`${IPTABLES_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_filter_chains="${chains}"
|
|
|
|
# Set the policy to ACCEPT on all default chains.
|
|
for c in ${chains}
|
|
do
|
|
policy=ACCEPT
|
|
if [ "${t}" = "filter" ]
|
|
then
|
|
eval "policy=\${FIREHOL_${c}_ACTIVATION_POLICY}"
|
|
fi
|
|
${IPTABLES_CMD} -t "${t}" -P "${c}" $policy || exit 1
|
|
done
|
|
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
|
|
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.
|
|
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.
|
|
for c in ${chains}
|
|
do
|
|
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
|
|
}
|
|
|
|
# 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
|
|
}
|
|
|
|
# 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
|
|
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}"
|
|
|
|
# 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
|
|
|
|
# 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
|
|
}
|
|
|
|
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
|
|
progress "Processing file '${FIREHOL_CONFIG}'"
|
|
ret=0
|
|
|
|
# check if the user has given any iptables commands directly.
|
|
if [ ! -z "`${CAT_CMD} ${FIREHOL_CONFIG} | ${EGREP_CMD} "(${IPTABLES_CMD}|${IP6TABLES_CMD})"`" ]
|
|
then
|
|
echo >&2
|
|
echo >&2
|
|
echo >&2 "ERROR:"
|
|
echo >&2 "${FIREHOL_CONFIG} contains ${IPTABLES_CMD} or ${IP6TABLES_CMD} statements."
|
|
echo >&2
|
|
echo >&2 "Replace these statements iptables or ip6tables respectively,"
|
|
echo >&2 "without a path, so that FireHOL can execute these commands at"
|
|
echo >&2 "firewall activation."
|
|
echo >&2
|
|
echo >&2
|
|
exit 1
|
|
fi
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Run the configuration file.
|
|
|
|
if [ -n "$WAIT_FOR_IFACE" ]
|
|
then
|
|
for i in "$WAIT_FOR_IFACE"
|
|
do
|
|
wait_for_interface $i
|
|
done
|
|
fi
|
|
|
|
enable -n trap # Disable the trap buildin shell command.
|
|
enable -n exit # Disable the exit buildin shell command.
|
|
|
|
FORCE_CONFIG_LINEID=
|
|
{ source ${FIREHOL_CONFIG} "$@"; } # Run the configuration as a normal script.
|
|
source_status=$?
|
|
[ $source_status -ne 0 ] && ret=$[ret + 1]
|
|
[ $source_status -ne 0 ] && FIREHOL_CLEAN_TMP=0
|
|
FORCE_CONFIG_LINEID="FIN"
|
|
LAST_CONFIG_LINE="${FORCE_CONFIG_LINEID}"
|
|
|
|
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]
|
|
|
|
if [ ${work_error} -gt 0 -o $ret -gt 0 ]
|
|
then
|
|
failure # "Processing file '${FIREHOL_CONFIG}'"
|
|
echo >&2
|
|
echo >&2
|
|
echo >&2 "NOTICE: No changes made to your firewall."
|
|
exit 1
|
|
fi
|
|
|
|
success # "Processing file '${FIREHOL_CONFIG}'"
|
|
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
|
|
if [ ${FIREHOL_FAST_ACTIVATION} -eq 1 ]
|
|
then
|
|
if [ ${FIREHOL_TRY} -eq 1 -a "$[FIREHOL_WAIT_USER_BEFORE_TRY]" -gt 60 ]
|
|
then
|
|
syslog info "Waiting user to try the new firewall."
|
|
echo >&2
|
|
echo >&2 "Your firewall is ready to be fast-activated..."
|
|
echo >&2 "If you don't continue, no changes will have been made to your firewall."
|
|
read >&2 -p "Activate the firewall? (just press enter to confirm or Control-C to stop) : " -t ${FIREHOL_WAIT_USER_BEFORE_TRY} -e || exit 1
|
|
echo >&2
|
|
fi
|
|
|
|
# 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
|
|
|
|
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
|
|
|
|
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
|
|
exit 1
|
|
fi
|
|
|
|
# apply the new ipsets
|
|
if [ ${ENABLE_IPSET} -eq 1 ]
|
|
then
|
|
ipsets_apply || exit 1
|
|
fi
|
|
|
|
progress "Fast activating new firewall"
|
|
|
|
initialize_firewall
|
|
|
|
# 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.
|
|
file close 21
|
|
source "${FIREHOL_OUTPUT}" "${@}"
|
|
if [ $? -ne 0 ]
|
|
then
|
|
work_runtime_error=$[work_runtime_error+1]
|
|
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"
|
|
|
|
work_runtime_error=$[work_runtime_error+1]
|
|
|
|
# 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`
|
|
${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 ]
|
|
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]
|
|
|
|
# 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`
|
|
${CAT_CMD} "${FIREHOL_OUTPUT}.fast6" | ${HEAD_CMD} -n $line | ${TAIL_CMD} -n 1 >&2
|
|
echo >&2
|
|
|
|
# the rest of the script will restore the original firewall
|
|
else
|
|
finalize_firewall
|
|
fi
|
|
fi
|
|
fi
|
|
else
|
|
|
|
if [ "${FIREHOL_MODE}" = "DEBUG" ]
|
|
then
|
|
file close 21
|
|
${CAT_CMD} ${FIREHOL_OUTPUT}
|
|
exit 1
|
|
fi
|
|
|
|
# apply the new ipsets
|
|
if [ ${ENABLE_IPSET} -eq 1 ]
|
|
then
|
|
ipsets_apply || exit 1
|
|
fi
|
|
|
|
syslog info "Activating new firewall from ${FIREHOL_CONFIG} (translated to ${FIREHOL_COMMAND_COUNTER} iptables rules)."
|
|
progress "Activating new firewall (${FIREHOL_COMMAND_COUNTER} rules)"
|
|
|
|
initialize_firewall
|
|
|
|
file close 21
|
|
source "${FIREHOL_OUTPUT}" "$@"
|
|
if [ $? -ne 0 ]
|
|
then
|
|
work_runtime_error=$[work_runtime_error+1]
|
|
else
|
|
finalize_firewall
|
|
fi
|
|
fi
|
|
|
|
|
|
if [ ${work_runtime_error} -gt 0 ]
|
|
then
|
|
failure # "Activating new firewall"
|
|
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 "${PROGRAM_FILE} nofast "
|
|
printf >&2 "%q " "${FIREHOL_ORIGINAL_ARGS[@]}"
|
|
printf >&2 "\n"
|
|
fi
|
|
|
|
exit 1
|
|
fi
|
|
success # "Activating new firewall (${FIREHOL_COMMAND_COUNTER} rules)"
|
|
|
|
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 >&2
|
|
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 >&2 "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
|
|
|
|
|
|
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
|
|
# last, keep a copy of the firewall we activated, on disk
|
|
|
|
file close 20
|
|
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
|
|
|
|
|
|
# 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 >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
if [ $ENABLE_IPV6 -eq 1 -a -z "${FIREHOL_AUTOSAVE6}" ]
|
|
then
|
|
error "Cannot find where to save ip6tables file. Please set FIREHOL_AUTOSAVE6."
|
|
echo >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [ $ENABLE_IPV4 -eq 1 ]
|
|
then
|
|
progress "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 # "Saving firewall to '${FIREHOL_AUTOSAVE}'"
|
|
exit 1
|
|
fi
|
|
|
|
syslog info "New firewall saved to '${FIREHOL_AUTOSAVE}'."
|
|
success # "Saving firewall to '${FIREHOL_AUTOSAVE}'"
|
|
fi
|
|
|
|
if [ $ENABLE_IPV6 -eq 1 ]
|
|
then
|
|
progress "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 # "Saving IPv6 firewall to '${FIREHOL_AUTOSAVE6}'"
|
|
exit 1
|
|
fi
|
|
|
|
syslog info "New IPv6 firewall saved to '${FIREHOL_AUTOSAVE6}'."
|
|
success # "Saving IPv6 firewall to '${FIREHOL_AUTOSAVE6}'"
|
|
fi
|
|
|
|
exit 0
|
|
fi
|