#!/bin/bash
#################################################################
# Created by: Eduardo Jonck                                     #
# Email: eduardo@eduardojonck.com                               #
# Website: http://openfw.com.br                                 # 
#################################################################

# 1. Static paths definition
DB_FILE="/var/lib/fail2ban/fail2ban.sqlite3"
SETTINGS_FILE="/var/efw/fail2ban/settings"
WHITELIST_FILE="/var/efw/fail2ban/whitelist"

# 2. Load ALL settings variables at once into memory natively
if [ -f "$SETTINGS_FILE" ]; then
    source "$SETTINGS_FILE"
fi

# 3. Map internal names to maintain compatibility with the rules below
TURNOFF=$FAIL2BAN_ENABLE
BANTIME=$BANTIME
MAXRETRYWEBGUI=$MAXRETRYWEBGUI
MAXRETRYSSH=$MAXRETRYSSH
MAXRETRYOPENVPN=$MAXRETRYOPENVPN
PORTOPENVPN=$PORTOPENVPN
PORTSSH=$PORTSSH

# 4. Load Whitelist converting newlines to spaces (clean and fast)
if [ -f "$WHITELIST_FILE" ]; then
    WHITELIST=$(awk -F'|' '{print $1}' "$WHITELIST_FILE" | tr '\n' ' ')
fi

# Help function
print_help() {
  echo "Usage: fail2ban-webgui [OPTIONS]."
  echo
  echo -e "Options:"
  echo
  echo -e "   --restart       - Apply Settings/Whitelist configurations and restart Fail2ban"
  echo -e "   --status        - Check if the Fail2Ban service is running"
  echo -e "   --list-bips     - List active blocked IPs for the Unban table in the WebGUI"
  echo -e "   --unban [IP]    - Unban and manually remove an IP from Fail2Ban in real time"
}

