Mon, 30 Oct 2023 16:24:44 +0100
Version 0.0.27 Fix for igmp protocol, do not use -m option.
#!/bin/bash # --------------------------------------------------------------------------- # Copyright (C) 2013-2023 by Michiel Broek. # Homepage http://www.mbse.eu # Email mbse At mbse dOt eu # # This file is part of mbse-firewall. # # 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, 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; see the file COPYING. If not, write to the Free # Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. # --------------------------------------------------------------------------- MBSEFW_VERSION="0.0.27" # Sanity checks if [ "$(id -u)" != "0" ]; then echo "** You must be root to run this program" exit 1 fi # If possible, log events in /var/log/messages: if [ -f /var/run/syslogd.pid -a -x /usr/bin/logger ]; then LOGGER=/usr/bin/logger else # output to stdout/stderr: LOGGER=/bin/cat fi # IPv6 enabled? USE_IPV6="0" if [ -f /proc/sys/net/ipv6/conf/all/disable_ipv6 ] && [ "$(cat /proc/sys/net/ipv6/conf/all/disable_ipv6)" == "0" ]; then USE_IPV6="1" fi # Find programs IPTABLES=$(which iptables 2>/dev/null) IPTABLES_SAVE=$(which iptables-save 2>/dev/null) IPTABLES_RESTORE=$(which iptables-restore 2>/dev/null) LSMOD=$(which lsmod 2>/dev/null) AWK=$(which awk 2>/dev/null) GREP=$(which grep 2>/dev/null) IPSET=$(which ipset 2>/dev/null) SYSCTL=$(which sysctl 2>/dev/null) NFACCT=$(which nfacct 2>/dev/null) if [ "$USE_IPV6" = "1" ]; then IP6TABLES=$(which ip6tables 2>/dev/null) IP6TABLES_SAVE=$(which ip6tables-save 2>/dev/null) IP6TABLES_RESTORE=$(which ip6tables-restore 2>/dev/null) fi # Load configuration if [ ! -f /etc/mbse-firewall/firewall.conf ]; then echo "** /etc/mbse-firewall/firewall.conf not found, abort" exit 1 fi . /etc/mbse-firewall/firewall.conf # Some defaults, they are replaced when configured in # /etc/mbse-firewall/firewall.conf IF_EXT_AUTO_TO=${IF_EXT_AUTO_TO:=3600} IF_EXT_AUTO_LIMIT=${IF_EXT_AUTO_LIMIT:=5/hour} IF_EXT_AUTO_BURST=${IF_EXT_AUTO_BURST:=10} # --------------------------------------------------------------------------- # # Functions # # --------------------------------------------------------------------------- # Reset iptables back to Slackware default. reset_iptables() { if [ -f /proc/net/ip_tables_names ]; then cat /proc/net/ip_tables_names | while read table; do $IPTABLES -t $table -L -n | while read c chain rest; do if test "X$c" = "XChain" ; then $IPTABLES -t $table -F $chain fi done $IPTABLES -t $table -X done $IPTABLES -P INPUT $1 $IPTABLES -P OUTPUT $1 $IPTABLES -P FORWARD $1 echo "Reset iptables default policy $1" | $LOGGER fi if [ "$USE_IPV6" == "1" ] && [ -f /proc/net/ip6_tables_names ]; then cat /proc/net/ip6_tables_names | while read table; do $IP6TABLES -t $table -L -n | while read c chain rest; do if test "X$c" = "XChain" ; then $IP6TABLES -t $table -F $chain fi done $IP6TABLES -t $table -X done $IP6TABLES -P OUTPUT $1 $IP6TABLES -P INPUT $1 $IP6TABLES -P FORWARD $1 echo "Reset ip6tables default policy $1" | $LOGGER fi # Remove any ipset tables. HOST="$(hostname)" SETS="$(${IPSET} list -n | grep ${HOST})" for MySET in ${SETS}; do $IPSET flush ${MySET} $IPSET destroy ${MySET} echo "Destroyed IPSET table ${MySET}" | $LOGGER done } is_external_if4() { [ "x${IF_EXT}" == "x$1" ] && return 1 return 0 } is_external_if6() { if [ "$USE_IPV6" == "1" ]; then [ "x${IF_EXT6}" == "x$1" ] && return 1 [ "x${IF_EXT}" == "x$1" -a -z "${IF_EXT6}" ] && return 1 fi return 0 } reload_blocklist4() { BLOCKLIST="/etc/mbse-firewall/conf.d/blocklist4.conf" HOST="$(hostname)" if [ -f $BLOCKLIST ]; then echo "Reload $BLOCKLIST" | $LOGGER $IPSET create ${HOST}-new-mbsefw-blk4ip hash:ip counters -exist $IPSET create ${HOST}-new-mbsefw-blk4net hash:net counters -exist $GREP -Ev '^#|^;|^\s*$' $BLOCKLIST | while read L ; do set $L if echo $1 | $GREP -q "/" ; then $IPSET add ${HOST}-new-mbsefw-blk4net $1 -exist else $IPSET add ${HOST}-new-mbsefw-blk4ip $1 -exist fi done $IPSET swap ${HOST}-mbsefw-blk4net ${HOST}-new-mbsefw-blk4net $IPSET flush ${HOST}-new-mbsefw-blk4net $IPSET destroy ${HOST}-new-mbsefw-blk4net $IPSET swap ${HOST}-mbsefw-blk4ip ${HOST}-new-mbsefw-blk4ip $IPSET flush ${HOST}-new-mbsefw-blk4ip $IPSET destroy ${HOST}-new-mbsefw-blk4ip fi } reload_blocklist6() { BLOCKLIST="/etc/mbse-firewall/conf.d/blocklist6.conf" HOST="$(hostname)" if [ -f $BLOCKLIST ]; then echo "Reload $BLOCKLIST" | $LOGGER $IPSET create ${HOST}-new-mbsefw-blk6 hash:net family inet6 counters -exist $GREP -Ev '^#|^;|^\s*$' $BLOCKLIST | while read L ; do set $L ; $IPSET add ${HOST}-new-mbsefw-blk6 $1 -exist done $IPSET swap ${HOST}-mbsefw-blk6 ${HOST}-new-mbsefw-blk6 $IPSET flush ${HOST}-new-mbsefw-blk6 $IPSET destroy ${HOST}-new-mbsefw-blk6 fi } fw_init_nfacct() { NFACCTCONF="/etc/mbse-firewall/conf.d/nfacct.conf" if [ -f $NFACCTCONF ]; then echo "Init netfilter accounting" | $LOGGER $GREP -Ev '^#|^;|^\s*$' $NFACCTCONF | while read L ; do set $L if [ -z "$($NFACCT list | $GREP $1)" ]; then $NFACCT add $1 fi done fi } fw_init_sysctl() { # If we have bridges and don't want iptables to work between # the physical interfaces, turn it off. if [ "$FW_NO_BRIDGE_NF_CALL" = "1" ]; then $SYSCTL -e -q -w net.bridge.bridge-nf-call-arptables=0 $SYSCTL -e -q -w net.bridge.bridge-nf-call-ip6tables=0 $SYSCTL -e -q -w net.bridge.bridge-nf-call-iptables=0 fi # No arp about internal interfaces across the border. if [ "$IF_EXT_IS_BORDER_GW" = "1" ]; then $SYSCTL -q -w net.ipv4.conf.${IF_EXT}.arp_ignore=1 $SYSCTL -q -w net.ipv4.conf.${IF_EXT}.arp_announce=1 fi } fw_start_init() { echo "Init new firewall" | $LOGGER BLOCKLIST="/etc/mbse-firewall/conf.d/blocklist4.conf" HOST="$(hostname)" if [ -f $BLOCKLIST -a -n "$IF_EXT" ]; then echo " Install $BLOCKLIST" | $LOGGER $IPSET create ${HOST}-mbsefw-blk4ip hash:ip counters -exist $IPSET create ${HOST}-mbsefw-blk4net hash:net counters -exist $IPTABLES -A INPUT -i $IF_EXT -m state --state NEW -m set --match-set ${HOST}-mbsefw-blk4ip src -j DROP $IPTABLES -A INPUT -i $IF_EXT -m state --state NEW -m set --match-set ${HOST}-mbsefw-blk4net src -j DROP if [ "$FW_FORWARD" = "1" ]; then $IPTABLES -A FORWARD -i $IF_EXT -m state --state NEW -m set --match-set ${HOST}-mbsefw-blk4ip src -j DROP $IPTABLES -A FORWARD -i $IF_EXT -m state --state NEW -m set --match-set ${HOST}-mbsefw-blk4net src -j DROP fi $GREP -Ev '^#|^;|^\s*$' $BLOCKLIST | while read L ; do set $L if echo $1 | $GREP -q "/" ; then $IPSET add ${HOST}-mbsefw-blk4net $1 -exist else $IPSET add ${HOST}-mbsefw-blk4ip $1 -exist fi done echo -n "." fi BLOCKLIST="/etc/mbse-firewall/conf.d/blocklist6.conf" if [ -f $BLOCKLIST ]; then echo " Install $BLOCKLIST" | $LOGGER $IPSET create ${HOST}-mbsefw-blk6 hash:net family inet6 counters -exist if [ -n "$IF_EXT6" ]; then IF6=$IF_EXT6 else IF6=$IF_EXT fi $IP6TABLES -A INPUT -i $IF6 -m state --state NEW -m set --match-set ${HOST}-mbsefw-blk6 src -j DROP if [ "$FW_FORWARD" = "1" ]; then $IP6TABLES -A FORWARD -i $IF6 -m state --state NEW -m set --match-set ${HOST}-mbsefw-blk6 src -j DROP fi $GREP -Ev '^#|^;|^\s*$' $BLOCKLIST | while read L ; do set $L $IPSET add ${HOST}-mbsefw-blk6 $1 -exist done echo -n "." fi # If we use the global blocktables. if [ "$IF_EXT_GLOBAL_BLOCK" == "1" ]; then $IPSET create global-blk4 hash:ip counters -exist $IPTABLES -A INPUT -i $IF_EXT -m set --match-set global-blk4 src -j DROP if [ "$FW_FORWARD" = "1" ]; then $IPTABLES -A FORWARD -i $IF_EXT -m set --match-set global-blk4 src -j DROP fi if [ "$USE_IPV6" == "1" ]; then if [ -n "$IF_EXT6" ]; then IF6=$IF_EXT6 else IF6=$IF_EXT fi $IPSET create global-blk6 hash:net family inet6 counters -exist $IP6TABLES -A INPUT -i $IF6 -m set --match-set global-blk6 src -j DROP if [ "$FW_FORWARD" = "1" ]; then $IP6TABLES -A FORWARD -i $IF6 -m set --match-set global-blk6 src -j DROP fi fi echo -n "." fi fw_init_nfacct echo -n "." # accept established and related connections $IPTABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT $IPTABLES -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT [ "$FW_FORWARD" = "1" ] && $IPTABLES -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT if [ "$USE_IPV6" == "1" ]; then $IP6TABLES -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT $IP6TABLES -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT [ "$FW_FORWARD" = "1" ] && $IP6TABLES -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT fi # drop packets that do not match any valid state. This also blocks invalid # flag combinations that are used by portscans. $IPTABLES -A OUTPUT -m state --state INVALID -j DROP $IPTABLES -A INPUT -m state --state INVALID -j DROP [ "$FW_FORWARD" = "1" ] && $IPTABLES -A FORWARD -m state --state INVALID -j DROP if [ "$USE_IPV6" == "1" ]; then $IP6TABLES -A OUTPUT -m state --state INVALID -j DROP $IP6TABLES -A INPUT -m state --state INVALID -j DROP [ "$FW_FORWARD" = "1" ] && $IP6TABLES -A FORWARD -m state --state INVALID -j DROP fi # Allow everything on the loopback interface $IPTABLES -A INPUT -i lo -j ACCEPT $IPTABLES -A OUTPUT -o lo -j ACCEPT if [ "$USE_IPV6" == "1" ]; then $IP6TABLES -A INPUT -i lo -j ACCEPT $IP6TABLES -A OUTPUT -o lo -j ACCEPT fi # Anti spoofing on the external interface. Methods since the 3.3 kernel! if [ -n "$IF_EXT" ]; then # was 1, now 2 for IPTV. for f in $(ls /proc/sys/net/ipv4/conf/*/rp_filter); do echo 2 > $f done $IPTABLES -A PREROUTING -t raw -i $IF_EXT -m rpfilter --invert -j DROP if [ "$USE_IPV6" == "1" ]; then if [ -n "$IF_EXT6" ]; then $IP6TABLES -A PREROUTING -t raw -i $IF_EXT6 -m rpfilter --invert -j DROP else $IP6TABLES -A PREROUTING -t raw -i $IF_EXT -m rpfilter --invert -j DROP fi fi # Manual anti spoofing on the interfaces is configured using the # interfaces configuration and only if the system is a router. fi # IPv4 ssh backdoor if [ -n "$IPV4_BACKDOOR_SSH" ]; then $IPTABLES -A INPUT -p tcp -m tcp -s $IPV4_BACKDOOR_SSH --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT $IPTABLES -A OUTPUT -p tcp -m tcp -d $IPV4_BACKDOOR_SSH --sport 22 -m state --state ESTABLISHED,RELATED -j ACCEPT fi # IPv6 ssh backdoor if [ "$USE_IPV6" == "1" ] && [ -n "$IPV6_BACKDOOR_SSH" ]; then $IP6TABLES -A INPUT -p tcp -m tcp -s $IPV6_BACKDOOR_SSH --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT $IP6TABLES -A OUTPUT -p tcp -m tcp -d $IPV6_BACKDOOR_SSH --sport 22 -m state --state ESTABLISHED,RELATED -j ACCEPT fi # Usefull ICMPv4 $IPTABLES -A INPUT -p icmp -m icmp --icmp-type 3 -m hashlimit --hashlimit 15/second --hashlimit-mode srcip --hashlimit-name icmp -j ACCEPT $IPTABLES -A INPUT -p icmp -m icmp --icmp-type 0/0 -m hashlimit --hashlimit 15/second --hashlimit-mode srcip --hashlimit-name icmp -j ACCEPT $IPTABLES -A INPUT -p icmp -m icmp --icmp-type 8/0 -m hashlimit --hashlimit 15/second --hashlimit-mode srcip --hashlimit-name icmp -j ACCEPT $IPTABLES -A INPUT -p icmp -m icmp --icmp-type 11/0 -m hashlimit --hashlimit 15/second --hashlimit-mode srcip --hashlimit-name icmp -j ACCEPT $IPTABLES -A INPUT -p icmp -m icmp --icmp-type 11/1 -m hashlimit --hashlimit 15/second --hashlimit-mode srcip --hashlimit-name icmp -j ACCEPT $IPTABLES -A INPUT -p icmp -m limit --limit 10/minute -j "${FW_LOGDEST[@]}" "DENY=ICMPv4_INPUT " $IPTABLES -A INPUT -p icmp -j DROP $IPTABLES -A OUTPUT -p icmp -m icmp --icmp-type 3 -j ACCEPT $IPTABLES -A OUTPUT -p icmp -m icmp --icmp-type 0/0 -j ACCEPT $IPTABLES -A OUTPUT -p icmp -m icmp --icmp-type 8/0 -j ACCEPT $IPTABLES -A OUTPUT -p icmp -m icmp --icmp-type 11/0 -j ACCEPT $IPTABLES -A OUTPUT -p icmp -m icmp --icmp-type 11/1 -j ACCEPT $IPTABLES -A OUTPUT -p icmp -m limit --limit 10/minute -j "${FW_LOGDEST[@]}" "DENY=ICMPv4_OUTPUT " $IPTABLES -A OUTPUT -p icmp -j DROP if [ "$FW_FORWARD" = "1" ]; then $IPTABLES -A FORWARD -p icmp -m icmp --icmp-type 3 -m hashlimit --hashlimit 15/second --hashlimit-mode srcip --hashlimit-name icmp -j ACCEPT $IPTABLES -A FORWARD -p icmp -m icmp --icmp-type 0/0 -m hashlimit --hashlimit 15/second --hashlimit-mode srcip --hashlimit-name icmp -j ACCEPT $IPTABLES -A FORWARD -p icmp -m icmp --icmp-type 8/0 -m hashlimit --hashlimit 15/second --hashlimit-mode srcip --hashlimit-name icmp -j ACCEPT $IPTABLES -A FORWARD -p icmp -m icmp --icmp-type 11/0 -m hashlimit --hashlimit 15/second --hashlimit-mode srcip --hashlimit-name icmp -j ACCEPT $IPTABLES -A FORWARD -p icmp -m icmp --icmp-type 11/1 -m hashlimit --hashlimit 15/second --hashlimit-mode srcip --hashlimit-name icmp -j ACCEPT $IPTABLES -A FORWARD -p icmp -m limit --limit 10/minute -j "${FW_LOGDEST[@]}" "DENY=ICMPv4_FORWARD " $IPTABLES -A FORWARD -p icmp -j DROP fi # If this system has enabled IPv6 ... if [ "$USE_IPV6" == "1" ]; then # ICMPv6 $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type destination-unreachable -j ACCEPT $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type packet-too-big -j ACCEPT $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type time-exceeded -j ACCEPT $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type parameter-problem -j ACCEPT $IP6TABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type destination-unreachable -j ACCEPT $IP6TABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type packet-too-big -j ACCEPT $IP6TABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type time-exceeded -j ACCEPT $IP6TABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type parameter-problem -j ACCEPT if [ "$FW_FORWARD" = "1" ]; then $IP6TABLES -A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type destination-unreachable -j ACCEPT $IP6TABLES -A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type packet-too-big -j ACCEPT $IP6TABLES -A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type time-exceeded -j ACCEPT $IP6TABLES -A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type parameter-problem -j ACCEPT fi # Rate limited icmpv6 $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type echo-request -m limit --limit 15/second -j ACCEPT $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type echo-reply -m limit --limit 15/second -j ACCEPT $IP6TABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type echo-request -j ACCEPT $IP6TABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type echo-reply -j ACCEPT if [ "$FW_FORWARD" = "1" ]; then $IP6TABLES -A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type echo-request -m limit --limit 15/second -j ACCEPT $IP6TABLES -A FORWARD -p ipv6-icmp -m icmp6 --icmpv6-type echo-reply -m limit --limit 15/second -j ACCEPT fi if [ -n "$IF_EXT6" -a "$IF_EXT_IS_BORDER_GW" = "1" ]; then $IP6TABLES -A INPUT -o $IF_EXT6 -p ipv6-icmp -d ff00::/8 -m icmp6 --icmpv6-type 132 -j DROP $IP6TABLES -A OUTPUT -o $IF_EXT6 -p ipv6-icmp -d ff00::/8 -m icmp6 --icmpv6-type 132 -j DROP elif [ -n "$IF_EXT" -a "$IF_EXT_IS_BORDER_GW" = "1" ]; then $IP6TABLES -A INPUT -o $IF_EXT -p ipv6-icmp -d ff00::/8 -m icmp6 --icmpv6-type 132 -j DROP $IP6TABLES -A OUTPUT -o $IF_EXT -p ipv6-icmp -d ff00::/8 -m icmp6 --icmpv6-type 132 -j DROP fi $IP6TABLES -A INPUT -p ipv6-icmp -d ff00::/8 -m icmp6 --icmpv6-type 132 -j ACCEPT $IP6TABLES -A OUTPUT -p ipv6-icmp -d ff00::/8 -m icmp6 --icmpv6-type 132 -j ACCEPT # rules to permit IPv6 Neighbor discovery $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type router-solicitation -m hl --hl-eq 255 -j ACCEPT $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type router-solicitation -j DROP # Silent drop HOPLIMIT <> 255 $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type neighbour-solicitation -m hl --hl-eq 255 -j ACCEPT $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type neighbour-solicitation -j DROP # Silent drop HOPLIMIT <> 255 $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type neighbour-advertisement -m hl --hl-eq 255 -j ACCEPT $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type neighbour-advertisement -j DROP # Silent drop HOPLIMIT <> 255 $IP6TABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type router-solicitation -m hl --hl-eq 255 -j ACCEPT $IP6TABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type router-advertisement -m hl --hl-eq 255 -j ACCEPT $IP6TABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type neighbour-solicitation -m hl --hl-eq 255 -j ACCEPT $IP6TABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type neighbour-advertisement -m hl --hl-eq 255 -j ACCEPT # Allow inverse neighbour discovery solicitation (141) / advertisement (142) $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 141 -m hl --hl-eq 255 -j ACCEPT $IP6TABLES -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type 142 -m hl --hl-eq 255 -j ACCEPT $IP6TABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type 141 -m hl --hl-eq 255 -j ACCEPT $IP6TABLES -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type 142 -m hl --hl-eq 255 -j ACCEPT # MLD messages. DROP on external interface, but ACCEPT on others. if [ -n "$IF_EXT6" -a "$IF_EXT_IS_BORDER_GW" = "1" ]; then $IP6TABLES -A OUTPUT -o $IF_EXT6 -p ipv6-icmp -d ff00::/8 -m icmp6 --icmpv6-type 143 -j DROP elif [ -n "$IF_EXT" -a "$IF_EXT_IS_BORDER_GW" = "1" ]; then $IP6TABLES -A OUTPUT -o $IF_EXT -p ipv6-icmp -d ff00::/8 -m icmp6 --icmpv6-type 143 -j DROP fi $IP6TABLES -A OUTPUT -p ipv6-icmp -d ff00::/8 -m icmp6 --icmpv6-type 143 -j ACCEPT # Drop unmatched icmpv6 but log them so we can debug $IP6TABLES -A INPUT -p ipv6-icmp -m limit --limit 10/minute -j "${FW_LOGDEST[@]}" "DENY=ICMPv6_INPUT " $IP6TABLES -A INPUT -p ipv6-icmp -j DROP $IP6TABLES -A OUTPUT -p ipv6-icmp -m limit --limit 10/minute -j "${FW_LOGDEST[@]}" "DENY=ICMPv6_OUTPUT " $IP6TABLES -A OUTPUT -p ipv6-icmp -j DROP [ "$FW_FORWARD" = "1" ] && { $IP6TABLES -A FORWARD -p ipv6-icmp -m limit --limit 10/minute -j "${FW_LOGDEST[@]}" "DENY=ICMPv6_FORWARD " $IP6TABLES -A FORWARD -p ipv6-icmp -j DROP } fi if [ "$CLAMP_MSS_TO_PMTU" = "1" ]; then # ================ Tables 'filter' and 'mangle', automatic rules [ "$FW_FORWARD" = "1" ] && { $IPTABLES -t filter -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu $IPTABLES -t mangle -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu if [ "$USE_IPV6" == "1" ]; then $IP6TABLES -t filter -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu $IP6TABLES -t mangle -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu fi } fi # Filter all packets that have RH0 header if [ "$USE_IPV6" == "1" ]; then # Filter all packets that have RH0 header $IP6TABLES -A OUTPUT -m rt --rt-type 0 -j DROP $IP6TABLES -A INPUT -m rt --rt-type 0 -j DROP [ "$FW_FORWARD" = "1" ] && $IP6TABLES -A FORWARD -m rt --rt-type 0 -j DROP # Allow Link-Local sddresses $IP6TABLES -A INPUT -s fe80::/10 -j ACCEPT $IP6TABLES -A OUTPUT -s fe80::/10 -j ACCEPT # Allow Multicast $IP6TABLES -A INPUT -d ff00::/8 -j ACCEPT $IP6TABLES -A OUTPUT -d ff00::/8 -j ACCEPT fi # Traceroute if [ "$FW_TRACEROUTE" = "1" ]; then $IPTABLES -A OUTPUT -p udp -m udp --dport 33434:33524 -m state --state NEW -j ACCEPT $IPTABLES -A INPUT -p udp -m udp --dport 33434:33524 -m state --state NEW -j ACCEPT [ "$FW_FORWARD" = "1" ] && $IPTABLES -A FORWARD -p udp -m udp --dport 33434:33524 -m state --state NEW -j ACCEPT if [ "$USE_IPV6" == "1" ]; then $IP6TABLES -A OUTPUT -p udp -m udp --dport 33434:33524 -m state --state NEW -j ACCEPT $IP6TABLES -A INPUT -p udp -m udp --dport 33434:33524 -m state --state NEW -j ACCEPT [ "$FW_FORWARD" = "1" ] && $IP6TABLES -A FORWARD -p udp -m udp --dport 33434:33524 -m state --state NEW -j ACCEPT fi fi echo -n "." } fw_start_interface_chain() { local multi iodir IFS=\; INTF=$1 FCHAIN=$2 NCHAIN=$3 SCHAIN=$4 CONFFILE="/etc/mbse-firewall/conf.d/${INTF}-${FCHAIN}.conf" is_external_if4 $1 EXTERN4=$? is_external_if6 $1 EXTERN6=$? HOST="$(hostname)" # TODO: use subchains, but we need to do 2 passes on the config # files to make it work. # Are there rules for this chain? if [ -f $CONFFILE ]; then echo " Start chain ${NCHAIN} on interface ${INTF} is external ipv4: ${EXTERN4} ipv6: ${EXTERN6}" | $LOGGER # Install auto blacklisting if set for this interface and this is the # INPUT or FORWARD chain. In /etc/mbse-firewall/firewall.conf set then # IF_EXT_AUTO_TO value for the block timeout. Default is 3600 seconds. # See the end of this function for the actual test. if [ "$NCHAIN" = "INPUT" -o "$NCHAIN" = "FORWARD" ]; then if [ "$IF_EXT_AUTO_BLOCK" = "1" ]; then if [ "$EXTERN4" = "1" ]; then echo " Installing IPv4 auto blacklisting on interface ${INTF}" | $LOGGER $IPSET create ${HOST}-mbsefw-auto4 hash:ip timeout $IF_EXT_AUTO_TO counters -exist $IPTABLES -I $NCHAIN -m set --match-set ${HOST}-mbsefw-auto4 src -j DROP fi if [ "$EXTERN6" = "1" ]; then echo " Installing IPv6 auto blacklisting on interface ${INTF}" | $LOGGER $IPSET create ${HOST}-mbsefw-auto6 hash:ip family inet6 timeout $IF_EXT_AUTO_TO counters -exist $IP6TABLES -I $NCHAIN -m set --match-set ${HOST}-mbsefw-auto6 src -j DROP fi fi fi # Adjust for the direction of the chain if [ "$NCHAIN" = "OUTPUT" -o "$NCHAIN" = "POSTROUTING" ]; then iodir="-o" else iodir="-i" fi # Read the configuration $GREP -Ev '^#|^\s*$' $CONFFILE | while read L ; do set $L # Build command if [ "$1" = "6" ]; then CMD=$IP6TABLES else CMD=$IPTABLES fi if [ -n "$2" ]; then args=("-t" "$2" "-A" "$NCHAIN" "$iodir" "${INTF}") else args=("-A" "$NCHAIN" "$iodir" "${INTF}") fi # Protocol [ -n "$3" ] && { if [ "$3" = "igmp" ]; then args+=("-p" "$3") else args+=("-p" "$3" "-m" "$3") fi } # Test for multiport multi=0 [ -n "$5$7" ] && { [[ $5$7 == *","* ]] && multi=1 [[ $5$7 == *":"* ]] && multi=1 } [ "$multi" = "1" ] && args+=("-m" "multiport") # Source address [ -n "$4" ] && args+=("-s" "$4") # Source port(s) [ -n "$5" ] && { multi=0 [[ $5 == *","* ]] && multi=1 [[ $5 == *":"* ]] && multi=1 if [ "$multi" = "1" ]; then args+=("--sports" "$5") else args+=("--sport" "$5") fi } # Destination address [ -n "$6" ] && args+=("-d" "$6") # Destination port(s) [ -n "$7" ] && { multi=0 [[ $7 == *","* ]] && multi=1 [[ $7 == *":"* ]] && multi=1 if [ "$multi" = "1" ]; then args+=("--dports" "$7") else args+=("--dport" "$7") fi } # Rule options [ -n "$9" ] && { IFS=' ' for arg in $9; do args+=("$arg") done IFS=\; } # Rule action [ -n "$8" ] && { IFS=' ' args+=("-j") for arg in $8; do args+=("$arg") done IFS=\; } $CMD "${args[@]}" rc=$? echo " " $CMD "${args[@]}" | $LOGGER if [ $rc -ne 0 ]; then echo "Error in $CONFFILE" | $LOGGER fi done # In PREROUTING or POSTROUTING chains we are done here. if [ "$NCHAIN" = "PREROUTING" -o "$NCHAIN" = "POSTROUTING" ]; then return fi # Ignore timing problems with old connections $IPTABLES -A $NCHAIN $iodir ${INTF} -p tcp -m tcp --tcp-flags ACK,PSH ACK,PSH -j DROP [ "$USE_IPV6" = "1" ] && $IP6TABLES -A $NCHAIN $iodir ${INTF} -p tcp -m tcp --tcp-flags ACK,PSH ACK,PSH -j DROP # Install the final autoblock rule if this is the INPUT or FORWARD chain. # We allow upto 5 probes per minute or a burst of 10 probes. This should be # a good balance to catch the real bad guys. Note that until the IP is # blocked these systems are logged using the rule below this one. if [ "$IF_EXT_AUTO_BLOCK" = "1" -a "$NCHAIN" != "OUTPUT" ]; then if [ "${EXTERN4}" = "1" ]; then # First, ignore these. Can happen after a temporary network problem. $IPTABLES -A $NCHAIN $iodir ${INTF} -p tcp -m tcp --tcp-flags ALL ACK -j DROP # Now the real rule. $IPTABLES -A $NCHAIN $iodir ${INTF} \ -m hashlimit --hashlimit-above ${IF_EXT_AUTO_LIMIT} --hashlimit-burst ${IF_EXT_AUTO_BURST} --hashlimit-mode srcip --hashlimit-name hash-auto4 \ -j SET --add-set ${HOST}-mbsefw-auto4 src fi if [ "${EXTERN6}" = "1" ]; then # First, ignore these. Can happen after a temporary network problem. $IP6TABLES -A $NCHAIN $iodir ${INTF} -p tcp -m tcp --tcp-flags ALL ACK -j DROP # Now the real rule. $IP6TABLES -A $NCHAIN $iodir ${INTF} \ -m hashlimit --hashlimit-above ${IF_EXT_AUTO_LIMIT} --hashlimit-burst ${IF_EXT_AUTO_BURST} --hashlimit-mode srcip --hashlimit-name hash-auto6 \ -j SET --add-set ${HOST}-mbsefw-auto6 src fi fi # deny and log the rest $IPTABLES -A $NCHAIN $iodir ${INTF} -m limit --limit 10/minute -j "${FW_LOGDEST[@]}" "DENY=$NCHAIN " [ "$USE_IPV6" == "1" ] && $IP6TABLES -A $NCHAIN $iodir ${INTF} -m limit --limit 10/minute -j "${FW_LOGDEST[@]}" "DENY=$NCHAIN " $IPTABLES -A $NCHAIN $iodir ${INTF} -j DROP [ "$USE_IPV6" == "1" ] && $IP6TABLES -A $NCHAIN $iodir ${INTF} -j DROP echo -n "." fi } fw_start_interface() { fw_start_interface_chain $1 "prerouting" "PREROUTING" "pre" fw_start_interface_chain $1 "input" "INPUT" "in" fw_start_interface_chain $1 "output" "OUTPUT" "out" fw_start_interface_chain $1 "forward" "FORWARD" "fwd" fw_start_interface_chain $1 "postrouting" "POSTROUTING" "post" } fw_start_main() { i=0 [ -n "$IF_EXT" ] && fw_start_interface "$IF_EXT" [ -n "$IF_EXT6" ] && fw_start_interface "$IF_EXT6" while [ $i -lt 50 ]; do [ -z "${IF_TRUNK[$i]}" ] && break fw_start_interface "${IF_TRUNK[$i]}" i=$(($i+1)) done } fw_start_final() { # Deny and log everything else $IPTABLES -N FINAL_RULE $IPTABLES -A OUTPUT -j FINAL_RULE $IPTABLES -A INPUT -j FINAL_RULE [ "$FW_FORWARD" = "1" ] && $IPTABLES -A FORWARD -j FINAL_RULE $IPTABLES -A FINAL_RULE -m limit --limit 10/minute -j "${FW_LOGDEST[@]}" "DENY=999 " $IPTABLES -A FINAL_RULE -j DROP if [ "$USE_IPV6" = "1" ]; then $IP6TABLES -N FINAL_RULE $IP6TABLES -A OUTPUT -j FINAL_RULE $IP6TABLES -A INPUT -j FINAL_RULE [ "$FW_FORWARD" = "1" ] && $IP6TABLES -A FORWARD -j FINAL_RULE $IP6TABLES -A FINAL_RULE -m limit --limit 10/minute -j "${FW_LOGDEST[@]}" "DENY=999 " $IP6TABLES -A FINAL_RULE -j DROP fi echo "Firewall installed" | $LOGGER } fw_install() { echo "Installing $(basename $0) $MBSEFW_VERSION" | $LOGGER echo -n "Installing $(basename $0) $MBSEFW_VERSION: " reset_iptables DROP echo -n "." fw_init_sysctl echo -n "." fw_start_init fw_start_main fw_start_final echo " done." } fw_start() { if [ -f /etc/mbse-firewall/data/firewall-ipv4.data -a \ -f /etc/mbse-firewall/data/firewall-ipv6.data -a \ -f /etc/mbse-firewall/data/firewall-ipset.data ]; then # Do a full restore of all saved data echo -n "Starting $(basename $0) $MBSEFW_VERSION: " echo "Starting $(basename $0) $MBSEFW_VERSION" | $LOGGER echo "Start new firewall" | $LOGGER fw_init_nfacct reset_iptables DROP echo -n "." fw_init_sysctl $IPSET restore -exist < /etc/mbse-firewall/data/firewall-ipset.data echo " Restored /etc/mbse-firewall/data/firewall-ipset.data" | $LOGGER echo -n "." $IPTABLES_RESTORE < /etc/mbse-firewall/data/firewall-ipv4.data echo " Restored /etc/mbse-firewall/data/firewall-ipv4.data" | $LOGGER echo -n "." $IP6TABLES_RESTORE < /etc/mbse-firewall/data/firewall-ipv6.data echo " Restored /etc/mbse-firewall/data/firewall-ipv6.data" | $LOGGER echo " done." echo -n "New firewall active" | $LOGGER else # If there is no saved firewall, install a new one and save it. fw_install fw_save fi } fw_stop() { echo "Stopping $(basename $0) $MBSEFW_VERSION" | $LOGGER echo -n "Stopping $(basename $0) $MBSEFW_VERSION: " # Slackware defaults to ACCEPT when no firewall is active. reset_iptables ACCEPT echo "done." } # If there are blocklist tables, reload them. fw_reload() { echo "Reload $(basename $0) $MBSEFW_VERSION" | $LOGGER echo -n "Reload $(basename $0) $MBSEFW_VERSION: " reload_blocklist4 reload_blocklist6 echo done. } fw_save() { echo "Saving $(basename $0) $MBSEFW_VERSION" | $LOGGER echo -n "Saving $(basename $0) $MBSEFW_VERSION: " mkdir -p /etc/mbse-firewall/data [ -n "$IPTABLES_SAVE" ] && $IPTABLES_SAVE > /etc/mbse-firewall/data/firewall-ipv4.data echo -n "." [ -n "$IP6TABLES_SAVE" ] && $IP6TABLES_SAVE > /etc/mbse-firewall/data/firewall-ipv6.data echo -n "." rm -f /etc/mbse-firewall/data/firewall-ipset.data touch /etc/mbse-firewall/data/firewall-ipset.data if [ "$IF_EXT_GLOBAL_BLOCK" == "1" ]; then $IPSET save global-blk4 -t >> /etc/mbse-firewall/data/firewall-ipset.data if [ "$USE_IPV6" == "1" ]; then $IPSET save global-blk6 -t >> /etc/mbse-firewall/data/firewall-ipset.data fi fi HOST="$(hostname)" SETS="$($IPSET list -n | grep ${HOST})" for set in $SETS ; do if [ "$set" = "${HOST}-mbsefw-auto4" -o "$set" = "${HOST}-mbsefw-auto6" ]; then # Only save structure for auto blocklists $IPSET save $set -t >> /etc/mbse-firewall/data/firewall-ipset.data else $IPSET save $set >> /etc/mbse-firewall/data/firewall-ipset.data fi echo -n "." done echo " done." echo "Save firewall done in /etc/mbse-firewall/data" | $LOGGER } fw_status() { echo -n "$(basename $0) $MBSEFW_VERSION" IP_MODULES=$($LSMOD | $AWK '{print $1}' | $GREP '^ip') if [ "${IP_MODULES}x" = "x" ]; then echo " - You do not have any iptables loaded." return else echo " - You have the following ip modules loaded:" echo -n " " echo ${IP_MODULES} fi if [ ! -z "$( echo $IP_MODULES | $GREP iptable_filter )" ]; then echo echo ' FILTER TABLE IPv4' echo $IPTABLES -t filter -L -n -v --line-numbers fi if [ ! -z "$( echo $IP_MODULES | $GREP ip6table_filter )" ]; then echo echo ' FILTER TABLE IPv6' echo $IP6TABLES -t filter -L -n -v --line-numbers fi if [ ! -z "$( echo $IP_MODULES | $GREP iptable_nat )" ]; then echo echo ' NAT TABLE IPv4' echo $IPTABLES -t nat -L -v -n --line-numbers fi if [ ! -z "$( echo $IP_MODULES | $GREP ip6table_nat )" ]; then echo echo ' NAT TABLE IPv6' echo $IP6TABLES -t nat -L -v -n --line-numbers fi if [ ! -z "$( echo $IP_MODULES | $GREP iptable_raw )" ]; then echo echo ' RAW TABLE IPv4' echo $IPTABLES -t raw -L -v -n --line-numbers fi if [ ! -z "$( echo $IP_MODULES | $GREP ip6table_raw )" ]; then echo echo ' RAW TABLE IPv6' echo $IP6TABLES -t raw -L -v -n --line-numbers fi if [ ! -z "$( echo $IP_MODULES | $GREP iptable_mangle )" ]; then echo echo ' MANGLE TABLE IPv4' echo $IPTABLES -t mangle -L -v -n --line-numbers fi if [ ! -z "$( echo $IP_MODULES | $GREP ip6table_mangle )" ]; then echo echo ' MANGLE TABLE IPv6' echo $IP6TABLES -t mangle -L -v -n --line-numbers fi if [ ! -z "$( echo $IP_MODULES | $GREP iptable_security )" ]; then echo echo ' SECURITY TABLE IPv4' echo $IPTABLES -t security -L -v -n --line-numbers fi if [ ! -z "$( echo $IP_MODULES | $GREP ip6table_security )" ]; then echo echo ' SECURITY TABLE IPv6' echo $IP6TABLES -t security -L -v -n --line-numbers fi HOST="$(hostname)" if [ -n "$IPSET" ] && [ ! -z "$($IPSET list -n | grep ${HOST})" ]; then echo echo ' IPSET listing' SETS="$(${IPSET} list -n | grep ${HOST})" for MySET in ${SETS}; do echo ${IPSET} list ${MySET} done fi } # --------------------------------------------------------------------------- # # MAIN program part # # --------------------------------------------------------------------------- # See how we were called cmd=$1 case "$cmd" in start) [ -x /etc/rc.d/rc.ulogd ] && /etc/rc.d/rc.ulogd start fw_start ;; stop) fw_stop [ -x /etc/rc.d/rc.ulogd ] && /etc/rc.d/rc.ulogd stop ;; restart) fw_stop [ -x /etc/rc.d/rc.ulogd ] && /etc/rc.d/rc.ulogd restart fw_start ;; save) fw_save ;; install) fw_install ;; reload) fw_reload ;; status) fw_status ;; *) echo "Usage $0 [start|stop|restart|save|install|reload|status]" echo echo "start start a saved firewall" echo "stop stop firewall and set default ACCEPT state" echo "restart stop and start the firewall" echo "save save current installed firewall rules" echo "install install new firewall from configuration" echo "reload reload the blocklists" echo "status show the firewall rules and counters" ;; esac