Skip to content

Instantly share code, notes, and snippets.

@ianthetechie
Last active November 1, 2023 16:01
Show Gist options
  • Star 11 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save ianthetechie/bdf5af08cf4f877f2dfb58bcdff5fbfd to your computer and use it in GitHub Desktop.
Save ianthetechie/bdf5af08cf4f877f2dfb58bcdff5fbfd to your computer and use it in GitHub Desktop.
A short guide on how to set up an encrypted VoIP system using Twilio and Asterisk.

Twilio Asterisk Secure Trunking HOWTO

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!

1. Buy a phone number from Twilio

2. Set up a Centos 7 server and update packages

3. Install Asterisk

This section is a highly condensed version of http://www.ipcomms.net/sample-device-configurations/41-asterisk/179-asterisk-13-on-centos.

Install build tools and required packages

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

Install Jansson

cd /usr/src/
git clone https://github.com/akheron/jansson.git
cd jansson
autoreconf -i
./configure --prefix=/usr/
make && make install

Install SRTP

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

Build Asterisk

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

4. Set up a Twilio Elastic SIP Trunk

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 (where 1.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

5. Configure Asterisk

TLS Prep

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

sip.conf changes

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

extensions.conf changes

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})

voicemail.conf changes

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.

6. Start up Asterisk

systemctl start asterisk

7. Add some basic security to the sytem

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.

Random

  • 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

Still TODO

  • Fallback voicemail recording with Twilio
  • SMS??
@tminard
Copy link

tminard commented Aug 1, 2017

Very cool. You could probably install this on an EC2 instances and stick it behind a security group for a little extra protection (and so your server won't need to double as a bastion host)

@ianthetechie
Copy link
Author

Good suggestion. The less security you have to roll yourself, the better.

@brunofin
Copy link

SMS support would be completely awesome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment