SIP Encryption on VoIP.ms

My VoIP provider recently added support for TLS/SRTP-based call encryption. Here's what I did to enable this feature on my Asterisk server.

First of all, I changed the registration line in /etc/asterisk/sip.conf to use the "tls" scheme:

[general]
register => tls://mydid:mypassword@servername.voip.ms

then I enabled incoming TCP connections:

tcpenable=yes

and TLS:

tlsenable=yes
tlscapath=/etc/ssl/certs/

Finally, I changed my provider entry in the same file to:

[voipms]
type=friend
host=servername.voip.ms
secret=mypassword
username=mydid
context=from-voipms
allow=ulaw
allow=g729
insecure=port,invite
transport=tls
encryption=yes

(Note the last two lines.)

The dialplan didn't change and so I still have the following in /etc/asterisk/extensions.conf:

[pstn-voipms]
exten => _1NXXNXXXXXX,1,Set(CALLERID(all)=Francois Marier <5551234567>)
exten => _1NXXNXXXXXX,n,Dial(SIP/voipms/${EXTEN})
exten => _1NXXNXXXXXX,n,Hangup()
exten => _NXXNXXXXXX,1,Set(CALLERID(all)=Francois Marier <5551234567>)
exten => _NXXNXXXXXX,n,Dial(SIP/voipms/1${EXTEN})
exten => _NXXNXXXXXX,n,Hangup()
exten => _011X.,1,Set(CALLERID(all)=Francois Marier <5551234567>)
exten => _011X.,n,Authenticate(1234) ; require password for international calls
exten => _011X.,n,Dial(SIP/voipms/${EXTEN})
exten => _011X.,n,Hangup(16)

Server certificate

After setting everything up, I saw the following error in my logs:

asterisk[8691]: ERROR[8691]: tcptls.c:966 in __ssl_setup: TLS/SSL error loading cert file. <asterisk.pem>

due to the fact that I didn't set tlscertfile in /etc/asterisk/sip.conf and that it's using its default value of asterisk.pem, a non-existent file.

I initially thought that since my Asterisk server is only acting as a TLS client, and not a TLS server, there's probably no harm in not having a certificate. In practice however, my TLS connection was a little unreliable and it would regularly fail with the following TLS errors:

asterisk[534775]: ERROR[534879]: iostream.c:538 in ast_iostream_close: SSL_shutdown() failed: error:00000005:lib(0):func(0):DH lib, Underlying BIO error: Broken pipe
asterisk[534775]: ERROR[610289]: tcptls.c:553 in ast_tcptls_client_start: Unable to connect SIP socket to w.x.y.z:5061: Connection refused
asterisk[534775]: ERROR[610289]: tcptls.c:553 in ast_tcptls_client_start: Unable to connect SIP socket to w.x.y.z:5061: Connection reset by peer

I therefore decided to setup a Let's Encrypt certificate in Asterisk to eliminate the original error.

Firewall

This originally appeared not to be necessary, but I found that I ran into a number of intermittent connection errors such as:

asterisk[1280841]: ERROR[1537920]: tcptls.c:553 in ast_tcptls_client_start: Unable to connect SIP socket to w.x.y.z:5061: Connection reset by peer

and so I put the official firewall recommendations in /etc/network/iptables.up.rules:

# SIP and RTP on TCP/UDP (servername.voip.ms)
-A INPUT -s w.x.y.z/32 -p tcp --dport 5061 -j ACCEPT
-A INPUT -s w.x.y.z/32 -p udp --sport 5004:5005 --dport 10001:20000 -j ACCEPT

where w.x.y.z is the IP address of servername.voip.ms as returned by dig +short servername.voip.ms.

Installing Debian buster on a GnuBee PC 2

Here is how I installed Debian 10 / buster on my GnuBee Personal Cloud 2, a free hardware device designed as a network file server / NAS.

Flashing the LibreCMC firmware with Debian support

Before we can install Debian, we need a firmware that includes all of the necessary tools.

