Files
VOIP/docs/plans/2026-02-15-voip-asterisk-freepbx-implementation.md
consultoria-as 8f51fcfa05 docs: initial project setup - Asterisk 20 + FreePBX 17 PBX
Phase 1 installation completed (8/10 tasks):
- Ubuntu 24.04, Asterisk 20.18.2 compiled from source
- FreePBX 17.0.25 with PJSIP extensions (1001, 1002)
- MariaDB 10.11.14, Apache 2.4, PHP 8.3
- nftables firewall and Fail2ban security configured

Pending: softphone testing, IVR/queues config, Phase 2 (PSTN trunks)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 03:30:27 -06:00

27 KiB

VoIP PBX Empresarial — Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Deploy a production-ready Asterisk 20 LTS + FreePBX 17 PBX on a physical Debian 12 server, with internal SIP extensions first (Phase 1) and PSTN connectivity via SIP trunks for Mexico + US later (Phase 2).

Architecture: Two-phase deployment. Phase 1 installs the full PBX stack (Asterisk, FreePBX, MariaDB, Apache, security) for internal-only SIP communication. Phase 2 adds SIP trunk providers (Telnyx recommended) for external calling to Mexico and US phone numbers. All configuration is done via FreePBX web GUI where possible, with manual Asterisk config only where necessary.

Tech Stack: Debian 12, Asterisk 20 LTS, FreePBX 17, MariaDB 10.11+, PHP 8.2, Apache 2.4, Fail2ban, nftables

Design doc: docs/plans/2026-02-15-voip-asterisk-freepbx-design.md


PHASE 1: PBX INTERNO


Task 1: Prepare the OS — Base System

Context: Fresh Debian 12 (Bookworm) installation on bare metal. This task gets the OS ready with all prerequisites before touching any VoIP software. Everything runs as root.

Step 1: Verify Debian 12 is installed and updated

cat /etc/os-release | grep -E "PRETTY_NAME|VERSION_ID"

Expected: Debian GNU/Linux 12 (bookworm) and VERSION_ID="12"

apt update && apt upgrade -y

Expected: System fully updated, no errors.

Step 2: Set hostname and timezone

hostnamectl set-hostname pbx.tudominio.local
timedatectl set-timezone America/Mexico_City

Verify:

hostname -f
timedatectl

Expected: Hostname shows pbx.tudominio.local, timezone shows America/Mexico_City.

Step 3: Configure static IP (if not already done)

Edit /etc/network/interfaces (or use NetworkManager) to assign a static IP on the LAN interface. Example:

auto eth0
iface eth0 inet static
    address 192.168.1.10
    netmask 255.255.255.0
    gateway 192.168.1.1
    dns-nameservers 8.8.8.8 1.1.1.1

Then:

systemctl restart networking
ip addr show eth0

Expected: Static IP assigned, connectivity verified with ping 8.8.8.8.

Step 4: Install base dependencies

apt install -y build-essential git curl wget sudo \
  linux-headers-$(uname -r) libncurses5-dev libssl-dev \
  libxml2-dev libsqlite3-dev uuid-dev libjansson-dev \
  libedit-dev pkg-config autoconf libtool automake \
  sox mpg123 ffmpeg lame unixodbc unixodbc-dev \
  libcurl4-openssl-dev libvorbis-dev libspeex-dev \
  libspeexdsp-dev libopus-dev subversion

Verify:

gcc --version && git --version

Expected: Both commands return version info without errors.

Step 5: Commit — checkpoint note

Create a log file to track installation progress:

mkdir -p /root/VOIP/logs
echo "$(date) - Task 1 complete: OS base prepared" >> /root/VOIP/logs/install.log

Task 2: Install MariaDB

Context: FreePBX stores all configuration (extensions, trunks, routes, CDR) in MariaDB. This must be installed and secured before FreePBX.

Step 1: Install MariaDB server

apt install -y mariadb-server mariadb-client
systemctl enable mariadb
systemctl start mariadb

Verify:

systemctl status mariadb
mariadb --version

Expected: Active (running), version 10.11+.

