NEW The Taming of FusionPBX + Tutorial

Adding IPset, VoIP Blacklist, and APIban for Hacker Protection

We've got the two very best SIP attack blockers working now. You can read up on VoIP Blacklist and APIban here.

Before you begin, obtain an API key for APIban here. Then insert it when prompted below.

Here's how to install and activate these IPSET utilities with IPtables:
Code:
cd /
apt install ipset -y
wget https://filedn.com/lBgbGypMOdDm8PWOoOiBR7j/FusionPBX/ipset-additions.tar.gz
tar zxvf ipset-additions.tar.gz
nano -w /usr/local/sbin/apiban-init
chmod +x /etc/rc.local
reboot

Whenever you reboot or run iptables-restart, it will reload the IPset tables for VoIP Blacklist and APIban.
It always puts 127.0.0.1 and the OpenVPN IP addresses at the top of your IPtables rules just in case either VoIPbl or APIban gets polluted by the bad guys, in which case you'll still have OpenVPN access to your server. These two apps block about 1,200 known IP addresses of the bad guys.

You can verify everything is working by running iptables -nL
 
Last edited:
Blocking SPAM Callers

The most effective way we've found to block spammers is to force the caller to press a number before answering the call. This gets rid of most calls coming in from a Call Center. To implement this in FusionPBX, first upload the attached file into Applications -> Recordings. Then create a new IVR in Applications -> IVR Menus with one entry for option 7 using the press7.mp3 file attached and an extension number of 777. Forward successful callers to a Ring Group or Extension of your choice. Choose Hangup after a 600 miilisecond delay for others. Finally, set extension 777 as the Inbound Route destination for incoming calls to your DID.
 

Attachments

Adding Lenny to Your FusionPBX Dialplan

Usage:
Use this to forward spammers to Lenny after you answer a call. You could also use the Lenny Bridge as the Exit Action in your IVR instead of Hangup in the Press7 scenario above for callers that don't press 7.

1. Navigate to Applications -> Bridges and click Add button
Code:
Name: Lenny
Action: Profile
Profile: Internal
Destination: SIP/[email protected]
Enabled ON
Description: Lenny
2. Click SAVE

3. Navigate to Dialplan -> Dialplan Manager and click Add button
Code:
Name: Lenny
Condition: Destination Number  53669
Action: Bridges -> Lenny
Context: ip address of your server
Order: 200
Enabled: True
4. SAVE

5. Edit the Lenny entry in Dialplan Manager and add 53669 in Number
6. SAVE

7. Try it out by dialing L-E-N-N-Y (53669) from one of your extensions.
 
Last edited:
If you're test driving FusionPBX, be advised that Zoiper simply doesn't work on an Android device with FusionPBX. There is no way to get rid of the one-way audio so that you can talk from the Android side. Groundwire and the new VoIP.ms Softphone (apparently based on Groundwire) both work just fine inbound and outbound. Linphone reportedly works as well. So I don't think we can blame this one on FusionPBX.

By the way, if you happen to have an old Grandstream SIP phone lying around, they are plug-and-play as well. Plug in your extension number, password, and IP address of your server. Done.
 
Last edited:
If you're test driving FusionPBX, be advised that Zoiper simply doesn't work on an Android device with FusionPBX. There is no way to get rid of the one-way audio so that you can talk from the Android side. Groundwire and the new VoIP.ms Softphone (apparently based on Groundwire) both work just fine inbound and outbound. Linphone reportedly works as well. So I don't think we can blame this one on FusionPBX.

By the way, if you happen to have an old Grandstream SIP phone lying around, they are plug-and-play as well. Plug in your extension number, password, and IP address of your server. Done.
Same with a Fanvil x-series phone or even an ancient Aastra 9143i.
 
And here's my project for tomorrow. Hoping Google's Gemini AI got it right...

You're looking for a way to dynamically add a client's IP address to an Access Control List (ACL) in FusionPBX/FreeSWITCH after they successfully authenticate (register) their extension.

