Tag Archives: Ubuntu

Block and Unblock Ports on Your Firewall Quickly

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:

#!/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 -zPo "(\n?Chain $TYPE [^\n]*\n)\K(.|\n)+?[^\n][^Cc](.|\n)+?(?=\nChain [^\n]*\n)" | \
                egrep '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 -zPo "(\n?Chain $TYPE [^\n]*\n)\K(.|\n)+?[^\n][^Cc](.|\n)+?(?=\nChain [^\n]*\n)" | \
         egrep '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).

# 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:

# 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:

# 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:

# 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.

Permanently Ban Those Caught By Fail2Ban

Introduction

Fail2ban is probably one of the best intrusive detection based tools an administrator can deploy onto their system. This is especially the case if your system is connected to the internet. If you aren’t already using it; consider reading my blog entry here that talks about it.

In this blog, I provide a scripted solution that will capture the current list of banned users from Fail2Ban and make their ban permanent. This allows us to limit the constant emails one might receive about the same people trying to compromise our system(s). For those who aren’t using the emailing portion of Fail2Ban; this script still greatly takes the load off of Fail2Ban because it no longer has to manage the constant repeat offenders. Our logs are less cluttered too.

The script will address several things:

  1. It will handle multiple attacks from people within the same Class C type netmask.
  2. It will allow for the permanent bans to stick even after your system is rebooted. Unlike Fail2Ban’s list of blocked perpetrators, which is lost if the system (or iptables) is restarted.
  3. It will enforce the use of iptable’s DROP directive instead of REJECT. This is a slightly more secure approach in handling those who aren’t ever allowed to come back.
  4. It will support the fact that over time, you may want to add and remove from this new ban list I keep speaking of. Basically you can re-run the steps outlined in this blog again (and again) and not lose the addresses you’ve already blocked.
  5. The script maintains a global list of addresses in a simple delimited format. You can then choose to share this list with other systems (or colleagues) to block the same unwanted users on these systems too.

Script Dependencies and Requirements

For this script to work, you can virtually use any Linux/FreeBSD/Unix operating system as long as you’re also using fail2ban in conjunction with iptables.

The script makes use of sed, and gawk to massage the data. These tools are common an available to all operating systems. But not all of them necessarily have them installed by default. Make sure you’ve got them before proceeding:

# Fedora / CentOS 4,5,6 / RedHat 4,5,6
yum -i install gawk sed

# Ubuntu / Debian
apt-get install gawk sed

The Goods

Without further ado, the below code is documented quite heavily so that you can just copy the sections into your terminal screen as the systems root user. Don’t try the next section until you’re done with the previous.

Although this code works for me, I still must caution you all the same: I will not be held liable for any loss, damage or expense as a result of the actions taken by my script. I’ve only used it without problem with CentOS 6.x. That said, the simplicity of it should make it work with any other *nux based OS as well.

# Author: Chris Caron
# Date: Tue, Apr 7th, 2015
########################################
# Environment Variables
########################################
# YOU MUST CORRECTLY SET THESE OR THE REMAINING FUNCTIONS OF
# THIS CODE WILL NOT WORK CORRECTLY.
#
# THIS SCRIPT ASSUMES YOU ARE RUNNING AS 'root'
#
# Public Ethernet Device
# For my home system, i have this set to ppp0; You'll want to
# set this to the Ethernet device that harm can venture from.
# such as the internet.
PUBDEV=eth0

# The name of the iptables chain to manage the permanent bans
# within. The name doesn't matter so long that it doesn't
# collide with a chain you're already managing.
# Also, Do not change this value in the future because that
# will hinder the ability to upgrade/append new bans easily.
# It is doubtful that it's current set value will conflict
# with anything. Therefore just leave the name the way it is
# now:
FILTER=nuxref-perm-ban

# The script makes an effort to detect IP Addresses all
# coming from the same Class C type network.  Rather then
# have an entry per IP like fail2ban manages; we group
# them into 1 to speed the look-ups iptables preforms.
# You'll want to identify the minimum number of IP
# addresses matched within the same alike (Class C) network
# before this grouping takes place.
CLASSCGRP_MIN=2

# IP Tables configuration file read during your system
# startup.
IPTABLES_CFG=/etc/sysconfig/iptables

# IPTables Chain Index
# Identify where you want your ban list to be applied
# To reduce overhead, the banlist should be processed 'after'
# some core checks you do.  For example I have a series of other
# checks i do first such as allowing already established
# connections to pass through.  I didn't want the ban list
# being applied to these, so for my system, i set this to 11.
# Setting it to 1 is safe because it guarantees it's at least
# processed first on systems who don't actively maintain their
# own custom firewall list.
CHAINID=1

########################################
# Preparation
########################################