Step 2: Secure MariaDB

mariadb-secure-installation

During the interactive prompts:

  • Switch to unix_socket authentication: N (keep current)
  • Change root password: Y (set a strong password, save it securely)
  • Remove anonymous users: Y
  • Disallow root login remotely: Y
  • Remove test database: Y
  • Reload privilege tables: Y

Verify:

mariadb -u root -p -e "SELECT VERSION();"

Expected: Returns MariaDB version, login works with your password.

Step 3: Create Asterisk databases

mariadb -u root -p -e "
CREATE DATABASE IF NOT EXISTS asterisk CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE IF NOT EXISTS asteriskcdrdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS 'asterisk'@'localhost' IDENTIFIED BY 'CHANGE_THIS_STRONG_PASSWORD';
GRANT ALL PRIVILEGES ON asterisk.* TO 'asterisk'@'localhost';
GRANT ALL PRIVILEGES ON asteriskcdrdb.* TO 'asterisk'@'localhost';
FLUSH PRIVILEGES;
"

Verify:

mariadb -u asterisk -p -e "SHOW DATABASES;"

Expected: Shows asterisk and asteriskcdrdb databases.

Step 4: Log progress

echo "$(date) - Task 2 complete: MariaDB installed and configured" >> /root/VOIP/logs/install.log

Task 3: Install PHP 8.2 and Apache

Context: FreePBX's web GUI runs on Apache with PHP. FreePBX 17 requires PHP 8.2.

Step 1: Install Apache

apt install -y apache2
systemctl enable apache2
systemctl start apache2

Verify:

systemctl status apache2
curl -s -o /dev/null -w "%{http_code}" http://localhost

Expected: Active (running), HTTP 200.

Step 2: Install PHP 8.2 and required modules

apt install -y php8.2 php8.2-cli php8.2-common php8.2-mysql \
  php8.2-gd php8.2-mbstring php8.2-intl php8.2-xml \
  php8.2-curl php8.2-zip php8.2-bcmath php8.2-soap \
  php8.2-ldap libapache2-mod-php8.2

Verify:

php -v
php -m | grep -E "mysql|gd|mbstring|xml|curl"

Expected: PHP 8.2.x, all listed modules present.

Step 3: Configure PHP for FreePBX

Edit /etc/php/8.2/apache2/php.ini:

upload_max_filesize = 120M
post_max_size = 120M
memory_limit = 256M
max_execution_time = 120
max_input_time = 120
max_input_vars = 5000

Also enable the short_open_tag if not already:

short_open_tag = On

Step 4: Enable Apache modules

a2enmod rewrite
a2enmod headers
systemctl restart apache2

Verify:

apache2ctl -M | grep -E "rewrite|headers"

Expected: Both modules listed.

Step 5: Log progress

echo "$(date) - Task 3 complete: Apache + PHP 8.2 installed" >> /root/VOIP/logs/install.log

Task 4: Install Asterisk 20 LTS from Source

Context: Asterisk is the core PBX engine. We compile from source to get the LTS version with all modules we need. This is the longest task.

Important: Asterisk runs as user asterisk, not root.

Step 1: Create asterisk user

adduser --system --group --home /var/lib/asterisk --no-create-home asterisk
usermod -aG audio,dialout asterisk

Verify:

id asterisk

Expected: Shows uid, gid, and groups including audio, dialout.

Step 2: Download Asterisk 20 LTS

cd /usr/src
wget https://downloads.asterisk.org/pub/telephony/asterisk/asterisk-20-current.tar.gz
tar xzf asterisk-20-current.tar.gz
cd asterisk-20.*/

Verify:

ls -la /usr/src/asterisk-20.*/

Expected: Asterisk source tree present.

Step 3: Install prerequisites script

contrib/scripts/install_prereq install

Expected: Script installs any missing dependencies, exits with "install completed successfully".

Step 4: Download MP3 decoder (for music on hold)

contrib/scripts/get_mp3_source.sh

Expected: Downloads mp3 source to addons/mp3/.

Step 5: Configure Asterisk

