从0到壹营造自身的互连网电话系统

不久前流量卡越来越方便了,看看自个儿手里的“坑不死老用户”的联通卡,霎时觉获得深远的恶心,可是OPPO未有双卡功效,所以不得不本人入手营造贰个互联网电话系统托管联通卡,诺基亚使用流量卡,系统转换联通卡的呼唤到一加上,其实也没怎么人给本身打电话了[捂脸🤦‍♂️],首即使转发短信,方便接受验证码。当然,反过来也足以,在索爱上通过网络使用系统中的联通卡拨号,和发短信,可是发短信基本上用不到了。

实际上达成起来很简单,正是采纳是呀的各样现成的工具,累积木壹样搭出来。硬件清单如下:

  • Raspberry pi 3代B 一枚
  • 二A电源(小编用的是华为平板的充电器)
  • 1陆GB的SDXC卡+可读写miniSD卡的读卡器
  • 索尼爱立信的上网卡托E16九

开始

早就有包装了Asterisk和FreePBX的系统:RasPBX

  1. 首先下载RasPBX系统,烧录到TF闪存卡中,通电运行木莓派,接上显示屏和键盘(可能无须外接硬件),连上有线网。

    配置ssh登录。

  2. 依据RasPBX的设置教程首先应当raspbx-upgrade,不过在自家大天朝就别想那么百步穿杨,须求更加啥你懂的,至于怎么样丰硕啥大家就各显神通吧,假使您和本身同样使用ss那么能够参照这篇作品

    只要1切顺利,那么proxychains raspbx-upgrade,大致等个恐怕十多分钟就能更新完成

  3. FreePBX提供了二个融洽的Web界面供我们接纳,在浏览器中输入http://raspbx,mac输入http://raspbx.local报到,选取Administrator,使用暗中同意初叶账号admin,密码admin进行登录。

  4. 跻身管控台后,首先映入眼帘的是仪表盘
    图片 1

    先在局域网中测试一下能无法符合规律使用,然后在到公网上去。由于是在局域网中测试,所以布置很简短,直接在Applications->Extensions->Add new Chan_SIP Extension新建二个分机,在Generaltab页里面填好第叁个SIP账户的消息,例如
    图片 2

    然后在Advanced中设置NAT ModeYes - (force_rport,comedia)

    末尾点击右下角的submit

    再去Settings->Asterisk SIP Settings里面的Chan SIP Settingstab页上边包车型地铁第9个NAT设置未Never,然后Submit

    末段的末梢点击右上角的Apply Config运用刚才的铺排。

  5. 接下去去下载一个SIP客户端,我选用的是zoiper,安装到位以往,在Account中添加刚才在FreePBX中加上的账户,记住密码是secret中的值,千万别填上边包车型地铁,那多少个是该用户进入管控台的密码,如若用户名恐怕密码不得法只会回来40三而不会唤起用户名可能密码错误。那里的Domain就填写RasPBX在局域网中的ip地址。

    填好之后,点击上方的Register

  6. 比方唯有2个部手提式有线电话机的话,那么能够在总计机上再装四个SIP软件,笔者在Mac上设置了Telephone,然后再度上边包车型大巴步调再登记另二个账户,在Mac上登录。

    打多个对讲机测试一下吧,是否很激动:)

内网测试通过之后,就足以开头搭建国门外网访问了

  1. 到现在可以品味通过外网访问了,一般常见的有二种外网访问情形。

能力 责任? 狗屁? 爱谁谁

1. 如果你有公网IP,那么这是最省事的,可惜大多数人没有。

1. 用动态域名进行访问

1. 没有路由器权限或者路由器没有被分配公网IP,这种情况只能内网穿透。

