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:
config system automation-trigger
edit "SSLVPN Login Failed"
set event-type event-log
set logid 39426
next
end
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
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.
Nominating a forum post submits a request to create a new Knowledge Article based on the forum post topic. Please ensure your nomination includes a solution within the reply.
Hi There, your Python code worked great! but I'm having issues with fail2ban parsing and outputting to the text file. Were you successful with this during your installation?
PS amazing work and thank you for sharing!
Select Forum Responses to become Knowledge Articles!
Select the “Nominate to Knowledge Base” button to recommend a forum post to become a knowledge article.
User | Count |
---|---|
1665 | |
1077 | |
752 | |
446 | |
220 |
The Fortinet Security Fabric brings together the concepts of convergence and consolidation to provide comprehensive cybersecurity protection for all users, devices, and applications and across all network edges.
Copyright 2024 Fortinet, Inc. All Rights Reserved.