Support for TLS Proxy

I am experimenting to see if it is possible to offload TLS termination in front of Asterisk TCP SIP.

I currently have HAProxy running on 192.168.3.29:5061, terminating TLS (in TCP mode) and forwarding requests to my Asterisk server listening for TCP at 192.168.3.23:5060.

Caller1 can start calls as expected, but things start to fall apart when Asterisk send an INVITE to Caller2 through HAProxy. Asterisk sets the Contact as sip:asterisk@WAN:5061;transport=TCP.

Linphone receives the INVITE over TLS, but the URI is ‘sip’ instead of ‘sips’ and transport=TCP.

2025-04-21 19:20:50:662 [org.linphone/belle-sip] MESSAGE channel [0xb4000072d65a6d20]: received [1079] new bytes from [TLS://jruehlig.com:5061]:
INVITE sip:joshua@192.168.3.29:61553;transport=TCP SIP/2.0
Via: SIP/2.0/TCP 73.192.144.236:5061;rport;branch=z9hG4bKPj6839fb72-1f20-11f0-8fbe-0cc47a418007;alias
From: "Theodore Ruehlig" <sip:1003@192.168.3.23>;tag=6836b420-1f20-11f0-8fbe-0cc47a418007
To: <sip:joshua@192.168.3.29>
Contact: <sip:asterisk@73.192.144.236:5061;transport=TCP>
Call-ID: 6836b454-1f20-11f0-8fbe-0cc47a418007
CSeq: 30007 INVITE
Allow: OPTIONS, REGISTER, SUBSCRIBE, NOTIFY, PUBLISH, INVITE, ACK, BYE, CANCEL, UPDATE, PRACK, MESSAGE, INFO, REFER
Supported: 100rel, timer, replaces, norefersub, histinfo
Session-Expires: 1800
Min-SE: 90
Max-Forwards: 70
User-Agent: Asterisk PBX 22.3.0
Content-Type: application/sdp
Content-Length:   351

v=0
o=- 1101726724 1101726724 IN IP4 73.192.144.236
s=Asterisk
c=IN IP4 73.192.144.236
t=0 0
m=audio 10272 RTP/SAVP 0 8 107
a=crypto:1 AES_CM_128_HMAC_SHA1_80 inline:4gVxDgz1YFBCJAY1we5rcG/pwpuZV5U4zpM5rKmY
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:107 opus/48000/2
a=fmtp:107 useinbandfec=1
a=ptime:20
a=maxptime:60
a=sendrecv

Linphone responds over TCP which will not work.

2025-04-21 19:20:50:808 [org.linphone/belle-sip] MESSAGE channel [0xb4000072c7b76e00]: message sent to [TCP://2607:7700:0:13:0:2:49c0:90ec:5061], size: [369] bytes
SIP/2.0 100 Trying
Via: SIP/2.0/TCP 73.192.144.236:5061;received=2607:7700:0:13:0:2:49c0:90ec;rport;branch=z9hG4bKPj6839fb72-1f20-11f0-8fbe-0cc47a418007;alias
From: "Theodore Ruehlig" <sip:1003@192.168.3.23>;tag=6836b420-1f20-11f0-8fbe-0cc47a418007
To: sip:joshua@192.168.3.29
Call-ID: 6836b454-1f20-11f0-8fbe-0cc47a418007
CSeq: 30007 INVITE
Content-Length: 0

Is it possible to rewrite the contact that Asterisk sets? Here is relevant parts of pjsip.conf

[transport-tcp]
        type = transport
        protocol = tcp
        bind = 192.168.3.23
        local_net = 192.168.3.23/32
        external_media_address = jruehlig.com
        external_signaling_address = jruehlig.com
        external_signaling_port = 5061
        ;symmetric_transport = yes

[endpoint-basic](!)
        type = endpoint
        transport = transport-tcp
        ;force_rport = no
        rtp_symmetric = yes
        rewrite_contact = yes
        direct_media = no
        ;ice_support = yes
        ;media_address = jruehlig.com
        ;bind_rtp_to_media_address = yes

Attached are various logs I took to figure out what was happening.

The TCP2TCP log call works as expected. This scenario bypasses HAProxy, and proves I can get a call working with my Asterisk server behind NAT.
linphone.txt (319.9 KB)
rewrite_symmetric_TLS2TLS.txt (44.8 KB)
symmetric_TCP2TCP.txt (271.2 KB)

I have tried setting an outbound_proxy in Linphone but that doesn’t seem to be used when receiving a call.

What is interesting is when Asterisk receives the initial INVITE from Caller1, it actually correctly responds over the existing connection. But Linphone does not handle things the same. It instead tries to use the Contact URI provided in the INVITE from Asterisk. Some solutions I am currently exploring…

  • Manually Editing the Contact: transport= parameter that Asterisk sends, using set_var
  • Setting a NAPTR/SRV DNS Record

Thank you for any help!

No, it’s not possible. You’re trying to use a non-SIP aware proxy (HAProxy). You can’t offload the TLS SIP side to Asterisk when it’s not doing TLS.

Do you mind clarifying what you mean in your last sentence?
In my testing, HAProxy is already offloading the SIP messages, and Asterisk is processing them. Messages between Caller1 and Asterisk are flowing both directions through the TLS Proxy as expected. See ‘TLS2TLS’ log.

The problem arises because Asterisk is not aware of the TLS proxy, it is just talking TCP. So the Contact information passed to Caller2 will not work.

I may try hacking Asterisk source code to hardcode transport=TLS, just to see if I can get this working as a test.

Correct. SIP embeds routing information and other things within the SIP messages. This is part of why a SIP proxy exists, so it can be SIP aware and change the signalling accordingly to a proxy model.

Sticking HAProxy in front gives you TLS on the wire, but not TLS in the signalling itself. There is no capability in Asterisk to make it think it is doing TLS when it isn’t.

Ok, so I think you’re saying it is not possible given the current options/setup. But doesn’t seem like the protocol itself makes a transparent TLS proxy impossible.

We already have options to set external signaling address & port. This would just be another option to set external signaling protocol. I’ll experiment a bit with the source code, and see if hardcoding the transport parameter makes 2 way communication with Caller2 possible.

Possibly, though I don’t think I’ve ever heard of anyone doing it.

I searched around and found 1 or 2 people asking if it was possible on mailing lists years ago using another transparent TLS proxy (I think stunnel). But those inquiries looked like they went nowhere.

I also saw mention of using HAProxy in this post.

But I never reached out to see if he has HAProxy in front of SIP or only HTTP. I’ll register on that forum and confirm if he is using HAProxy > SIP, and if there is any magic setting to get the Contact Header correct.

Have you tried to put an actual SIP proxy in front of Asterisk? Something like dsiprouter? Have it terminate TLS and forward regular TCP/UDP to Asterisk?

I haven’t but that was another option I was considering. Ultimately I want TLS termination at my OPNsense router, where my TLS certs are managed.

I prefer the HAProxy route, because I am already running it to terminate TLS for HTTP. I would just have it listen on another port 5061, and forward in transparent (TCP) mode.

Adding cross-link to related further discussion on the FreePBX forums:

Where is ;transport=TCP being set when I have rewrite_contact=yes?

I tried patching res/res_pjsip.c and changing the four instances of …

  (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? ";transport=" : "",
- (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? pjsip_transport_get_type_name(type) : "");
+ (type != PJSIP_TRANSPORT_UDP && type != PJSIP_TRANSPORT_UDP6) ? "TLS" : "");

But I still get the following contact from pjsip show contacts

Contact:  <Aor/ContactUri..............................> <Hash....> <Status> <RTT(ms)..>
==========================================================================================

  Contact:  USER/sip:USER@192.168.3.29:38684;transport=TCP   02cef2cef5 NonQual         nan

Thanks!

Entirely possible it’s from the PJSIP library itself.

Ok, so possibly somewhere in res/res_pjsip?

I tried searching the Asterisk code on github for transport= and it seemed like res/res_pjsip.c was the spot. Guess I will need to keep digging. When I have rewrite_contact=no, the transport parameter is not overwritten, so I guess I could use that as a clue.

Then it’s probably res_pjsip_nat which handles that option.

You should also be aware that the “transport” parameter in a URI is used by the underlying PJSIP library during transport selection. So if it doesn’t rewrite it to TCP and stays as TLS for example then when PJSIP tries to send an outgoing request it may fail.

Ok thanks, I’ll check that file.

Yeah, I figured I might break stuff in certain cases. Just testing if I can get my scenario (transparent TLS proxy) working.

Communication still seems to partially work if I have have rewrite_contact=no, where transport=tls sent by Linphone is not overwritten. At least Asterisk is responding to Linphone’s INVITE. I think because Asterisk is choosing to use the already initiated TCP connection (which isn’t actually TLS).

So I did some hacking away on res/res_pjsip_nat.c, and was able to get Asterisk to rewrite the contact URI back to transport=tls before sending an INVITE to the receiving Linphone client (leg2). But, Linphone still chose to try to connect with TCP!

2025-04-27 00:04:40:997 [AppRun.wrapped/belle-sip] MESSAGE channel [0x560d2964e200]: received [1071] new bytes from [TLS://jruehlig.com:5061]:
INVITE sip:ada@192.168.3.29:58051;transport=TCP SIP/2.0
Via: SIP/2.0/TCP 73.192.144.236:5061;rport;branch=z9hG4bKPje3b963c8-2335-11f0-8fbe-0cc47a418007;alias
From: "Joshua Ruehlig" <sip:1001@192.168.3.23>;tag=e3b65b83-2335-11f0-8fbe-0cc47a418007
To: <sip:ada@192.168.3.29>
Contact: <sip:asterisk@73.192.144.236:5061;transport=tls>

...

2025-04-27 00:04:41:215 [AppRun.wrapped/belle-sip] MESSAGE channel [0x560d285bc160]: message sent to [TCP://73.192.144.236:5061], size: [326] bytes
SIP/2.0 100 Trying
Via: SIP/2.0/TCP 73.192.144.236:5061;rport;branch=z9hG4bKPje3b963c8-2335-11f0-8fbe-0cc47a418007;alias

So for Linphone I am suspecting it is reading the transport type in the Via Header from Asterisk. Do you have any clues where this is set?
I see places where the rport is being set/overwritten via->sent_by.port, but can’t figure out where the transport type is being set? Thanks!

I’ve avoided this one because I have difficulty getting round the details of the configuration, especially as it is either product dependent, or over specifies the products used.

However it seem to me that you have a proxied configuration with one side of the proxy TCP and the other TLS. In that case, it would be a protocol violation for any SIPS URI to be forwarded to the TCP side, as SIPS requires all hops to be TLS.

Also, if the hop to Asterisk is TCP, the only possible protocol in the Via header is TCP.

I’m beginning to suspect that your real problem is that the proxy is failing to add its own Via headers, and a Record-Route header, and this needs fixing in the proxy, not in Asterisk.

You’re correct. The proxy (HAProxy) is not adding any SIP related headers because it is a transparent TLS proxy. It doesn’t understand SIP, only TCP/TLS. I’m sure whatever I am doing is in violation of SIP standards, but at this point I only have 2 goals with my Asterisk server…

  • Calling between my video doorbell and me/my wife’s phones. This is mostly working, it video calls us, but I just can’t video call it.
  • Video calling between me/my wife’s phones and my kids tablets. We use Google Meet for this, but I want to drop the reliance on Google.

The user I referenced on the FreePBX forum has this working but is using different SIP clients on the TLS side. Currently this is my understanding of why my setup is not working with a transparent TLS proxy.

  • When Caller1 INVITEs Asterisk server with Via + Contact = TLS, communication is turned into TCP by HAProxy. Asterisk actually knows to keep replying in TCP an ignores Via+Contact.

  • When Asterisk INVITEs Caller2 I need to have rewrite_contact=yes otherwise Caller2 contact is stored with protocol=tls. Asterisk depends on on ContactURI for outbound call to find an available transport (unlike leg1 where call was initiated by Caller2).

  • When Linphone client (Caller2) receives INVITE it sees the Via header says TCP and replies using TCP even though the INVITE was over TLS.

So a transparent TLS proxy could be possible between Linphone and Asterisk Server, but at the moment what they rely on to select transport method is all different and causing hickups. I think ideally this is handled on the Linphone side, it should respond (like Asterisk does in leg1) using the same method it received and ignore the Via header. But I’m more comfortable hacking away and recompiling Asterisk then an Android app, lol.

Is there a way to read Registered header easily from the command line? I have been using pjsip show aor USER but I don’t see parameters I expect like x-ast-txp or anything from rewrite_contact.

Thanks!