🚀 Executive Summary

TL;DR: Manually checking server logs for unauthorized port scans is inefficient. This guide details how to automate real-time security alerts by integrating Fail2Ban with Discord webhooks, significantly improving operational efficiency and peace of mind.

🎯 Key Takeaways

  • Securely manage sensitive data like Discord webhook URLs using `config.env` and `python-dotenv` to prevent hard-coding secrets directly in scripts.
  • Extend Fail2Ban’s functionality by defining custom actions in `action.d` directories, allowing execution of external scripts like Python for real-time notifications.
  • Chain multiple Fail2Ban actions within `jail.local` files using the `%(action_)s` variable to combine default banning mechanisms with custom notification scripts.

Detecting Unauthorized Port Scans: Fail2Ban to Discord Alerts

Detecting Unauthorized Port Scans: Fail2Ban to Discord Alerts

Alright, let’s talk about efficiency. I used to spend the first 30 minutes of my day grepping through auth logs and Fail2Ban records just to see who was knocking on the door overnight. It was a complete time sink. Setting up a direct alert system from Fail2Ban to a Discord channel gave me back at least two hours a week, and more importantly, it gave me real-time awareness without having to actively hunt for it.

Today, I’m going to walk you through that exact setup. It’s a simple, high-value integration that turns a reactive log-checking task into a proactive, automated alert.

Prerequisites

Before we dive in, make sure you have the following ready to go:

  • A server with Fail2Ban already installed and running. I’m assuming you’re already using it to monitor services like SSH.
  • Python 3 installed on that same server.
  • A Discord account and a server where you have permissions to create a webhook.
  • Basic comfort with editing configuration files from the command line.

The Guide: From Ban to Notification

Step 1: Get Your Discord Webhook

First things first, we need a destination for our alerts. Discord’s webhooks make this incredibly easy.

  1. In your Discord server, go to Server Settings > Integrations > Webhooks.
  2. Click “New Webhook”.
  3. Give it a descriptive name like “Fail2Ban Alerts” and choose the channel you want the notifications to post in.
  4. Copy the “Webhook URL”. Treat this URL like a password. Anyone with it can post messages to that channel.

Step 2: The Python Notification Script

Now, let’s create the script that Fail2Ban will call. I’ll skip the standard virtualenv setup since you likely have your own workflow for that. Let’s jump straight to the Python logic. You will need to install the `requests` and `python-dotenv` libraries for your Python environment. A quick `pip install requests python-dotenv` should do the trick.

Create a file named notify_discord.py. This script will take an IP address as a command-line argument and send it to our webhook.

Pro Tip: Never hard-code secrets like webhook URLs directly in your scripts. It’s a security risk and a pain to manage. We’ll use a simple config.env file to store it, which our script will read from.

First, create a file in the same directory named config.env:

DISCORD_WEBHOOK_URL="your_webhook_url_goes_here"

Next, here is the content for notify_discord.py:

import requests
import os
import sys
from dotenv import load_dotenv
import datetime

def send_discord_notification(ip_address):
    """Sends a formatted message to a Discord webhook."""
    load_dotenv('config.env')
    webhook_url = os.getenv('DISCORD_WEBHOOK_URL')

    if not webhook_url:
        print("Error: DISCORD_WEBHOOK_URL not found in config.env")
        return

    # For more robust data, you can pass more args from Fail2Ban
    # But for now, we'll keep it simple.
    timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S UTC")

    data = {
        "embeds": [
            {
                "title": "🚨 Fail2Ban Alert: IP Banned",
                "description": f"The IP address **{ip_address}** has been banned for suspicious activity.",
                "color": 15158332,  # A nice red color
                "fields": [
                    {
                        "name": "Banned IP",
                        "value": ip_address,
                        "inline": True
                    },
                    {
                        "name": "Timestamp",
                        "value": timestamp,
                        "inline": True
                    }
                ],
                "footer": {
                    "text": "Automated alert from TechResolve Security Monitor"
                }
            }
        ]
    }

    response = requests.post(webhook_url, json=data)

    try:
        response.raise_for_status()
    except requests.exceptions.HTTPError as err:
        print(f"Error sending notification: {err}")