# First we built a massive sorted and unique list of
# already banned IP addresses.
# The below is clever enough to include previous content
# you've banned from before (if any exists):
(
   # carry over old master list
   iptables -L -n | awk "/Chain $FILTER/, $NF ~ /RETURN/" | 
    egrep DROP | sed -e 's/^[^0-9.]*([0-9]+.[0-9]+.[0-9]+).([0-9/]+).*/1 .2/g'
   # update master list with new data
   iptables -L -n | egrep REJECT | 
    sed -e 's/^[^0-9.]*([0-9]+.[0-9]+.[0-9]+).([0-9]+).*/1 .2/g'
) | sort -n | uniq > list
 
# Now we build a separate list (in memory) that will track
# all of the ip addresses that met our $CLASSCGRP_MIN flag.
CLASSC_LIST=$(cat list | cut -f1 -d' ' | uniq -c | 
    sed 's/^ *//g' | sort -n | 
    awk "int($1)>=$CLASSCGRP_MIN" | cut -f2 -d' ')
 
# We eliminate the duplicates already pulled into memory
# from the master list of IPs
for NW in $CLASSC_LIST; do sed -i -e "/^$NW /d" list; done

# Now we scan our list and repopulate them into our master
# list. We place these entries at the head of the file so
# that they'll be added to our iptable ban chain first.
for NW in $CLASSC_LIST; do sed -i "1s/^/$NW .0/24n/" list; done

# Using our list of banned IP addresses, we now generate
# the actual iptable entries (into a file called
# 'commands':
(
   # Creates the chain
   echo iptables -N $FILTER
 
   # Build List of Addresses using our list file
   while read line; do           
      IP=$(echo $line | tr -d '[:space:]')
      echo iptables -A $FILTER -s $IP -j DROP;
   done < list
 
   # Allow future iptable processing to continue 
   # after reading through this chain by appending
   # the RETURN action.
   echo iptables -A $FILTER -j RETURN
 
   # Add chain to INPUT
   echo iptables -t filter -I INPUT $CHAINID -i $PUBDEV -j $FILTER
) > commands

########################################
# IPTables (Temporary Instalment)
########################################

# Have a look at our commands if you want:
cat commands

# Apply these new rules now with the following command:
sh commands

# The commands generated in the 'commands' text file 
# are only temporary; they will be lost if your
# machine (or iptables) is ever restarted

########################################
# IPTables (Permanent Installation)
########################################
# Consider making a backup of your configuration in case you
# need to roll back
/bin/cp -f $IPTABLES_CFG $IPTABLES_CFG.backup

# Now we generate all of the commands needed to place
# into our iptables configuration file:
sed -e 's/^iptables[ ]*//g' commands | 
    egrep "^-A $FILTER -s " > commands-iptables
# Clean up old configuration
sed -i -e "/^:$FILTER -/d" "$IPTABLES_CFG"
sed -i -e "/^-A INPUT .* $FILTER$/d" "$IPTABLES_CFG"
sed -i -e "/^-A $FILTER -/d" "$IPTABLES_CFG"
 
# Now push all of our new ban entries into the iptables file
sed -i -e "/:OUTPUT .*/a:$FILTER - [0:0]" "$IPTABLES_CFG"
sed -i -e "/:$FILTER - .*/a-A INPUT -i $PUBDEV -j $FILTER" "$IPTABLES_CFG"
sed -i -e "/-A INPUT -i $PUBDEV -j $FILTER.*/r commands-iptables" "$IPTABLES_CFG"

# Now preform the following to reset all of the fail2ban
# jails you've got as well load your new permanent ban setup
service fail2ban stop
service iptables restart
service fail2ban start

# If you have a problem; just roll back your backup you
# created and rerun the 3 commands above again. You can
# have a look at the table with the following command:
iptables -L -n -v

########################################
# IPTables (Optional Tiding)
########################################
# the above will insert the banlist at the top
# The below will just correct this and move it
# clean entry(s) from INPUT
(
   while [ 1 -eq 1 ]; do
      ID=$(iptables -nL --line-numbers | 
           egrep "^[0-9]+[ t]+$FILTER " | 
           head -n 1 | 
           sed -e 's/^([0-9]+).*/1/g')
      [ -z "$ID" ] && break;
      iptables -D INPUT $ID
   done
   # Re insert at the correct $CHAINID
   iptables -t filter -I INPUT $CHAINID -i $PUBDEV -j $FILTER
)

# have another look if you want (if you tidied)
iptables -L -n -v

########################################
# Cleanup (Optional)
########################################
# Remove temporary files when your done; or save them if you
# want to port this data to another server:
rm -f list commands commands-iptables

You can undo and destroy the new entries an any time using the following:

# disassociate filter from INPUT
while [ 1 -eq 1 ]; do
   ID=$(iptables -nL --line-numbers | 
        egrep "^[0-9]+[ t]+$FILTER " | 
        head -n 1 | 
        sed -e 's/^([0-9]+).*/1/g')
   [ -z "$ID" ] && break;
   iptables -D INPUT $ID
done
# flush filter
iptables -F $FILTER
# remove filter
iptables -X $FILTER

Credit

This blog took some time to put together and test! 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.

Sources

There were not many sources used to make this entry. Most of it is just shell scripting knowledge I’ve adopted over the years mixed with some iptable commands. Here are some sources anyway that are applicable: