This is a short guide on how to set up an encrypted VoIP system using Twilio and Asterisk. I was a little annoyed that just about everything these days still uses unencrypted RTP for media (though just about everyone supports SIP over TLS). So I spent a weekend looking at options, and settled on a totally overkill solution involving Twilio's secure trunking to an Asterisk PBX. While all bets are off once it hits the PSTN, at least you won't be blasting your conversations over the internet in clear text.
This is a very sparse guide. If something to be missing, let me know, but in general, I assume you can do basic tasks yourself and I only explain the ones that are a little more arcane.
There are certainly some places this guide could be improved. Suggestions are most welcome. Hope this helps someone out there!
This section is a highly condensed version of http://www.ipcomms.net/sample-device-configurations/41-asterisk/179-asterisk-13-on-centos.
yum groupinstall –y Development Tools
yum install –y ncurses-devel uuid-devel libuuid-devel libxml2-devel sqlite-devel bison words openssl openssl-devel subversion git-core wget
cd /usr/src/
git clone https://github.com/akheron/jansson.git
cd jansson
autoreconf -i
./configure --prefix=/usr/
make && make install
cd /usr/src
wget https://downloads.sourceforge.net/project/srtp/srtp/1.4.4/srtp-1.4.4.tgz
tar zxvf srtp-1.4.4.tgz
cd srtp
./configure CFLAGS=-fPIC --prefix=/usr/local/lib
make
Now we need to fix a bug in the tests... Change line 7 of test/rtpw_test.sh
to RTPW=./rtpw
.
make runtest
make install
cd /usr/src/
wget http://downloads.asterisk.org/pub/telephony/asterisk/asterisk-13-current.tar.gz
tar xzvf asterisk-13-current.tar.gz
cd asterisk-13.*
./bootstrap.sh
./configure --with-srtp=/usr/local/lib
make && make install
make samples
make config
chkconfig asterisk on
Go to Twilio's website and create an Elastic SIP Trunk. Then perform the following configuration steps:
- Enable secure trunking
- Pick a domain name for termination (calls out to the PSTN)
- Set up credentials AND an ACL for termination
- Configure an origination URI to something like
sip:1.2.3.4
(where1.2.3.4
is the public IP of your Asterisk server; this tells Twilio where your PBX is when a call comes in to your number) - Add phone number(s) to the trunk
This is an abbreviated HOWTO synthesized from https://wiki.asterisk.org/wiki/display/AST/Secure+Calling+Tutorial#SecureCallingTutorial-Part2(SRTP). If something doesn't make sense, check out that page. It has a lot more detail.
First, we need to make some certs. We set up a directory and run a contrib script that sets up a CA and generates a server cert. You'll answer a bunnch of prompts for the same password. It gets old ;)
mkdir /etc/asterisk/keys
cd /usr/src/asterisk-13.*/contrib/scripts
./ast_tls_cert -C pbx.mycompany.com -O "Company Name" -d /etc/asterisk/keys
OPTIONAL: Generate a client cert. Not all clients are capable of using these, but the ones that can are nice.
./ast_tls_cert -m client -c /etc/asterisk/keys/ca.crt -k /etc/asterisk/keys/ca.key -C phone1.mycompany.com -O "Company Name" -d /etc/asterisk/keys -o keyname
In the general section, make the following edits. These settings will mostly exist already, so just search the file and replace the existing values.
context=bogus ; Makes sure we don't do anything stupid
allowguest=no
preferred_codec_only=yes
disallow=all
allow=ulaw ; Twilio does G.711 ulaw only
sipdebug=yes ; Optional - better debug logging
tlsenable=yes
tlsbindaddr=0.0.0.0
tlscertfile=/etc/asterisk/keys/asterisk.pem
tlscafile=/etc/asterisk/keys/ca.crt
tlsdontverifyserver=yes ; It would be nice to figure out how to avoid doing this...
tlsclientmethod=tlsv1
The following sections can all be added to the end of the file. First, we need to set up the trunk in Asterisk.
The exclamation mark creates a sort of template (so we could theoretically re-use the core settings for any Twilio trunk).
The twilio-termination
block defines our outbound trunk, and inherits the settings from twilio-trunk
.
[twilio-trunk](!)
type=peer
context=from-twilio ; Set the dialplan to use for incoming calls
dtmfmode=auto
canreinivite=no
encryption=yes
transport=tls
media_encryption=sdes
[twilio-termination](twilio-trunk) ; Outbound termination
host=mydomain.pstn.twilio.com
remotesecret=trunk-password-you-set-up-in-twilio
defaultuser=trunk-username-you-set-up-in-twilio
Next, we have to do some access control and create contexts for each of Twilio's SIP trunking servers. This is important enough to do as a whitelist. https://www.twilio.com/docs/api/sip-trunking/getting-started#whitelist
; Northern VA
[twilio-va1](twilio-trunk)
host=54.172.60.0/23
[twilio-va2](twilio-trunk)
host=34.203.250.0/23
; Oregon
[twilio-us2](twilio-trunk)
host=54.244.51.0/24
; Ireland
[twilio-ie1](twilio-trunk)
host=54.171.127.192/26
[twilio-ie2](twilio-trunk)
host=52.215.127.0/24
; Frankfurt
[twilio-de1](twilio-trunk)
host=35.156.191.128/25
[twilio-de2](twilio-trunk)
host=3.122.181.0/24
; Tokyo
[twilio-jp1](twilio-trunk)
host=54.65.63.192/26
[twilio-jp2](twilio-trunk)
host=3.112.80.0/24
; Singapore
[twilio-sg1](twilio-trunk)
host=54.169.127.128/26
[twilio-sg2](twilio-trunk)
host=3.1.77.0/24
; Sydney
[twilio-au1](twilio-trunk)
host=54.252.254.64/26
[twilio-au2](twilio-trunk)
host=3.104.90.0/24
; Sao Paulo
[twilio-br1](twilio-trunk)
host=177.71.206.192/26
[twilio-br2](twilio-trunk)
host=18.228.249.0/24
Finally, we set up our first extension. This is what lets you connect to the PBX with a softphone, desk phone, etc. Note that in a single office deployment with multiple phones, it may make sense for all phones to use the same secret, in which case it can also be stored in the template. This is a judgement call per implementaiton. Make sure you set a valid CallerID. The number should match one of the numbers associated with your trunk.
[office-phone](!)
type=friend
context=from-phones ; Referenced later in extensions.conf
host=dynamic ; If an extension uses a static IP or something, put it here. Otherwise use dynamic.
dtmfmode=auto
disallow=all
allow=ulaw
transport=tls
encryption=yes
[1001](office-phone)
secret=asdf1234 ; Use something long and secure here. This is what softphones etc. use when registering with the PBX.
callerid="Ian Wagner" <+12345678901>
mailbox=1001@default
Now we get to configure dialing rules!
First, comment out just about everything in extensions.conf
except for general
, stdexten
and stdPrivacyexten
. We don't need/want the demo stuff. You can keep the time extension and such towards the end of the file if you like.
Then, add the following lines to the end of the file. The from-twilio
dialplan
is referenced from the trunk configuration template in sip.conf
. The "concrete implementations" that inherit
from the template which form the IP whitelist in effect direct all incoming SIP traffic from Twilio trunk IP
addresses to the from-twilio
dialplan.
The from-twilio
dialplan will need to be configured based on your Twilio phone number(s) and local extension(s).
In the example below, we have a rule to match incoming calls from the number +12345678901
which dials the extension
we created in sip.conf
. Instead of a simple Dial()
, I have included the stdexten
context and invoke the rules there
via GoSub
. This ensures that we get niceties like failover to voicemail. In a simple setup, you might add
additional numbers and map them each to a single extension, but it's not at all difficult to imagine a more complex setup.
Finally, the from-phones
group configures what happens when an extensiondials a number. First, we set up an extension
for voicemail access (more on that later). Then we have the main catch-app for outbound numbers. The dial pattern here is
quite permissive and will match just about anything. This may not be valid for all set-ups. For example, if you
wanted to restrict international dialing. This also assumes that CallerID is correctly set by the extension to a
valid number associated with your trunk.
[from-twilio]
include => stdexten
exten => +12345678901,1,GoSub(1001,stdexten(SIP/1001))
[from-phones]
exten => 8500,1,VoiceMailMain() ; Voicemail extension for users to dial
exten => _XXXX,1,Dial(SIP/${EXTEN}) ; Call local 4-digit extensions
exten => _+X.,1,Dial(SIP/twilio-termination/${EXTEN})
Here we set up our voicemail boxes. For simplicity, I've kept the voicemail box number the same as the extension number.
Note the entry in sip.conf
. You may wish to disable some of the default boxes as well. The voicemail config file
also has contexts, like the other files, but there is not really any relation. You can add mailboxes to whichever context
you wish and use them for organization. For simplicity, I have put my extension mailbox in the default context.
The format of each line is `mailbox_numer => passcode,name
[default]
1001 => 1234,Ian Wagner
Now we're set up for voicemail.
systemctl start asterisk
yum install -y epel-release
yum install -y fail2ban
systemctl enable fail2ban
Create the file /etc/fail2ban/jail.local
with the following contents:
[DEFAULT]
# Ban hosts for one hour:
bantime = 3600
# Override /etc/fail2ban/jail.d/00-firewalld.conf:
banaction = iptables-multiport
[sshd]
enabled = true
[asterisk]
enabled = true
Then restart fail2ban.
TODO: Flesh this out with more details. This can be greatly improved. Also
include a section on firewall config with iptables
since we know Twilio's
media IP addresses as well.
- Cost per typical secure trunked call (in the US): $0.0055/min incoming; $0.015/min outgoing
- Cost per number: typically $1/mo
- To connect to the asterisk CLI:
asterisk -r
- To watch asterisk logs:
tail -f /var/log/asterisk/messages
- CLI debug dialplan:
dialplan show +12345678901@from-twilio
- Fallback voicemail recording with Twilio
- SMS??
Good suggestion. The less security you have to roll yourself, the better.