./configure --libdir=/usr/lib64 --with-pjproject-bundled --with-jansson-bundled

Expected: Configure completes with no errors. Look for configure: Package configured for: linux at the end.

Step 6: Select modules with menuselect

make menuselect.makeopts
menuselect/menuselect \
  --enable app_macro \
  --enable format_mp3 \
  --enable CORE-SOUNDS-EN-WAV \
  --enable CORE-SOUNDS-EN-ULAW \
  --enable CORE-SOUNDS-ES-WAV \
  --enable CORE-SOUNDS-ES-ULAW \
  --enable MOH-ORSOUND-WAV \
  --enable MOH-ORSOUND-ULAW \
  --enable EXTRA-SOUNDS-EN-WAV \
  --enable EXTRA-SOUNDS-EN-ULAW \
  menuselect.makeopts

Note: We enable Spanish sounds (ES) since the PBX will serve Mexico. app_macro is needed by FreePBX for older dialplan compatibility.

Verify:

menuselect/menuselect --check-deps menuselect.makeopts

Expected: No unmet dependencies.

Step 7: Compile and install

make -j$(nproc)
make install
make samples
make config
make install-logrotate
ldconfig

Expected: Each command completes without errors.

Step 8: Set permissions

chown -R asterisk:asterisk /var/run/asterisk \
  /etc/asterisk /var/lib/asterisk \
  /var/log/asterisk /var/spool/asterisk \
  /usr/lib64/asterisk
chmod -R 750 /var/spool/asterisk

Step 9: Configure Asterisk to run as user asterisk

Edit /etc/default/asterisk:

AST_USER="asterisk"
AST_GROUP="asterisk"

Edit /etc/asterisk/asterisk.conf:

[options]
runuser = asterisk
rungroup = asterisk

Step 10: Start Asterisk and verify

systemctl enable asterisk
systemctl start asterisk
asterisk -rvvv -x "core show version"

Expected: Shows Asterisk 20.x.x version string, running as user asterisk.

Step 11: Log progress

echo "$(date) - Task 4 complete: Asterisk 20 LTS compiled and running" >> /root/VOIP/logs/install.log

Task 5: Install FreePBX 17

Context: FreePBX provides the web-based GUI for managing Asterisk. It handles extensions, trunks, routes, IVR, voicemail, and more. Installed on top of a running Asterisk.

Step 1: Install Node.js (required by FreePBX 17)

curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
apt install -y nodejs

Verify:

node -v && npm -v

Expected: Node 18.x and npm version.

Step 2: Download FreePBX 17

cd /usr/src
wget https://mirror.freepbx.org/modules/packages/freepbx/freepbx-17.0-latest-EDGE.tgz
tar xzf freepbx-17.0-latest-EDGE.tgz
cd freepbx/

Note: Check https://www.freepbx.org/downloads/ for the latest FreePBX 17 URL. The filename may differ.

Step 3: Run FreePBX installer

./install -n --dbuser asterisk --dbpass CHANGE_THIS_STRONG_PASSWORD --webroot /var/www/html

Flags:

  • -n: Non-interactive
  • --dbuser/--dbpass: MariaDB credentials from Task 2
  • --webroot: Apache document root

Expected: Installer completes with "You have successfully installed FreePBX".

Step 4: Install FreePBX base modules

fwconsole ma installall
fwconsole reload
fwconsole chown

Expected: Modules install without errors, fwconsole reload completes.

Step 5: Configure Apache for FreePBX

cat > /etc/apache2/sites-available/freepbx.conf << 'EOF'
<VirtualHost *:80>
    DocumentRoot /var/www/html
    ServerName pbx.tudominio.local

    <Directory /var/www/html>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>
EOF

a2dissite 000-default.conf
a2ensite freepbx.conf
systemctl restart apache2

Step 6: Verify FreePBX web GUI

curl -s -o /dev/null -w "%{http_code}" http://localhost/admin/

Expected: HTTP 200 or 302 (redirect to setup wizard).

Open a browser and navigate to http://<SERVER_IP>/admin/. You should see the FreePBX setup wizard. Create an admin user with a strong password.

