Support Forum
The Forums are a place to find answers on a range of Fortinet products from peers and product experts.
xfg
New Contributor

Automatically Ban Malicious Inbound IPs using Webhook Automation and External Connector

Using a combination of a Webhook automation stitch, External Connector, and a Linux machine running a simple webapp, it is possible to automatically add malicious IPs (e.g. brute-force SSLVPN login attempts or otherwise) to a threatfeed and prevent the IPs from accessing the resource.

 

This tutorial will walk through the setup for banning IPs that have multiple failed SSLVPN login attempts.

Full disclosure, I'm not a programmer and ChatGPT helped with the python code.

 

 

On the Linux machine, install fail2ban:

 

sudo apt-get update
sudo apt-get install fail2ban

 

 

Create a custom Filter to match the malicious IP in a log file.

 

sudo nano /etc/fail2ban/filter.d/fortigate_remote_ip.conf
[Definition]
failregex = remip=(?P<ip4>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})
ignoreregex =

 

 

Configure a Jail (note: creating a jail.local file will override the default fail2ban jail settings):

Adjust "maxretry" depending on number of times you want to permit failed attempts before the IP is banned permanently.

 

sudo nano /etc/fail2ban/jail.local
[fortigate-vpn-loginfail]
enabled = true
filter = fortigate_remote_ip
action = add-to-list
logpath = /path/to/logfile/failed_logins.log
findtime = 60 #in seconds 
maxretry = 2 #max attempts before ban
bantime = 3600 #optional

 

 

Create the Action (add-to-list):

 

sudo nano /etc/fail2ban/action.d/add-to-list.conf
[Definition]
actionstart =
actionstop =
actioncheck =
actionban = echo "<ip>" >> /path/to/blocked_ips_list.txt
actionunban =

 

 

Restart fail2ban to apply changes:

 

sudo systemctl restart fail2ban

 

 

Install Python (if not already installed) and install Flask:

 

pip install Flask

 

 

Configure a Flask web application to host the list of banned IPs and to receive the log files from the firewall:

Note: to keep things simple, the blocked_ips_list file, log file, and web app file should all be in the same directory on the Linux machine

 

sudo nano /path/to/blocked_ips_list/web_app.py
from flask import Flask, request, jsonify, send_from_directory, abort

app = Flask(__name__)  # Initialize the Flask application

# Route to handle POST requests at '/receive-logs' endpoint
@app.route('/receive-logs', methods=['POST'])
def receive_logs():
    if request.method == 'POST':  # Check if the current request is a POST
        log_data = request.form  # Extract the data from the form sent with POST
        print("Received log:", log_data)  # Debug statement to print received log data

        # Convert the ImmutableMultiDict to a regular dictionary and convert to string for logging
        log_dict = dict(log_data)
        log_string = str(log_dict)

        # Open a file in append mode and write the log string to it
        with open('failed_logins.log', 'a') as f:
            f.write(log_string + '\n')  # Append a new line for each new log entry

        # Return a JSON response indicating success
        return jsonify({'status': 'success'}), 200

# Route to handle GET requests at '/blocked-ips' endpoint
@app.route('/blocked-ips', methods=['GET'])
def send_blocked_ips():
    try:
        # Attempt to serve the blocked_ips_list.txt file from the current directory
        return send_from_directory('.', 'blocked_ips_list.txt')  # Serve the file to the client
    except FileNotFoundError:
        # If the file does not exist, return a 404 error
        abort(404)

# Main block to run the application
if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)  # Start the server on port 5000 and allow connections from any host

 

 

Run the web app to start the server on its default port 5000:

 

python3 web_app.py

 

 

On the Fortigate, configure an Automation stitch:

  • Create a Trigger for failed SSL VPN Logins

 

config system automation-trigger
    edit "SSLVPN Login Failed"
        set event-type event-log
        set logid 39426
    next
end

 

 

  • Create an Action to send the log entry to the Linux server

 

config system automation-action
    edit "Send_VPN_LoginFail_Logs"
        set action-type webhook
        set uri "IP_or_hostname_of_Linux_server/receive-logs"
        set http-body "%%log%%"
        set port 5000
        config http-headers
            edit 1
                set key "Header"
                set value "Fortigate Webhook"
            next
        end
    next
end

 

 

  • Create the Stitch to tie the Trigger and Action together:

 

config system automation-stitch
    edit "Add_IP_to_Blacklist"
        set trigger "SSLVPN Login Failed"
        config actions
            edit 1
                set action "Send_VPN_LoginFail_Logs"
                set required enable
            next
        end
    next
end

 

 

Create a new External Connector for an IP Address threat feed:

 

config system external-resource
    edit "Internal fail2ban List"
        set type address
        set resource "http://ip_or_hostname_of_linux_server:5000/blocked-ips"
    next
end

 

 

Apply the external connector/threat feed to your firewall's inbound Deny policy (or negated source on SSL VPN settings page)

 

Once everything is configured correctly, failed SSLVPN login attempt logs will be sent via webhook to the Linux server. The malicious IP will be parsed by fail2ban and added to the list of banned IPs. The Fortigate's external connector will look to the list of IPs (default every 5 minutes) and permanently deny further login attempts from the threat actor's IP address.

 

  • If a legitimate user's IP address is banned (e.g. they entered their credentials incorrectly too many times), it can be unbanned by removing the IP address from the blocked_ips_list.txt file on the Linux server and refreshing the External Connector.

 

 

1 REPLY 1
AEK
SuperUser
SuperUser

Nice solution! Thanks for sharing, @xfg .

AEK
AEK
Labels
Top Kudoed Authors