Commit fab0e69a authored by Branko Mikić's avatar Branko Mikić
Browse files

No update in a long time now they come all at once.

~ Warnings, errors and about are now printed to stderr instead stdout to keep
  the rules files clean.
~ Dry-run added via argument -d which will print all iptables commands instead
  of executing them.
~ RESET function can now reset the firewall with an optional default policy
  argument like ACCEPT, DROP and REJECT.
~ ALLOW_DHCP_CLIENT function and it's rules were completely revised. Since the
  new code can handle client and server modes the function has been renamed to
  ALLOW_DHCP. DHCP is hard to track in the filter chains therefore it's now
  handled in the magle table marking packets on interfaces configured by the
  ALLOW_DHCP function. The marked packets are then finally accepted in the
  filter chains. Further it allow implcit IPv6 link local addresses for the
  dhcp ports only. If you want full link local access you still need the
  ALLOW_LINK_LOCAL function.
~ The REMOVE_RULES functions has been renamed to REMOVE.
~ NEW! Now the log facility used can be selected with LOG and NFLOG argument.
  Be aware that log modes can't be mixed when additional parts of the ruleset
  are executed at later time.
~ setupEnv() function removed and it's code now resides in the main code. It's
  only used once.
~ getLinkID() function revised to avoid fails under strange bash conditions.
~ obtainNetPrefix() reimplemented. The old version had different problems. Some
  minor bash errors fixed which could ocur in different scenarios.
~ NEW! probeChains() can now handle chains from different tables. When the first
  argument starts with -t TABLENAME followed by chain names to probe.
~ Limiter chain revised into two stages. The first is the usual rate limiting
  eg. for people bashing on the ssh port. The second stage is triggered when
  there are IP adresses intensifying attacks which now gets blocked for longer
  periods.
~ BLOCK chain removed and block rules placed directly into the corresponding
  chains.
~ LOCAL chain heavily revised and is now the main chain for internal interfaces.
~ ALLOW_SERVICE_DISCOVERY is still available but considered obsolete. It was to
  tedious to handle any multicast traffic like mDNS, LLMNR, ... etc.
~ NEW! It has been replaced by ALLOW_MULTICAST_ADDRS which uses the 'addrtype'
  feature. Wihtout any optional ports argument it allows any multicast traffic
  but to achieve the same behavior as ALLOW_SERVICE_DISCOVERY it's possible to
  give a list of port arguments.
~ NEW! ALLOW_STATEFUL_PACKETS added to allow fine grained control of the
  stateful firewall mechanism to allow NEW packets out in only RELATED,
  ESTABLISHED packets in. Instead of allowing this for any interface this chain
  can be set for an interface explicitly which is very useful when you have
  multiple WAN interfaces.
~ ALLOW_LINK_LOCAL revised to limit link local traffic only on the interface it
  has been configured for. This similar to the behavior before but now the
  addrtype is additionally checked which ensures that the interface the link
  local traffic is going through is an routeable address on an interface of the
  host.
~ Simple LIST function added. Same like 'iptables --line-numbers -nvL'
~ Some error conditions were replaced by warnings and aren't stopping execution
  anymore. Especially when it's desired to place rules for interfaces which
  aren't available at the time the rule is invoked. Some may be some functions
  like ALLOW_SUBNETS or FORWARD_SUBNET which read IP addresses from the
  interfaces and therefore can only be used when the interface is already active
  but this may change in the future.