Step 7: Enable FreePBX cron and service

fwconsole chown
fwconsole reload

Verify crontab exists:

crontab -u asterisk -l

Expected: Shows FreePBX cron entries.

Step 8: Log progress

echo "$(date) - Task 5 complete: FreePBX 17 installed, web GUI accessible" >> /root/VOIP/logs/install.log

Task 6: Configure Firewall (nftables)

Context: VoIP servers are constant targets for SIP brute-force attacks and toll fraud. A properly configured firewall is mandatory before exposing any ports.

Step 1: Install nftables (if not present)

apt install -y nftables
systemctl enable nftables

Step 2: Create firewall rules

cat > /etc/nftables.conf << 'NFTEOF'
#!/usr/sbin/nft -f

flush ruleset

table inet filter {
    chain input {
        type filter hook input priority 0; policy drop;

        # Loopback
        iif "lo" accept

        # Established connections
        ct state established,related accept

        # Drop invalid
        ct state invalid drop

        # ICMP (ping)
        ip protocol icmp accept
        ip6 nexthdr icmpv6 accept

        # SSH (restrict to your admin IP if possible)
        tcp dport 22 accept

        # HTTP/HTTPS (FreePBX web GUI - restrict to LAN)
        tcp dport { 80, 443 } ip saddr 192.168.0.0/16 accept

        # SIP signaling (restrict to LAN + trunk provider IPs)
        udp dport { 5060, 5061 } ip saddr 192.168.0.0/16 accept
        tcp dport { 5060, 5061 } ip saddr 192.168.0.0/16 accept

        # RTP media (audio)
        udp dport 10000-20000 accept

        # IAX2 (optional, only if using IAX trunks)
        # udp dport 4569 accept

        # Log and drop everything else
        log prefix "nftables-drop: " limit rate 5/minute
        drop
    }

    chain forward {
        type filter hook forward priority 0; policy drop;
    }

    chain output {
        type filter hook output priority 0; policy accept;
    }
}
NFTEOF

Step 3: Apply and verify

nft -f /etc/nftables.conf
nft list ruleset

Expected: Rules listed, no errors.

Step 4: Test connectivity

# From the server itself
curl -s -o /dev/null -w "%{http_code}" http://localhost/admin/
# SSH should still work from your admin machine

Expected: HTTP 200, SSH still accessible.

Step 5: Log progress

echo "$(date) - Task 6 complete: nftables firewall configured" >> /root/VOIP/logs/install.log

Task 7: Configure Fail2ban for Asterisk

Context: Fail2ban monitors Asterisk logs for failed SIP authentication attempts and automatically bans offending IPs. Critical for preventing toll fraud.

Step 1: Install Fail2ban

apt install -y fail2ban
systemctl enable fail2ban

Step 2: Configure Asterisk security logging

Edit /etc/asterisk/logger.conf and ensure this line exists under [logfiles]:

[logfiles]
security_log => security

Reload Asterisk logger:

asterisk -rx "logger reload"

Verify:

ls -la /var/log/asterisk/security_log

Expected: File exists (may be empty initially).

Step 3: Create Fail2ban jail for Asterisk

cat > /etc/fail2ban/jail.d/asterisk.conf << 'EOF'
[asterisk]
enabled  = true
filter   = asterisk
action   = nftables-allports[name=asterisk, protocol=all]
logpath  = /var/log/asterisk/security_log
maxretry = 3
findtime = 600
bantime  = 3600
ignoreip = 127.0.0.1/8 192.168.0.0/16
EOF

This means: 3 failed attempts within 10 minutes = banned for 1 hour. LAN IPs are whitelisted.

Step 4: Start Fail2ban and verify

systemctl restart fail2ban
fail2ban-client status
fail2ban-client status asterisk

Expected: Shows asterisk jail active with 0 currently banned.

Step 5: Log progress

echo "$(date) - Task 7 complete: Fail2ban configured for Asterisk" >> /root/VOIP/logs/install.log

Task 8: Create SIP Extensions in FreePBX