This is a more advanced scenario than simply setting NAT options, as it implies you want to restrict initial connection attempts but then loosen the restriction once the device is proven legitimate.

While FusionPBX's GUI doesn't have a direct "whitelist on successful login" checkbox, you can achieve this through a combination of:

  1. Strict ACLs on your SIP Profile: Initially restrict who can connect.
  2. FreeSWITCH Event Socket Library (ESL) scripting: This is the most direct way to dynamically manipulate FreeSWITCH configurations.
  3. External scripting (e.g., Python, PHP) or FreeSWITCH Lua scripts: To listen for registration events and issue ACL commands.
Here's a conceptual outline and how you might approach it:

Understanding the Challenge

  • Initial Connection: When a phone first tries to register, FreeSWITCH checks its apply-register-acl (if configured on the SIP Profile) and its firewall rules. If the IP isn't allowed, the registration fails before authentication.
  • Dynamic Whitelisting: You want to add the IP to an allow list only after the REGISTER request successfully authenticates.
Proposed Solution: Using ESL and a Script

This method involves a custom script that monitors FreeSWITCH events and updates ACLs.

Step 1: Set up a Restricted ACL for Registrations

First, create an ACL that is initially very restrictive.

  1. Create a New ACL:
    • Go to Advanced > Access Controls.
    • Click the + button.
    • Name: dynamic_registration_acl (or similar)
    • Default: Set this to deny (this is crucial).
    • Nodes: You might add allow for 127.0.0.1/32 (localhost) and 192.168.x.x/xx (your internal PBX network if phones are on it) so that your server can always manage itself and internal LAN phones.
    • Save.
  2. Apply this ACL to your SIP Profile:
    • Go to Advanced > SIP Profiles.
    • Edit your "internal" SIP Profile (or the one your extensions register to).
    • Find the parameter apply-register-acl and set its value to dynamic_registration_acl.
    • Save the SIP Profile.
    • Reload the SIP Profile (Status > SIP Status > "internal" > Rescan/Restart).
Now, by default, only IPs explicitly allowed in dynamic_registration_acl can register.

Step 2: Create a Script to Monitor Registrations and Update ACL

You'll need a script that connects to FreeSWITCH via the Event Socket Library (ESL), monitors for successful registration events, extracts the client's IP, and then issues a command to add that IP to your dynamic_registration_acl.

This script could be written in Python, Node.js, PHP, or even a FreeSWITCH Lua script. For simplicity, let's conceptualize a Python script using the ESL library.

Prerequisites for Scripting:

  • Enable mod_event_socket: This module is usually enabled by default in FusionPBX. Verify in /etc/freeswitch/autoload_configs/modules.conf.xml.
  • FreeSWITCH ESL password: You'll need the password for ESL (usually defined in /etc/freeswitch/autoload_configs/event_socket.conf.xml). The default is ClueCon.
  • Python ESL library: pip install ESL
Conceptual Python Script (dynamic_acl_updater.py):

Python

see code in next post


Step 3: Run the Script

  • Save the script (e.g., dynamic_acl_updater.py) on your FusionPBX server.
  • Make it executable: chmod +x dynamic_acl_updater.py
  • Run it: python3 dynamic_acl_updater.py
  • For production, use a process manager like systemd or supervisord to keep it running in the background and restart on failure.
How it Works:

  1. The script connects to FreeSWITCH's Event Socket.
  2. It subscribes to <span>sofia::register</span> custom events, which are generated when a SIP registration occurs.

  3. When a successful registration happens, the script extracts the Network-IP (the public IP address the phone is seen coming from).
  4. It then uses the ESL api command to issue acl add YOUR_ACL_NAME CLIENT_IP/32 to add the IP to your dynamic_registration_acl.
  5. Immediately after, it issues reloadacl to apply the change in FreeSWITCH.