parent e03c86cb
......@@ -5,38 +5,43 @@
printAbout()
{
printf "Usage: $0
-4=IPv4|-6=IPv6
-4=IPv4*|-6=IPv6|-d=dry-run
[KERNEL_PARAMS param1,param2,...,paramN]
[PROBE_KERNEL_MODS mod1,mod2,...,modN]
[RESET [default_policy]]
[RESET [default_policy=ACCEPT|DROP|REJECT]]
[BASE_RULE_SET]
[ALLOW_DHCP_CLIENT on_link]
[ALLOW_DHCP on_link]
[ALLOW_SUBNETS on_link]
[ALLOW_LINK_LOCAL on_link]
[ALLOW_MULTICAST_ADDRS ]
[ALLOW_SERVICE_DISCOVERY on_link]
[ALLOW_STATEFUL_PACKETS ip|link]
[ALLOW_TUNNEL mode on_link from_address]
[ALLOW_PORT|ALLOW_SERVICE port[,port,port,...] ip|link [tcp|udp]]
[FORWARD_SUBNET subnet/mask to_link]
[FORWARD_SUBNET_PROTECTIVE subnet/mask to_link]
[FORWARD_SUBNET from_subnet/mask to_link]
[FORWARD_SUBNET_PROTECTIVE from_subnet/mask to_link]
[FORWARD_PORT|FORWARD_ROUTING ip|link port(s) to_address[:to_port] [tcp|udp]]
[MAC_FILTER chain mac_address]
[POSTROUTING_MASQUERADE subnet/mask on_link]
[DEBUG_CHAIN chain_name]
[REMOVE_RULES pattern]
[REMOVE rule_regex_pattern]
[LIST [CHAIN]]
[LOG*|NFLOG]
*=Default if omitted
Written by Branko Mikic in Nov 2ol4
All copyrights reserved. Free use of this software is granted under the terms of the GNU General Public License (GPLv3).
See man page (ipturntables.8) for a detailed documentation.
"
" >&2
exit 5
}
msg()
{
echo ""
echo "${0##*/}: $1"
echo "" >&2
echo "${0##*/}: $1" >&2
}
error()
......@@ -53,42 +58,6 @@ function isInteger() { [[ ${1} == ?(-)+([0-9]) ]]; }
function isFloat() { [[ ${1} == ?(-)@(+([0-9]).*([0-9])|*([0-9]).+([0-9]))?(E?(-|+)+([0-9])) ]]; }
function isNumber() { isNaturalNumber $1 || isInteger $1 || isFloat $1; }
###
### Command environment setup for either IPv(4|6)
###
### $1: Desired protocol either 4=IPv4 or 6=IPv6
###
setupEnv()
{
[ $1 -ne 4 ] && [ $1 -ne 6 ] && error 1001 "setupEnv(): Mode must be an integer of 4 or 6."
DEPMOD=$(which depmod)
[ -z "$DEPMOD" ] && error 4 "Can't use depmod command. Please install missing command."
MODPROBE=$(which modprobe)
[ -z "$MODPROBE" ] && error 2 "Can't use modprobe command. Please install missing command."
IP=$(which ip)
(( $? != 0 )) && error 3 "Can't use ip command. Please install missing command."
if [ $1 -eq 4 ]; then
IPTABLES=$(which iptables)
(( $? != 0 )) && error 1 "Can't use iptables command. Please install missing command."
fi
if [ $1 -eq 6 ]; then
IPTABLES=$(which ip6tables)
(( $? != 0 )) && error 1 "Can't use ip6tables command. Please install missing command."
ICMPTAG+="v6"
IP+=" -6"
fi
ENVID=$1
ICMPTAG="icmp"
}
###
### $1: A list of kernel module names to probe for.
###
......@@ -165,7 +134,7 @@ checkLink()
}
###
### $1: an interface node (eg: eth0 or eth1, ...)
### $1: an interface (eg: eth0 or eth1, ...)
###
### Returns an unique hex ID derived from the MAC address.
### Virtual network devices return a special hash value since
......@@ -173,11 +142,12 @@ checkLink()
###
getLinkID()
{
local i;
local i; local p;
[ -z "$1" ] && error 1203 "getLinkID(): device argument is mandatory."
if [ $(cat /sys/class/net/$1/addr_len) -gt 0 ]; then
p=$(cat /sys/class/net/$1/addr_len 2>/dev/null) || p=0;
if [ $p -gt 0 ]; then
printf "0x%s" $(cat /sys/class/net/$1/address | sed "s/://g")
else
printf "0x"
......@@ -188,7 +158,7 @@ getLinkID()
}
###
### $1: an interface node (eg: eth0 or eth1, ...)
### $1: an interface (eg: eth0 or eth1, ...)
###
### Returns the MAC address of the desired network interface
###
......@@ -234,8 +204,6 @@ obtainRouteToIP()
###
### $1: Desired IP address to obtain corresponding net prefix
### $2: A link the IP address is configured on
### (Optional for IPv4 | Mandatory for IPv6 !!!)
###
### Returns a subnet prefix
###
......@@ -243,18 +211,29 @@ obtainNetPrefix()
{
local i;
if [ $ENVID -eq 4 ]; then
# obtain ip of this host from given ip
if ! $IP addr show $1 &> /dev/null ; then
i=$(obtainRouteToIP $1)
else
i=$1
fi
printf "%s" $($IP -o route list | grep -oP "(\d+\.){3}+\d+\/\d+(?=.* src $i)")
fi
[ $ENVID -eq 6 ] && printf "%s" $($IP -o route list | grep -oP "${1%::*}::\/\d* (?=dev $2)")
[ $ENVID -eq 4 ] && \
printf "%s" $(ip -4 r l match $1 | grep -v ^default | grep -m1 -oP "^(\d+\.){3}+\d+\/\d+")
[ $ENVID -eq 6 ] && \
printf "%s" $(ip -6 r l match $1 | grep -oP "^[^\s]*")
echo ;
}
#obtainNetPrefixOBSOLETE()
#{
# local i;
#
# if [ $ENVID -eq 4 ]; then
# # obtain ip of this host from given ip
# if ! $IP addr show $1 &> /dev/null ; then
# i=$(obtainRouteToIP $1)
# else
# i=$1
# fi
# printf "%s" $($IP -o route list | grep -oP "(\d+\.){3}+\d+\/\d+(?=.* src $i)")
# fi
# [ $ENVID -eq 6 ] && printf "%s" $($IP -o route list | grep -oP "${1%::*}::\/\d* (?=dev $2)")
#}
###
### $1: a net prefix (eg: 192.168.0.0/16)
###
......@@ -292,7 +271,8 @@ checkIPArgFormat()
[ $ENVID -eq 4 ] && sz="(\d+\.){3}\d+((\:)+(\d)+)*$"
# TODO: check for IPv6 address! Format is untested and likely to fail
[ $ENVID -eq 6 ] && sz="^([\da-fA-F]+:)+(:[\da-fA-F]+)+$"
# [ $ENVID -eq 6 ] && sz="^([\da-fA-F]+:)+(:[\da-fA-F]+)+$"
[ $ENVID -eq 6 ] && sz="^([\da-fA-F]*:+)*:*[\da-fA-F]+$"
echo "$1" | grep -q -P "$sz" 1>/dev/nul 2>/dev/nul
return $?
......@@ -341,7 +321,7 @@ returnRuleCount()
[ -z $1 ] && error 1 "returnRuleCount(): chain argument is mandatory."
let sz=$($IPTABLES --line-numbers -L $1 | grep -P -c ^[0-9]+)
let sz=$($IPTABLES --line-numbers -nL $1 | grep -P -c ^[0-9]+)
return $sz
}
......@@ -419,11 +399,20 @@ deleteRules()
###
probeChains()
{
local sz;
for sz in $@; do
$IPTABLES -nvL $sz 1>/dev/nul 2>/dev/nul
local sz; local t;
[ -z $1 ] && error 10 "probeChain(NULL): chain argument is mandatory."
while (( ${#@} > 0 )); do
[[ $1 =~ -t ]] && {
t=$1; shift;
(( ${#t} <= 2 )) && {
t+=$1; shift;
}
}
$IPTABLES $t -nvL $1 1>/dev/nul 2>/dev/nul
(( $? != 0 )) && return 1
shift;
done
return 0
}
......@@ -440,7 +429,7 @@ allocChain()
$IPTABLES -nvL $1 1>/dev/nul 2>/dev/nul
if (( $? == 0 )); then
$IPTABLES --zero $1
$IPTABLES --zero $1;
$IPTABLES --flush $1
else
$IPTABLES -N $1
......@@ -492,24 +481,75 @@ formatAsHexID() {
(( ${#@} == 0 )) && printAbout
DEPMOD=$(which depmod)
[ -z "$DEPMOD" ] && error 2 "Can't use depmod command. Please install missing command."
MODPROBE=$(which modprobe)
[ -z "$MODPROBE" ] && error 3 "Can't use modprobe command. Please install missing command."
IP=$(which ip)
(( $? != 0 )) && error 4 "Can't use ip command. Please install missing command."
# we are a user of 'netdev' group or root?
$(groups $USER | grep netdev) >/dev/null
[ "$USER" != "root" ] && (( $? == 1 )) && error 46 "Superuser priviledges needed. This script makes heavy use of 'iptables' command which is only allowed to be used by root or a user being a member of 'netdev' group."
# we are not root! and we aren't a user of 'netdev' group either?
[ "$1" == "-6" ] && arg=6
[ "$1" == "-4" ] && arg=4
if [ ! -v arg ]; then
# default environment is IPv4
arg=4
else
shift
[ "$USER" != "root" ] && (( $? == 1 )) && \
error 5 "Superuser priviledges needed. This script makes heavy use of 'iptables' command which is only allowed to be used by root or a user being a member of 'netdev' group."
sz=$(echo "$@" | grep -ioP "(LOG|NFLOG)");
(( $? != 0 )) \
&& { [ -v NFLOG ] && sz="NFLOG" || sz="LOG"; } \
|| sz=${sz^^};
[ "$sz" == LOG ] && TO_LOG="-j LOG --log-prefix";
[ "$sz" == NFLOG ] && TO_LOG="-j NFLOG --nflog-group 32 --nflog-prefix";
[ -z "$TO_LOG" ] && error 6 "Malformed log argument: Must be either 'LOG' or 'NFLOG'.";
printf "# iptables log statement: '%s'\n" "$TO_LOG";
sz=$(echo "$@" | grep -oP "\-(4|6)+")
sz=${sz//-/}
(( ${#sz} == 0 )) && { sz=4; printf >&2 "WARNING! Protocol version not specified. Using: IPv%s\n" $sz; };
(( ${#sz} != 1 )) && error 7 "Malformed protocol version. Specify either -4 or -6 once!";
if [ $sz -eq 4 ]; then
IPTABLES=$(which iptables)
(( $? != 0 )) && error 8 "Can't use iptables command. Please install missing command."
fi
if [ $sz -eq 6 ]; then
IP+=" -6"
IPTABLES=$(which ip6tables)
(( $? != 0 )) && error 9 "Can't use ip6tables command. Please install missing command."
fi
ENVID=$sz
sz=$(echo "$@" | grep -oP "\-(d|D)+")
sz=${sz//-/}
if [ "${sz^^}" == 'D' ]; then
unset sz;
[ $ENVID -eq 6 ] && sz=$ENVID
IPTABLES="echo -e \nip${sz}tables"
printf "Using dry-run, executing echo instead of iptables command.\n"
fi
setupEnv $arg
# define dhcp ports depending on wether 4 or 6 is used
[ $ENVID -eq 4 ] && {
LLsubnet="169.254.0.0/16";
dhcp_client=68; dhcp_server=67;
}
[ $ENVID -eq 6 ] && {
LLsubnet="fe80::/10";
dhcp_client=546; dhcp_server=547;
}
DHCP_CLIENT_MARK=1;
DHCP_SERVER_MARK=2;
((DHCP_BOTH_MARK = DHCP_CLIENT_MARK + DHCP_SERVER_MARK ));
while (( ${#@} >= 1 )); do
arg=${1^^}
shift
case $arg in
-4|-6|-d|-D|LOG|NFLOG)
# simply ignore
;;
KERNEL_PARAMS)
printf "#\n# /proc/sys/net/ipv$ENVID:\n"
for sz in $(echo $1 | sed "s/,/ /g"); do
......@@ -549,6 +589,8 @@ formatAsHexID() {
$IPTABLES -P FORWARD $sz
# delete all user-specified chains
$IPTABLES -X
$IPTABLES -t nat -X
$IPTABLES -t mangle -X
# reset all counters
$IPTABLES -Z
......@@ -565,43 +607,73 @@ formatAsHexID() {
$IPTABLES -P FORWARD DROP
$IPTABLES -P OUTPUT DROP
# create a LOG & DROP chain
BLOCK_INVALID=1
BLOCK_LIMITER=2
BLOCK_ANTI_FLOOD=4
$IPTABLES -N BLOCK
$IPTABLES -A BLOCK --match hashlimit --hashlimit-name DROP_SILENTLY \
--hashlimit-above 3/min -j DROP
$IPTABLES -A BLOCK -m state --state INVALID -j MARK --set-mark $BLOCK_INVALID
$IPTABLES -A BLOCK -m mark --mark $BLOCK_LIMITER -j LOG --log-prefix "[BLOCK] (LIMITER) "
$IPTABLES -A BLOCK -m mark --mark $BLOCK_INVALID -j LOG --log-prefix "[BLOCK] (INVALID) "
$IPTABLES -A BLOCK -m mark --mark $BLOCK_ANTI_FLOOD -j LOG --log-prefix "[BLOCK] (ANTIFLOOD) "
$IPTABLES -A BLOCK -m mark --mark 0 -j LOG --log-prefix "[BLOCK] "
$IPTABLES -A BLOCK -j DROP
# DHCP
$IPTABLES -t mangle -N DHCP
$IPTABLES -t mangle -A DHCP -p udp \
-m addrtype --src-type LOCAL --limit-iface-out \
-m udp --sport $dhcp_client --dport $dhcp_server \
-m mark --mark $DHCP_CLIENT_MARK/$DHCP_CLIENT_MARK \
-j MARK --set-mark 0xff \
-m comment --comment "sending request from local interface";
$IPTABLES -t mangle -A DHCP -p udp \
-m udp --sport $dhcp_server --dport $dhcp_client \
-m mark ! --mark 0/$DHCP_BOTH_MARK \
-j MARK --set-mark 0xff \
-m comment --comment "request/discover/reply from server";
[ $ENVID == "6" ] && {
$IPTABLES -t mangle -A DHCP -p udp \
-s $LLsubnet \
-m addrtype --dst-type MULTICAST \
-m udp --sport $dhcp_client --dport $dhcp_server \
-m mark --mark $DHCP_SERVER_MARK/$DHCP_SERVER_MARK \
-j MARK --set-mark 0xff \
-m comment --comment "receiving requests via link-local (IPv6 only)";
}
$IPTABLES -t mangle -A DHCP -m mark --mark 0xff -j RETURN;
$IPTABLES -t mangle -A DHCP $TO_LOG "[BLOCK] (DHCP) ";
$IPTABLES -t mangle -A DHCP -j DROP;
# mark incoming dhcp packets very early
$IPTABLES -t mangle -I PREROUTING -p udp -m multiport --port ${dhcp_client},${dhcp_server} \
-j DHCP -m comment --comment "dhcp in"
# mark follow up dhcp packets leaving
$IPTABLES -t mangle -I OUTPUT -p udp -m multiport --port ${dhcp_client},${dhcp_server} \
-j DHCP -m comment --comment "dhcp out"
# for everyone wondering why a DHCP request via broadcast like
# 0.0.0.0.68 > 255.255.255.255.67: BOOTP/DHCP ... is never seen by
# netfilter on any outgoing chain on any table that's because most
# dhcp daemons are using PF_PACKET sockets for the _first_, _initial_
# request which bypasses the whole netfilter.
# To keep things more confusing this doesn't apply for any 'ordinary'
# follow up dhcp packet (only the first one!)
# https://unix.stackexchange.com/questions/447440/ufw-iptables-not-blocking-dhcp-udp-port-67
# create an ANTI-FLOOD protection chain. Maximises the rate of incoming connections.
# Used for protection against SYN or PING_OF_DEATH flooding
$IPTABLES -N ANTI-FLOOD
$IPTABLES -A ANTI-FLOOD -m limit --limit 3/s -j RETURN
$IPTABLES -A ANTI-FLOOD -m mark --mark $BLOCK_ANTI_FLOOD -j BLOCK
$IPTABLES -A ANTI-FLOOD $TO_LOG "[BLOCK] (ANTI-FLOOD) "
$IPTABLES -A ANTI-FLOOD -j DROP
# limits the connection attempts
$IPTABLES -N LIMITER
$IPTABLES -A LIMITER -m state ! --state NEW -j RETURN
$IPTABLES -A LIMITER --match hashlimit --hashlimit-name LIMITER --hashlimit-mode srcip \
--hashlimit-upto 3/hour --hashlimit-burst 3 -j RETURN
$IPTABLES -A LIMITER -j MARK --set-mark $BLOCK_LIMITER
$IPTABLES -A LIMITER -j BLOCK
# Will be used in INPUT, OUTPUT to add allowed subnets on internal links
$IPTABLES -A LIMITER --match hashlimit --hashlimit-name LIMITER \
--hashlimit-mode srcip --hashlimit-upto 3/hour --hashlimit-burst 3 \
--hashlimit-htable-size 256 --hashlimit-htable-max 1024 -j RETURN
$IPTABLES -A LIMITER --match hashlimit --hashlimit-name DROP_SILENTLY \
--hashlimit-mode srcip --hashlimit-srcmask 16 \
--hashlimit-above 16/day -j DROP
$IPTABLES -A LIMITER $TO_LOG "[BLOCK] (LIMITER) "
$IPTABLES -A LIMITER -j DROP
# Will be used in INPUT & OUTPUT to add allowed subnets on local links
$IPTABLES -N LOCAL
$IPTABLES -A LOCAL -m addrtype --dst-type LOCAL -j RETURN
$IPTABLES -A LOCAL -m addrtype --dst-type MULTICAST -j RETURN
[ $ENVID == "4" ] && $IPTABLES -A LOCAL -m addrtype --dst-type BROADCAST -j RETURN
$IPTABLES -A LOCAL -j BLOCK
# When this chain retuns it's either a local, multi- or broadcast packet
# DHCPv4 packets pass wondrously but DHCPv6 needs explicit permission
[ $ENVID == "6" ] && {
$IPTABLES -A LOCAL -p udp -m multiport --port ${dhcp_client},${dhcp_server} -m mark --mark 0xff \
-j ACCEPT -m comment --comment "allow our marked dhcp packets from prerouting";
}
# will be used in INPUT, OUTPUT & FORWARD
# this chain should return always! (don't place DROP rules on specific ICMPs here!)
......@@ -615,9 +687,6 @@ formatAsHexID() {
$IPTABLES -A INPUT -i lo -j ACCEPT
$IPTABLES -A OUTPUT -o lo -j ACCEPT
# stateful packets intiated by ourself coming back
$IPTABLES -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IPv4
if [ $ENVID == "4" ]; then
......@@ -648,7 +717,7 @@ formatAsHexID() {
# https://serverfault.com/questions/410321/debian-ip6tables-rules-setup-for-ipv6/527334
# echo reply allow explicitly on link local
$IPTABLES -A ICMP -p ipv6-icmp -m icmp6 --icmpv6-type 129 -s fe80::/10 -j ACCEPT -m comment --comment "echo-reply allowed on link local"
$IPTABLES -A ICMP -p ipv6-icmp -m icmp6 --icmpv6-type 129 -s $LLsubnet -j ACCEPT -m comment --comment "echo-reply allowed on link local"
# accept harmless ICMP requests
$IPTABLES -A ICMP -p ipv6-icmp -m icmp6 --icmpv6-type 1 -j ACCEPT -m comment --comment "destination unreachable"
$IPTABLES -A ICMP -p ipv6-icmp -m icmp6 --icmpv6-type 2 -j ACCEPT -m comment --comment "packet too big"
......@@ -664,6 +733,8 @@ formatAsHexID() {
$IPTABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 135 -m hl --hl-eq 255 -j ACCEPT -m comment --comment "neighbor solicitation"
$IPTABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 136 -m hl --hl-eq 255 -j ACCEPT -m comment --comment "neighbor advertisement"
$IPTABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type 133 -m hl --hl-eq 255 -j ACCEPT -m comment --comment "multicast listener done"
#TODO: RAs out on specific interface
#ip6tables -I OUTPUT 3 -o ppp0 -p ipv6-icmp -m icmp6 --icmpv6-type 134 -m hl --hl-eq 255 -j ACCEPT -m comment --comment "router advertisement"
$IPTABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type 135 -m hl --hl-eq 255 -j ACCEPT -m comment --comment "neighbor solicitation"
$IPTABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type 136 -m hl --hl-eq 255 -j ACCEPT -m comment --comment "neighbor advertisement"
$IPTABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type 143 -m hl --hl-eq 1 -j ACCEPT -m comment --comment "V2 multicast listener report"
......@@ -674,10 +745,13 @@ formatAsHexID() {
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ for both IPv(4|6)
# only routable subnets allowed
# only routable internal subnets allowed
$IPTABLES -A INPUT -j LOCAL
# only routable subnets allowed
$IPTABLES -A OUTPUT -j LOCAL
$IPTABLES -A INPUT -m state --state INVALID -j BLOCK
$IPTABLES -A INPUT -m state --state INVALID $TO_LOG "[BLOCK] (INVALID) "
$IPTABLES -A INPUT -m state --state INVALID -j DROP
# syn-flood protection
$IPTABLES -A INPUT -p tcp --syn -j ANTI-FLOOD
......@@ -689,133 +763,216 @@ formatAsHexID() {
$IPTABLES -A INPUT -p tcp -m tcp --dport 22 -j LIMITER -m comment --comment "avoid bashing on ssh port"
$IPTABLES -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT -m comment --comment "ssh"
# stateful packets intiated by ourself going out
$IPTABLES -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
# new packets are allowed to go out from anywhere
$IPTABLES -A OUTPUT -m state --state NEW -j ACCEPT
# only routable subnets allowed
$IPTABLES -A OUTPUT -j LOCAL
# ~~~ any of the default chains should end with this!
# user customized rules for traffic coming in & going out
$IPTABLES -A INPUT -j USER-IN -m comment --comment "add your custom INPUT rules in the USER-IN chain!"
$IPTABLES -A OUTPUT -j USER-OUT -m comment --comment "add your custom OUTPUT rules in the USER-OUT chain!"
# stateful packets intiated by ourself coming back
# but only of it's destined to a local address, no (multi|broad)cast!
#$IPTABLES -A INPUT -m addrtype --dst-type LOCAL --limit-iface-in \
# -m state --state RELATED,ESTABLISHED -j ACCEPT
# stateful packets intiated by ourself going out
# but only for traffic coming from a local address.
# (multi|broad)casts need to be allowed explicitly!
$IPTABLES -A OUTPUT -m addrtype --src-type LOCAL --limit-iface-out \
-m state --state RELATED,ESTABLISHED -j ACCEPT
# default log rules
$IPTABLES -A INPUT -m limit --limit 8/min --limit-burst 16 -j LOG --log-prefix "[IN$ENVID-DROP] "
$IPTABLES -A OUTPUT -m limit --limit 8/min --limit-burst 16 -j LOG --log-prefix "[OU$ENVID-DROP] "
$IPTABLES -A FORWARD -m limit --limit 8/min --limit-burst 16 -j LOG --log-prefix "[FW$ENVID-DROP] "
$IPTABLES -A INPUT -m limit --limit 8/min --limit-burst 16 $TO_LOG "[IN$ENVID-DROP] "
$IPTABLES -A OUTPUT -m limit --limit 8/min --limit-burst 16 $TO_LOG "[OU$ENVID-DROP] "
$IPTABLES -A FORWARD -m limit --limit 8/min --limit-burst 16 $TO_LOG "[FW$ENVID-DROP] "
;;
ALLOW_DHCP_CLIENT)
sz=""
checkLink $1
(( $? != 0 )) && sz=" (WARNING! '$1' is currently not available. Maybe invoked later?)"
printf "# allowing DHCP (IPv%s) client requests on '%s'.%s\n" $ENVID $1 "$sz"
ALLOW_DHCP)
probeChains -tmangle DHCP
(( $? != 0 )) && \
error 42 "The 'DHCP' chain is missing. Setup a new base firewall with BASE_RULE_SET first."
ID="ALLOW_DHCP_CLIENT_on_$(getLinkID $1)"
deleteRules USER-IN "$ID"
checkLink $1;
(( $? != 0 )) && \
printf >&2 "WARNING! '$1' is currently not available. Maybe invoked later?\n" ;
if [ $ENVID -eq 4 ]; then
$IPTABLES -A USER-IN -p udp -m udp -i $1 -s 0.0.0.0 --sport 68 -d 255.255.255.255 --dport 67 -j ACCEPT -m comment --comment "$ID"
$IPTABLES -A USER-IN -p udp -m udp -i $1 --sport 67 --dport 68 -j ACCEPT -m comment --comment "$ID (dhcp/bootp)"
fi
# $2 = dhcp mode
unset sz;
if [ $ENVID -eq 6 ]; then
$IPTABLES -A USER-IN -p udp -m udp -i $1 -s fe80::/10 --sport 547 -d fe80::/10 --dport 546 -j ACCEPT -m comment --comment "$ID"
fi
sz=${2,,};
[ -z "$sz" ] && \
error 43 "ALLOW_DHCP expects a dhcp mode (eg. client|server|both)";
shift
[ $sz == "client" ] && sz=$DHCP_CLIENT_MARK;
[ $sz == "server" ] && sz=$DHCP_SERVER_MARK;
[ $sz == "both" ] && sz=$DHCP_BOTH_MARK;
printf "# allowing DHCP (IPv%s) client requests on '%s'.\n" $ENVID $1;
[ $ENVID == 6 ] && \
printf >&2 "ATTENTION! DHCPv6 allows implicit link local traffic on corresponding ports only.\n";
ID="ALLOW_DHCP_$(getLinkID $1)";
deleteRules -t mangle DHCP "$ID";
$IPTABLES -t mangle -I DHCP -i $1 -j MARK --set-mark $sz -m comment --comment "$ID";
$IPTABLES -t mangle -I DHCP -o $1 -j MARK --set-mark $sz -m comment --comment "$ID";
shift; shift;
;;
ALLOW_SUBNETS)
checkLink $1
(( $? != 0 )) && error 41 "ALLOW_SUBNETS expects an interface node argument (eg: eth0)"
probeChains LOCAL
(( $? != 0 )) && error 42 "The 'LOCAL' chain is missing. Setup a new base firewall with BASE_RULE_SET first."
checkLink $1
(( $? != 0 )) && error 41 "ALLOW_SUBNETS expects an interface argument (eg: eth0)"
ID="ALLOW_SUBNETS_$(getLinkID $1)"
deleteRules LOCAL "$ID"
printf "# allowing subnets on $1 link:"
unset p
for addr in $(obtainIPs $ENVID global $1); do
printf " %s" $addr
subnet=$(obtainNetPrefix $addr $1)
# exclude 'manually' configured link-local addresses (for whatever reason)
p=$LLsubnet
for addr in $(obtainIPs $ENVID global $1); do
subnet=$(obtainNetPrefix $addr)
# omit a subnet when it has been processed already!
echo "$p" | grep "$subnet" 1>/dev/nul 2>/dev/nul
(( $? == 0 )) && continue
$IPTABLES -I LOCAL 1 -i $1 -s $subnet -j ACCEPT -m comment --comment "$ID"
printf " %s" $addr
$IPTABLES -I LOCAL 1 -i $1 -s $subnet -d $addr -j ACCEPT -m comment --comment "$ID"
$IPTABLES -I LOCAL 1 -o $1 -s $addr -d $subnet -j ACCEPT -m comment --comment "$ID"
p+=" $subnet"
done
printf " (broadcast)"
$IPTABLES -I LOCAL 1 -i $1 -m pkttype --pkt-type broadcast -j ACCEPT -m comment --comment "$ID (broadcast)"
$IPTABLES -I LOCAL 1 -i $1 -m pkttype --pkt-type broadcast -j ACCEPT -m comment --comment "$ID (broadcast in)"
$IPTABLES -I LOCAL 1 -o $1 -m pkttype --pkt-type broadcast -j ACCEPT -m comment --comment "$ID (broadcast out)"
# not sure about implicitly allowing link local since this is called alllow_subnets
# for IPv6 on this link
if [ $ENVID -eq 6 ]; then
addr=$(obtainIPs $ENVID link $1)
[[ $addr =~ fe80 ]] || error 43 "A link local address is not available for $1."
printf " %s" $addr
$IPTABLES -I LOCAL 1 -o $1 -s $addr -j ACCEPT -m comment --comment "$ID (outbound from link-local)"
fi
# [ $ENVID -eq 6 ] && {
# printf " %s" $LLsubnet;
# $IPTABLES -I LOCAL 1 -o $1 -s $LLsubnet \
# -m addrtype --src-type LOCAL --limit-iface-out \
# -j ACCEPT -m comment --comment "$ID (link-local out)";
# };
echo ""
shift
unset addr; unset subnet; unset p
;;
ALLOW_LINK_LOCAL)
checkLink $1
(( $? != 0 )) && error 41 "ALLOW_LINK_LOCAL expects an interface node argument (eg: eth0)"
ALLOW_STATIC_SUBNETS)
probeChains LOCAL
(( $? != 0 )) && error 42 "The 'LOCAL' chain is missing. Setup a new base firewall with BASE_RULE_SET first."
#TODO: check back if interface has configured the correct subnet (169.254.0.0/16 fe80::/10)
[ -z "$1" ] && error 44 "ALLOW_STATIC_SUBNETS expects an interface argument (eg: eth0)"
[ -z "$2" ] && error 45 "ALLOW_STATIC_SUBNETS expects a comma separated list of subnets to allow (eg: 192.168.0.0/16)"
subnets=${2//,/ };
for s in $subnets; do
checkSubnetArgFormat $s;
(( $? != 0 )) && error 46 "ALLOW_STATIC_SUBNETS can't handle malformed subnet argument: $s";
done;