On another machine, do the following:

  1. Download the latest librecmc-ramips-mt7621-gb-pc1-squashfs-sysupgrade_*.bin.
  2. Mount a vfat-formatted USB stick.
  3. Copy the file onto it and rename it to gnubee.bin.
  4. Unmount the USB stick

Then plug a network cable between your laptop and the black network port and plug the USB stick into the GnuBee before rebooting the GnuBee via ssh:

ssh 192.68.10.1
reboot

If you have a USB serial cable, you can use it to monitor the flashing process:

screen /dev/ttyUSB0 57600

otherwise keep an eye on the LEDs and wait until they are fully done flashing. When you want to exit screen, use Ctrl-a then k.

Getting ssh access to LibreCMC

Once the firmware has been updated, turn off the GnuBee manually using the power switch and turn it back on.

Now enable SSH access via the built-in LibreCMC firmware:

  1. Plug a network cable between your laptop and the black network port.
  2. Open web-based admin panel at http://192.168.10.1.
  3. Go to System | Administration.
  4. Set a root password.
  5. Disable ssh password auth and root password logins.
  6. Paste in your RSA ssh public key.
  7. Click Save & Apply.
  8. Go to Network | Firewall.
  9. Select "accept" for WAN Input.
  10. Click Save & Apply.

Finaly, go to Network | Interfaces and note the ipv4 address of the WAN port since that will be needed in the next step.

Installing Debian

The first step is to install Debian jessie on the GnuBee.

Connect the blue network port into your router/switch and ssh into the GnuBee using the IP address you noted earlier:

ssh root@192.168.1.xxx

and the root password you set in the previous section.

Then use fdisk /dev/sda to create the following partition layout on the first drive:

Device       Start       End   Sectors   Size Type
/dev/sda1     2048   8390655   8388608     4G Linux swap
/dev/sda2  8390656 234441614 226050959 107.8G Linux filesystem

Note that I used an 120GB solid-state drive as the system drive in order to minimize noise levels.

Then format the swap partition:

mkswap /dev/sda1

and download the latest version of the jessie installer:

wget --no-check-certificate https://raw.githubusercontent.com/gnubee-git/GnuBee_Docs/master/GB-PCx/scripts/jessie_3.10.14/debian-jessie-install

(Yes, the --no-check-certificate is really unfortunate. Please leave a comment if you find a way to work around it.)

The stock installer fails to bring up the correct networking configuration on my network and so I have modified the install script by changing the eth0.1 blurb to:

auto eth0.1
iface eth0.1 inet static
    address 192.168.10.1
    netmask 255.255.255.0

Then you should be able to run the installer succesfully:

sh ./debian-jessie-install

and reboot:

reboot

Restore ssh access in Debian jessie

Once the GnuBee has finished booting, login using the serial console:

  • username: root
  • password: GnuBee

and change the root password using passwd.

Look for the IPv4 address of eth0.2 in the output of the ip addr command and then ssh into the GnuBee from your desktop computer:

ssh root@192.168.1.xxx  # type password set above
mkdir .ssh
vim .ssh/authorized_keys  # paste your ed25519 ssh pubkey

Finish the jessie installation

With this in place, you should be able to ssh into the GnuBee using your public key:

ssh root@192.168.1.172

and then finish the jessie installation:

wget --no-check-certificate https://raw.githubusercontent.com/gnubee-git/gnubee-git.github.io/master/debian/debian-modules-install
bash ./debian-modules-install
reboot

After rebooting, I made a few tweaks to make the system more pleasant to use:

update-alternatives --config editor  # choose vim.basic
dpkg-reconfigure locales  # enable the locale that your desktop is using

Upgrade to stretch and then buster

To upgrade to stretch, put this in /etc/apt/sources.list:

deb http://httpredir.debian.org/debian stretch main
deb http://httpredir.debian.org/debian stretch-updates main
deb http://security.debian.org/ stretch/updates main

Then upgrade the packages:

apt update
apt full-upgrade
apt autoremove
reboot

To upgrade to buster, put this in /etc/apt/sources.list:

