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>
1154 lines
27 KiB
Markdown
1154 lines
27 KiB
Markdown
# 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**
|
|
|
|
```bash
|
|
cat /etc/os-release | grep -E "PRETTY_NAME|VERSION_ID"
|
|
```
|
|
|
|
Expected: `Debian GNU/Linux 12 (bookworm)` and `VERSION_ID="12"`
|
|
|
|
```bash
|
|
apt update && apt upgrade -y
|
|
```
|
|
|
|
Expected: System fully updated, no errors.
|
|
|
|
**Step 2: Set hostname and timezone**
|
|
|
|
```bash
|
|
hostnamectl set-hostname pbx.tudominio.local
|
|
timedatectl set-timezone America/Mexico_City
|
|
```
|
|
|
|
Verify:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
systemctl restart networking
|
|
ip addr show eth0
|
|
```
|
|
|
|
Expected: Static IP assigned, connectivity verified with `ping 8.8.8.8`.
|
|
|
|
**Step 4: Install base dependencies**
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
apt install -y mariadb-server mariadb-client
|
|
systemctl enable mariadb
|
|
systemctl start mariadb
|
|
```
|
|
|
|
Verify:
|
|
|
|
```bash
|
|
systemctl status mariadb
|
|
mariadb --version
|
|
```
|
|
|
|
Expected: Active (running), version 10.11+.
|
|
|
|
**Step 2: Secure MariaDB**
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
mariadb -u root -p -e "SELECT VERSION();"
|
|
```
|
|
|
|
Expected: Returns MariaDB version, login works with your password.
|
|
|
|
**Step 3: Create Asterisk databases**
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
mariadb -u asterisk -p -e "SHOW DATABASES;"
|
|
```
|
|
|
|
Expected: Shows `asterisk` and `asteriskcdrdb` databases.
|
|
|
|
**Step 4: Log progress**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
apt install -y apache2
|
|
systemctl enable apache2
|
|
systemctl start apache2
|
|
```
|
|
|
|
Verify:
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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`:
|
|
|
|
```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:
|
|
|
|
```ini
|
|
short_open_tag = On
|
|
```
|
|
|
|
**Step 4: Enable Apache modules**
|
|
|
|
```bash
|
|
a2enmod rewrite
|
|
a2enmod headers
|
|
systemctl restart apache2
|
|
```
|
|
|
|
Verify:
|
|
|
|
```bash
|
|
apache2ctl -M | grep -E "rewrite|headers"
|
|
```
|
|
|
|
Expected: Both modules listed.
|
|
|
|
**Step 5: Log progress**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
adduser --system --group --home /var/lib/asterisk --no-create-home asterisk
|
|
usermod -aG audio,dialout asterisk
|
|
```
|
|
|
|
Verify:
|
|
|
|
```bash
|
|
id asterisk
|
|
```
|
|
|
|
Expected: Shows uid, gid, and groups including audio, dialout.
|
|
|
|
**Step 2: Download Asterisk 20 LTS**
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
ls -la /usr/src/asterisk-20.*/
|
|
```
|
|
|
|
Expected: Asterisk source tree present.
|
|
|
|
**Step 3: Install prerequisites script**
|
|
|
|
```bash
|
|
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)**
|
|
|
|
```bash
|
|
contrib/scripts/get_mp3_source.sh
|
|
```
|
|
|
|
Expected: Downloads mp3 source to `addons/mp3/`.
|
|
|
|
**Step 5: Configure Asterisk**
|
|
|
|
```bash
|
|
./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**
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
menuselect/menuselect --check-deps menuselect.makeopts
|
|
```
|
|
|
|
Expected: No unmet dependencies.
|
|
|
|
**Step 7: Compile and install**
|
|
|
|
```bash
|
|
make -j$(nproc)
|
|
make install
|
|
make samples
|
|
make config
|
|
make install-logrotate
|
|
ldconfig
|
|
```
|
|
|
|
Expected: Each command completes without errors.
|
|
|
|
**Step 8: Set permissions**
|
|
|
|
```bash
|
|
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`:
|
|
|
|
```bash
|
|
AST_USER="asterisk"
|
|
AST_GROUP="asterisk"
|
|
```
|
|
|
|
Edit `/etc/asterisk/asterisk.conf`:
|
|
|
|
```ini
|
|
[options]
|
|
runuser = asterisk
|
|
rungroup = asterisk
|
|
```
|
|
|
|
**Step 10: Start Asterisk and verify**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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)**
|
|
|
|
```bash
|
|
curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
|
|
apt install -y nodejs
|
|
```
|
|
|
|
Verify:
|
|
|
|
```bash
|
|
node -v && npm -v
|
|
```
|
|
|
|
Expected: Node 18.x and npm version.
|
|
|
|
**Step 2: Download FreePBX 17**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
./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**
|
|
|
|
```bash
|
|
fwconsole ma installall
|
|
fwconsole reload
|
|
fwconsole chown
|
|
```
|
|
|
|
Expected: Modules install without errors, `fwconsole reload` completes.
|
|
|
|
**Step 5: Configure Apache for FreePBX**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
fwconsole chown
|
|
fwconsole reload
|
|
```
|
|
|
|
Verify crontab exists:
|
|
|
|
```bash
|
|
crontab -u asterisk -l
|
|
```
|
|
|
|
Expected: Shows FreePBX cron entries.
|
|
|
|
**Step 8: Log progress**
|
|
|
|
```bash
|
|
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)**
|
|
|
|
```bash
|
|
apt install -y nftables
|
|
systemctl enable nftables
|
|
```
|
|
|
|
**Step 2: Create firewall rules**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
nft -f /etc/nftables.conf
|
|
nft list ruleset
|
|
```
|
|
|
|
Expected: Rules listed, no errors.
|
|
|
|
**Step 4: Test connectivity**
|
|
|
|
```bash
|
|
# 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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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]`:
|
|
|
|
```ini
|
|
[logfiles]
|
|
security_log => security
|
|
```
|
|
|
|
Reload Asterisk logger:
|
|
|
|
```bash
|
|
asterisk -rx "logger reload"
|
|
```
|
|
|
|
Verify:
|
|
|
|
```bash
|
|
ls -la /var/log/asterisk/security_log
|
|
```
|
|
|
|
Expected: File exists (may be empty initially).
|
|
|
|
**Step 3: Create Fail2ban jail for Asterisk**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
systemctl restart fail2ban
|
|
fail2ban-client status
|
|
fail2ban-client status asterisk
|
|
```
|
|
|
|
Expected: Shows `asterisk` jail active with 0 currently banned.
|
|
|
|
**Step 5: Log progress**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
asterisk -rx "pjsip show endpoints"
|
|
```
|
|
|
|
Expected: Shows endpoints 1001 and 1002 (status will be "Unavailable" until phones connect).
|
|
|
|
**Step 5: Log progress**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
asterisk -rx "pjsip show registrations"
|
|
```
|
|
|
|
Expected: Telnyx trunk shows `Registered` status.
|
|
|
|
**Step 3: Log progress**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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:
|
|
- `52.2.64.0/23`
|
|
- `52.41.52.0/23`
|
|
- `169.55.30.0/23`
|
|
- (verify current list at https://support.telnyx.com)
|
|
|
|
**Step 2: Add Telnyx IPs to nftables**
|
|
|
|
Add these rules to `/etc/nftables.conf` in the `input` chain, before the final drop:
|
|
|
|
```nft
|
|
# 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**
|
|
|
|
```bash
|
|
nft -f /etc/nftables.conf
|
|
nft list ruleset | grep telnyx -i
|
|
```
|
|
|
|
Verify trunk is still registered:
|
|
|
|
```bash
|
|
asterisk -rx "pjsip show registrations"
|
|
```
|
|
|
|
Expected: Trunk still registered.
|
|
|
|
**Step 4: Log progress**
|
|
|
|
```bash
|
|
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**
|
|
|
|
```bash
|
|
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
|