Context: Extensions are the internal "phone numbers" for each user. We use PJSIP (not the deprecated chan_sip). All configuration is done via the FreePBX web GUI.

Step 1: Access FreePBX GUI

Open http://<SERVER_IP>/admin/ in a browser. Log in with the admin credentials created in Task 5.

Step 2: Verify PJSIP is the default driver

Navigate to: Settings > Advanced Settings

Find SIP Channel Driver and ensure it is set to chan_pjsip. This is the default in FreePBX 17.

Step 3: Create test extensions

Navigate to: Applications > Extensions > Add Extension > Add New PJSIP Extension

Create at least 2 extensions for testing:

Field Extension 1 Extension 2
User Extension 1001 1002
Display Name Usuario Prueba 1 Usuario Prueba 2
Secret (auto-generated, 16+ chars) (auto-generated, 16+ chars)
Voicemail Enabled Enabled
VM Password 1001 1002

Click Submit after each, then click the red Apply Config button.

Step 4: Verify extensions registered in Asterisk

asterisk -rx "pjsip show endpoints"

Expected: Shows endpoints 1001 and 1002 (status will be "Unavailable" until phones connect).

Step 5: Log progress

echo "$(date) - Task 8 complete: SIP extensions created" >> /root/VOIP/logs/install.log

Task 9: Connect Softphones and Test Internal Calls

Context: We connect two softphones to the PBX and verify that internal calls work end-to-end. This validates the entire Phase 1 stack.

Recommended softphones:

  • Desktop: Zoiper5 (free), MicroSIP (free, Windows)
  • Mobile: Zoiper, Obrafonos, Obrafonos Groundwire

Step 1: Configure Softphone 1 (extension 1001)

In the softphone, create a new SIP account:

Setting Value
Username 1001
Password (secret from FreePBX)
Domain/Server 192.168.1.10 (server IP)
Transport UDP
Port 5060

Step 2: Configure Softphone 2 (extension 1002)

Same settings but with extension 1002 credentials. Use a different device or a second softphone instance.

Step 3: Verify registration

asterisk -rx "pjsip show endpoints" | grep -E "1001|1002"

Expected: Both endpoints show status "Not in use" (meaning registered and idle).

Step 4: Make a test call

From softphone 1001, dial 1002. Softphone 2 should ring. Answer. Verify:

  • Audio flows in both directions
  • Call quality is good (no choppy audio, no one-way audio)
  • Caller ID shows "Usuario Prueba 1 <1001>"

Step 5: Test voicemail

From 1001, dial *981002 (direct voicemail for 1002). Leave a message. Then from 1002, dial *97 to check voicemail. Verify the message plays back.

Step 6: Log progress

echo "$(date) - Task 9 complete: Internal calls working, Phase 1 DONE" >> /root/VOIP/logs/install.log

Task 10: Configure IVR, Queues, and Ring Groups (Optional Enhancements)

Context: These features make the PBX useful for business. All configured via FreePBX web GUI.

Step 1: Create a Ring Group

Navigate to: Applications > Ring Groups > Add Ring Group

Field Value
Ring Group Number 600
Group Description Oficina Principal
Extension List 1001, 1002
Ring Strategy ringall
Ring Time 20 seconds
Destination if no answer Voicemail (1001)

Submit and Apply Config.

Step 2: Create an IVR

Navigate to: Applications > IVR > Add IVR

Field Value
IVR Name Menu Principal
Timeout 10 seconds
Option 1 Extension 1001 (Ventas)
Option 2 Extension 1002 (Soporte)
Option 0 Ring Group 600 (Operadora)
Invalid/Timeout Destination Ring Group 600

Record or upload a greeting audio file in Spanish.

Submit and Apply Config.

Step 3: Create a Call Queue (optional)

Navigate to: Applications > Queues > Add Queue

Field Value
Queue Number 400
Queue Name Soporte
Static Agents 1001, 1002
Ring Strategy rrmemory (round-robin with memory)
Max Wait Time 300 seconds
Music on Hold default

Submit and Apply Config.