deb http://httpredir.debian.org/debian buster main
deb http://httpredir.debian.org/debian buster-updates main
deb http://security.debian.org/debian-security buster/updates main

and upgrade the packages:

apt update
apt full-upgrade
apt autoremove
reboot

At this point, my GnuBee is running the latest version of Debian stable, however there are two remaining issues to fix:

  1. openssh-server doesn't work and I am forced to access the GnuBee via the serial interface.

  2. The firmware is running an outdated version of the Linux kernel.

Both of these issues can be resolved by upgrading the firmware to a recent version of Linux.

Upgrading the firmware

In order to move to the firmware that Neil Brown has been working on for a while, I prepared a USB stick:

$ sudo fdisk -l /dev/sdc
Disk /dev/sdc: 3.77 GiB, 4027580416 bytes, 7866368 sectors
Disk model: USB Disk
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x6cb65e6c

Device     Boot Start     End Sectors  Size Id Type
/dev/sdc1        2048 7866367 7864320  3.8G  c W95 FAT32 (LBA)

$ sudo mkfs.vfat /dev/sdc1

using a dos partition table and a W95 FAT32 (LBA) partition.

Then I grabbed the latest gnubee-*-gbpc2.bin file from https://neil.brown.name/gnubee/ and copied it onto the USB stick with the appropriate name:

cp gnubee-5.4.14-gbpc2.bin /media/usbdisk/GNUBEE.BIN

I plugged the stick into the GnuBee and rebooted it to upgrade the firmware, watching the process using the serial console.

Once booted, I had to delete /etc/udev/rules.d/70-persistent-net.rules in order to fix a long timeout while bringing up the network interfaces during boot.

If you want to see the boot messages to ensure there are no errors, run journalctl -b.

Finally, I cleaned up a deprecated and no-longer-needed package:

apt purge ntpdate

and removed its invocation from /etc/rc.local and /etc/cron.d/ntp.

Fixing the serial console

The serial console, automatically started by systemd, seems to get corrupted every now and then. If you see garbled output (i.e. binary characters instead of text), then you are running into this problem.

The fix, suggested by Jernej Jakob, is to override the default systemd unit file by creating a /etc/systemd/system/serial-getty@ttyS0.service.d/override.conf with the following contents:

[Service]
ExecStart=
ExecStart=-/sbin/agetty -o '-p -- \\u' 57600 %I $TERM

Fixing the hardware clock between restarts

When the GnuBee boots, you may have noticed that the clock is wrong until systemd-timesyncd updates the time using NTP. This leads to messages like these:

Aug 23 02:46:15 hostname systemd-fsck[839]: GNUBEE-ROOT: Superblock last mount time is in the future.
Aug 23 02:46:15 hostname systemd-fsck[839]: #011(by less than a day, probably due to the hardware clock being incorrectly set)
...
Aug 23 02:46:41 hostname systemd[1]: systemd-fsckd.service: Succeeded.
Aug 23 13:04:30 hostname systemd-timesyncd[1309]: Synchronized to time server for the first time 162.159.200.1:123 (time.cloudflare.com).

and unnecessary executions of fsck.

Often these hardware issues are due to a lack of a battery to keep the clock alive while the unit is powered down. In order to work around this, I installed the fake-hwclock package and then edited the /lib/systemd/system/fake-hwclock.service file to change the following line from:

Before=sysinit.target

to:

Before=sysinit.target systemd-fsck-root.service

so that the clock is restored before the filesystem check.

I also added the following to /etc/.gitignore to make etckeeper happy:

/fake-hwclock.data

since fake-hwclock unfortunately keeps its data file in /etc/.

Fixing network drops

I regularly see the GnuBee drop its network connections and nothing short of a reboot will fix it in my experience. Not sure whether that's a driver/kernel problem, but I decided to try to prevent it by scheduling a daily reboot in /etc/cron.d/reboot-daily:

30 18 * * * root    /bin/grep --quiet finish= /proc/mdstat || /bin/systemctl reboot

The extra check is there to ensure that the machine doesn't reboot if it's busy re-synchronizing the RAID array.