if __name__ == "__main__":
    # The script expects the IP as the first argument from the Fail2Ban action
    if len(sys.argv) > 1:
        banned_ip = sys.argv[1]
        send_discord_notification(banned_ip)
    else:
        print("Usage: python3 notify_discord.py <IP_ADDRESS>")

This script loads the webhook URL, constructs a nicely formatted Discord embed message, and sends it off. Simple and effective.

Step 3: Create a Custom Fail2Ban Action

Fail2Ban needs to know *how* to use our script. We do this by defining a custom action. Create a new file inside your Fail2Ban `action.d` directory. Let’s call it discord-notify.conf.

The path to this directory varies, but it’s usually alongside the main `fail2ban.conf` and `jail.conf` files.

[Definition]

# Action to be taken on ban
actionban = python3 /srv/scripts/notify_discord.py <ip>

# Action to be taken on unban (optional, but good practice)
actionunban =

[Init]
# Any initial setup if needed

The key line here is `actionban`. It tells Fail2Ban: “When you ban an IP, run this command.” The `<ip>` placeholder is automatically replaced by Fail2Ban with the offender’s IP address. Make sure the path `/srv/scripts/notify_discord.py` is the absolute path to where you saved your Python script.

Step 4: Enable the Action in Your Jail

Finally, we need to tell a specific Fail2Ban “jail” (a set of rules for a service) to use our new action. I always edit my jail.local file for this, as it overrides the defaults in jail.conf and won’t be overwritten by package updates.

Find the jail you want to monitor, for example, `[sshd]`, and modify the `action` line. In my production setups, I like to chain actions. We still want the default banning behavior, but we want to *add* our notification on top of it.

[sshd]
enabled = true
port    = ssh
logpath = %(sshd_log)s
backend = %(sshd_backend)s
# ... other settings ...

# This is the line to modify
action = %(action_)s
         discord-notify

The `%(action_)s` variable includes the default banning action (usually iptables). By adding `discord-notify` on a new line, we’re telling Fail2Ban to execute both.

Now, just restart the Fail2Ban service for the changes to take effect. The command for this varies by system, but it’s often something like `systemctl restart fail2ban`.

Common Pitfalls (Where I Usually Mess Up)

  • File Permissions: This is the number one culprit. The user that the Fail2Ban service runs as (often `root`) needs read and execute permissions on your notify_discord.py script and read access to config.env.
  • Incorrect Paths: Fail2Ban executes commands from a very minimal environment. Always use absolute paths for both the python interpreter and your script in the discord-notify.conf file.
  • Webhook Typos: A single wrong character in the webhook URL within `config.env` will cause the request to fail. Fail2Ban won’t crash, but you won’t get any notifications. Double-check it.
  • Firewall Rules: Make sure your server’s outbound firewall rules allow it to make HTTPS connections to `discord.com`.

Conclusion

And that’s it. You now have a lean, effective monitoring system that pushes critical security events directly to you. This frees you from the chore of manual log reviews and provides immediate insight when something is wrong. It’s a small change that, in my experience, has a massive impact on both operational efficiency and peace of mind.

Darian Vance - Lead Cloud Architect

Darian Vance

Lead Cloud Architect & DevOps Strategist

With over 12 years in system architecture and automation, Darian specializes in simplifying complex cloud infrastructures. An advocate for open-source solutions, he founded TechResolve to provide engineers with actionable, battle-tested troubleshooting guides and robust software alternatives.


🤖 Frequently Asked Questions

âť“ How can I get real-time alerts for unauthorized port scans?

Integrate Fail2Ban with Discord by creating a custom Python script that sends formatted embed messages to a Discord webhook whenever Fail2Ban bans an IP address for suspicious activity.

âť“ How does this automated alert system compare to manual log review?

This automated system provides immediate, proactive security awareness, eliminating the time-consuming, reactive process of manually grepping logs, thereby significantly boosting operational efficiency and peace of mind.

âť“ What is a common implementation pitfall when setting up Fail2Ban Discord alerts?

Incorrect file permissions are a frequent issue; ensure the user running the Fail2Ban service (often `root`) has read and execute permissions for the Python notification script and read access for the `config.env` file.

Leave a Reply

Discover more from TechResolve - SaaS Troubleshooting & Software Alternatives

Subscribe now to keep reading and get access to the full archive.

Continue reading