我没有公网IP,所以1不行,本来我家里的路由器也没有被分配公网IP,因为路由器连接电信的光猫使用DHCP联网的,公网IP被分配到了电信的光猫上了,所以选择3,进行内网穿透,使用ssh反向代理,但是发现ssh可以转发TCP,对于UDP就无能为力了,于是打算把SIP的协议改成TCP,但是发现通信用的RTP只能使用UDP,没法改。因此,又琢磨着使用IAX,结果发现IAX好像也改不成UDP,或许是我的姿势不对。不得已只能试试2了,本来想破解电信的光猫,然后将其改成桥接模式,让路由器进行PPOE拨号上网,看了很多破解教程,发现网上流传的漏洞都被堵死了,至此打算放弃了,最后抱着试试看的心态,联系了电信客服,客服说已为我报修,之后会有工程师联系我,过了不久,工程师给我来电了,我说要把光猫改成桥接模式,工程师很爽快的答应了,立马就远程改好了,叫我过20分钟重启,重启之后果然变成了桥接模式。接着我在路由器中使用PPPOE进行拨号上网,路由器就被分配了公网IP,真是踏破铁鞋无觅处,得来全不费功夫:)
  1. 使用动态域名实行走访

    先拿到路由器被分配的公网IP,能够直接在路由器中查阅,或许在路由器下边包车型地铁高脚菠派中经过极端:curl ip.cn查看该IP。

    开辟freepbx的web界面,登录管理员界面,Settings->Asterisk SIP SettingsDetect Network Settings,自动检测IP,应该和上一步中获得的IP相同,再检查上边包车型客车内网地址是不是科学。
    图片 3
    然后去Chan SIP Settingstab页上边的NAT设置Static IP,暗许值正是事先检查实验到的结果,假设没错就不用改,然后submit,applyconfig
    图片 4

    再到路由器中装置端口转载,因为12分公网IP是路由器的地点。各种路由器的安装都差异,请自行检索一下,其实很简短,就是把路由器的5060端口转载到地仙泡派的5060端口,协议最棒采纳TCP/UDP,还有一千一~两千0端口也要转化到四月泡派的一千1~两千0,那些是SportageTP通讯端口能够挑选转载UDP.这里没须求转载两万个端口,三回通信只必要多少个端口,所以转5个端口一千一~一千肆用来测试就能够了。

    到现在去客户端中把Domain改成公网IP,应该力所能及拨通分机。

    用手提式有线电话机的四G网络再试二次,依然健康这就透过了。

  2. 因为这一个IP是邮电通讯运营商动态分配的,随时都有十分的大希望变化,所以须要不断检查评定当前IP地址,然后通过DNS服务转移解析。最不难易行的做法得以一贯用花生壳等服务商的劳动,然则自个儿不爱好这一个服务商,打算本身写个脚本来完成,这样既有成就感又能一心通晓,用花生壳的客户端给本人一种感觉:总有刁民想害朕。

    云计算井喷式的开拓进取,笔者等小P民也能美美的用上了,去Ali云买个方便人民群众的ip地址和dns云解析一年也才50多块,Ali云的云解析的TTL最快达到1s。
    图片 5

    木莓派暗中认可已经设置了python环境,那就用python吧,调用Ali云的sdk就足以了,分分钟的事务。脚本参考连接,原版的书文者已经写得很好了,只是有2个至极意况小编未有碰着,那正是ip.cn宕机了,所以小编在本子中简单处理了一晃那种情景。
    在沙窝窝派中,新建vim aliyun_ddns.py,把下部的代码复制进去,部分地点依据真实意况修改。

    # -*- coding: UTF-8 -*-
    
    import json
    import os
    import re
    import sys
    from datetime import datetime
    
    from aliyunsdkalidns.request.v20150109 import UpdateDomainRecordRequest,    DescribeDomainRecordsRequest, \
        DescribeDomainRecordInfoRequest
    from aliyunsdkcore import client
    
    #请填写你的Access Key ID
    access_key_id = "你的keyID"
    
    #请填写你的Access Key Secret
    access_Key_secret = "你的Secret"
    
    #请填写你的账号ID
    account_id = "你的账号ID"
    
    #如果选择yes,则运行程序后仅现实域名信息,并不会更新记录,用于获取解析记录ID。
    #如果选择NO,则运行程序后不显示域名信息,仅更新记录
    i_dont_know_record_id = 'yes'
    
    #请填写你的一级域名
    rc_domain = '域名'
    
    #请填写你的解析记录
    rc_rr = '你的解析记录'
    
    #请填写你的记录类型,DDNS请填写A,表示A记录
    rc_type = 'A'
    
    #请填写解析记录ID
    rc_record_id = '解析记录ID'
    
    #请填写解析有效生存时间TTL,单位:秒
    rc_ttl = '1'
    
    #请填写返还内容格式,json,xml
    rc_format = 'json'
    
    def my_ip():
        get_ip_method = os.popen('curl -s ip.cn')
        get_ip_responses = get_ip_method.readlines()[0]
        get_ip_pattern = re.compile(r'\d+\.\d+\.\d+\.\d+')
        get_ip_value = get_ip_pattern.findall(get_ip_responses)[0]
        return get_ip_value


    def check_records(dns_domain):
        clt = client.AcsClient(access_key_id, access_Key_secret, 'cn-hangzhou')
        request = DescribeDomainRecordsRequest.DescribeDomainRecordsRequest()
        request.set_DomainName(dns_domain)
        request.set_accept_format(rc_format)
        result = clt.do_action_with_exception(request)
        return result


    def old_ip():
        clt = client.AcsClient(access_key_id, access_Key_secret, 'cn-hangzhou')
        request = DescribeDomainRecordInfoRequest.DescribeDomainRecordInfoRequest()
        request.set_RecordId(rc_record_id)
        request.set_accept_format(rc_format)
        result = clt.do_action_with_exception(request)
        result = json.JSONDecoder().decode(result)
        result = result['Value']
        return result


    def update_dns(dns_rr, dns_type, dns_value, dns_record_id, dns_ttl, dns_format):
        clt = client.AcsClient(access_key_id, access_Key_secret, 'cn-hangzhou')
        request = UpdateDomainRecordRequest.UpdateDomainRecordRequest()
        request.set_RR(dns_rr)
        request.set_Type(dns_type)
        request.set_Value(dns_value)
        request.set_RecordId(dns_record_id)
        request.set_TTL(dns_ttl)
        request.set_accept_format(dns_format)
        result = clt.do_action_with_exception(request)
        return result

    def write_to_file():
        time_now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        current_script_path = sys.path[7]
        print current_script_path
        log_file = current_script_path + '/' + 'aliyun_ddns_log.txt'
        write = open(log_file, 'a')
        write.write(time_now + ' ' + str(rc_value) + '\n')
        write.close()
        return 

    if i_dont_know_record_id == 'yes':
        print check_records(rc_domain)
    elif i_dont_know_record_id == 'no':
        try:
          rc_value = my_ip()
        except:
          rc_value = old_ip()
        rc_value_old = old_ip()
        if rc_value_old == rc_value:
            print 'The specified value of parameter Value is the same as old'
        else:
            print update_dns(rc_rr, rc_type, rc_value, rc_record_id, rc_ttl, rc_format)
            write_to_file()

