记得很久以前看过一个留学生使用华为上网棒架设越洋VOIP服务器实现境外使用国内SIM卡拨打电话的文章,颇为有意思,操作简单来说就是购买华为特定型号的USB上网棒(Dongle Modem),手机卡使用特定运营商的SIM,当然这里选择最便宜的套餐,国内使用树莓派安装RasPBX系统,将上网棒接入树莓派,配置FreePBX之后就可以愉快使用树莓派接打电话或收发短信了,而且在地球的任意角落也可以通过互联网实现境内通话资费。
一直对这种新鲜Geek范儿的事情比较感兴趣,正好手头闲置有树莓派,于是动手实施,当然本着不折腾不舒服的理由,我没有直接安装RasPBX这个定制的系统,当然另外的考虑就是树莓派仅作为话务中心服务对于我来说没有充分榨干其价值,毕竟上面还跑有其他各种特殊的服务,于是参考了RasPBX的定制脚本开始了折腾之路......遗憾的是这个事情已经过去有六七年了,当时没有记录折腾的步骤,虽然配置成功了,但因为FreePBX这个系统太占资源了,导致树莓派不堪重负(其间还配了大流量电源),后来我停掉了IVR和通话功能,仅保留了接收短信和发送短信的功能,于是这个电话卡就用来自己注册接收短信用。
再后来某天我整理弱电箱的奇奇怪怪的设备,发现了这个吃灰的玩意儿,想到其默默工作了这些年也该提档升级发挥最大功能了,考虑到FreePBX的高资源占用,这次我将FreePBX移到了x86 mini主机上,上了固态和DDR4,完全满足FreePBX对资源的渴求,当然现在也不像以前那么爱折腾了,正好近两年来我大部分网络设施都Docker化,于是顺水推舟,Asterisk+FreePBX+MySQL什么的全部移入Docker中,这里使用的是tiredofit/docker-freepbx,当然做了修改,尤其是chan_dongle.so
对于中文支持问题,使用wdoekes/asterisk-chan-dongle,解决at_response_cmgr: [dongle0] Error parsing incoming message: Cannot parse UCS-2
错误。
1 NAT导致通话声音的问题
如果一切顺利的话其实也就没有这篇文章了,刚开始配置确实顺利调试通过,接打电话也完全OK,当然这之前有小插曲,那就是接打电话没有声音和30秒挂断,后来发现是防火墙仅开通了5060和5061端口,但没有打开UDP10000-20000的端口转发,语音是需要这个端口范围的。接打电话OK,短信OK,难道事情如此顺利?非也非也,第二天当我满怀欣喜的去单位炫耀我最新成果时出现了尴尬的场面,同事电话打过来,接听没有声音,当然我说话对方也无法听见,这就让人当场社死了。
1.1 问题根源
回去尝试解决这个问题,最终搜索网络发现是NAT配置问题,在Settings->Asterisk SIP Settings->General SIP Settings
配置菜单里面,NAT Settings
栏目里有个External Address
,这个获取的地址与实际IP不符,需要重新点击Detect Network Settings
获取最新地址并提交,再Apply Config才能修复问题。我知道这是因为宽带运营商(ISP)定期刷新IP池所导致的,当然宽带运营商能够大发慈悲的给我提供动态公网IP我已经很感谢了,不指望这个IP能够固定下来(运营商:固定IP加钱可得),但我总不能时刻检查IP地址变动并手动更新这里配置吧,难道不支持DDNS吗?
1.2 尝试DDNS
实际上General SIP Settings
的External Address
的配置应用的是chan_pjsip
模块,切换到SIP Settings [chan_pjsip]
就能看到相关设置,可能读者注意到选项卡旁边还有chan_sip
模块的配置(SIP Legacy Settings),点进去看到了Dynamic Host
,这个不是我正需要的DDNS吗?但是很遗憾chan_pjsip并不支持,我尝试切换到chan_sip模块,但我的SIP终端又出现了问题。
其实FreePBX对于chan_pjsip是支持DDNS的,但其官方配置说明仅限于商业版本,购买昂贵的商业版本对于我这个小众爱好者明显不太现实,那么我抖机灵的将External Address
填成DDNS主机是否可以呢?
答案是可以是可以但不确定是否有效,我并没有找到相关说明表示这里支持DDNS主机, 但搜索网络让我有了新的发现。
【2022年2月22日更新】新版本的Asterisk的pjsip是支持DDNS的,具体可以参考官方博客《PJSIP: DNS Manager (dnsmgr) and Full Dynamic Hostname Support, Coming Soon!》也就是说External Address
填成DDNS主机是完全OK的,但是这个有个前提就是最新版本且开通了DNS Manager (dnsmgr) ,可以通过asterisk -rvvvvvv连接后使用dnsmgr status查看状态:
CLI> dnsmgr status DNS Manager: enabled Refresh Interval: 300 seconds Number of entries: 0
如上所示,出现enabled
说明已经启用了DNS Manager,如果没有启用可以通过编辑/etc/asterisk/dnsmgr.conf
文件为以下内容启用:
[general] enable=yes refreshinterval=300
这里refreshinterval
表明刷新IP的最小间隔,完成配置后重启asterisk服务就可以了,如果是老版本系统或者不支持dnsmgr,可以继续看下去。
2 使用定时任务运行Bash Shell脚本自动配置
2.1 Bash Shell自动化脚本
为什么不直接通过cron定时任务定期检测IP地址并自动更新External Address
栏呢?TECHNOTES这篇文章《A Bash script to rewrite the "static" IP address in the FreePBX Asterisk SIP Settings when it is changed by your ISP》给出了一个方案,当然考虑到实际情况我对其脚本进行了修改以适配最新的FreePBX 15,修改后的脚本如下:
#!/bin/bash
# This program gets the current IP address (as assigned by the ISP) from
# OpenDNS and modifies the FreePBX Asterisk SIP settings if the external IP
# address has changed. Invoke it as cron job that runs every 5 minutes.
# THIS SCRIPT IS STILL CONSIDERED EXPERIMENTAL - USE AT YOUR OWN RISK!!!
user="asterisk"
pass="yourpass" # 改为你的asterisk数据库密码
host="localhost" # 改为你的数据库主机名或者地址
check=$(dig +short myip.opendns.com @resolver1.opendns.com) || {
check=$(curl -4s http://checkip.amazonaws.com) || { echo "Problem getting current IP address"; exit 1; }
}
if [[ ! $check =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "Invalid IP address"; exit 1;
fi
readip=$(mysql asterisk -u $user -p$pass -h$host -se 'SELECT val FROM kvstore_Sipsettings where `key` = "externip"') || { echo "Can't read externip from MySQL asterisk database"; exit 1; }
if [ "$check" != "$readip" ]; then
# IP address has changed
echo "It appears that PBX external IP address has been changed to $check, old IP address $readip will be replaced."
mysql_response=$(mysql asterisk -u $user -p$pass -h$host -se 'update kvstore_Sipsettings set val='\"$check\"' where `key`="externip" ')
# Reload Asterisk
#/var/lib/asterisk/bin/module_admin reload
/usr/sbin/fwconsole reload
fi
exit 0
注意上面脚本的user
、pass
和host
需要修改为你的正确配置,如果不清楚,可以查看/etc/amportal.conf
或者/etc/freepbx.conf
配置文件的AMPDBUSER
、AMPDBPASS
和AMPDBHOST
内容。另外刷新配置这块需要注意,原文是采用/var/lib/asterisk/bin/module_admin reload
方式,我这里注释掉了并使用新版本的刷新命令/usr/sbin/fwconsole reload
,各位根据实际情况取舍吧。
有网友反映这里设置不能实时更新到asterisk,具体表现为每次脚本执行IP变更后,仍然需要手动重启asterisk服务service asterisk restart
,不然设置不能生效,即使执行了fwconsole reload
也不行,有这个问题的朋友可以尝试开启SIP Settings [chan_pjsip] -> Allow Transports Reload选项,这样在脚本执行fwconsole reload
的时候,Transports也能随之更新(此段2022年2月22日更新)。
2.2 配置定时任务
将2.1节脚本保存为/var/lib/asterisk/agi-bin/updateip.sh
并运行chmod +x /var/lib/asterisk/agi-bin/update.sh
赋予执行权限,最后执行定时任务crontab -e
,插入下面一行:
*/5 * * * * /var/lib/asterisk/agi-bin/update.sh >/dev/null 2>&1
这里设定为每个5分钟执行脚本,如果外网IP改变则立即重新配置FreePBX和刷新Asterisk。
2.3 tiredofit/docker-freepbx的定时任务配置
对于tiredofit/docker-freepbx
需要注意的是自己下载官方Docker,找到install文件夹,并在其中assets文件夹内创建cron文件夹,cron文件夹里建立文本文档crontab
,完整的路径是./install/assets/cron/crontab
,修改crontab
并插入2.2节的定时任务,保存文件并docker build即可(记得把2.1节updateip.sh
脚本也编译进镜像,否则cron会找不到执行程序)。
3 总结
整体上FreePBX对Asterisk提供了友好易用的Web GUI,而且FreePBX对于社区版本开源并开放了大部分功能,这些功能对于我们个人来说足够,即使有特别的需求也可以通过其他方式进行Hack。
注:Asterisk和FreePBX是Sangoma Technologies公司注册商标。
要继续使用评论发布功能请先理解本站 隐私策略,然后选择允许评论服务所需要的如下 Cookies 类型:
点击下面的按钮将会弹出对话框请求您选择您允许使用的 Cookies 。
我已理解并确认 Cookie 设置