Important Considerations and Caveats:

  • Security:
    • ESL Password: CHANGE THE DEFAULT ClueCon PASSWORD! This is a critical security vulnerability if left default.
    • ACL default deny: The default deny on dynamic_registration_acl is your first line of defense.
    • Fail2ban: Ensure Fail2ban is still running and active for brute-force attacks before registration. This dynamic whitelisting is for after a successful (authenticated) registration.
  • IP Changes: If the client's IP changes frequently (which is the premise), this script will continuously add new /32 entries to your ACL. Over time, this could lead to a very large ACL. FreeSWITCH handles large ACLs reasonably well, but it's something to monitor.
  • Old Entries: This script doesn't automatically remove old IP addresses. You might need a separate mechanism (e.g., a cron job) to periodically clear old, stale entries from the dynamic_registration_acl based on registration expiry times or inactivity.
  • Race Conditions: There's a tiny window between an IP changing and the script updating the ACL where an inbound call might fail. NAT keep-alives (as discussed in the previous answer) help minimize this by making IPs stable for longer.
  • Persistent ACL: ACL changes made via the acl add command are not persistentacross FreeSWITCH restarts. If FreeSWITCH restarts, your dynamically added IPs will be lost.
    • To make them persistent, you'd need the script to either:
      • Write to the acl.conf.xml file directly (not recommended for robustness).
      • Or, more practically, regenerate the ACL entries from a persistent database of currently registered devices (which FusionPBX itself manages). This is significantly more complex and would involve querying FusionPBX's database directly or using its API.
Alternative (Simpler but Less Secure if Not Managed): Event Guard

FusionPBX has an Event Guard application (Status > Event Guard). While its primary purpose is to block abusive registrations, it "uses the Advanced -> Access Control allowed nodes as a white list." You might explore if Event Guard can be configured to achieve this dynamic whitelisting in a more managed way within FusionPBX itself. However, it's generally more focused on blocking than allowing dynamically after successful auth.

Conclusion:

Dynamically whitelisting a client IP after a successful login is a custom solution in FusionPBX/FreeSWITCH. The most effective way is through ESL scripting to monitor registration events and update ACLs. Be mindful of the security implications and the management of dynamically growing ACLs. For most common dynamic IP scenarios, robust NAT settings on the SIP profile and phone, combined with disabling SIP ALG, are usually sufficient and less complex.
 
Code:
#!/usr/bin/env python3

import ESL
import time
import sys

# FreeSWITCH ESL Connection Details
ESL_HOST = '127.0.0.1' # Or your FusionPBX IP if running remotely
ESL_PORT = 8021
ESL_PASSWORD = 'ClueCon' # IMPORTANT: Change this in production!

ACL_NAME = 'dynamic_registration_acl' # The ACL you created in FusionPBX

def connect_esl():
    """Connects to FreeSWITCH ESL."""
    try:
        conn = ESL.ESLconnection(ESL_HOST, ESL_PORT, ESL_PASSWORD)
        if conn.connected():
            print(f"Connected to FreeSWITCH ESL at {ESL_HOST}:{ESL_PORT}")
            return conn
        else:
            print(f"Failed to connect to FreeSWITCH ESL at {ESL_HOST}:{ESL_PORT}")
            return None
    except Exception as e:
        print(f"Error connecting to ESL: {e}")
        return None