然后导入阿里云的python sdk:`pip install aliyun-python-sdk-alidns`。

先去阿里云控制台手动解析一下,生成一个记录就OK了。

首次运行脚本前,把上面的脚本中的`i_dont_know_record_id`填写`yes`,然后运行`python /path/of/aliyun_ddns.py`,拿到结果中就包含了你的解析记录ID,

    {
    "PageNumber": 1,
    "TotalCount": 1,
    "PageSize": 1,
    "RequestId": "xxxxx-xxxx-xxxx-xxxx-xxxxxx",
    "DomainRecords": {
        "Record": [
        {
            "RR": "xxxx",
            "Status": "xxxxx",
            "Value": "xxxxxx",
            "RecordId": "xxxxxxx",
            "Type": "A",
            "DomainName": "xxxx",
            "Locked": false,
            "Line": "default",
            "TTL": "1"
        }
        ]
    }
    }

把`RecordId`的值填到脚本中`rc_record_id`,然后把`i_dont_know_record_id`改为`no`,  
再次执行,如果没有异常那就通过了。

然后用cron做成定时任务。  
输入`crontab -e`,添加:

    */1 * * * * /usr/bin/python2.7 ~/aliyun_ddns.py > /dev/null 1>/dev/null

注意脚本的路径。
  1. 去FreePBX的保管理控制制长沙,Settings->Asterisk SIP SettingsDetect Network Settings,将其删除,那一个输入框留空,因为我们要用动态域名,所以那里不能填IP,到Chan SIP Settingstab页上边包车型大巴NAT设置Dynamic IP,输入买来的域名,下边包车型大巴检验间隔设置为60s。Submit->Apply Config

  2. 瞩目:最佳在路由器中设置给红树莓派3个静态ip,幸免木莓派的ip地址变更了,导致路由器的端口转载实际效果。

  3. 在用客户端中,把Domain改成域名,测试三回,分机之间能够互相通信。