# Service restart function (Native Settings & Whitelist)
restart() {
    if [ "$TURNOFF" == "on" ] || [ "$TURNOFF" == "1" ]; then

        if [ ! -f "/var/log/openvpn/openvpn.log" ]; then
            mkdir -p /var/log/openvpn
            touch /var/log/openvpn/openvpn.log
            chmod 640 /var/log/openvpn/openvpn.log
            chown nobody:nogroup /var/log/openvpn/openvpn.log
        fi
        # ----------------------------------------------------------------------------------------------------------------

        # The 'ignoreip' parameter below ensures Fail2Ban NEVER drops the Whitelist IPs
        echo "[DEFAULT]
ignoreip = $WHITELIST
bantime = $BANTIME
banaction = iptables-multiport

[sshd]
enabled = true
port = $PORTSSH
protocol = tcp
logpath = /var/log/messages
maxretry = $MAXRETRYSSH
backend = polling

[sshd-ddos]
enabled = true
port = $PORTSSH
protocol = tcp
logpath = /var/log/messages
maxretry = $MAXRETRYSSH

[apache-auth]
enabled  = true
port     = 80,443,10443
protocol = tcp
filter   = apache-auth
logpath  = /var/log/httpd/error_log
maxretry = $MAXRETRYWEBGUI

[apache-noscript]
enabled  = true
port     = 80,443,10443
protocol = tcp
filter   = apache-noscript
logpath  = /var/log/httpd/error_log
maxretry = $MAXRETRYWEBGUI

[apache-botsearch]
enabled  = true
port     = 80,443,10443
protocol = tcp
logpath  = /var/log/httpd/error_log
maxretry = $MAXRETRYWEBGUI

[openvpn-tcp]
enabled  = true
port     = $PORTOPENVPN
filter   = openvpn
protocol = tcp
logpath  = /var/log/openvpn/openvpn.log
maxretry = $MAXRETRYOPENVPN

[openvpn-udp]
enabled  = true
port     = $PORTOPENVPN
filter   = openvpn
protocol = udp
logpath  = /var/log/openvpn/openvpn.log
maxretry = $MAXRETRYOPENVPN" > /etc/fail2ban/jail.local


        echo "[Init]
chain = INPUT
name = default
port = ssh
protocol = tcp
blocktype = REJECT --reject-with icmp-port-unreachable
returntype = RETURN
lockingopt =
iptables = iptables <lockingopt>

[Init?family=inet6]
blocktype = REJECT --reject-with icmp6-port-unreachable
iptables = ip6tables <lockingopt>" > /etc/fail2ban/action.d/iptables-common.conf

        echo "[DEFAULT]
loglevel = INFO
logtarget = SYSLOG[facility=local0]
syslogsocket = auto
socket = /var/run/fail2ban/fail2ban.sock
pidfile = /var/run/fail2ban/fail2ban.pid
dbfile = $DB_FILE
dbpurgeage = 1d
dbmaxmatches = 10

[Definition]

[Thread]" > /etc/fail2ban/fail2ban.conf


        echo "[INCLUDES]
before = iptables-common.conf

[Definition]
actionstart = <iptables> -N f2b-<name>
              <iptables> -A f2b-<name> -j <returntype>
              <iptables> -I <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>

actionstop = <iptables> -D <chain> -p <protocol> -m multiport --dports <port> -j f2b-<name>
             <actionflush>
             <iptables> -X f2b-<name>

actioncheck = <iptables> -n -L <chain> | grep -q 'f2b-<name>[ \t]'

actionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>
            <iptables> -I f2b-<name> 1 -s <ip> -j NFLOG --nflog-prefix 'FAIL2BAN-SERVICE:DROP'

actionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>
              <iptables> -D f2b-<name> -s <ip> -j NFLOG --nflog-prefix 'FAIL2BAN-SERVICE:DROP'

[Init]" > /etc/fail2ban/action.d/iptables-multiport.conf

        /etc/init.d/fail2ban restart &>/dev/null
        chmod 755 /var/run/fail2ban/fail2ban.pid
        rm -rf /tmp/nohup-fail2ban-webgui-restart.err
        rm -rf /tmp/fail2ban-webgui-restart.out

        # Guarantee: Preemptively remove any IP listed in the Whitelist from the block list
        if [ -f "$WHITELIST_FILE" ]; then
            for ip_white in $(cat "$WHITELIST_FILE"); do
                if [[ $ip_white =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
                    /usr/bin/fail2ban-client unban "$ip_white" &>/dev/null
                fi
            done
        fi

        # Add monitoring to Monit when service is enabled
        echo "check process fail2ban with pidfile /var/run/fail2ban/fail2ban.pid
    start program = \"/etc/init.d/fail2ban start\"
    stop program  = \"/etc/init.d/fail2ban stop\"" > /etc/monit.d/fail2ban
        /etc/init.d/monit reload &>/dev/null

        if [ -f /var/efw/inithooks/start.local ]; then
            sed -i '/fail2ban-webgui/d' /var/efw/inithooks/start.local
            echo "/usr/local/bin/fail2ban-webgui --restart" >> /var/efw/inithooks/start.local
        else
            echo "#!/bin/bash" > /var/efw/inithooks/start.local
            echo "/usr/local/bin/fail2ban-webgui --restart" >> /var/efw/inithooks/start.local
            chmod 755 /var/efw/inithooks/start.local
        fi

    else
        # If disabled in WebGUI, stop service and clean local startup hooks
        sed -i '/fail2ban/d' /var/efw/inithooks/start.local
        /etc/init.d/fail2ban stop &>/dev/null

        # Remove monitoring from Monit when service is disabled
        rm -f /etc/monit.d/fail2ban
        /etc/init.d/monit reload &>/dev/null
    fi
}

# Function to list active blocked IPs (Unban list)
list_bips() {
    if [ -f "$DB_FILE" ]; then
        /usr/bin/sqlite3 -separator '|' "$DB_FILE" "SELECT jail, ip, timeofban, bantime FROM bips ORDER BY timeofban DESC;"
    fi
}

# Function to remove IP from Fail2Ban firewall in real time
unban_ip() {
    local target_ip=$1
    if [[ $target_ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        /usr/bin/fail2ban-client unban "$target_ip" 2>&1
        exit $?
    else
        echo "Error: Invalid IP address format."
        exit 1
    fi
}

# Function to check if the service is UP
status() {
    service=`iptables -L -n |grep "Chain f2b" | wc -l`
    if [ $service -gt 0 ]; then
        /etc/init.d/fail2ban status
        echo -e "\n - Fail2Ban Iptables rules are active! Nothing to be done....\n"
    else
        echo "Fail2Ban rules were not applied. Applying rules..."
        restart
    fi
}

# Argument handling case for WebGUI / Sudo
case "$1" in
    --help|-h)
        print_help
        exit 0
        ;;
    --restart) 
        restart
        exit 0
        ;;
    --status)  
        status
        exit 0
        ;;
    --list-bips)
        list_bips
        exit 0
        ;;
    --unban)
        if [ -z "$2" ]; then
            echo "Error: Please specify an IP address to unban."
            exit 1
        fi
        unban_ip "$2"
        ;;
    *)
        print_help
        exit 3 
        ;;
esac