In a proof-of-concept, ayonik experts have connected the open source PBX Asterisk to Microsoft Teams via Direct Routing. Inbound- and Outbound calls from and to the PSDN can be routed this way. Here we explain, how it works - and why you shouldn't use this in a productive environment.
Update 30.04.2021: The PowerShell module "SkypeOnlineConnector" is not supported anymore. Blog post updated to reflect new "MicrosoftTeams" module.
REMARK: All shown configurations are just a proof of concept, which we publish for testing and education purposes. The solutions is NOT certified by Microsoft and does NOT consider all necessary security measures. This is NOT planned to be used in a productive environment. Normally for example an SBC will be used between the PBX and MS Teams, which isn't here. Ignoring this warning might lead to hakers intrude your sytem and start expensive calls on your bill! All information below is provided on an as-is basis without any warranty. ayonik is not liable for any damages, that will arise by using this information.
All given information below is as of November, 2019.
Short conclusion:
- We got Asterisk to work with Microsoft Direct Routing, BUT
- Asterisk doesn't work with Microsoft Teams without a (small but dirty) code change. Reason is, that Asterisk/pjsip resloves FQDN's in the SIP Header to IP's by default, which cannot be changed by configuration. But Microsoft Teams needs the FQDN.
- Microsoft does not list Asterisk as a supported PBX. So, even when it works, it's dangerous. Microsoft or Asterisk/pjsip might introduce changes, which can stop this solution from working
- So this solution should not be used in a productive environment, where one relies on the functionality.
If you have any suggestions or questions about this article, just contact us on asterisk (at) ayonik.de.
Prerequisites
- You should be familiar with Asterisk installation and configuration with pjsip, as well as with Office 365 configuration
- Installed Asterisk System
- One of the following licences:
- Office 365 Enterprise E3 (including SfB Plan2, Exchange Plan2, and Teams) + Phone System
- Office 365 Enterprise E5 (including SfB Plan2, Exchange Plan2, Teams, and Phone System)
- Remark: A calling plan is not needed, as we route outgoing Teams calls via our Asterisk PBX.
- The PowerShell-Extension "SkypeOnlineConnector" (download here). This in turn needs PowerShell 5.0, plus a special version of Visual C++ 2017 Runtime.
- The PowerShell-Extension "MicrosoftTeams" (installation instructions here). This in turn needs PowerShell 5.1.
- Certificate for a Domain, which points to the external IP address of Asterisk (might be a wildcard certificate)
Please consider, that- *.domain.tld only allows e.g. sipconnect.domain.tld, but not sipconnect.test.domain.tld!
- The certificate has to be issued by one of the root CA's, which are trusted by Microsoft. See here for details:
https://docs.microsoft.com/en-us/microsoftteams/direct-routing-plan#public-trusted-certificate-for-the-sb
Currently Asterisk does not fulfil the requirement, that SIP options packets must contain the FQDN of your Asterisk machine in the "CONTACT" header. Instead of that, Asterisk resolves the FQDN to an IP address, which does not work with Microsoft Teams.
Therefore a small code change is necessary, which then has to be compiled and installed.
ATTENTION: This change hardcodes the FQDN into the CONTACT and VIA header and cannot be re-configured in any way. This is only a workaround, until Asterisk/pjsip possibly allows FQDN's in CONTACT header via configuration)
The change is to be made in the res_pjsip_nat.c (which resides in our debian test system here: /usr/src/asterisk-16.5.1/res):
In function static pj_status_t nat_on_tx_message(pjsip_tx_data *tdata)
replace this line pj_strdup2(tdata->pool, &uri->host, ast_sockaddr_stringify_host(&transport_state->external_signaling_address));
with this line pj_strdup2(tdata->pool, &uri->host, "<your asterisk FQDN>");
and this line pj_strdup2(tdata->pool, &via->sent_by.host, ast_sockaddr_stringify_host(&transport_state->external_signaling_address));
with this line pj_strdup2(tdata->pool, &via->sent_by.host, "<your asterisk FQDN>");
(This hint was found here: https://asterisk-dev.digium.narkive.com/ucZYhaLE/asterisk-16-pjsip-invite-contact-field-and-fqdn#post12)
After this change, the Asterisk system has to be compiled and installed. Information on compiling and installing Asterisk is available in various internet resources and therefore is not covered here.
Configuration of Microsoft Teams
Register your Domain in Office 365
If not done already, open your Office 365 Admin Center (https://admin.microsoft.com) and go to the domain management page. From there, add your domain and follow the instructions. Please consider, that you have to have access to your domain management tool at your registrar.
If your Asterisk is e.g. on sipconnect.domain.tld, you have to add "domain.tld" in Office 365 custom domain configuration (not the server sipconnect.domain.tld).
Next, the Asterisk SIP Trunk has to be made known to MS Teams. To do so, a Windows Power Shell has to be connected to Microsoft Teams.
Connect PowerShell to Microsoft Teams
- Execute a Windows PowerShell as Administrator
- Execute these commands:
Import-Module SkypeOnlineConnector
$userCredential = Get-Credential This command asks for Office 365 Administrator credentials. Please enter the Office 365 Administrator credentials.
$sfbSession = New-CsOnlineSession -Credential $userCredential
Import-PSSession $sfbSession
Install-Module MicrosoftTeams
Import-Module MicrosoftTeams
$credential = Get-Credential This command asks for Office 365 Administrator credentials. Please enter the Office 365 Administrator credentials.
Connect-MicrosoftTeams -Credential $credential
Remark: When you get an error message in line 3, then probably the "Windows Remoteverwaltung" service is not running. Execute "services.msc" via "Windows-R" key combination and start "Windows Remoteverwaltung" service. Alternatively, the Basic Authentication has to be switched on (set Computer\HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WinRM\Client\AllowBasic = 0x00000001 with Registry Editor). But be really sure, what you do, before doing this. - With Get-Command *onlinePSTNGateway* you should get:
CommandType Name Version Source
----------- ---- ------- ------
Function Get-CsOnlinePSTNGateway 1.0 tmp_xq5wu12r.dxr
Function New-CsOnlinePSTNGateway 1.0 tmp_xq5wu12r.dxr
Function Remove-CsOnlinePSTNGateway 1.0 tmp_xq5wu12r.dxr
Function Set-CsOnlinePSTNGateway 1.0 tmp_xq5wu12r.dxr
CommandType Name Version Source
----------- ---- ------- ------
Function Get-CsOnlinePSTNGateway 2.3.0 MicrosoftTeams
Function New-CsOnlinePSTNGateway 2.3.0 MicrosoftTeams
Function Remove-CsOnlinePSTNGateway 2.3.0 MicrosoftTeams
Function Set-CsOnlinePSTNGateway 2.3.0 MicrosoftTeams -
Make your Asterisk SIP Trunk known to Microsoft Teams
New-CsOnlinePSTNGateway -Fqdn <Your Asterisk FQDN> -SipSignallingPort <Your Aterisk Port for TLS communication> -MaxConcurrentSessions <Max Concurrent Sessions the SBC/PBX can handle> -Enabled $true
Example: New-CsOnlinePSTNGateway -Fqdn sipconnect.mydomain.com -SipSignallingPort 5061 -MaxConcurrentSessions 5 -Enabled $true
These are the connection points for Direct Routing on Microsoft side (see here: https://docs.microsoft.com/de-de/microsoftteams/direct-routing-plan#sip-signaling-fqdns)
- pstnhub.microsoft.com – Global FQDN – must be tried first. When the SBC sends a request to resolve this name, the Microsoft Azure DNS servers return an IP address pointing to the primary Azure datacenter assigned to the SBC. The assignment is based on performance metrics of the datacenters and geographical proximity to the SBC. The IP address returned corresponds to the primary FQDN.
- pstnhub.microsoft.com – Secondary FQDN – geographically maps to the second priority region.
- pstnhub.microsoft.com – Tertiary FQDN – geographically maps to the third priority regionn
These three FQDN's will resolve to these IP addresses:
- 52.114.148.0
- 52.114.132.46
- 52.114.75.24
- 52.114.76.76
- 52.114.7.24
- 52.114.14.70
So you should open your firewall between these IP addresses and your asterisk machine (port MS teams source port 5061 and your asterisk destination port 5061 for signaling as well as 10000:20000 for voice)
Configure MS Teams incoming call routing
To configure the incoming call routing within MS Teams, you have to tell MS Teams, which user uses which PSTN number:
Set-CsUser -Identity "<Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein!>" -EnterpriseVoiceEnabled $true -HostedVoiceMail $true -OnPremLineURI tel:<your phone number in E.164 format (e.g. +491234567890)>
Replace information within brackets "<>" with your data (removing the <>).
Configure Teams outgoing call routing
This topic is a little bit complex and is explained in more detail here: https://docs.microsoft.com/en-us/microsoftteams/direct-routing-configure#configure-voice-routing
ATTENTION: This changes the call routing policy for ALL users!!!
In short:
- Create a PSTN Usage
Set-CsOnlinePstnUsage -Identity Global -Usage @{Add="All"}
(Use Get-CsOnlinePstnUsage to check) -
Create Voice Route for all Calls
New-CsOnlineVoiceRoute -Identity "All" -NumberPattern ".*" -OnlinePstnGatewayList <Your PBX FQDN> -Priority 0 -OnlinePstnUsages "All"
(Use Get-CsOnlineVoiceRoute) - Update Global Voice Routing Policy
Set-CsOnlineVoiceRoutingPolicy "Global" -OnlinePstnUsages "All"
(Use Get-CsOnlineVoiceRoutingPolicy to check) - Assign Policy to user (user, you assigned the phone number, above)
Grant-CsOnlineVoiceRoutingPolicy -Identity "<Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein!>" -PolicyName "Global”
(Use Get-CsOnlineUser -Identiy "<Diese E-Mail-Adresse ist vor Spambots geschützt! Zur Anzeige muss JavaScript eingeschaltet sein!>" to check. Search in the output for the line named "OnlineVoiceRoutingPolicy")
Setup Asterisk configuration
Add these sections to your /etc/asterisk/pjsip.conf
A transport, which uses TLS, because MS Teams Direct Routing uses TLS:
protocol=tls
bind=0.0.0.0:5061
cert_file=/etc/asterisk/ssl/<your certificate file for the Asterisk FQDN>
priv_key_file=/etc/asterisk/ssl/<your key file for the Asterisk FQDN>
cipher=ECDHE-ECDSA-AES256-SHA,ECDHE-RSA-AES256-SHA,DHE-RSA-AES256-SHA,ECDHE-ECDSA-AES128-SHA,ECDHE-RSA-AES128-SHA,DHE-RSA-AES128-SHA,AES256-SHA,AES128-SHA
method=tlsv1
local_net=<your local net, e.g. 10.0.0.0/8>
external_media_address=<External IP Address of your Asterisk>
external_signaling_address=<External IP Address of your Asterisk>
IMPORTANT: Create the /etc/asterisk/ssl directrory and change user and group to the user and group, the asterisk service runs under (e.g. asterisk:asterisk). Access rights: drwx------. Copy the certificate and key files for the Asterisk FQDN to that directory. The files have to have the same user and group as the directory and these access rights: -rw-r--r--
An endpoint to route calls from the PSTN to MS Teams to:
type=endpoint
transport=transport-tls
disallow=all
allow=ulaw,alaw,gsm
aors = aor_msteams_trunk_out
media_encryption=sdes
from_domain=<Your PBX FQDN>
type = aor
qualify_frequency=60
contact = sip:sip.pstnhub.microsoft.com
An endpoint to catch calls to the PSTN, which have been originated in Microsoft Teams:
transport=transport-tls
context = msteams_in
disallow = all
allow = ulaw, alaw, gsm
media_encryption=sdes
type=identify
endpoint=msteams_trunk_in
match=sip-all.pstnhub.microsoft.com
(The "sip-all.pstnhub.microsoft.com' resolves to all IP addresses Microsoft uses for Direct Routing.)
In the dialplan /etc/asterisk/extensions.conf
Add a section to handle calls from Microsoft Teams to PSTN (here a sample with sipgate as destination endpoint):
[msteams_in]
; to react on incoming SIP Options Packates with an "200 OK"
exten => msteams_trunk_out,1,HangUp()
exten => _[+X].,1,Noop(Processing outgoing call from MS Teams to PSTN)
; Following line is optional
same => n,Set(CALLERID(num)=<your sender phone number in E.164 format (e.g. +4912345678)>)
same => n,Dial(PJSIP/${EXTEN}@sipgate)
same => n,HangUp()
Here a sample to handle calls from sipgate (basic) to a MS Teams user:
; Handling sipgate.de related stuff
exten => <your sipgate user id>,1,NoOp(Call via sipgate routing to s@incoming now)
; try to extract phone number from sip header P-Asserted-Identity
same => n,Set(FON=$["${PJSIP_HEADER(read,P-Asserted-Identity)}" : "<sip:([0-9]+)"])
same => n,Set(CALLERID(dnid)=$[ "${PJSIP_HEADER(read,TO)}" : "<sip:([0-9e]+)" ])
same => n,Set(CALLERID(dnid)=+$[ "${CALLERID(dnid)}" : "00([0-9]+)" ])
same => n,GotoIf($["${CALLERID(dnid)}" = "<MSN1 in E.164 format (e.g. +4912345678)>"]?incoming-1,s,1)
exten => s,1,NoOp(Route to <+4912345678> on MS Teams Trunk)
same => n,Dial(PJSIP/${CALLERID(dnid)}@msteams_trunk) Alternatively dial some other number, which is configured in MS Teams
exten => s,1,NoOp(Route to <+4912345679> on MS Teams Trunk)
same => n,Dial(PJSIP/${CALLERID(dnid)}@msteams_trunk) Alternatively dial some other number, which is configured in MS Teams
same => n,Hangup()
The configuration might differ in details, depending on your environment, PSTN provider, etc..
If things do not work, it's a good idea to debug SIP packets within Asterisk:
Start asterisk console
asterisk -rvvvv
Switch on logging:
pjsip set logger on
core set debug 4
Hope, this path helps you get through your MS Teams Direct Routing PBX connection.