Introduction
This blog just shares and explains the function of a script I created for *nux based systems that can be used to quickly block and unblock ports on your firewall. It works with both IPTables and FirewallD. It can also be easily configured to work with a cron and ran at key times.
There are several reasons why you might want to quickly block a port on your firewall.
- Maybe you suspect someone is intruding or violating your system.
- Maybe you want to leave your computer running (it’s crunching data for you), but for security reasons you want to close all of the ports when you’re not around (such as after work hours or over the weekend).
The Goods
At the present time, the script supports blocking inbound tcp and udp ports. It also supports blocking port ranges.
Simply copy this into a script file which I called blockport.sh (don’t forget to make it executable) and place it in your path:
001 002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 017 018 019 020 021 022 023 024 025 026 027 028 029 030 031 032 033 034 035 036 037 038 039 040 041 042 043 044 045 046 047 048 049 050 051 052 053 054 055 056 057 058 059 060 061 062 063 064 065 066 067 068 069 070 071 072 073 074 075 076 077 078 079 080 081 082 083 084 085 086 087 088 089 090 091 092 093 094 095 096 097 098 099 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | #!/bin/sh # Name: blockport.sh # Author: Chris Caron <lead2gold@gmail.com> # Date: Jul 17th, 2015 # Description: A simple script that blocks and unblocks outbound traffic # The intent is to set up a cron that will block stuff at night # and then unblock it in the morning every day preventing # any unnecessary things from making outbound requests. # # Dependencies: sed, logger, grep, and iptables # # This script must run as root! SCRIPTNAME=$( basename $0) IPTABLES_BIN=iptables IPTABLES_PRINT= echo # Set the command to what you want to actually do # some scripts explicitly reference the above IPTABLES=$IPTABLES_PRINT IPTABLES=$IPTABLES_BIN # Default Line Numbers (where inserts will take place IPTABLES_OUTPUT_LINE=${IPTABLES_OUTPUT_LINE:=1} IPTABLES_INPUT_LINE=${IPTABLES_INPUT_LINE:=1} show_syntax () { echo "Syntax: \$> $SCRIPTNAME <Action> [Options]" echo " Actions:" echo " -l" echo " --list List the ports blocked by this script." echo echo " -bo PORT" echo " --block-outbound=PORT Blocks outbound ports (both udp and tcp). You can" echo " use space or comma to delimite multiple ports" echo " -uo PORT" echo " --unblock-outbound=PORT Unblocks outbound ports (both udp and tcp)" echo " previously blocked. You can use space or comma to" echo " delimit multiple ports" echo " -bi PORT" echo " --block-inbound=PORT Blocks inbound ports (both udp and tcp). You can" echo " use space or comma to delimite multiple ports" echo " -ui PORT" echo " --unblock-inbound=PORT Unblocks inbound ports (both udp and tcp)" echo " previously blocked. You can use space or comma to" echo " delimit multiple ports" echo "" } clean_ports(){ # A Simple function that cleans up port ranges local PORTS=$( echo $1 | sed -e 's/[, \\/]\+/ /g' -e 's/[;-]\+/:/' \ -e 's/[^:0-9 ]//g' -e 's/^[^0-9]\+//g' -e 's/[^0-9]\+$//g' ) [ -z "$PORTS" ] && return 1 echo "$PORTS" return 0 } indexes() { # Possible types are OUTPUT, INPUT, and FORWARD local TYPE=$1 # A simple function that returns the index(es) of a iptable # entry (if it's blocked or not). local PORTS=$(clean_ports $2) [ $? - ne 0 ] && return 1 local INDEXES= "" # This magical line was constructed off of what i learned here: # http://unix.stackexchange.com/questions/129087/grep-log-and-get-text-between-log-delimiters # It extracts the DROP lines we created in the OUTPUT Chain for PORT in $PORTS; do INDEX=$($IPTABLES_BIN -nL --line-numbers | \ grep -a -zPo "(\n?Chain $TYPE [^\n]*\n)\K(.|\n)+?[^\n][^Cc](.|\n)+?(?=\nChain [^\n]*\n)" | \ egrep -a 'DROP' | egrep '^[0-9]' | egrep " dpts?:$PORT[ \t]*\$" | \ sed -e 's/^[ \t]*\([0-9]\+\)[^0-9].*/\1/g' ) INDEXES= "$INDEXES $INDEX" done [ -z "$INDEXES" ] && return 1 # Sort the INDEXES (largest # to smallest) because we want to # process the list backwards INDEXES=$( echo $INDEXES | tr -s '[:space:]' '\n' | sort -n -r | uniq ) echo $INDEXES return 0 } unblock() { # Possible types are OUTPUT, INPUT, and FORWARD local TYPE=$1 # A simple function that returns the index(es) of a iptable # entry (if it's blocked or not). local PORTS=$2 # Defaults [ -z "$TYPE" ] && TYPE=INPUT # Stores the indexes (if set) local INDEXES= "$(indexes $TYPE $PORTS)" [ -z "$INDEXES" ] && return 0 # Sort the INDEXES (largest # to smallest) because we want to # process the list backwards INDEXES=$( echo $INDEXES | tr -s '[:space:]' '\n' | sort -n -r | uniq ) for INDEX in $INDEXES; do $IPTABLES -D $TYPE $INDEX done logger "INFO - blockport.sh: Unblocked $TYPE $PORTS" return 0 } block(){ # Possible types are OUTPUT, INPUT, and FORWARD local TYPE=$1 # A simple function that returns the index(es) of a iptable # entry (if it's blocked or not). local PORTS=$(clean_ports $2) [ $? - ne 0 ] && return 1 # Defaults [ -z "$TYPE" ] && TYPE=INPUT # If indexes already exist, then we don't have to do anything local INDEXES= "$(indexes $TYPE $PORTS)" [ ! -z "$INDEXES" ] && return 0 local LINE=$( eval "echo \$IPTABLES_${TYPE}_LINE" ) [ -z $LINE ] && LINE=1 for PORT in $PORTS; do $IPTABLES -I $TYPE $LINE -p tcp -s 0 /0 --destination-port $PORT -j DROP $IPTABLES -I $TYPE $LINE -p udp -s 0 /0 --destination-port $PORT -j DROP done logger "INFO - blockport.sh: Blocked $TYPE $PORTS" return 0 } list() { echo for TYPE in "INPUT" "OUTPUT" ; do echo "Listing $TYPE Ports Blocked:" $IPTABLES_BIN -nL --line-numbers | \ grep -a -zPo "(\n?Chain $TYPE [^\n]*\n)\K(.|\n)+?[^\n][^Cc](.|\n)+?(?=\nChain [^\n]*\n)" | \ egrep -a 'DROP' | egrep '^[0-9]' | egrep " dpts?:[0-9:]+[ \t]*\$" echo done return 0 } ########################################################## ## Main ## ########################################################## PATH= /bin : /sbin : /usr/bin : /usr/sbin if [ $( whoami ) != "root" ]; then echo "Error: you must be root to execute this script." fi ACTION= "x" RETVAL=0 while : ; do case $1 in -l) list; exit 0 ;; --list) list; exit 0 ;; -bo) ACTION= 'bo' ; block "OUTPUT" $2; [ $? - ne 0 ] && RETVAL=1 shift ; shift ;; --block-outbound) ACTION= 'bo' ; block "OUTPUT" $( echo $1 | sed -e 's/--block-outbound=//g' -e "s/'//g" -e 's/\" //g ') [ $? - ne 0 ] && RETVAL=1 shift ;; -uo) ACTION= 'uo' ; unblock "OUTPUT" $2; [ $? - ne 0 ] && RETVAL=1 shift ; shift ;; --unblock-outbound) ACTION= 'uo' ; unblock "OUTPUT" $( echo $1 | sed -e 's/--unblock-outbound=//g' -e "s/'//g" -e 's/\" //g ') [ $? - ne 0 ] && RETVAL=1 shift ;; -bi) ACTION= 'bi' ; block "INPUT" $2; [ $? - ne 0 ] && RETVAL=1 shift ; shift ;; --block-inbound) ACTION= 'bi' ; block "INPUT" $( echo $1 | sed -e 's/--block-inbound=//g' -e "s/'//g" -e 's/\" //g ') [ $? - ne 0 ] && RETVAL=1 shift ;; -ui) ACTION= 'ui' ; unblock "INPUT" $2; [ $? - ne 0 ] && RETVAL=1 shift ; shift ;; --unblock-inbound) ACTION= 'ui' ; unblock "INPUT" $( echo $1 | sed -e 's/--unblock-inbound=//g' -e "s/'//g" -e 's/\" //g ') [ $? - ne 0 ] && RETVAL=1 shift ;; -h) show_syntax ; exit 0 ;; --help) show_syntax ; exit 0 ;; *) if [ -z "$1" ]; then break ; fi echo "[error] Invalid option '$1' specified; see --help (-h) for more info." exit 1 ;; esac done if [ $ACTION == "x" ]; then show_syntax exit 1 fi exit $RETVAL |
Again, I state: this script must be ran as root (because it wraps IPTables). So stick sudo in front of it’s calls if running as a regular user (assuming you’re set up with sudoer’s privileges).
Syntax
Syntax: $> blockport.sh [Options] Actions: -l --list List the ports blocked by this script. -bo PORT --block-outbound=PORT Blocks outbound ports (both udp and tcp). You can use space or comma to delimit multiple ports -uo PORT --unblock-outbound=PORT Unblocks outbound ports (both udp and tcp) previously blocked. You can use space or comma to delimit multiple ports -bi PORT --block-inbound=PORT Blocks inbound ports (both udp and tcp). You can use space or comma to delimit multiple ports -ui PORT --unblock-inbound=PORT Unblocks inbound ports (both udp and tcp) previously blocked. You can use space or comma to delimit multiple ports
Demo
Here is a simple example that just blocks 2 ports. We can chain more than one port by placing a comma in between each one. It is also valid to keep using one switch after another (they’ll be executed in order).
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # Here is how easy it is to use; first we'll block (inbound) ports 80 and 443 blockport.sh -bi 80,443 # This is also valid syntax: # blockport.sh -bi 80 -bi 443 # We know they're blocked now, but we can have a look anyway: blockport.sh -l # The output will look like this: # Listing INPUT Ports Blocked: # 1 DROP udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:443 # 2 DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 # 3 DROP udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:80 # 4 DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 # # Listing OUTPUT Ports Blocked: # # We can reverse this by typing: blockport.sh -ui 80,443 # This is also valid syntax: # blockport.sh -ui 80 -ui 443 |
You can do ranges too; just use the colon (:) or hyphen (-). In the example below, we block a range of outgoing traffic from a system:
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # Below blocks outbound ports 20, 21, and all ports (and including) 8000-8500. blockport.sh -bo 20,21,8000-8500 # This is also valid syntax: # blockport.sh -bo 20 -bo 21 -bo 8000-8500 # We know they're blocked now, but we can have a look anyway: blockport.sh -l # The output will look like this: # Listing INPUT Ports Blocked: # # Listing OUTPUT Ports Blocked: # 1 DROP udp -- 0.0.0.0/0 0.0.0.0/0 udp dpts:8000:8500 # 2 DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpts:8000:8500 # 3 DROP udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:21 # 4 DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:21 # 5 DROP udp -- 0.0.0.0/0 0.0.0.0/0 udp dpt:20 # 6 DROP tcp -- 0.0.0.0/0 0.0.0.0/0 tcp dpt:20 # We can reverse this by typing: blockport.sh -uo 20,21,8000-8500 # This is also valid syntax: # blockport.sh -uo 20 -uo 21 -uo 8000-8500 |
Note: You can only unblock what you block. Here is an example of what I mean:
01 02 03 04 05 06 07 08 09 10 11 12 | # Blocking a range: blockport.sh -bo 8000-8500 # You CAN NOT just unblock a port from it (this will not work): blockport.sh -uo 8400 # Similarly, you can not block individual ports and then try to unblock them # as a range: blockport.sh -bo 20,21 # You CAN NOT just unblock this as a range (this will not work): blockport.sh -uo 20-21 |
Caution
This script is intended to be an instant port blocker. It intentionally destroys any pre-established connections utilizing the port marked. Keep this in mind so that you don’t block yourself out of your own Server or VPS. Hence DON’T CLOSE PORT 22 unless you know what and why you’re doing it. You have been warned!
Use Cases
Suppose you want to deny access out of a server you host for your company after hours, you could create a cron like this:
1 2 3 4 5 6 7 | # Block defined outbound ports at around 5:30pm every evening on Weekdays # (Mon - Fri) 30 17 * * 1-5 /root/bin/blockport .sh -bo 80,443,22,21 &> /dev/null # Unblock the defined ports every morning at 7am on Weekdays # (Mon - Fri) keeping them blocked over the weekends 0 7 * * 1-5 /root/bin/blockport .sh -uo 80,443,22,20,21 &> /dev/null |
Alternatively, maybe something looked bad in /var/log/messages or /var/log/audit/audit.log to you and you simply just want to immediately block the port.
Credit
If you like what you see and wish to copy and paste this HOWTO, please reference back to this blog post at the very least. It’s really all I ask.