使用上网卡托接打电话

  1. 那时候就须求3G
    chan_gongle了,小编是在某宝上买的NokiaE16九。chan_dongle兼容的产品列表

    先安装驱动install-dongle,安装进程中会询问多少个难题,认真回复:)

    Please enter the phone number of your SIM card (defaults to +1234567890 if left blank):
    输入U棒里的 sim卡手机号码,直接回车则默认为 +1234567890
    
    Send incoming SMS to email address (leave empty to disable SMS forwarding):
    设置邮箱,以便将U棒收到的短信内容转发过去,直接回车可取消该功能
    
    Forward incoming SMS to mobile phone number (via dongle0) (leave empty to disable):
    设置一个手机号码,以便将U棒收到的短信通过 dongle0 转发至该号码,直接回车可取消该功能
    
    Would you like to install a webpage for sending SMS with chan_dongle? (http://raspbx/sms/) [y/N]
    是否安装发送短信的web页面,可回答 y 并按提示设置一个登录密码。
    
  2. 追加贰个trunk:connectivity->Trunks
    图片 6
    填上dongle/dongle0/$OUTNUM$
    图片 7

    Submit->ApplyConfig

  3. 编写拨号路由:connectivity->Outbound Routes,命名Route Name,选用刚刚新建的Trunk
    图片 8

    设置拨号规则,相称号码,若是您的编号相称那一个规则,那么就采取大家的Trunk拨号,那里自身添加了多少个规则:1.相配一开首的享有号码,prefix=9即首先数字是9的持有号码。
    图片 9

    Submit->ApplyConfig

  4. 编排接听路由:connectivity->Inbound Routes,填写Description描述一下,Set Destination对象选取801分机
    图片 10
    Submit->ApplyConfig

  5. 至今在客户端中拨号一**********,应该就足以拨通了,再发条短信到作者的号码上,应该也会自行转载到指标号码上。可是短信转载协理不完美,平常缺点和失误内容,所以干脆禁止使用该意义,使用邮件转载收到的短信。

由此邮件转载收到的短信

  1. 配置exim4:dpkg-reconfigure exim4-config如故间接改动文件/etc/exim4/update-exim4.conf.conf,例如笔者使用本身的QQ邮箱,别的邮箱自行在邮箱设置中找到呼应的计划

    dc_eximconfig_configtype='smarthost'
    dc_other_hostnames='raspberrypi'
    dc_local_interfaces='127.0.0.1'
    dc_readhost=''
    dc_relay_domains=''
    dc_minimaldns='false'
    dc_relay_nets=''
    dc_smarthost='smtp.qq.com'
    CFILEMODE='644'
    dc_use_split_config='false'
    dc_hide_mailname='false'
    dc_mailname_in_oh='true'
    dc_localdelivery='mail_spool'
    
  2. smtp的帐号密码设置/etc/exim4/passwd.client

    smtp.qq.com:邮箱账号:邮箱密码
    

    本人那边的信箱密码使用的是腾讯的授权码。

  3. 系统邮箱地址/etc/email-addresses

    root: 邮箱账号
    asterisk: 邮箱账号
    

    那里一定要填写asterisk,因为chan_dongle使用帐户asterisk调用sendemil一声令下,所以一旦不写,邮箱服务端不肯定。添加root,是为了测试用。

    update-exim4.conf履新一下。

    命令send_test_email your_email@someisp.com,测试一下是或不是可以发送成功。

  4. 在意日志文件是/var/log/exim4/mainlog,假诺有任何难题先去日志里看望。

  5. 配置/etc/asterisk/dongle.conf

    context=from-trunk              ; context for incoming calls
    group=0                         ; calling group
    rxgain=0                        ; increase the incoming volume; may be negative
    txgain=0                        ; increase the outgoint volume; may be negative
    autodeletesms=yes               ; auto delete incoming sms
    resetdongle=yes                 ; reset dongle during initialization with ATZ command
    u2diag=0                        ; set ^U2DIAG parameter on device (0 = disable  everything except modem function) ; -1 not use ^U2DIAG command
    

    后续往下翻,配置dongle0,ttyUSB*和你系统中的保持一致,底下的imeiimsi能够因而命令:asterisk -rx "dongle show devices"找到。

    ; dongle required settings
    [dongle0]
    audio=/dev/ttyUSB1              ; tty port for audio connection;        no default  value
    data=/dev/ttyUSB2               ; tty port for AT commands;             no default  value
    
    ; or you can omit both audio and data together and use imei=123456789012345 and/or  imsi=123456789012345
    ;  imei and imsi must contain exactly 15 digits !
    ;  imei/imsi discovery is available on Linux only
    imei=xxxx
    imsi=xxxx
    
  6. 设置chan_dongle的/etc/asterisk/extensions_custom.conf

    [from-trunk]
    exten => s,n,goto(from-trunk,${DONGLEIMEI},1)
    
    exten => sms,1,Verbose(Incoming SMS from ${CALLERID(num)} ${SMS})
    exten => sms,n,System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLENAME}    - ${CALLERID(num)}: ${SMS}' >> /var/log/asterisk/sms.txt)
    exten => sms,n,System( echo "Receiver:${DONGLENUMBER}\nDate:${STRFTIME(${EPOCH},,   %Y-%m-%d %H:%M:%S)}\nContent:${SMS}" | /usr/bin/mail -s '[SMS]From:${CALLERID(num)}'   邮箱地址)
    exten => sms,n,Hangup()
    
    exten => ussd,1,Verbose(Incoming USSD: ${USSD})
    exten => ussd,n,System(echo '${STRFTIME(${EPOCH},,%Y-%m-%d %H:%M:%S)} - ${DONGLENAME}   : ${USSD}' >> /var/log/asterisk/ussd.txt)
    exten => ussd,n,Hangup()
    
  7. 输入指令amportal admin reload,更新配备。

  8. 输入asterisk -vvvvvr进入调节和测试形式,观察调节和测试音信,那时候发送一条短信到你的数码上,

    Incoming SMS from 对方号码 test
    -- Executing [sms@from-trunk:2] System("Local/sms@from-trunk-00000011;1", "echo '2017-07-06 18:08:18 - dongle0 - 对方号码: test' >> /var/log/asterisk/sms.txt") in new stack
    -- Executing [sms@from-trunk:3] System("Local/sms@from-trunk-00000011;1", " echo "Receiver:+我的号码\nDate:2017-07-06 18:08:18\nContent:test" | /usr/bin/mail -s '[SMS]From:对方号码' 我的邮箱") in new stack
    -- Executing [sms@from-trunk:4] Hangup("Local/sms@from-trunk-00000011;1", "") in new stack
    == Spawn extension (from-trunk, sms, 4) exited non-zero on    'Local/sms@from-trunk-00000011;1'
      -- Executing [h@from-trunk:1] Macro("Local/sms@from-trunk-00000011;1", "hangupcall,") in new stack
      -- Executing [s@macro-hangupcall:1] GotoIf("Local/sms@from-trunk-00000011;1", "1?theend") in new stack
      -- Goto (macro-hangupcall,s,3)
      -- Executing [s@macro-hangupcall:3] ExecIf("Local/sms@from-trunk-00000011;1", "0?Set(CDR(recordingfile)=)") in new stack
      -- Executing [s@macro-hangupcall:4] Hangup("Local/sms@from-trunk-00000011;1", "") in new stack
    == Spawn extension (macro-hangupcall, s, 4) exited non-zero on    'Local/sms@from-trunk-00000011;1' in macro 'hangupcall'
    == Spawn extension (from-trunk, h, 1) exited non-zero on  'Local/sms@from-trunk-00000011;1'
    
  9. 只要邮件发送失利,注意观看日志tail /var/log/exim4/mainlog