Step 4: Test

  • Dial 600 from any extension — both phones should ring
  • Dial the IVR number — menu should play, options should route correctly
  • Dial 400 — queue should play hold music and ring agents

Step 5: Log progress

echo "$(date) - Task 10 complete: IVR, Ring Groups, Queues configured" >> /root/VOIP/logs/install.log

PHASE 2: CONEXION PSTN (TRUNK SIP)

Prerequisites: Phase 1 must be fully working. You need an account with your chosen SIP trunk provider and internet access from the PBX server.


Task 11: Contract SIP Trunk Provider

Context: This is a manual/administrative task. You need to sign up with a provider and get your SIP credentials.

Recommended: Telnyx (best balance for MX+US)

Step 1: Create Telnyx account

  1. Go to https://telnyx.com and sign up
  2. Complete identity verification
  3. Add payment method (credit card or prepay)

Step 2: Create a SIP Connection (Credential Auth)

In Telnyx Mission Control portal:

  1. Navigate to Voice > SIP Connections
  2. Click Add SIP Connection
  3. Name: freepbx-office
  4. Connection type: Credential Authentication
  5. Username: (save this)
  6. Password: (save this)
  7. Note the SIP server address (e.g., sip.telnyx.com)

Step 3: Purchase DIDs

  1. Navigate to Numbers > Buy Numbers
  2. Buy 1 US number (for testing)
  3. (Later) Buy 1 Mexico number
  4. Assign both numbers to the freepbx-office SIP connection

Step 4: Note your credentials

Save securely:

  • SIP Username
  • SIP Password
  • SIP Server: sip.telnyx.com
  • Outbound Proxy: sip.telnyx.com
  • DID numbers purchased

Task 12: Configure SIP Trunk in FreePBX

Context: Connect FreePBX to your SIP trunk provider so calls can reach the PSTN.

Step 1: Add trunk in FreePBX

Navigate to: Connectivity > Trunks > Add Trunk > Add SIP (chan_pjsip) Trunk

General tab:

Field Value
Trunk Name Telnyx
Outbound CallerID +1NXXNXXXXXX (your US DID)
Maximum Channels 0 (unlimited)

PJSIP Settings tab — General:

Field Value
Username (from Telnyx)
Secret (from Telnyx)
Authentication Outbound
Registration Send
SIP Server sip.telnyx.com
SIP Server Port 5060
Context from-pstn

PJSIP Settings tab — Advanced:

Field Value
From Domain sip.telnyx.com
From User (your Telnyx username)
DTMF Mode RFC 4733
Codecs ulaw, alaw (priority order)

Submit and Apply Config.

Step 2: Verify trunk registration

asterisk -rx "pjsip show registrations"

Expected: Telnyx trunk shows Registered status.

Step 3: Log progress

echo "$(date) - Task 12 complete: SIP trunk configured and registered" >> /root/VOIP/logs/install.log

Task 13: Configure Outbound Routes

Context: Outbound routes tell FreePBX which trunk to use for different dialed numbers. We create separate patterns for US, Mexico, and emergency calls.

Step 1: Create US outbound route

Navigate to: Connectivity > Outbound Routes > Add Outbound Route

Field Value
Route Name US-Outbound
Trunk Sequence 0: Telnyx
Route CID (leave blank, uses trunk default)

Dial Patterns:

Prepend Prefix Match Pattern
+1 NXXNXXXXXX
+1 1 NXXNXXXXXX
911

This handles:

  • 10-digit US dialing (prepends +1)
  • 1+10-digit US dialing (prepends +1, strips leading 1... actually keeps it)
  • 911 emergency

Step 2: Create Mexico outbound route

Add another outbound route:

Field Value
Route Name MX-Outbound
Trunk Sequence 0: Telnyx

Dial Patterns:

Prepend Prefix Match Pattern
+52 52 XXXXXXXXXX
+52 011 52 XXXXXXXXXX

This handles:

  • Dialing 52 + 10 digits (Mexico format, prepends +52)
  • Dialing 011 52 + 10 digits (US international format)

Step 3: Set route priority