def main():
    conn = connect_esl()
    if not conn:
        sys.exit(1)

    # Subscribe to relevant events
    # We're interested in 'CUSTOM' events specifically from 'sofia::gateway_reg' or 'sofia::register'
    # and potentially CHANNEL_STATE events for full lifecycle if needed.
    # For a simple "registered, add IP", SOFIA_REGISTER is key.
    # The `RELOAD` event type might be useful if you need to know when ACLs are reloaded.
    conn.events('plain', 'CUSTOM sofia::register') # Subscribe to successful SIP registrations

    print("Listening for SIP registration events...")

    while conn.connected():
        try:
            event = conn.recvEvent()
            if event:
                event_name = event.getHeader("Event-Name")
                event_subclass = event.getHeader("Event-Subclass")

                if event_name == "CUSTOM" and event_subclass == "sofia::register":
                    status = event.getHeader("Register-Status")
                    caller_ip = event.getHeader("Network-IP")
                    extension_id = event.getHeader("Caller-Username") # or other appropriate header

                    if status == "Success" and caller_ip:
                        print(f"Successful registration from Extension: {extension_id}, IP: {caller_ip}")

                        # Check if IP is already in ACL to avoid redundant additions
                        # This would require querying the ACL via ESL, which is more complex.
                        # For simplicity, we'll just try to add it. FreeSWITCH handles duplicates gracefully.

                        # Command to add IP to ACL
                        # The 'reloadacl' is important for changes to take effect immediately
                        acl_command = f"acl add {ACL_NAME} {caller_ip}/32"
                        reload_command = "reloadacl"

                        print(f"Executing: {acl_command}")
                        response = conn.api(acl_command)
                        print(f"Response: {response.getBody().strip()}")

                        print(f"Executing: {reload_command}")
                        response = conn.api(reload_command)
                        print(f"Response: {response.getBody().strip()}")

                        print(f"IP {caller_ip} added to {ACL_NAME} and ACLs reloaded.")
                    elif status == "Failed" and caller_ip:
                        print(f"Failed registration from IP: {caller_ip} (Reason: {event.getHeader('Reply-Reason')})")

        except Exception as e:
            print(f"Error processing event: {e}")
            # Attempt to reconnect if connection lost
            conn = connect_esl()
            if not conn:
                print("Lost connection and failed to reconnect. Exiting.")
                sys.exit(1)
            # Resubscribe after reconnecting
            conn.events('plain', 'CUSTOM sofia::register')
            time.sleep(1) # Small delay before retrying

if __name__ == "__main__":
    main()
 
IPset scripts above have been modified for better long-term server management. IPset tables also are refreshed nightly. We're now averaging about one bad guy registration attempt every 3 days.
 
Yeah. I've only had 2 or 3 each day since activating the VOIPBL stuff.

I'm surprised at the number of abuse attempts coming from Montana, Belgium and the Netherlands.
 
When they get tired of molesting children, probably not a lot going on in Montana. :oops:
 
Just a heads up that we have successfully integrated OpenVPN into FusionPBX so that there is no exposure of trunks, extensions, or routes except through access using OpenVPN IP addresses. This means cell phones, tablets, softphones, and PCs/Macs with an OpenVPN IP address can register to an extension. Desktop SIP phones can only register if they have an OpenVPN IP address or are sitting behind a router with an OpenVPN address. Gateways can still be registered using the public IP address of your FusionPBX server. VoIP.ms works flawlessly. BulkVS doesn't work at all. Here's how and hopefully @markjcrane will check my work...

Code:
1. In CLI, edit /etc/freeswitch/vars.xml
2. Change the two IP addresses from public IP address to your server's OpenVPN address.
3. systemctl daemon-reload
4. systemctl restart freeswitch
5. In FusionPBX browser window, create a new Domain and admin user for your server's OpenVPN address.
6. Switch to the new OpenVPN domain (upper right) and create new Extensions, Gateway, and Routes
7. Navigate to Advanced -> SIP Profiles.
8. Set external profile for ext-rtp-ip, ext-sip-ip, rtp-ip, and sip-ip to FusionPBX public IP address.
9. Set same four entries in internal profile to OpenVPN address from step #2.
10. From CLI, start fs_cli and enter: sofia profile external restart
11. From CLI, in fs_cli enter: sofia profile internal restart
12. Reboot.
13. Verify Profile & Gateway settings in FusionPBX browser window:  Status -> SIP Status (see below)

Screen Shot 2025-06-17 at 12.36.51 PM.png
Be advised that Groundwire Push Notifications will not work with this OpenVPN setup because Groundwire registers your public IP address in their SIPIS server. The workaround we are using is to create a FollowMe setup that forwards calls from an OpenVPN-registered softphone to a cell phone for unanswered, busy, and unregistered calls.