尾声

  1. ##### 使用TCP优化通讯

    其实很不难,Settings->Asterisk SIP Settings里面的Chan SIP Settings以此tab页上面的Enable TCP选择Yes,然后Submit,别忘了Apply Config.

    下一场再把前边建好的分机Extensions改一下商谈。Applications->ExtensionsAdvanced中设置TransportTCP Only,那里先改贰个zoiper客户端使用的分机Extension

    时到现在天可以用TCP协议了,那么在客户端中账户的Network Settings里面的Transport设置为TCP,而Telephone唯其如此用UDP,由此上一步中不用改Telephone上用的分机Extension

    OK,再用手机上的Zoiper通话给Telephone,假若通了那就OK。

  2. ##### 为虎傅翼

    为了在DNS解析不如时等万分意况下,能够远程登录到欧洲红树莓派上拓展操作,能够用ssh进行反向代理控制托盘派,其实入手相比较不难,可是原理相比较难懂。使用ssh开启反向代理隧道,让走访服务器的某部端口都被ssh转载到地面包车型大巴5060端口。

    这一步相比较难懂,可是简单实现,参考下边包车型大巴课程:

    1. 先用ssh搭建反向隧道,再用autossh保障隧道的稳定性

    2. 下一场做成服务使其能够开机自动运行,注意最佳钦点autossh的督察端口

    那里面要专注服务器的防火墙要对以上所需的TCP端口打开。

  3. ##### 通过ssh反向代理揭示FreePBX的WebUI

    合法提供了三种安全的暴光FreePBX的WebUI的姿势:

    1. 使用SSL
    2. 使用VPN
    3. 利用SSH反向代理
      纯属不要向来暴露在公网上,固然管理员密码设的很复杂,因为官方说攻击者能够选择PHP的漏洞,切记!!!
      有了上一步的经历,这一步就很简短了
      ssh -fNR *:8800:localhost:80 服务器账号@服务器地址,那样直接待上访问服务器的8800端口就能进来WebUI管理界面了。
  4. ##### 使用Fail二Ban保险安全

    小编的FreePBX刚爆出到公网就被人连连的强行破解,真是恶心的格外。

    参考那篇小说学习下怎么利用Fail2Ban

    下一场参考本条链接配置asterisk

The Last Thing

千万别忘记备份一下单月没反常好了吧zhe那把相应不会有毛病了吧,俨然了

再见也许不再见

暌违或者成永诀

老实执言勇气可嘉

相关文章