From 1a80afc8c88978aa36ff8fc80ff6ef54dd61ec0d Mon Sep 17 00:00:00 2001 From: "Costa Tsaousis (ktsaou)" Date: Sun, 8 Feb 2015 10:44:00 +0200 Subject: [PATCH] blacklist and iptrap except rules now accept negative expressions too; firehol explain now has a history --- doc/firehol/firehol-iptrap.5.md | 36 ++++-- sbin/firehol.in | 191 ++++++++++++++++++++------------ 2 files changed, 150 insertions(+), 77 deletions(-) diff --git a/doc/firehol/firehol-iptrap.5.md b/doc/firehol/firehol-iptrap.5.md index 6f3d5a9..3d9037b 100755 --- a/doc/firehol/firehol-iptrap.5.md +++ b/doc/firehol/firehol-iptrap.5.md @@ -19,16 +19,12 @@ firehol-iptrap - dynamically put IP addresses in an ipset `ipuntrap` deletes the IP adresses of the matching packets from `ipset`. -Both helpers do not affect the flow of traffic. They does not `ACCEPT`, +Both helpers do not affect the flow of traffic. They do not `ACCEPT`, `REJECT`, `DROP` packets or affect the firewall in any way. `type` selects which of the IP addresses of the matching packets will be used (added or removed from the ipset). `type` can be `src`, `dst`, `src,dst`, -`dst,src`. - -Both helpers will create the `ipset` specified, if that ipset is not already -created by other statements. When the ipset is created by the `iptrap` helper, -the ipset will not be reset (emptied) when the firewall is restarted. +`dst,src`. If type is a pair, then the ipset must be an ipset of pairs too. `timeout` is required by `iptrap` and gives the duration in seconds of the lifetime of each IP address that is added to `ipset`. Every matching packet @@ -52,12 +48,38 @@ incoming traffic. `iptrap` and `ipuntrap` cannot setup both IPv4 and IPv6 traps with one call. The reason is that the `ipset` can either be IPv4 or IPv6. +Both helpers will create the `ipset` specified, if that ipset is not already +created by other statements. When the ipset is created by the `iptrap` helper, +the ipset will not be reset (emptied) when the firewall is restarted. + +The ipset options used when these helpers create ipsets can be controlled +with the variable IPTRAP_DEFAULT_IPSET_OPTIONS. + # EXAMPLES ~~~~ -iptrap4 src trap 3600 inface eth0 proto tcp dport 80 log "TRAPPED HTTP" +# Example: mini-IDS +# add to the ipset `trap` for an hour (3600 seconds) all IPs from all packets +# comming from eth0 and going to tcp/3306 (mysql). +iptrap4 src trap 3600 inface eth0 proto tcp dport 3306 log "TRAPPED HTTP" +# block them +blacklist4 full inface eth0 log "BLOCKED" src ipset:trap except src ipset:whitelist + +# Example: ipuntrap ipuntrap4 src trap inface eth0 src ipset:trap proto tcp dport 80 log "UNTRAPPED HTTP" + +# Example: a knock +# The user will be able to knock at tcp/12345 +iptrap4 src knock1 30 inface eth0 proto tcp dport 12345 log "KNOCK STEP 1" +# in 30 seconds knock at tcp/23456 +iptrap4 src knock2 60 inface eth0 proto tcp dport 23456 src ipset:knock1 log "KNOCK STEP 2" +# in 60 seconds knock at tcp/34566 +iptrap4 src knock3 90 inface eth0 proto tcp dport 34567 src ipset:knock2 log "KNOCK STEP 3" +# +# and in 90 seconds ssh +interface ... + server ssh accept src ipset:knock3 ~~~~ # SEE ALSO diff --git a/sbin/firehol.in b/sbin/firehol.in index 4bc5a50..6f0201c 100755 --- a/sbin/firehol.in +++ b/sbin/firehol.in @@ -3701,7 +3701,7 @@ blacklist() { iptables_both -t mangle -N "${chain}.in" # add the excepted rules - test ! -z "${1}" && ( rule nonot table mangle chain "${chain}.in" in action RETURN "${@}" || return 1 ) + test ! -z "${1}" && ( rule return_negatives_in_place ignore_empty_positive table mangle chain "${chain}.in" in action RETURN "${@}" || return 1 ) # add the accounting rules test ! -z "${accounting}" && ( iptables_both -t mangle -A "${chain}.in" -m nfacct --nfacct-name "${accounting}" || return 1 ) @@ -3851,7 +3851,7 @@ iptrap() { iptables_both -t mangle -N "${chain}" # add the excepted rules - test ! -z "${1}" && ( rule nonot table mangle chain "${chain}" in action RETURN "${@}" || return 1 ) + test ! -z "${1}" && ( rule return_negatives_in_place ignore_empty_positive table mangle chain "${chain}" in action RETURN "${@}" || return 1 ) # do the job if [ ${undo} -eq 1 ] @@ -3860,7 +3860,12 @@ iptrap() { iptables_both -t mangle -A "${chain}" -j SET --del-set ${ipset} ${type} else # add the ip - iptables_both -t mangle -A "${chain}" -j SET --add-set ${ipset} ${type} --exist --timeout ${timeout} + if [ -z "${timeout}" -o "${timeout}" = "default" ] + then + iptables_both -t mangle -A "${chain}" -j SET --add-set ${ipset} ${type} + else + iptables_both -t mangle -A "${chain}" -j SET --add-set ${ipset} ${type} --exist --timeout ${timeout} + fi fi # log and return @@ -5897,7 +5902,7 @@ rule() { local nolog=0 # if set to 1, negative expressions will give an error - local nonot=0 + local nonot=0 return_negatives_in_place=0 ignore_empty_positive=0 # if set to 1, detection algorithm about overwriting optional rule # parameters will take place. @@ -5914,6 +5919,16 @@ rule() { shift ;; + return_negatives_in_place) + return_negatives_in_place=1 + shift + ;; + + ignore_empty_positive) + ignore_empty_positive=1 + shift + ;; + reverse|REVERSE) reverse=1 shift @@ -7100,6 +7115,7 @@ rule() { inf= outf= inph= outph= \ mc= ipv= iptables= src= dst= s= d= sp= dp= \ negative_chain= negative_action= \ + not= \ DYNAMIC_CHAIN_COUNTER # log mode selection @@ -7162,13 +7178,27 @@ rule() { return 1 fi - if [ ${action_is_chain} -eq 1 ] + if [ ${return_negatives_in_place} -eq 1 ] + then + # we can implement negatives by entering + # RETURN rules in the main flow. + # this should only be done on dedicated chains + # i.e. chains that they will end at the positive + # rules + + negative_chain="${chain}" + negative_action= + not="!" + + elif [ ${action_is_chain} -eq 1 ] then # if the action is a chain name, then just add the negative # expressions to this chain. Nothing more. negative_chain="${action}" negative_action= + not= + else # if the action is a native iptables action, then create # an intermidiate chain to store the negative expression, @@ -7182,13 +7212,14 @@ rule() { iptables_both ${table} -N "${negative_chain}" negative_action="${action}" action="${negative_chain}" + not= fi if [ ! -z "${infacenot}" ] then for inf in ${inface} do - iptables_both ${table} -A "${negative_chain}" -i "${inf}" -j RETURN + iptables_both ${table} -A "${negative_chain}" ${not} -i "${inf}" -j RETURN done infacenot= inface=any @@ -7198,7 +7229,7 @@ rule() { then for outf in ${outface} do - iptables_both ${table} -A "${negative_chain}" -o "${outf}" -j RETURN + iptables_both ${table} -A "${negative_chain}" ${not} -o "${outf}" -j RETURN done outfacenot= outface=any @@ -7208,7 +7239,7 @@ rule() { then for inph in ${physin} do - iptables_both ${table} -A "${negative_chain}" -m physdev ${physbridge} --physdev-in "${inph}" -j RETURN + iptables_both ${table} -A "${negative_chain}" -m physdev ${physbridge} ${not} --physdev-in "${inph}" -j RETURN done physinnot= physin=any @@ -7218,7 +7249,7 @@ rule() { then for outph in ${physout} do - iptables_both ${table} -A "${negative_chain}" -m physdev ${physbridge} --physdev-out "${outph}" -j RETURN + iptables_both ${table} -A "${negative_chain}" -m physdev ${physbridge} ${not} --physdev-out "${outph}" -j RETURN done physoutnot= physout=any @@ -7228,7 +7259,7 @@ rule() { then for mc in ${mac} do - iptables_both ${table} -A "${negative_chain}" -m mac --mac-source "${mc}" -j RETURN + iptables_both ${table} -A "${negative_chain}" -m mac ${not} --mac-source "${mc}" -j RETURN done macnot= mac=any @@ -7255,10 +7286,10 @@ rule() { [ ${IPSET_WARNING} -eq 1 ] && ipset_warning s="${s/ipset:/}" test -z "${FIREHOL_IPSETS_USED[$s]}" && FIREHOL_IPSETS_USED[$s]="USED" - ${iptables} ${table} -A "${negative_chain}" -m set --match-set "${s}" src -j RETURN + ${iptables} ${table} -A "${negative_chain}" -m set ${not} --match-set "${s}" src -j RETURN ;; *) - ${iptables} ${table} -A "${negative_chain}" -s "${s}" -j RETURN + ${iptables} ${table} -A "${negative_chain}" ${not} -s "${s}" -j RETURN ;; esac done @@ -7289,10 +7320,10 @@ rule() { [ ${IPSET_WARNING} -eq 1 ] && ipset_warning d="${d/ipset:/}" test -z "${FIREHOL_IPSETS_USED[$d]}" && FIREHOL_IPSETS_USED[$d]="USED" - ${iptables} ${table} -A "${negative_chain}" -m set --match-set "${d}" dst -j RETURN + ${iptables} ${table} -A "${negative_chain}" -m set ${not} --match-set "${d}" dst -j RETURN ;; *) - ${iptables} ${table} -A "${negative_chain}" -d "${d}" -j RETURN + ${iptables} ${table} -A "${negative_chain}" ${not} -d "${d}" -j RETURN ;; esac done @@ -7312,7 +7343,7 @@ rule() { for pr in ${proto} do - iptables_both ${table} -A "${negative_chain}" -p "${pr}" -j RETURN + iptables_both ${table} -A "${negative_chain}" ${not} -p "${pr}" -j RETURN done protonot= proto=any @@ -7330,7 +7361,7 @@ rule() { do for pr in ${proto} do - iptables_both ${table} -A "${negative_chain}" -p "${pr}" --sport "${sp}" -j RETURN + iptables_both ${table} -A "${negative_chain}" -p "${pr}" ${not} --sport "${sp}" -j RETURN done done sportnot= @@ -7349,7 +7380,7 @@ rule() { do for pr in ${proto} do - iptables_both ${table} -A "${negative_chain}" -p "${pr}" --dport "${dp}" -j RETURN + iptables_both ${table} -A "${negative_chain}" -p "${pr}" ${not} --dport "${dp}" -j RETURN done done dportnot= @@ -7360,7 +7391,7 @@ rule() { then for tuid in ${uid} do - iptables_both ${table} -A "${negative_chain}" -m owner --uid-owner "${tuid}" -j RETURN + iptables_both ${table} -A "${negative_chain}" -m owner ${not} --uid-owner "${tuid}" -j RETURN done uidnot= uid=any @@ -7370,7 +7401,7 @@ rule() { then for tgid in ${gid} do - iptables_both ${table} -A "${negative_chain}" -m owner --gid-owner "${tgid}" -j RETURN + iptables_both ${table} -A "${negative_chain}" -m owner ${not} --gid-owner "${tgid}" -j RETURN done gidnot= gid=any @@ -7380,7 +7411,7 @@ rule() { then for tpid in ${pid} do - iptables_both ${table} -A "${negative_chain}" -m owner --pid-owner "${tpid}" -j RETURN + iptables_both ${table} -A "${negative_chain}" -m owner ${not} --pid-owner "${tpid}" -j RETURN done pidnot= pid=any @@ -7390,7 +7421,7 @@ rule() { then for tsid in ${sid} do - iptables_both ${table} -A "${negative_chain}" -m owner --sid-owner "${tsid}" -j RETURN + iptables_both ${table} -A "${negative_chain}" -m owner ${not} --sid-owner "${tsid}" -j RETURN done sidnot= sid=any @@ -7400,7 +7431,7 @@ rule() { then for tcmd in ${cmd} do - iptables_both ${table} -A "${negative_chain}" -m owner --cmd-owner "${tcmd}" -j RETURN + iptables_both ${table} -A "${negative_chain}" -m owner ${not} --cmd-owner "${tcmd}" -j RETURN done cmdnot= cmd=any @@ -7410,7 +7441,7 @@ rule() { then for tmark in ${mark} do - iptables_both ${table} -A "${negative_chain}" -m mark --mark "${tmark}" -j RETURN + iptables_both ${table} -A "${negative_chain}" -m mark ${not} --mark "${tmark}" -j RETURN done marknot= mark=any @@ -7420,7 +7451,7 @@ rule() { then for ttos in ${tos} do - iptables_both ${table} -A "${negative_chain}" -m tos --tos "${ttos}" -j RETURN + iptables_both ${table} -A "${negative_chain}" -m tos ${not} --tos "${ttos}" -j RETURN done tosnot= tos=any @@ -7430,57 +7461,59 @@ rule() { then for tdscp in ${dscp} do - iptables_both ${table} -A "${negative_chain}" -m dscp --dscp${dscptype} "${tdscp}" -j RETURN + iptables_both ${table} -A "${negative_chain}" -m dscp ${not} --dscp${dscptype} "${tdscp}" -j RETURN done dscpnot= dscp=any fi - - # in case this is temporary chain we created for the negative expression, - # just make it have the final action of the rule. - # ipvall - for ipv in ${ipvall} - do - case "${ipv}" in - ipv4) iptables="iptables";; - ipv6) iptables="ip6tables";; - esac - - for pr in ${proto} + if [ ! -z "${negative_action}" ] + then + # in case this is temporary chain we created for the negative expression, + # just make it have the final action of the rule. + # ipvall + for ipv in ${ipvall} do - case ${pr} in - any|ANY) - proto_arg=() - ;; - - *) - proto_arg=("-p" "${pr}") - ;; + case "${ipv}" in + ipv4) iptables="iptables";; + ipv6) iptables="ip6tables";; esac - # since this is the target of the positive rules, - # logging and accounting can be attached here - [ "$logrule" = "limit" ] && ${iptables} ${table} -A "${negative_chain}" -m limit --limit "${FIREHOL_LOG_FREQUENCY}" --limit-burst "${FIREHOL_LOG_BURST}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}" - [ "$logrule" = "normal" ] && ${iptables} ${table} -A "${negative_chain}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}" - [ ! -z "${accounting}" ] && ${iptables} ${table} -A "${negative_chain}" -m nfacct --nfacct-name "${accounting}" + for pr in ${proto} + do + case ${pr} in + any|ANY) + proto_arg=() + ;; + + *) + proto_arg=("-p" "${pr}") + ;; + esac - # the original action of the rule - test ! -z "${negative_action}" && ( rule_action_param ${iptables} "${negative_action}" "${pr}" "" "" "${table}" "${action_param[@]}" -- ${table} -A "${negative_chain}" "${proto_arg[@]}" || failed=$[failed + 1] ) + # since this is the target of the positive rules, + # logging and accounting can be attached here + [ "$logrule" = "limit" ] && ( ${iptables} ${table} -A "${negative_chain}" -m limit --limit "${FIREHOL_LOG_FREQUENCY}" --limit-burst "${FIREHOL_LOG_BURST}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}" || failed=$[failed + 1] ) + [ "$logrule" = "normal" ] && ( ${iptables} ${table} -A "${negative_chain}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}" || failed=$[failed + 1] ) + [ ! -z "${accounting}" ] && ( ${iptables} ${table} -A "${negative_chain}" -m nfacct --nfacct-name "${accounting}" || failed=$[failed + 1] ) + + # the original action of the rule + rule_action_param ${iptables} "${negative_action}" "${pr}" "" "" "${table}" "${action_param[@]}" -- ${table} -A "${negative_chain}" "${proto_arg[@]}" || failed=$[failed + 1] + done done - done - # The positive rules will just send traffic to a - # chains - there is not need for params - action_param=() + # The positive rules will just send traffic to a + # chains - there is not need for params + action_param=() - # disable logging and accounting - # already did it above - logrule=none - accounting= + # disable logging and accounting + # already did it above + logrule=none + accounting= + fi fi - + # ---------------------------------------------------------------------------------- # Process the positive rules @@ -7791,9 +7824,11 @@ rule() { # build the command basecmd=("${inf_arg[@]}" "${outf_arg[@]}" "${physdev_arg[@]}" "${inph_arg[@]}" "${outph_arg[@]}" "${limit_arg[@]}" "${iplimit_arg[@]}" "${proto_arg[@]}" "${s_arg[@]}" "${sp_arg[@]}" "${d_arg[@]}" "${dp_arg[@]}" "${owner_arg[@]}" "${uid_arg[@]}" "${gid_arg[@]}" "${pid_arg[@]}" "${sid_arg[@]}" "${cmd_arg[@]}" "${addrtype_arg[@]}" "${stp_arg[@]}" "${dtp_arg[@]}" "${state_arg[@]}" "${mc_arg[@]}" "${mark_arg[@]}" "${tos_arg[@]}" "${dscp_arg[@]}" ${custom}) - [ "$logrule" = "limit" ] && ${iptables} ${table} -A "${chain}" "${basecmd[@]}" -m limit --limit "${FIREHOL_LOG_FREQUENCY}" --limit-burst "${FIREHOL_LOG_BURST}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}" - [ "$logrule" = "normal" ] && ${iptables} ${table} -A "${chain}" "${basecmd[@]}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}" - [ ! -z "${accounting}" ] && ${iptables} ${table} -A "${chain}" "${basecmd[@]}" -m nfacct --nfacct-name "${accounting}" + [ -z "${basecmd[*]}" -a ${ignore_empty_positive} -eq 1 ] && continue + + [ "$logrule" = "limit" ] && ( ${iptables} ${table} -A "${chain}" "${basecmd[@]}" -m limit --limit "${FIREHOL_LOG_FREQUENCY}" --limit-burst "${FIREHOL_LOG_BURST}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}" || failed=$[failed + 1] ) + [ "$logrule" = "normal" ] && ( ${iptables} ${table} -A "${chain}" "${basecmd[@]}" -j ${FIREHOL_LOG_MODE} ${FIREHOL_LOG_OPTIONS} "${logopts_arg[@]}" || failed=$[failed + 1] ) + [ ! -z "${accounting}" ] && ( ${iptables} ${table} -A "${chain}" "${basecmd[@]}" -m nfacct --nfacct-name "${accounting}" || failed=$[failed + 1] ) # do it! rule_action_param ${iptables} "${action}" "${pr}" "${statenot}" "${state}" "${table}" "${action_param[@]}" -- ${table} -A "${chain}" "${basecmd[@]}" || failed=$[failed + 1] @@ -9022,12 +9057,17 @@ You can now start typing firehol configuration directives. Special interactive commands: help, show, quit EOF - + HISTFILE="${HOME}/.firehol_history" + test ! -f ${HISTFILE} && touch ${HISTFILE} + history -r + while [ 1 = 1 ] do - echo -en "${COLOR_RESET}#${COLOR_GREEN} FireHOL ${COLOR_RESET}[${COLOR_BOLD}${COLOR_BLUE}${work_cmd}${COLOR_RESET}:${COLOR_CYAN}${work_name}${COLOR_RESET}]" - read -p " > " -e -r + prompt="${COLOR_RESET}#${COLOR_GREEN} FireHOL ${COLOR_RESET}[${COLOR_BOLD}${COLOR_BLUE}${work_cmd}${COLOR_RESET}:${COLOR_CYAN}${work_name}${COLOR_RESET}] > " + eval "read -ep \$'${prompt}' -e -r REPLY" test -z "${REPLY}" && continue + history -s "${REPLY}" + history -w set_work_function -ne "Executing user input" @@ -9051,7 +9091,8 @@ Additionaly, you can use the following commands: quit to show the interactively given configuration file and quit. - in same as typing: interface eth0 internet + in|in4|in6 + same as typing: interface(4|6) eth0 world This is used as a shortcut to get into the server/client mode in which you can test the rules for certain services. @@ -9067,7 +9108,7 @@ EOF break ;; - quit) + quit|exit) echo ${CAT_CMD} "${FIREHOL_TEMP_CONFIG}" echo @@ -9075,7 +9116,17 @@ EOF ;; in) - REPLY="interface eth0 internet" + REPLY="interface eth0 world" + continue + ;; + + in4) + REPLY="interface4 eth0 world" + continue + ;; + + in6) + REPLY="interface6 eth0 world" continue ;;