148e64ca1SJose Luis Duran#!/bin/sh 248e64ca1SJose Luis Duran#echo "run $@" 1>&2 348e64ca1SJose Luis Duran#set -x 448e64ca1SJose Luis Duran# $1 command 548e64ca1SJose Luis Duran# $2 rulename 648e64ca1SJose Luis Duran# $3 protocol 748e64ca1SJose Luis Duran# $4 address 848e64ca1SJose Luis Duran# $5 mask 948e64ca1SJose Luis Duran# $6 port 1048e64ca1SJose Luis Duran# $7 id 1148e64ca1SJose Luis Duran 1248e64ca1SJose Luis Duranpf= 1348e64ca1SJose Luis Duranif [ -f "/etc/ipfw-blocklist.rc" ]; then 1448e64ca1SJose Luis Duran pf="ipfw" 1548e64ca1SJose Luis Duran . /etc/ipfw-blocklist.rc 1648e64ca1SJose Luis Duran ipfw_offset=${ipfw_offset:-2000} 1748e64ca1SJose Luis Duranfi 1848e64ca1SJose Luis Duran 1948e64ca1SJose Luis Duranif [ -z "$pf" ]; then 2048e64ca1SJose Luis Duran for f in npf pf ipfilter ipfw; do 2148e64ca1SJose Luis Duran if [ -x /etc/rc.d/$f ]; then 2248e64ca1SJose Luis Duran if /etc/rc.d/$f status >/dev/null 2>&1; then 2348e64ca1SJose Luis Duran pf="$f" 2448e64ca1SJose Luis Duran break 2548e64ca1SJose Luis Duran fi 2648e64ca1SJose Luis Duran elif [ -f "/etc/$f.conf" ]; then 2748e64ca1SJose Luis Duran # xxx assume a config file means it can be enabled -- 2848e64ca1SJose Luis Duran # and the first one wins! 2948e64ca1SJose Luis Duran pf="$f" 3048e64ca1SJose Luis Duran break 3148e64ca1SJose Luis Duran fi 3248e64ca1SJose Luis Duran done 3348e64ca1SJose Luis Duranfi 3448e64ca1SJose Luis Duran 3548e64ca1SJose Luis Duranif [ -z "$pf" -a -x "/sbin/iptables" ]; then 3648e64ca1SJose Luis Duran pf="iptables" 3748e64ca1SJose Luis Duranfi 3848e64ca1SJose Luis Duran 3948e64ca1SJose Luis Duranif [ -z "$pf" ]; then 4048e64ca1SJose Luis Duran echo "$0: Unsupported packet filter" 1>&2 4148e64ca1SJose Luis Duran exit 1 4248e64ca1SJose Luis Duranfi 4348e64ca1SJose Luis Duran 4448e64ca1SJose Luis Duranflags= 4548e64ca1SJose Luis Duranif [ -n "$3" ]; then 4648e64ca1SJose Luis Duran raw_proto="$3" 4748e64ca1SJose Luis Duran proto="proto $3" 4848e64ca1SJose Luis Duran if [ $3 = "tcp" ]; then 4948e64ca1SJose Luis Duran flags="flags S/SAFR" 5048e64ca1SJose Luis Duran fi 5148e64ca1SJose Luis Duranfi 5248e64ca1SJose Luis Duran 5348e64ca1SJose Luis Duranif [ -n "$6" ]; then 5448e64ca1SJose Luis Duran raw_port="$6" 5548e64ca1SJose Luis Duran port="port $6" 5648e64ca1SJose Luis Duranfi 5748e64ca1SJose Luis Duran 5848e64ca1SJose Luis Duranaddr="$4" 5948e64ca1SJose Luis Duranmask="$5" 6048e64ca1SJose Luis Durancase "$4" in 6148e64ca1SJose Luis Duran::ffff:*.*.*.*) 6248e64ca1SJose Luis Duran if [ "$5" = 128 ]; then 6348e64ca1SJose Luis Duran mask=32 6448e64ca1SJose Luis Duran addr=${4#::ffff:} 6548e64ca1SJose Luis Duran fi;; 6648e64ca1SJose Luis Duranesac 6748e64ca1SJose Luis Duran 6848e64ca1SJose Luis Durancase "$1" in 6948e64ca1SJose Luis Duranadd) 7048e64ca1SJose Luis Duran case "$pf" in 7148e64ca1SJose Luis Duran ipfilter) 7248e64ca1SJose Luis Duran # N.B.: If you reload /etc/ipf.conf then you need to stop and 7348e64ca1SJose Luis Duran # restart blocklistd (and make sure blocklistd_flags="-r"). 7448e64ca1SJose Luis Duran # This should normally already be implemented in 7548e64ca1SJose Luis Duran # /etc/rc.d/ipfilter, but if then not add the following lines to 7648e64ca1SJose Luis Duran # the end of the ipfilter_reload() function: 7748e64ca1SJose Luis Duran # 7848e64ca1SJose Luis Duran # if checkyesnox blocklistd; then 7948e64ca1SJose Luis Duran # /etc/rc.d/blocklistd restart 8048e64ca1SJose Luis Duran # fi 8148e64ca1SJose Luis Duran # 8248e64ca1SJose Luis Duran # XXX we assume the following rule is present in /etc/ipf.conf: 8348e64ca1SJose Luis Duran # (should we check? -- it probably cannot be added dynamically) 8448e64ca1SJose Luis Duran # 8548e64ca1SJose Luis Duran # block in proto tcp/udp from any to any head blocklistd 8648e64ca1SJose Luis Duran # 8748e64ca1SJose Luis Duran # where "blocklistd" is the default rulename (i.e. "$2") 8848e64ca1SJose Luis Duran # 8948e64ca1SJose Luis Duran # This rule can come before any rule that logs connections, 9048e64ca1SJose Luis Duran # etc., and should be followed by final rules such as: 9148e64ca1SJose Luis Duran # 9248e64ca1SJose Luis Duran # # log all as-yet unblocked incoming TCP connection 9348e64ca1SJose Luis Duran # # attempts 9448e64ca1SJose Luis Duran # log in proto tcp from any to any flags S/SAFR 9548e64ca1SJose Luis Duran # # last "pass" match wins for all non-blocked packets 9648e64ca1SJose Luis Duran # pass in all 9748e64ca1SJose Luis Duran # pass out all 9848e64ca1SJose Luis Duran # 9948e64ca1SJose Luis Duran # I.e. a "pass" rule which will be the final match and override 10048e64ca1SJose Luis Duran # the "block". This way the rules added by blocklistd will 10148e64ca1SJose Luis Duran # actually block packets, and prevent logging of them as 10248e64ca1SJose Luis Duran # connections, because they include the "quick" flag. 10348e64ca1SJose Luis Duran # 10448e64ca1SJose Luis Duran # N.b.: $port is not included/used in rules -- abusers are cut 10548e64ca1SJose Luis Duran # off completely from all services! 10648e64ca1SJose Luis Duran # 10748e64ca1SJose Luis Duran # Note RST packets are not returned for blocked SYN packets of 10848e64ca1SJose Luis Duran # active attacks, so the port will not appear to be closed. 10948e64ca1SJose Luis Duran # This will probably give away the fact that a firewall has been 11048e64ca1SJose Luis Duran # triggered to block connections, but it prevents generating 11148e64ca1SJose Luis Duran # extra outbound traffic, and it may also slow down the attacker 11248e64ca1SJose Luis Duran # somewhat. 11348e64ca1SJose Luis Duran # 11448e64ca1SJose Luis Duran # Note also that we don't block all packets, just new attempts 11548e64ca1SJose Luis Duran # to open connections (see $flags above). This allows us to do 11648e64ca1SJose Luis Duran # counterespionage against the attacker (or continue to make use 11748e64ca1SJose Luis Duran # of any other services that might be on the same subnet as the 11848e64ca1SJose Luis Duran # supposed attacker). However it does not kill any active 11948e64ca1SJose Luis Duran # connections -- we rely on the reporting daemon to do its own 12048e64ca1SJose Luis Duran # protection and cleanup. 12148e64ca1SJose Luis Duran # 12248e64ca1SJose Luis Duran # N.B.: The rule generated here must exactly match the 12348e64ca1SJose Luis Duran # corresponding rule generated for the "rem" command below! 12448e64ca1SJose Luis Duran # 12548e64ca1SJose Luis Duran echo block in log quick $proto \ 12648e64ca1SJose Luis Duran from $addr/$mask to any $flags group $2 | \ 12748e64ca1SJose Luis Duran /sbin/ipf -A -f - >/dev/null 2>&1 && echo OK 12848e64ca1SJose Luis Duran ;; 12948e64ca1SJose Luis Duran 13048e64ca1SJose Luis Duran ipfw) 13148e64ca1SJose Luis Duran # use $ipfw_offset+$port for rule number 13248e64ca1SJose Luis Duran rule=$(($ipfw_offset + $6)) 13348e64ca1SJose Luis Duran tname="port$6" 13448e64ca1SJose Luis Duran /sbin/ipfw table $tname create type addr 2>/dev/null 13548e64ca1SJose Luis Duran /sbin/ipfw -q table $tname add "$addr/$mask" 13648e64ca1SJose Luis Duran # if rule number $rule does not already exist, create it 13748e64ca1SJose Luis Duran /sbin/ipfw show $rule >/dev/null 2>&1 || \ 13848e64ca1SJose Luis Duran /sbin/ipfw add $rule drop $3 from \ 13948e64ca1SJose Luis Duran table"("$tname")" to any dst-port $6 >/dev/null && \ 14048e64ca1SJose Luis Duran echo OK 14148e64ca1SJose Luis Duran ;; 14248e64ca1SJose Luis Duran 14348e64ca1SJose Luis Duran iptables) 14448e64ca1SJose Luis Duran if ! /sbin/iptables --list "$2" >/dev/null 2>&1; then 14548e64ca1SJose Luis Duran /sbin/iptables --new-chain "$2" 14648e64ca1SJose Luis Duran fi 14748e64ca1SJose Luis Duran /sbin/iptables --append INPUT --proto "$raw_proto" \ 14848e64ca1SJose Luis Duran --dport "$raw_port" --jump "$2" 14948e64ca1SJose Luis Duran /sbin/iptables --append "$2" --proto "$raw_proto" \ 15048e64ca1SJose Luis Duran --source "$addr/$mask" --dport "$raw_port" --jump DROP 15148e64ca1SJose Luis Duran echo OK 15248e64ca1SJose Luis Duran ;; 15348e64ca1SJose Luis Duran 15448e64ca1SJose Luis Duran npf) 15548e64ca1SJose Luis Duran /sbin/npfctl rule "$2" add block in final $proto from \ 15648e64ca1SJose Luis Duran "$addr/$mask" to any $port 15748e64ca1SJose Luis Duran ;; 15848e64ca1SJose Luis Duran 15948e64ca1SJose Luis Duran pf) 16048e64ca1SJose Luis Duran # if the filtering rule does not exist, create it 16148e64ca1SJose Luis Duran /sbin/pfctl -a "$2/$6" -sr 2>/dev/null | \ 16248e64ca1SJose Luis Duran grep -q "<port$6>" || \ 16348e64ca1SJose Luis Duran echo "block in quick $proto from <port$6> to any $port" | \ 16448e64ca1SJose Luis Duran /sbin/pfctl -a "$2/$6" -f - 16548e64ca1SJose Luis Duran # insert $ip/$mask into per-protocol/port anchored table 16648e64ca1SJose Luis Duran /sbin/pfctl -qa "$2/$6" -t "port$6" -T add "$addr/$mask" && \ 16748e64ca1SJose Luis Duran /sbin/pfctl -qk "$addr" && echo OK 16848e64ca1SJose Luis Duran ;; 16948e64ca1SJose Luis Duran 17048e64ca1SJose Luis Duran esac 17148e64ca1SJose Luis Duran ;; 17248e64ca1SJose Luis Duranrem) 17348e64ca1SJose Luis Duran case "$pf" in 17448e64ca1SJose Luis Duran ipfilter) 17548e64ca1SJose Luis Duran # N.B.: The rule generated here must exactly match the 17648e64ca1SJose Luis Duran # corresponding rule generated for the "add" command above! 17748e64ca1SJose Luis Duran # 17848e64ca1SJose Luis Duran echo block in log quick $proto \ 17948e64ca1SJose Luis Duran from $addr/$mask to any $flags group $2 | \ 18048e64ca1SJose Luis Duran /sbin/ipf -A -r -f - >/dev/null 2>&1 && echo OK 18148e64ca1SJose Luis Duran ;; 18248e64ca1SJose Luis Duran 18348e64ca1SJose Luis Duran ipfw) 18448e64ca1SJose Luis Duran /sbin/ipfw table "port$6" delete "$addr/$mask" 2>/dev/null && \ 18548e64ca1SJose Luis Duran echo OK 18648e64ca1SJose Luis Duran ;; 18748e64ca1SJose Luis Duran 18848e64ca1SJose Luis Duran iptables) 18948e64ca1SJose Luis Duran if /sbin/iptables --list "$2" >/dev/null 2>&1; then 19048e64ca1SJose Luis Duran /sbin/iptables --delete "$2" --proto "$raw_proto" \ 19148e64ca1SJose Luis Duran --source "$addr/$mask" --dport "$raw_port" \ 19248e64ca1SJose Luis Duran --jump DROP 19348e64ca1SJose Luis Duran fi 19448e64ca1SJose Luis Duran echo OK 19548e64ca1SJose Luis Duran ;; 19648e64ca1SJose Luis Duran 19748e64ca1SJose Luis Duran npf) 19848e64ca1SJose Luis Duran /sbin/npfctl rule "$2" rem-id "$7" 19948e64ca1SJose Luis Duran ;; 20048e64ca1SJose Luis Duran 20148e64ca1SJose Luis Duran pf) 20248e64ca1SJose Luis Duran /sbin/pfctl -qa "$2/$6" -t "port$6" -T delete "$addr/$mask" && \ 20348e64ca1SJose Luis Duran echo OK 20448e64ca1SJose Luis Duran ;; 20548e64ca1SJose Luis Duran 20648e64ca1SJose Luis Duran esac 20748e64ca1SJose Luis Duran ;; 20848e64ca1SJose Luis Duranflush) 20948e64ca1SJose Luis Duran case "$pf" in 21048e64ca1SJose Luis Duran ipfilter) 21148e64ca1SJose Luis Duran # 21248e64ca1SJose Luis Duran # N.B. WARNING: This is obviously not reentrant! 21348e64ca1SJose Luis Duran # 21448e64ca1SJose Luis Duran # First we flush all the rules from the inactive set, then we 21548e64ca1SJose Luis Duran # reload the ones that do not belong to the group "$2", and 21648e64ca1SJose Luis Duran # finally we swap the active and inactive rule sets. 21748e64ca1SJose Luis Duran # 21848e64ca1SJose Luis Duran /sbin/ipf -I -F a 21948e64ca1SJose Luis Duran # 22048e64ca1SJose Luis Duran # "ipf -I -F a" also flushes active accounting rules! 22148e64ca1SJose Luis Duran # 22248e64ca1SJose Luis Duran # Note that accounting rule groups are unique to accounting 22348e64ca1SJose Luis Duran # rules and have nothing to do with filter rules, though of 22448e64ca1SJose Luis Duran # course theoretically one could use the same group name for 22548e64ca1SJose Luis Duran # them too. 22648e64ca1SJose Luis Duran # 22748e64ca1SJose Luis Duran # In theory anyone using any such accounting rules should have a 22848e64ca1SJose Luis Duran # wrapper /etc/rc.conf.d/blocklistd script (and corresponding 22948e64ca1SJose Luis Duran # /etc/rc.conf.d/ipfilter script) that will record and 23048e64ca1SJose Luis Duran # consolidate the values accumulated by such accounting rules 23148e64ca1SJose Luis Duran # before they are flushed, since otherwise their counts will be 23248e64ca1SJose Luis Duran # lost forever. 23348e64ca1SJose Luis Duran # 23448e64ca1SJose Luis Duran /usr/sbin/ipfstat -io | fgrep -v "group $2" | \ 23548e64ca1SJose Luis Duran /sbin/ipf -I -f - >/dev/null 2>&1 23648e64ca1SJose Luis Duran # 23748e64ca1SJose Luis Duran # This MUST be done last and separately as "-s" is executed 23848e64ca1SJose Luis Duran # _while_ the command arguments are being processed! 23948e64ca1SJose Luis Duran # 24048e64ca1SJose Luis Duran /sbin/ipf -s && echo OK 24148e64ca1SJose Luis Duran ;; 24248e64ca1SJose Luis Duran 24348e64ca1SJose Luis Duran ipfw) 24448e64ca1SJose Luis Duran /sbin/ipfw table "port$6" flush 2>/dev/null && echo OK 24548e64ca1SJose Luis Duran ;; 24648e64ca1SJose Luis Duran 24748e64ca1SJose Luis Duran iptables) 24848e64ca1SJose Luis Duran if /sbin/iptables --list "$2" >/dev/null 2>&1; then 24948e64ca1SJose Luis Duran /sbin/iptables --flush "$2" 25048e64ca1SJose Luis Duran fi 25148e64ca1SJose Luis Duran echo OK 25248e64ca1SJose Luis Duran ;; 25348e64ca1SJose Luis Duran 25448e64ca1SJose Luis Duran npf) 25548e64ca1SJose Luis Duran /sbin/npfctl rule "$2" flush 25648e64ca1SJose Luis Duran ;; 25748e64ca1SJose Luis Duran 25848e64ca1SJose Luis Duran pf) 25948e64ca1SJose Luis Duran # dynamically determine which anchors exist 26048e64ca1SJose Luis Duran for anchor in $(/sbin/pfctl -a "$2" -s Anchors 2> /dev/null); do 261*2347ca21SJose Luis Duran /sbin/pfctl -a "$anchor" -t "port${anchor##*/}" -T flush 2> /dev/null 26248e64ca1SJose Luis Duran /sbin/pfctl -a "$anchor" -F rules 26348e64ca1SJose Luis Duran done 26448e64ca1SJose Luis Duran echo OK 26548e64ca1SJose Luis Duran ;; 26648e64ca1SJose Luis Duran esac 26748e64ca1SJose Luis Duran ;; 26848e64ca1SJose Luis Duran*) 26948e64ca1SJose Luis Duran echo "$0: Unknown command '$1'" 1>&2 27048e64ca1SJose Luis Duran exit 1 27148e64ca1SJose Luis Duran ;; 27248e64ca1SJose Luis Duranesac 273