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