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