Ensure routes are ordered correctly:

  1. Emergency (911) — highest priority
  2. US-Outbound
  3. MX-Outbound

Apply Config.

Step 4: Log progress

echo "$(date) - Task 13 complete: Outbound routes configured" >> /root/VOIP/logs/install.log

Task 14: Configure Inbound Routes

Context: Inbound routes determine what happens when someone calls your DIDs from the outside.

Step 1: Create inbound route for US DID

Navigate to: Connectivity > Inbound Routes > Add Inbound Route

Field Value
Description US-Main-Number
DID Number +1NXXNXXXXXX (your US DID)
CallerID Number (leave blank — any caller)
Destination IVR: Menu Principal (or Ring Group 600)

Submit and Apply Config.

Step 2: Create inbound route for Mexico DID (when purchased)

Same process, different DID:

Field Value
Description MX-Main-Number
DID Number +52XXXXXXXXXX (your MX DID)
Destination IVR: Menu Principal

Step 3: Test inbound call

From any external phone (cell phone), call your US DID number. Verify:

  • Call connects to the PBX
  • IVR greeting plays (or Ring Group rings)
  • You can reach an extension

Step 4: Log progress

echo "$(date) - Task 14 complete: Inbound routes configured" >> /root/VOIP/logs/install.log

Task 15: Configure Firewall for Trunk Provider IPs

Context: Now that we have a trunk provider, we need to allow their SIP traffic through the firewall. Telnyx publishes their signaling IPs.

Step 1: Get Telnyx signaling IPs

Check Telnyx documentation for current IP ranges. As of writing:

Step 2: Add Telnyx IPs to nftables

Add these rules to /etc/nftables.conf in the input chain, before the final drop:

# Telnyx SIP trunk signaling
udp dport { 5060, 5061 } ip saddr { 52.2.64.0/23, 52.41.52.0/23, 169.55.30.0/23 } accept
tcp dport { 5060, 5061 } ip saddr { 52.2.64.0/23, 52.41.52.0/23, 169.55.30.0/23 } accept

Step 3: Apply and verify

nft -f /etc/nftables.conf
nft list ruleset | grep telnyx -i

Verify trunk is still registered:

asterisk -rx "pjsip show registrations"

Expected: Trunk still registered.

Step 4: Log progress

echo "$(date) - Task 15 complete: Firewall updated for trunk provider" >> /root/VOIP/logs/install.log

Task 16: End-to-End Testing

Context: Final validation that everything works together.

Step 1: Test internal call

Dial from 1001 to 1002. Verify two-way audio.

Step 2: Test outbound US call

From extension 1001, dial a US number (e.g., your cell phone). Verify:

  • Call connects
  • CallerID shows your DID number
  • Two-way audio works

Step 3: Test outbound Mexico call

From extension 1001, dial a Mexico number (52 + 10 digits). Verify same as above.

Step 4: Test inbound call

From an external phone, call your US DID. Verify:

  • IVR plays
  • Can reach extensions
  • Audio works both ways

Step 5: Test voicemail on external call

Call your DID, navigate to an extension, let it ring to voicemail. Leave a message. Check from the extension.

Step 6: Check CDR (Call Detail Records)

Navigate to: Reports > CDR Reports

Verify all test calls appear with correct source, destination, duration, and disposition.

Step 7: Final log

echo "$(date) - Task 16 complete: End-to-end testing PASSED. System ready." >> /root/VOIP/logs/install.log
echo "$(date) - === PHASE 2 COMPLETE ===" >> /root/VOIP/logs/install.log

POST-INSTALLATION CHECKLIST

After both phases are complete:

  • All extensions can call each other (internal)
  • Outbound calls work to US and Mexico numbers
  • Inbound calls reach the IVR/ring group
  • Voicemail works (leave and retrieve messages)
  • CDR records all calls
  • Firewall blocks unauthorized SIP traffic
  • Fail2ban is active and monitoring
  • FreePBX admin password is strong and unique
  • SIP extension passwords are 16+ random characters
  • Backup strategy is in place (FreePBX Backup module)
  • UPS protects the server from power outages