Home | History | Annotate | Line # | Download | only in libexec
blocklistd-helper revision 1.4
      1  1.1  christos #!/bin/sh
      2  1.1  christos #echo "run $@" 1>&2
      3  1.1  christos #set -x
      4  1.1  christos # $1 command
      5  1.1  christos # $2 rulename
      6  1.1  christos # $3 protocol
      7  1.1  christos # $4 address
      8  1.1  christos # $5 mask
      9  1.1  christos # $6 port
     10  1.1  christos # $7 id
     11  1.1  christos 
     12  1.1  christos pf=
     13  1.1  christos if [ -f "/etc/ipfw-blocklist.rc" ]; then
     14  1.1  christos 	pf="ipfw"
     15  1.1  christos 	. /etc/ipfw-blocklist.rc
     16  1.1  christos 	ipfw_offset=${ipfw_offset:-2000}
     17  1.1  christos fi
     18  1.1  christos 
     19  1.1  christos if [ -z "$pf" ]; then
     20  1.2  christos 	for f in npf pf ipfilter; do
     21  1.1  christos 		if [ -f "/etc/$f.conf" ]; then
     22  1.1  christos 			pf="$f"
     23  1.1  christos 			break
     24  1.1  christos 		fi
     25  1.1  christos 	done
     26  1.1  christos fi
     27  1.1  christos 
     28  1.2  christos if [ -z "$pf" -a -x "/sbin/iptables" ]; then
     29  1.2  christos 	pf="iptables"
     30  1.2  christos fi
     31  1.2  christos 
     32  1.1  christos if [ -z "$pf" ]; then
     33  1.1  christos 	echo "$0: Unsupported packet filter" 1>&2
     34  1.1  christos 	exit 1
     35  1.1  christos fi
     36  1.1  christos 
     37  1.2  christos flags=
     38  1.1  christos if [ -n "$3" ]; then
     39  1.2  christos 	raw_proto="$3"
     40  1.1  christos 	proto="proto $3"
     41  1.2  christos 	if [ $3 = "tcp" ]; then
     42  1.2  christos 		flags="flags S/SAFR"
     43  1.2  christos 	fi
     44  1.1  christos fi
     45  1.1  christos 
     46  1.1  christos if [ -n "$6" ]; then
     47  1.2  christos 	raw_port="$6"
     48  1.1  christos 	port="port $6"
     49  1.1  christos fi
     50  1.1  christos 
     51  1.1  christos addr="$4"
     52  1.1  christos mask="$5"
     53  1.1  christos case "$4" in
     54  1.1  christos ::ffff:*.*.*.*)
     55  1.1  christos 	if [ "$5" = 128 ]; then
     56  1.1  christos 		mask=32
     57  1.1  christos 		addr=${4#::ffff:}
     58  1.1  christos 	fi;;
     59  1.1  christos esac
     60  1.1  christos 
     61  1.1  christos case "$1" in
     62  1.1  christos add)
     63  1.1  christos 	case "$pf" in
     64  1.1  christos 	ipf)
     65  1.2  christos 		# N.B.:  If you reload /etc/ipf.conf then you need to stop and
     66  1.2  christos 		# restart blocklistd (and make sure blocklistd_flags="-r"):
     67  1.2  christos 		#
     68  1.2  christos 		#	/etc/rc.d/ipfilter reload
     69  1.2  christos 		#	/etc/rc.d/blocklistd restart
     70  1.2  christos 		#
     71  1.2  christos 		# XXX we assume the following rule is present in /etc/ipf.conf:
     72  1.2  christos 		#
     73  1.2  christos 		#	block in proto tcp/udp from any to any head blocklistd
     74  1.2  christos 		#
     75  1.2  christos 		# where "blocklistd" is the default rulename (i.e. "$2")
     76  1.2  christos 		#
     77  1.2  christos 		# This rule can come before any rule that logs connections,
     78  1.2  christos 		# etc., and should be followed by final rules such as:
     79  1.2  christos 		#
     80  1.2  christos 		#	# log all as-yet unblocked incoming TCP connection
     81  1.2  christos 		#	# attempts
     82  1.2  christos 		#	log in proto tcp from any to any flags S/SAFR
     83  1.2  christos 		#	# last "pass" match wins for all non-blocked packets
     84  1.2  christos 		#	pass in all
     85  1.2  christos 		#	pass out all
     86  1.2  christos 		#
     87  1.2  christos 		# I.e. a "pass" rule which will be the final match and override
     88  1.2  christos 		# the "block".  This way the rules added by blocklistd will
     89  1.2  christos 		# actually block packets, and prevent logging of them as
     90  1.2  christos 		# connections, because they include the "quick" flag.
     91  1.2  christos 		#
     92  1.2  christos 		# N.b.:  $port is not included -- abusers are cut off completely
     93  1.2  christos 		# from all services!
     94  1.2  christos 		#
     95  1.2  christos 		# Note RST packets are not returned for blocked SYN packets of
     96  1.2  christos 		# active attacks, so the port will not appear to be closed.
     97  1.2  christos 		# This will probably give away the fact that a firewall has been
     98  1.2  christos 		# triggered to block connections, but it prevents generating
     99  1.2  christos 		# extra outbound traffic, and it may also slow down the attacker
    100  1.2  christos 		# somewhat.
    101  1.2  christos 		#
    102  1.2  christos 		# Note also that we don't block all packets, just new attempts
    103  1.2  christos 		# to open connections (see $flags above).  This allows us to do
    104  1.2  christos 		# counterespionage against the attacker (or continue to make use
    105  1.2  christos 		# of any other services that might be on the same subnet as the
    106  1.2  christos 		# attacker).  However it does not kill any active connections --
    107  1.2  christos 		# we rely on the reporting daemon to do its own protection and
    108  1.2  christos 		# cleanup.
    109  1.2  christos 		#
    110  1.2  christos 		# N.B.:  The generated must exactly match the rule generated for
    111  1.2  christos 		# the "rem" command below!
    112  1.2  christos 		#
    113  1.2  christos 		echo block in log quick $proto \
    114  1.2  christos 		     from $addr/$mask to any $flags group $2 | \
    115  1.2  christos 		    /sbin/ipf -A -f - >/dev/null 2>&1 && echo OK
    116  1.1  christos 		;;
    117  1.2  christos 
    118  1.1  christos 	ipfw)
    119  1.1  christos 		# use $ipfw_offset+$port for rule number
    120  1.1  christos 		rule=$(($ipfw_offset + $6))
    121  1.1  christos 		tname="port$6"
    122  1.1  christos 		/sbin/ipfw table $tname create type addr 2>/dev/null
    123  1.1  christos 		/sbin/ipfw -q table $tname add "$addr/$mask"
    124  1.1  christos 		# if rule number $rule does not already exist, create it
    125  1.1  christos 		/sbin/ipfw show $rule >/dev/null 2>&1 || \
    126  1.1  christos 			/sbin/ipfw add $rule drop $3 from \
    127  1.1  christos 			table"("$tname")" to any dst-port $6 >/dev/null && \
    128  1.1  christos 			echo OK
    129  1.1  christos 		;;
    130  1.2  christos 
    131  1.2  christos 	iptables)
    132  1.2  christos 		if ! /sbin/iptables --list "$2" >/dev/null 2>&1; then
    133  1.2  christos 			/sbin/iptables --new-chain "$2"
    134  1.2  christos 		fi
    135  1.2  christos 		/sbin/iptables --append INPUT --proto "$raw_proto" \
    136  1.2  christos 		    --dport "$raw_port" --jump "$2"
    137  1.2  christos 		/sbin/iptables --append "$2" --proto "$raw_proto" \
    138  1.2  christos 		    --source "$addr/$mask" --dport "$raw_port" --jump DROP
    139  1.2  christos 		echo OK
    140  1.2  christos 		;;
    141  1.2  christos 
    142  1.1  christos 	npf)
    143  1.1  christos 		/sbin/npfctl rule "$2" add block in final $proto from \
    144  1.1  christos 		    "$addr/$mask" to any $port
    145  1.1  christos 		;;
    146  1.2  christos 
    147  1.1  christos 	pf)
    148  1.1  christos 		# if the filtering rule does not exist, create it
    149  1.1  christos 		/sbin/pfctl -a "$2/$6" -sr 2>/dev/null | \
    150  1.1  christos 		    grep -q "<port$6>" || \
    151  1.1  christos 		    echo "block in quick $proto from <port$6> to any $port" | \
    152  1.1  christos 		    /sbin/pfctl -a "$2/$6" -f -
    153  1.1  christos 		# insert $ip/$mask into per-protocol/port anchored table
    154  1.4  christos 		/sbin/pfctl -qa "$2/$6" -t "port$6" -T add "$addr/$mask" && \
    155  1.4  christos 		    /sbin/pcftl -qk "$addr" && echo OK
    156  1.1  christos 		;;
    157  1.2  christos 
    158  1.1  christos 	esac
    159  1.1  christos 	;;
    160  1.1  christos rem)
    161  1.1  christos 	case "$pf" in
    162  1.1  christos 	ipf)
    163  1.2  christos 		echo block in log quick $proto \
    164  1.2  christos 		     from $addr/$mask to any $flags group $2 | \
    165  1.2  christos 		    /sbin/ipf -A -r -f - >/dev/null 2>&1 && echo OK
    166  1.1  christos 		;;
    167  1.2  christos 
    168  1.1  christos 	ipfw)
    169  1.1  christos 		/sbin/ipfw table "port$6" delete "$addr/$mask" 2>/dev/null && \
    170  1.1  christos 		    echo OK
    171  1.1  christos 		;;
    172  1.2  christos 
    173  1.2  christos 	iptables)
    174  1.2  christos 		if /sbin/iptables --list "$2" >/dev/null 2>&1; then
    175  1.2  christos 			/sbin/iptables --delete "$2" --proto "$raw_proto" \
    176  1.2  christos 			    --source "$addr/$mask" --dport "$raw_port" \
    177  1.2  christos 			    --jump DROP
    178  1.2  christos 		fi
    179  1.2  christos 		echo OK
    180  1.2  christos 		;;
    181  1.2  christos 
    182  1.1  christos 	npf)
    183  1.1  christos 		/sbin/npfctl rule "$2" rem-id "$7"
    184  1.1  christos 		;;
    185  1.2  christos 
    186  1.1  christos 	pf)
    187  1.4  christos 		/sbin/pfctl -qa "$2/$6" -t "port$6" -T delete "$addr/$mask" && \
    188  1.1  christos 		    echo OK
    189  1.1  christos 		;;
    190  1.2  christos 
    191  1.1  christos 	esac
    192  1.1  christos 	;;
    193  1.1  christos flush)
    194  1.1  christos 	case "$pf" in
    195  1.1  christos 	ipf)
    196  1.2  christos 		#
    197  1.2  christos 		# XXX this is a slightly convoluted way to remove all the rules
    198  1.2  christos 		# in the group added for "$2" (i.e. normally by default
    199  1.2  christos 		# "blocklistd").
    200  1.2  christos 		#
    201  1.2  christos 		# N.B. WARNING:  This is obviously not reentrant!
    202  1.2  christos 		#
    203  1.2  christos 		/sbin/ipf -I -F a
    204  1.2  christos 		/usr/sbin/ipfstat -io | fgrep -v "group $2" | \
    205  1.2  christos 		    /sbin/ipf -I -f - >/dev/null 2>&1
    206  1.2  christos 		# XXX this MUST be done last and separately as "-s" is executed
    207  1.2  christos 		# _while_ the command arguments are being processed!
    208  1.2  christos 		/sbin/ipf -s && echo OK
    209  1.1  christos 		;;
    210  1.2  christos 
    211  1.1  christos 	ipfw)
    212  1.1  christos 		/sbin/ipfw table "port$6" flush 2>/dev/null && echo OK
    213  1.1  christos 		;;
    214  1.2  christos 
    215  1.2  christos 	iptables)
    216  1.2  christos 		if /sbin/iptables --list "$2" >/dev/null 2>&1; then
    217  1.2  christos 			/sbin/iptables --flush "$2"
    218  1.2  christos 		fi
    219  1.2  christos 		echo OK
    220  1.2  christos 		;;
    221  1.2  christos 
    222  1.1  christos 	npf)
    223  1.1  christos 		/sbin/npfctl rule "$2" flush
    224  1.1  christos 		;;
    225  1.2  christos 
    226  1.1  christos 	pf)
    227  1.4  christos 		# dynamically determine which anchors exist
    228  1.4  christos 		for anchor in $(/sbin/pfctl -a "$2" -s Anchors); do
    229  1.4  christos 		       /sbin/pfctl -a "$anchor" -t "port${anchor##*/}" -T flush
    230  1.4  christos 		       /sbin/pfctl -a "$anchor" -F rules
    231  1.4  christos 		done
    232  1.4  christos 		echo OK
    233  1.1  christos 		;;
    234  1.1  christos 	esac
    235  1.1  christos 	;;
    236  1.1  christos *)
    237  1.1  christos 	echo "$0: Unknown command '$1'" 1>&2
    238  1.1  christos 	exit 1
    239  1.1  christos 	;;
    240  1.1  christos esac
    241