Well, part of. Another important part is this one.
But I think I should tell something about the basic theory behind to get a better understanding of how things are working together.
What’s the goal?
Achieving as much security and availability by design. This is basically done by “divide et impera”. Therefore, lets begin to divide … .
What should be divided?
There are two “objects” to handle: your phones (FreePBX calls them “Extensions”) calling to each other and on the other side the way to the world, provided by your SIP provider (FreePBX calls it “Trunk”).
What’s the technical difference between a Trunk and an Extension? An Extension (= phone) has to register to FreePBX (-> meaning: Asterisk acts as server) - whereas a Trunk registers itself to the SIP provider (meaning: Asterisk acts as client).
From a network perspective, this means:
Extension: The Phone starts the connection to Asterisk - Asterisk has to provide a listener to allow the opening of new incoming IP connections.
Trunk: Asterisk itself starts a connection as client to the SIP-Provider (no need for a listener at all nowadays by using flows).
What exactly is responsible for the network connectivity?
It’s the transport, which is primarily a function of pjsip, which is used by Asterisk.
The question now is: how to separate Extensions and Trunks?
The answer: Provide different transports to Extensions and Trunks (and even different transports for different trunks). This provides the ability to easily use different IPs or even Networks or protocols for each “object”.
Why is it useful to provide different transports: because it’s possible to kind of “restart” a transport individually without influencing other connections.
Ok - coming back to your problem here:
You should define an own transport for your trunk in pjsip.transports_custom.conf. E.g. (here: tls):
bind=192.168.13.27:0 # That's the IP of your interface you're using for NAT - the transport for your extensions should use another IP.
external_media_address=[your FQDN to external IP]
external_signaling_address=[your FQDN to external IP]
nobind=1 # This can be used only, if you apply and correctly rebuild Asterisk / pjsip with the nobind patch mentioned [here](https://www.ip-phone-forum.de/threads/asterisk-pjsip-und-nat-nicht-so-einfach-wie-man-glaubt.310575). It greatly improves security, because there isn't any way any more to achieve your server from outside - no need for fail2ban.
Next step is to bind the trunk configuration to the desired transport. This must be done manually, too in
If you got this working (i.e.: your trunk registers successfully to your SIP provider) and you’re able to place calls and receive calls (and you verified in the SIP trace, that all values in the SIP headers are correct), you are at the point to start the “reconnect” tests, after your external IP has changed.
Your first job at this moment is: detect the change (there are several ways to detect it - depending on your local conditions)! If you detected the change, take care that the “your FQDN to external IP” already contains the new external IP-address.
Next: refresh the external IP-address to use by asterisk by refreshing the DNS-manager:
/usr/sbin/asterisk -x "dnsmgr refresh"
Now, Asterisk knows the new external IP.
Next and last step is to “force” a restart of the transport used by this trunk definition (I’m waiting 1 second after the dns manager restart):
/usr/sbin/asterisk -x "pjsip send register *all"
What’s happening now?
Asterisk uses the existing connection (remember the “flow”) to register to the trunk. This must fail, because this connection doesn’t exist any more (remember: you’re external IP changed).
Therefore, Asterisk times out, pjsip closes this connection and restarts the TCP / TLS connection from scratch by sending SYN. Voila - you’re done.
To speed up the timeout and the restart of the trunk transport, apply this configuration to pjsip.registration.conf (this can be done with the normal GUI interface of FreePBX in the trunk definition - “general retry interval”):
retry_interval=10 ; (in type=registration) - or shorter
How to debug it?
You can see it in the REGISTER package: There is a new port and the new external IP address used in the Via header (compared to before). If you enabled pjsip logging, you will find entries like these (here for Deutsche Telekom AllIP):
[2021-10-10 19:16:09] DEBUG pjproject: tlsc0x7f88a431df38 TLS client transport created
[2021-10-10 19:16:09] DEBUG pjproject: tlsc0x7f88a431df38 TLS transport 192.168.13.27:51281 is connecting to tel.t-online.de:5061...
[2021-10-10 19:16:09] DEBUG pjproject: tlsc0x7f88a431df38 TLS transport 192.168.13.27:51281 is connected to tel.t-online.de:5061
And you will see entries like these, which close existing connections:
[2021-10-10 19:26:48] DEBUG pjproject: tlsc0x7f88a4309398 TLS connection closed
[2021-10-10 19:26:48] DEBUG pjproject: sip_transport.c Transport tlsc0x7f88a4309398 shutting down, force=0
[2021-10-10 19:26:48] DEBUG pjproject: tlsc0x7f88a4309398 TLS transport destroyed with reason 470006: EVP lib
How to activate pjsip logging:
Add to logger_logfiles_custom.conf:
pjsip.log => debug
This creates a pjsip log file pjsip.log in /var/log/asterisk
Why is it necessary to do all those things described here? Because dynamic IPs are mostly not supported by Asterisk at all and pjsip doesn’t fully implement RFC 5626. As an example: pjsip implemented a TLS or TCP ping (to keep open NAT gateways) - but the (missing) answer isn’t evaluated. If they would do this, it wouldn’t be necessary at all to do many of the steps mentioned here … .
The described way shows how to securely set up a trunk connection and how to handle a change of the external IP address with NAT evolved without restarting anything and without breaking internal calls by separating Extensions and Trunks by using different transports and IPs / networks.
I hope, I didn’t forget some things I additionally did to get it working - Hope it helps.