If this is all Greek to you, congratulations. You're in the same position that I was in a week ago.
 
Last edited:
FusionPBX Event Guard will not block the following:
  • IP address of a registered phone or endpoint. It is automatically whitelisted.
  • If someone logs into the web interface successfully, their IP is automatically whitelisted.
  • Allowed IP or CIDR addresses in the Access Control List (ACL).
With fail2ban, one of its weaknesses is that it could block the IP address of actively registered phones. When this happens, it would block an entire office of phones. I wanted to prevent this problem, and so this was one of the motivations for making Event Guard.
 
Last edited:
FusionPBX Event Guard will not block the following:
  • IP address of a registered phone or endpoint. It is automatically whitelisted.
  • If someone logs into the web interface successfully, their IP is automatically whitelisted.
  • Allowed IP or CIDR addresses in the Access Control List (ACL).
With fail2ban, one of its weaknesses is that it could block the IP address of actively registered phones. When this happens, it would block an entire office of phones. I wanted to prevent this problem, and so this was one of the motivations for making Event Guard.
Thanks, @markjcrane. What our testing demonstrated was that folks attempting to register a softphone while traveling almost always got their connection blocked by EventGuard. That is a major problem for companies with many traveling salespeople. Because there is a slight delay before EventGuard picks up and blocks the remote connection, what would be ideal would be a whitelist solution that whitelisted IP addresses immediately after a successful registration. I documented an AI solution to this above, but I haven't had the time to try it. For the short term, our OpenVPN approach solves this as well.
 
Now I understand your use case for the directions you provided.

What I mentioned above is a little different; one or more registrations from the same IP address can help protect the new registrations.

One thing to note Event Guard treats registrations to the IP address as untrusted and blocks the IP address. Bots go for the IP address, so a registration to an IP address is not trusted. For more trusted registrations, use a domain name. Then the mobile client should only be blocked if they have a bad username and password.
 
Now I understand your use case for the directions you provided.

What I mentioned above is a little different; one or more registrations from the same IP address can help protect the new registrations.

One thing to note Event Guard treats registrations to the IP address as untrusted and blocks the IP address. Bots go for the IP address, so a registration to an IP address is not trusted. For more trusted registrations, use a domain name. Then the mobile client should only be blocked if they have a bad username and password.
Great tip. Thanks, @markjcrane.
 
With the tips from @markjcrane and lots of queries to gemini.google.com's AI engine, we have a pretty spectacular FusionPBX platform running at HostMediaUK using an inexpensive platform that provides weekly backups. So we'll see what we see, but it looks pretty amazing now that all the pieces are in place. We ended up using Mark's tip to build everything on an FQDN which makes traveling with portable devices a breeze. Even push notifications work flawlessly. I'm tempted to write this up on Nerd Vittles with a cautionary note that this is not a suitable platform for any but the most seasoned network and VoIP experts. I don't include myself in that group because I made dozens of really stupid mistakes and hundreds of test calls along the way.
 
Last edited:
Great! so there is a new toy coming our way! I can't wait to play with it.
I played with FusionPBX several years ago. It was setup by a coworker who knew how to configure it. So, I never felt any of the frustrations that I read in the forums. I guess I was lucky. But we elected to stay on an Asterisk platform as it fit our check-list better. But it kept intriguing me ...
One other toy that I'm playing with right now is dsiprouter. It's amazing!
Maybe some day it will hit @wardmundy 's radar too :)
 
Last edited by a moderator:

Members online

No members online now.

Latest Posts

Forum statistics

Threads
26,731
Messages
174,675
Members
20,290
Latest member
Zuhlbrittany
Get 3CX - Absolutely Free!

Link up your team and customers Phone System Live Chat Video Conferencing

Hosted or Self-managed. Up to 10 users free forever. No credit card. Try risk free.

3CX
A 3CX Account with that email already exists. You will be redirected to the Customer Portal to sign in or reset your password if you've forgotten it.
Back
Top