APIBAN - does anyone use it?

Voipy

Member
Feb 5, 2026
92
10
8
56
I have been looking at apiban to proactively block known IP addresses of sip scanners. It seems like a great idea. Has anyone used this and are there any downsides you can think of?
 
Hi - no not with fail2ban although I see your link there so perhaps they have added something, or I have misunderstood its exact workings. It was in fact created because of the developer's dislike of fail2ban for various reasons. There is an easy client that dips in every X minutes and synchronises bad IP addresses and adds them to iptables to block. Every 7 days I think it releases them so that the list does not grow too large.

One of the things the developer did not like about fail2ban was that it's reactive - they have to first have probed you before the ban comes so by then they have already received a response and they know it's a pbx. This will prevent this as they are blocked before they even try. From what I have read it's very effective and cuts out the vast majority of attempts.
 
Hmmm. Unless there is significant overhead, having real time reactive blocking and known lists seems like it would be better. What you reckon?
 
I can only see upsides and it's apparently really lightweight so at this point I am intending to try it out. But will see if anyone comes with concerns or bad experiences for a bit.
 
  • Like
Reactions: RandomSeed
For a public-facing FSPBX/FreeSWITCH box, I used AI and this as a basis at first and modified it as needed (https://nerdvittles.com/icing-on-the-cake-meet-fs-pbx-its-fusionpbx-on-steroids/) to confg\igure and automate two free SIP blacklists — APIBAN (apiban.org) and VoIP Blacklist (voipbl.org) — using ipset rather than raw iptables, since voipbl alone is 90,000+ ranges and a hash set matches in constant time versus linear rule evaluation. iptables references each set just once high in the INPUT chain (-m set --match-set voipbl src -j DROP), so bad sources are dropped before reaching FreeSWITCH. One gotcha: APIBAN is individual IPs so it needs hash:ip, while voipbl is CIDR ranges so it needs hash:net — mismatch the type and the atomic swap silently fails, leaving your list stale. Refresh scripts download each list into a _temp set then do an instant ipset swap, which means the iptables rules never get touched (no flush, no reload). A systemd oneshot refreshes both after boot and a daily 3am cron re-downloads and saves them back to disk.

I have not experienced a downside yet. Upside is scanners and fraud dropped at the network layer, event_guard is not even being hit.
 
Instead of trying to block every single hacker, a game you will certainly lose, especially with AI hacking these days, I suggest using non-standard SIP ports, then you won't have to worry about blocking anyone at all. In my experiments, even unprotected systems stay fairly untouched. I call it security by obscurity. There are people out there who are strictly against this strategy, but I believe that is because they simply didn't try it in the real world. You should still have a firewall for very intricate situations. These days, a firewall actually blocks more good traffic for me than it helps to prevent the bad one. That's just my experience.
 
I use a combination of APIBAN and VOIPBL. I have had zero attempts to hack into the system since implementing them. The following is an excerpt from a NerdVittles.com article on how to install them. Credit to Ward Mundy at IncrediblePBX.com and Nerdvittles.com.. Be aware that this restricts voip access to whitelisted IP's only so it won't be for those who have roving users unless you run a sip client and VPN on their phones.

Securing Your FS PBX Server​

If you’ve been following Nerd Vittles for the past couple decades, then you know that we are sticklers when it comes to security. So bear with us while we add a few extra layers of security to FS PBX. Log back into your server as root using SSH. First, we need to change the default port for future SSH access. Edit /etc/ssh/sshd_config. Uncomment the Port line and enter a new port number. A good choice might be the year you were born. Then save the file. Don’t restart the SSH service just yet, or you will lock yourself out of your server.

Next, edit /etc/iptables/rules.v4. Scroll down to the line containing –dport 22 and duplicate the line. Change the number in the duplicated line to the port number you assigned for future SSH access. Then SAVE the file and reboot your server.

Log back in: ssh -p 1234 root@fspbx.yourdomain.com where 1234 is the port you assigned for SSH access. Assuming you got back in, edit /etc/iptables/rules.v4 again and comment out or delete the –dport 22 line. Save the file and restart IPtables: systemctl restart iptables.

Now we’re ready to add a few layers of protection for the FS PBX web interface. We’ll be using two free services: VoIP Blacklist and APIban. These two services implement a VoIP blacklist of over 100,000 bad guys, and we have found the listings block virtually all attacks on FS PBX which, by design, exposes its SIP ports on the public internet.

You will need an APIkey for APIban which you can obtain here. Once you have your APIkey, issue the following commands:

cd /
apt install ipset -y
wget https://filedn.com/lBgbGypMOdDm8PWOoOiBR7j/FusionPBX/ipset-additions.tar.gz
tar zxvf ipset-additions.tar.gz
# insert your APIkey when the text editor opens below & save the file
nano -w /usr/local/sbin/apiban-init
chmod +x /etc/rc.local
echo "2 0 * * * root rm -f /var/log/*.gz >/dev/null 2>&1" >> /etc/crontab
echo "2 5 * * * root rm -f /var/log/*.1 >/dev/null 2>&1" >> /etc/crontab
echo "9 5 * * * root rm -f /var/log/freeswitch/*.1 >/dev/null 2>&1" >> /etc/crontab
echo "3 */6 * * * root /usr/local/sbin/iptables-restart >/dev/null 2>&1" >> /etc/crontab
reboot


Depending upon the amount of RAM available in your new server, IPtables may or may not restart after you reboot. You can check it by logging in as root and issuing the command: iptables -nL. If you see the following entries as the first five in your INPUT chain, all is well and you can move on to the next section:

ACCEPT 0 -- 0.0.0.0/0 0.0.0.0/0
ACCEPT 0 -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
ACCEPT 0 -- 10.8.0.0/24 0.0.0.0/0
DROP 0 -- 0.0.0.0/0 0.0.0.0/0 match-set voipbl src
DROP 0 -- 0.0.0.0/0 0.0.0.0/0 match-set apiban src

If you see a bunch of DROP entries in the iptables listing, then the likely culprit is that /etc/rc.local didn’t get executed when you rebooted. You can fix this with the following commands:

cd /etc/systemd/system
rm -f rc-local.service
ln -s /lib/systemd/system/rc-local.service rc-local.service
reboot
After rebooting, issue the iptables -nL command again and check for the five entries shown above. If they’re there, you’re done. If the series of DROP commands are also missing, then we need to address the memory constraints of your server. Edit /etc/rc.local and replace the existing contents with the following:

#!/bin/sh -e

/usr/local/sbin/apiban-init
#/usr/local/sbin/voipbl-init
systemctl restart iptables
systemctl restart ip6tables
systemctl restart fail2ban
/usr/sbin/iptables -I INPUT -m set --match-set apiban src -j DROP
#/usr/sbin/iptables -I INPUT -m set --match-set voipbl src -j DROP
/usr/sbin/iptables -I INPUT -s 10.8.0.0/24 -j ACCEPT
/usr/sbin/iptables -I INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
/usr/sbin/iptables -I INPUT -i lo -j ACCEPT

exit 0
When you issue the iptables -nL command now, you should see only four entries at the top of the INPUT chain:

ACCEPT 0 -- 0.0.0.0/0 0.0.0.0/0
ACCEPT 0 -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
ACCEPT 0 -- 10.8.0.0/24 0.0.0.0/0
DROP 0 -- 0.0.0.0/0 0.0.0.0/0 match-set apiban src

The VoIP Blacklist IPset will be run as a cron job during the day to add the necessary protection for FS PBX.
 
Hi - using non standard sip ports - I wish we could. We have too many phones out there to go that route anymore. Thousands of phones that do not autoprovision - different makes, models, soft phones, etc etc. So that's not an option unfortunately. So we have to find ways of reducing the number of attempts. I have no doubt that we won't block them all but from what I gather - and @kenn10 seems to have success with this - it will reduce this to a handful of attempts.

Once I bite the bullet I will feed back her on the success or lack thereof!
 
If anyone needs a script for DDNS update of the firewall for multiple dynamic dns entries, I have one I created with the assistance of AI. I have loaded it on FS PBX on both Debian-12 and Debian-13 and it adjusts for the differences in the version of Debian. You set the script to run as a cron job as often as you like (I run it every 5 minutes). This is handy for remote users who can run a ddns update script on their router or smartphone to keep the PBX updated.

I contribute this freely without warranty or license. It is a contributed script for anyone who wants to use it.

Instructions:

1. Create the update script

Create a script at /usr/local/bin/update-dynamic-whitelist.sh:

2. Insert this code into the script:
Code:
#!/bin/bash
# /usr/local/bin/update-dynamic-whitelist.sh
# Dynamic full-access whitelist for multiple FQDNs (nftables fixed)

set -euo pipefail

# ================== CONFIGURATION ==================
FQDNS=(
    "ddns1.ddns.net"
    "ddns2.ddns.net"
)
# ====^ SET Your Dynamic DNS FQDN's above ^.  You can add as many as needed ====

COMMENT="Dynamic FQDN full access"
STATE_DIR="/var/cache/dynamic-whitelist"
LOGFILE="/var/log/dynamic-whitelist.log"
# ===================================================

mkdir -p "$STATE_DIR"

# ================== HELPER FUNCTIONS ==================
init_nftables() {
    local nft_cmd="${NFT_CMD:-$(command -v /usr/sbin/nft || command -v nft)}"
    
    if sudo "$nft_cmd" list table inet dynamic_whitelist >/dev/null 2>&1; then
        return 0
    fi

    echo "$(date '+%Y-%m-%d %H:%M:%S') Initializing nftables dynamic whitelist table..." | tee -a "$LOGFILE"
    sudo "$nft_cmd" -f - <<EOF
table inet dynamic_whitelist {
    chain input {
        type filter hook input priority 10; policy accept;
        # Dynamic rules will be added here
    }
}
EOF
}

update_fqdn() {
    local fqdn="$1"
    local state_file="${STATE_DIR}/last-ip-$(echo "$fqdn" | tr '[:upper:]' '[:lower:]' | tr -c '[:alnum:]-.' '_').txt"

    local current_ip
    current_ip=$(dig +short A "$fqdn" | tail -n1)

    if [[ -z "$current_ip" ]] || ! [[ $current_ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') ERROR: Failed to resolve $fqdn" | tee -a "$LOGFILE"
        return 1
    fi

    local prev_ip=""
    if [[ -f "$state_file" ]]; then
        prev_ip=$(cat "$state_file")
    fi

    if [[ "$current_ip" == "$prev_ip" ]]; then
        echo "$(date '+%Y-%m-%d %H:%M:%S') No change: $fqdn is still $current_ip" | tee -a "$LOGFILE"
        return 0
    fi

    echo "$(date '+%Y-%m-%d %H:%M:%S') IP changed for $fqdn: ${prev_ip:-none} → $current_ip" | tee -a "$LOGFILE"

    # Detect firewall (improved for Debian 13 + nftables)
    if [[ -z "${FIREWALL:-}" ]]; then
        if command -v ufw >/dev/null 2>&1; then
            FIREWALL="ufw"
        elif command -v /usr/sbin/nft >/dev/null 2>&1 || command -v nft >/dev/null 2>&1; then
            FIREWALL="nft"
            NFT_CMD=$(command -v /usr/sbin/nft || command -v nft)
            init_nftables
        elif command -v /usr/sbin/iptables >/dev/null 2>&1 || command -v iptables >/dev/null 2>&1; then
            FIREWALL="iptables"
        else
            echo "$(date '+%Y-%m-%d %H:%M:%S') ERROR: No supported firewall found" | tee -a "$LOGFILE"
            echo "   Hint: Run 'sudo apt install nftables' on Debian 13" | tee -a "$LOGFILE"
            return 1
        fi
    fi

    # Remove old rule if IP changed
    if [[ -n "$prev_ip" && "$prev_ip" != "$current_ip" ]]; then
        case $FIREWALL in
            ufw)
                sudo ufw delete allow from "$prev_ip" to any comment "$COMMENT" 2>/dev/null || true
                ;;
            nft)
                # Delete any existing rule for the old IP
                sudo "$NFT_CMD" delete rule inet dynamic_whitelist input ip saddr "$prev_ip" accept 2>/dev/null || true
                ;;
            iptables)
                sudo iptables -D INPUT -s "$prev_ip" -j ACCEPT 2>/dev/null || true
                ;;
        esac
    fi

    # Add new full-access rule
    case $FIREWALL in
        ufw)
            sudo ufw allow from "$current_ip" to any comment "$COMMENT"
            ;;
        nft)
            sudo "$NFT_CMD" add rule inet dynamic_whitelist input ip saddr "$current_ip" accept comment "$COMMENT"
            ;;
        iptables)
            sudo iptables -I INPUT -s "$current_ip" -j ACCEPT -m comment --comment "$COMMENT"
            ;;
    esac

    echo "$current_ip" > "$state_file"
    echo "$(date '+%Y-%m-%d %H:%M:%S') Success: Full access granted to $current_ip ($fqdn)" | tee -a "$LOGFILE"
}

# Main
for fqdn in "${FQDNS[@]}"; do
    update_fqdn "$fqdn"
done

echo "$(date '+%Y-%m-%d %H:%M:%S') Completed check for all FQDNs" | tee -a "$LOGFILE"

3. Set the file executable:
chmod +x /usr/local/bin/update-dynamic-whitelist.sh

4. Create a file to store IP's addresses
mkdir -p /var/cache/dynamic-whitelist

5. test running the script:
/usr/local/bin/update-dynamic-whitelist.sh

6. Set up crontab to run it every 5 minutes
crontab -e
*/5 * * * * /usr/local/bin/update-dynamic-whitelist.sh >> /var/log/dynamic-whitelist.log 2>&1

7. Check the log at /var/log/dynamic-whitelist.log to see the results. The DDNS FQDN IP’s are in the /var/cache/dynamic-whitelist directory