#!/usr/bin/env bash working_dir="$(dirname $(realpath $0))" ipt="${working_dir}/ip64tables" default_iptables_chain=custom-`hostname -s` function usage() { echo [ -n "$1" ] && echo -e "Error: $1\n" cat< Adds the given address to chain. Address can be either a network name, a hostname, a network IP address (with /mask), or a plain IP address. If no chain ist given on the command line, (default) chain '$default_iptables_chain' will be used. If no chain for adding the address exists, a chain will be created. Prints an error, if the given IP address already exist in chain. -F Cleanup (flush) the chain. All entries for blocking ip-addresises will be removed from chain -f Reads 'Deny from
' directives from apache style .htaccess file and adds each found address to chain. If no chain for adding the address exists, a chain will be created. -h Prints this help. -L Only affected in conjuction with '-N'. If '-L' is given, an additional chains for logging the bloccking action into file '/var/log/debug' will be created. -N Creates a new chain. If no chain is given on the command-line, chain '$default_iptables_chain' will be created. Prints an error, if chain already exist. -S Show a list of dropped ip-addresses by chain. If no chain is given on the command-line, chain '$default_iptables_chain' will be listed. Prints an error, if chain does not exist. -X Deletes a chain. If no chain is given on the command-line, chain '$default_iptables_chain' will be deleted. Prints an error, if chain does not exist. -x Deletes the given address from chain. Address can be either a network name, a hostname, a network IP address (with /mask), or a plain IP address. If no chain ist given on the command line, (default) chain '$default_iptables_chain' will be used. Prints an error, if chain does not exist. Prints also an error, if the given IP address does not exist in chain. Example: Create a new chain with logging enabled using default name ($default_iptables_chain) `basename $0` -L -N Create a new chain called 'drop-custom' without logging enabled: `basename $0` -N drop-custom Add address(es) to block '54.72.0.0/13' (Amazon) to chain 'drop-custom' `basename $0` -a 54.72.0.0/13 drop-custom Read addresses to block, given as directive in apache style from, file. Add these addresse to (default) chain '$default_iptables_chain' `basename $0` -f /vservers/nd/var/www/html/projekte/nd/htdocs/.htaccess EOF exit 1 } get_help=false add_to_chain=false cleanup_chain=false read_htaccess_file=false log_firewall=false create_chain=false list_chain=false drop_chain=false delete_from_chain=false while getopts a:Ff:hLNSXx: opt ; do case $opt in a) add_to_chain=true _ban_address=$OPTARG ;; F) cleanup_chain=true ;; f) read_htaccess_file=true htaccess_file=$OPTARG ;; h) get_help=true usage ;; L) log_firewall=true ;; N) create_chain=true ;; S) list_chain=true ;; X) drop_chain=true ;; x) delete_from_chain=true _unban_address=$OPTARG ;; \?) usage esac done shift `expr $OPTIND - 1` if $add_to_chain ; then if $create_chain || $list_chain \ || $drop_chain || $delete_from_chain \ || $read_htaccess_file || $cleanup_chain ; then usage "To many arguments!" fi elif $create_chain ; then if $add_to_chain || $list_chain \ || $drop_chain || $delete_from_chain \ || $read_htaccess_file || $cleanup_chain ; then usage "To many arguments!" fi elif $list_chain ; then if $add_to_chain || $create_chain \ || $drop_chain || $delete_from_chain \ || $read_htaccess_file || $cleanup_chain ; then usage "To many arguments!" fi elif $drop_chain ; then if $add_to_chain || $create_chain \ || $list_chain || $delete_from_chain \ || $read_htaccess_file || $cleanup_chain ; then usage "To many arguments!" fi elif $delete_from_chain ; then if $add_to_chain || $create_chain \ || $list_chain || $drop_chain \ || $read_htaccess_file || $cleanup_chain ; then usage "To many arguments!" fi elif $read_htaccess_file ; then if $add_to_chain || $create_chain \ || $list_chain || $drop_chain \ || $delete_from_chain || $cleanup_chain ; then usage "To many arguments!" fi elif $cleanup_chain ; then if $add_to_chain || $create_chain \ || $list_chain || $drop_chain \ || $delete_from_chain || $read_htaccess_file ; then usage "To many arguments!" fi fi if ! $add_to_chain && ! $create_chain \ && ! $list_chain && ! $drop_chain \ && ! $delete_from_chain \ && ! $read_htaccess_file && ! $cleanup_chain ; then usage "No options given!" fi iptables_chain=$1 iptables_chain=${iptables_chain:=$default_iptables_chain} if [ "$iptables_chain" = "$default_iptables_chain" ]; then echo -e "\n\tNo chain wa given. Using default chain '$iptables_chain'" else echo -e "\n\tUsing iptables chain: $iptables_chain" fi ## --- Some functions ## --- ## Check if a given array (parameter 2) contains a given string (parameter 1) containsElement () { local e for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done return 1 } get_address () { if [ "$#" != "1" ]; then return 1 fi _given=$1 _ip=${_given%/*} if [ "$_ip" != "$_given" ]; then _mask=${_given##*/} fi _ip_1=$(echo ${_ip} | tr "." " " | awk '{ print $1 }') _ip_2=$(echo ${_ip} | tr "." " " | awk '{ print $2 }') _ip_3=$(echo ${_ip} | tr "." " " | awk '{ print $3 }') _ip_4=$(echo ${_ip} | tr "." " " | awk '{ print $4 }') if [ -z "$_ip_4" ]; then if [ -n "$_mask" ]; then return 1 fi _ip_4=0 _mask=24 if [ -z "$_ip_3" ]; then _ip_3=0 _mask=16 if [ -z "$_ip_2" ]; then _ip_2=0 _mask=8 fi fi elif [ "$_ip_4" = "0" ]; then if [ -z "$_mask" -o "$_mask" = "32" ] ; then _mask=24 fi fi _check_address="$_ip_1.$_ip_2.$_ip_3.$_ip_4" if [ -n "$_mask" ]; then _check_address="${_check_address}/$_mask" fi _ip_address=`sipcalc $_check_address | grep -e "Network address" | awk '{print$4}'` if [ -z "$_ip_address" ]; then echo -e "\n\tWrong enty\n" return 1 elif [ -n "$_mask" ]; then _ip_address="${_ip_address}/$_mask" fi echo "$_ip_address" return 0 } create_chain () { $ipt -N $iptables_chain > /dev/null 2>&1 if [ "$?" != "0" ]; then return 1 fi $ipt -A $iptables_chain -j RETURN > /dev/null 2>&1 if [ "$?" != "0" ]; then return 1 fi #$ipt -I INPUT -p tcp -m multiport --dports http,https -j $iptables_chain > /dev/null 2>&1 $ipt -I INPUT -p all -j $iptables_chain > /dev/null 2>&1 if [ "$?" != "0" ]; then return 1 fi if $log_firewall ; then $ipt -N ${iptables_chain}-log > /dev/null 2>&1 if [ "$?" != "0" ]; then return 1 fi $ipt -I ${iptables_chain}-log -j LOG --log-prefix "$iptables_chain: " --log-level debug > /dev/null 2>&1 if [ "$?" != "0" ]; then return 1 fi $ipt -A ${iptables_chain}-log -j DROP > /dev/null 2>&1 if [ "$?" != "0" ]; then return 1 fi fi return 0 } remove_chain () { if `$ipt -n -L ${iptables_chain}-log > /dev/null 2>&1` ; then $ipt -F ${iptables_chain}-log > /dev/null 2>&1 if [ "$?" != "0" ]; then return 1 fi fi #$ipt -D INPUT -p tcp -m multiport --dports http,https -j $iptables_chain > /dev/null 2>&1 $ipt -D INPUT -p all -j $iptables_chain if [ "$?" != "0" ]; then return 1 fi $ipt -F $iptables_chain > /dev/null 2>&1 if [ "$?" != "0" ]; then return 1 fi if `$ipt -n -L ${iptables_chain}-log > /dev/null 2>&1` ; then $ipt -X ${iptables_chain}-log > /dev/null 2>&1 if [ "$?" != "0" ]; then return 1 fi fi $ipt -X $iptables_chain > /dev/null 2>&1 if [ "$?" != "0" ]; then return 1 fi return 0 } flush_chain () { if `$ipt -n -L ${iptables_chain}-log > /dev/null 2>&1` ; then $ipt -F ${iptables_chain}-log > /dev/null 2>&1 if [ "$?" != "0" ]; then return 1 fi fi $ipt -F $iptables_chain > /dev/null 2>&1 if [ "$?" != "0" ]; then return 1 fi return 0 } echononl(){ echo X\\c > /tmp/shprompt$$ if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then echo "$*\\c" 1>&2 else echo -e -n "$*" 1>&2 fi rm /tmp/shprompt$$ } info (){ echo "" echo -e "\t[ \033[32m\033[1mInfo\033[m ]: $*" echo "" } warn (){ echo "" echo -e "\t[ \033[33m\033[1mWarning\033[m ]: $*" echo "" } error (){ echo "" echo -e "\t[ \033[31m\033[1mError\033[m ]: $*" echo "" } echo_ok() { echo -e "\033[60G[ \033[32mok\033[m ]" } echo_failed(){ echo -e "\033[60G[ \033[1;31mfailed\033[m ]" } echo_skipped() { echo -e "\033[60G[ \033[33m\033[1mskipped\033[m ]" } ## --- ## --- END: functions echo if $create_chain ; then echononl "\tCreate Chain '$iptables_chain'.." if `$ipt -n -L $iptables_chain > /dev/null 2>&1` ; then echo_skipped warn "Chain '$iptables_chain' already exists" else create_chain if [ "$?" = "0" ]; then echo_ok else echo_failed fi fi echo exit fi if $cleanup_chain ; then echononl "\tFlush Chain '$iptables_chain'.." if ! `$ipt -n -L $iptables_chain > /dev/null 2>&1` ; then echo_failed error "Chain '$iptables_chain' does not exist!" else flush_chain if [ "$?" = "0" ]; then echo_ok else echo_failed fi fi echo exit fi if $drop_chain ; then echononl "\tDrop Chain '$iptables_chain'.." if ! `$ipt -n -L $iptables_chain > /dev/null 2>&1` ; then echo_skipped warn "Chain '$iptables_chain' does not exist!" else remove_chain if [ "$?" = "0" ]; then echo_ok else echo_failed fi fi echo exit fi if $list_chain ; then echononl "\tList of dropped IPs (Chain '$iptables_chain').." if ! `$ipt -n -L $iptables_chain > /dev/null 2>&1` ; then echo_failed error "Chain '$iptables_chain' does not exist!" else echo_ok echo _tmp_file=`mktemp` if `$ipt -n -L ${iptables_chain}-log > /dev/null 2>&1` ; then $ipt -n -L $iptables_chain | grep -i -e "^${iptables_chain}-log\ " > $_tmp_file else $ipt -n -L $iptables_chain | grep -i -e "^DROP\ " > $_tmp_file fi if [ -z "`cat $_tmp_file`" ]; then warn "No IPs to drop in Chain '$iptables_chain" else while IFS='' read -r line || [[ -n $line ]]; do echo -e "\t$line" done < $_tmp_file rm -f $_tmp_file fi echo fi echo exit fi if $add_to_chain ; then ban_address=`get_address $_ban_address` if [ "$?" != "0" ];then error "Given address may be wrong" exit 1 fi if ! `$ipt -n -L $iptables_chain > /dev/null 2>&1` ; then warn "Chain '$iptables_chain' does not exist. Trying to create one.." echononl "\tCreate chain '$iptables_chain'.." create_chain if [ "$?" = "0" ]; then echo_ok echo else echo_failed echo exit 1 fi fi ## - Create empty array ## - _ip_banned_arr=() echononl "\tAdd IP '$ban_address' to chain '$iptables_chain'.." if ! `$ipt -n -L $iptables_chain > /dev/null 2>&1` ; then echo_skipped error "Chain '$iptables_chain' does not exist!" else _ips_banned=`iptables -n -L $iptables_chain | grep -i -e "^DROP\ " | awk '{print$4}'` for _ip in $_ips_banned ; do _ip_banned_arr+=($_ip); done if containsElement "$ban_address" "${_ip_banned_arr[@]}" ; then echo_skipped warn "Chain '$iptables_chain' contains already IP 'ban_address'" else if `$ipt -n -L ${iptables_chain}-log > /dev/null 2>&1` ; then $ipt -I $iptables_chain 1 -s $ban_address -j ${iptables_chain}-log else $ipt -I $iptables_chain 1 -s $ban_address -j DROP fi if [ "$?" = "0" ]; then echo_ok else echo_failed fi fi fi echo exit fi if $delete_from_chain ; then unban_address=`get_address $_unban_address` if [ "$?" != "0" ];then error "Given address may be wrong" exit 1 fi ## - Create empty array ## - _ip_banned_arr=() echononl "\tRemove IP '$unban_address' from chain '$iptables_chain'.." if ! `$ipt -n -L $iptables_chain > /dev/null 2>&1` ; then echo_skipped warn "Chain '$iptables_chain' does not exist!" else ## - Read list of already blocked addresses ## - if `$ipt -n -L ${iptables_chain}-log > /dev/null 2>&1` ; then _ips_banned=`iptables -n -L $iptables_chain | grep "${iptables_chain}-log" | awk '{print$4}'` else _ips_banned=`iptables -n -L $iptables_chain | grep -i -e "^DROP\ " | awk '{print$4}'` fi for _ip in $_ips_banned ; do _ip_banned_arr+=($_ip); done ## - Check if given address is in list of blocked addresses. If ## - address is not blocked, nothing to do.. ## - if ! containsElement "$unban_address" "${_ip_banned_arr[@]}" ; then echo_skipped warn "Chain '$iptables_chain' does not caontain IP '$unban_address'" else if `$ipt -n -L ${iptables_chain}-log > /dev/null 2>&1` ; then $ipt -D $iptables_chain -s $unban_address -j ${iptables_chain}-log else $ipt -D $iptables_chain -s $unban_address -j DROP fi if [ "$?" = "0" ]; then echo_ok else echo_failed fi fi fi echo exit fi if $read_htaccess_file ; then if [ ! -f "$htaccess_file" ]; then error "File '$htaccess_file' not found!" exit 1 fi if ! `$ipt -n -L $iptables_chain > /dev/null 2>&1` ; then warn "Chain '$iptables_chain' does not exist. Trying to create one.." echononl "\tCreate chain '$iptables_chain'.." create_chain if [ "$?" = "0" ]; then echo_ok echo else echo_failed echo exit 1 fi fi ## - Read adresses from htaccess file ## - _ips_to_ban=`cat $htaccess_file | grep -e "^\s*Deny from " | awk '{print$3}'` for _ban_address in $_ips_to_ban ; do echononl "\tAdd IP '$_ban_address' to chain '$iptables_chain'.." ban_address=`get_address $_ban_address` if [ "$?" != "0" ];then echo_failed error "Given address may be wrong" continue fi ## - Read list of already blocked addresses ## - if `$ipt -n -L ${iptables_chain}-log > /dev/null 2>&1` ; then _ips_banned=`iptables -n -L $iptables_chain | grep "${iptables_chain}-log" | awk '{print$4}'` else _ips_banned=`iptables -n -L $iptables_chain | grep -i -e "^DROP\ " | awk '{print$4}'` fi ## - Check if given address is in list of blocked addresses. If ## - address is already in that list, nothing to do.. ## - for _ip in $_ips_banned ; do _ip_banned_arr+=($_ip); done if containsElement "$ban_address" "${_ip_banned_arr[@]}" ; then echo_skipped warn "Chain '$iptables_chain' contains already IP 'ban_address'" else if `$ipt -n -L ${iptables_chain}-log > /dev/null 2>&1` ; then $ipt -I $iptables_chain 1 -s $ban_address -j ${iptables_chain}-log > /dev/null 2>&1 else $ipt -I $iptables_chain 1 -s $ban_address -j DROP > /dev/null 2>&1 fi if [ "$?" = "0" ]; then echo_ok else echo_failed fi fi done echo exit 0 fi exit 0 for _ip in ${_ip_banned_arr[@]} ; do echo $_ip done exit 0