Skip to content

fail2ban

In this laboratory, we will explore the security implications of network connected devices and how to protect them from external attacks. You have a NanoPi device with a buildroot Linux system installed that we will modify to have the relevant tools installed. That is: netfilter and iptables.

Preparation tasks

The goal of this part of the lab is to setup your NanoPi with a newly modified configuration to that you can make use of the said tools.

For this you will need to:

  1. Ensure glibc is configured and with the correct other settings
    Toolchain  --->
        Toolchain type → Buildroot toolchain
        C library → glibc
    

  1. Install python

    Target packages --->
        Interpreter languages and scripting --->
            python3 → enable
    

  2. Install a second user

    1. create a users’ file (e.g. users.conf) whose location should be board/friendlyarm/nanopi-neo-plus2/users.conf

    2. add it to the configuration

      System configuration --->
          (board/friendlyarm/nanopi-neo-plus2/users.conf) Path to the users tables
      

    3. add the desired user(s) configuration - here the exemple for a foo user

      foo -1 bar -1 !=blabla /home/foo /bin/sh wheel,adm Foo user
      

      Warning

      Should you have any issue logging in with user foo, use the passwd command from the root account

      Tip

      The reason for having a second user is that in case you gets blocked out, there is a second user with

fail2ban installation

In order to achieve the installation of fail2ban, follow the steps described below.

  1. On the host

    1. Clone the repository https://github.com/fail2ban/fail2ban.git
    2. Create dedicated SeS branch
    3. As the /etc/fail2ban/filter.d/sshd.conf filter is not working with the logging format we use, apply the following patch to it

      diff --git a/config/filter.d/sshd.conf b/config/filter.d/sshd.conf
      index 80d0c349..3375aa4b 100644
      --- a/config/filter.d/sshd.conf
      +++ b/config/filter.d/sshd.conf
      @@ -106,9 +106,13 @@ cmnfailre-failed-pub-ignore =
      
       cfooterre = ^<F-NOFAIL>Connection from</F-NOFAIL> <HOST>
      
      -failregex = %(cmnfailre)s
      -            <mdre-<mode>>
      -            %(cfooterre)s
      +
      +# --- BusyBox match ---
      +#failregex = ^.*sshd\[.*\]: Failed password for .* from <HOST> port \d+ ssh2$
      +#failregex = ^Failed password for .* from <HOST> port \d+ ssh2$
      +failregex = ^.*sshd(?:\[\d+\])?: Failed password for (?:invalid user )?.* from <HOST>
      +            ^.*sshd(?:\[\d+\])?: Invalid user .* from <HOST>
      +            ^.*sshd(?:\[\d+\])?: maximum authentication attempts exceeded for .* from <HOST>
      
       # Parameter "mode": normal (default), ddos, extra or aggressive (combines all)
       # Usage example (for jail.local):
      

      Warning

      It may be that you need to adapt the patch containing ^.*sshd(?:\[\d+\]) needs to be adapted to your installation. For instance, you may have sshd-session) and not simply sshd.

    4. Copy fail2ban on your target using (do change what is needed)

      #!/bin/bash
      
      #############################################
      # Configuration
      #############################################
      TARGET_IP="192.168.0.14"            # <-- Set target device IP here
      TARGET_USER="root"
      TARGET_BASE="/usr/share/fail2ban"   # runtime files directory
      TARGET_BIN="/usr/bin"               # executables go here
      SOURCE_DIR="."                      # local clone of Fail2ban repo
      
      # Detect Python version on target
      PYVER=$(ssh ${TARGET_USER}@${TARGET_IP} "python3 -c 'import sys; print(f\"{sys.version_info.major}.{sys.version_info.minor}\")'")
      
      TARGET_SITEPKG="/usr/lib/python${PYVER}/site-packages"
      TARGET_ETC="/etc/fail2ban"
      TARGET_BIN="/usr/bin"
      
      echo "[+] Creating directory structure on target..."
      ssh ${TARGET_USER}@${TARGET_IP} "
          mkdir -p ${TARGET_SITEPKG} &&
          mkdir -p ${TARGET_ETC} &&
          mkdir -p ${TARGET_BIN}
      "
      
      echo "[+] Transferring Python package fail2ban -> ${TARGET_SITEPKG} ..."
      scp -r \
          ${SOURCE_DIR}/fail2ban \
          ${TARGET_USER}@${TARGET_IP}:${TARGET_SITEPKG}/
      
      echo "[+] Transferring config directory -> ${TARGET_ETC} ..."
      scp -r \
          ${SOURCE_DIR}/config/* \
          ${TARGET_USER}@${TARGET_IP}:${TARGET_ETC}/
      
      echo "[+] Transferring executables -> ${TARGET_BIN} ..."
      scp \
          ${SOURCE_DIR}/bin/fail2ban-server \
          ${SOURCE_DIR}/bin/fail2ban-client \
          ${SOURCE_DIR}/bin/fail2ban-regex \
          ${TARGET_USER}@${TARGET_IP}:${TARGET_BIN}/
      
      ssh ${TARGET_USER}@${TARGET_IP} "
          chmod +x ${TARGET_BIN}/fail2ban-server
          chmod +x ${TARGET_BIN}/fail2ban-client
          chmod +x ${TARGET_BIN}/fail2ban-regex
      "
      
      echo "[+] Deployment complete!"
      echo "[+] To start fail2ban"
      echo "    fail2ban-client start"
      echo "(Once you have configured the system ;-)))"
      

  2. On the target

    1. Create a jail.local with the following content
      [ssh]
      enabled = true
      port = ssh
      filter = sshd
      logpath = /var/log/messages
      action = iptables[name=SSH, port=ssh, protocol=tcp]
      maxretry = 3
      bantime = 600
      
    2. Make it permanent by creating a S45fail2ban init script (to be located in /etc/init.d)
      #!/bin/sh
      # Fail2ban init script for BusyBox systems
      
      ### BEGIN INIT INFO
      # Provides:          fail2ban
      # Required-Start:    
      # Required-Stop:     
      # Default-Start:     3 4 5
      # Default-Stop:      0 1 2 6
      # Short-Description: Start fail2ban
      ### END INIT INFO
      
      umask 077
      
      LOGTAG="fail2ban-init"
      
      log() {
          # Print to console and syslog
          echo "$1"
          logger -t "$LOGTAG" "$1"
      }
      
      start() {
          log "Starting fail2ban..."
      
          [ -d /var/run/fail2ban ] || mkdir -p /var/run/fail2ban
      
          if /usr/bin/fail2ban-client start; then
              touch /var/lock/fail2ban
              log "Fail2ban started successfully."
              return 0
          else
              log "Failed to start fail2ban."
              return 1
          fi
      }
      
      stop() {
          log "Stopping fail2ban..."
      
          if /usr/bin/fail2ban-client stop; then
              rm -rf /var/run/fail2ban
              rm -f /var/lock/fail2ban
              log "Fail2ban stopped successfully."
              return 0
          else
              log "Failed to stop fail2ban."
              return 1
          fi
      }
      
      restart() {
          stop
          start
      }
      
      case "$1" in
        start)
              start
              ;;
        stop)
              stop
              ;;
        restart|reload)
              restart
              ;;
        *)
              echo "Usage: $0 {start|stop|restart}"
              exit 1
      esac
      
      exit $?
      
  3. Experiment with login attempts

    • use ssh to login onto the NanoPi as user foo
    • make sure you type the wrong password to see fail2ban in action
    • check iptables -L after 3 failed attempts
    • try out other options - your imagination may be the sole limit 😉

Questions

Tip

  • In order to know how to check the validity of one’s configuration, issue :
    PYTHONPATH=. python3 bin/fail2ban-regex messages config/filter.d/yourfilter.conf
    

Question

Warning

Before answering, make sure you add your ses_deconfig to your repository with an appropriate tag.

  1. Write down the content of iptables

    1. when no user or machine has been banned
    2. when at least one user or machine has been banned
  2. Assuming you have access to the NanoPi still, how could you remove the rules? Write down the command used for it.

  3. If a target had the following configured in jail.local

    [DEFAULT]
    protocol = all
    banaction = iptables-allports
    
    [ssh]
    enabled = true
    filter = sshd
    logpath = /var/log/messages
    action = iptables-allports[name=SSH]
    maxretry = 3
    bantime = 600
    
    what would be the difference(s) in iptables content and behaviour? Try it out on your NanoPi and report the outcome.

    Note

    You would need the following in /etc/fail2ban/action.d/iptables-allports.local

    [Definition]
    # Override the jump insertion
    actionstart = iptables -N f2b-<name>
                iptables -I INPUT -p all -j f2b-<name>
    
    actionstop = iptables -D INPUT -p all -j f2b-<name>
                iptables -F f2b-<name>
                iptables -X f2b-<name>
    
  4. Patching sshd.conf as it is done in this description is against what is promoted by the fail2ban project. As a result, create a cleaner, dedicated configuration. Write down

    1. what you did - including the used commands
    2. add the files that were modified or created to the report
  5. OPTIONAL (may give a bonus)

    Copying files around is not the best of solutions. A better one would be to create a dedicated package that does all the tedious manual work. So, how to create such a dedicated package? Write down all the steps and files that are needed for achieving this.