I'm attaching my config (changed the substitutions to fake ones).

On 7/11/2025 10:23 PM, Jeremy Weibert via sr-users wrote:
> I'm new to Kamailio, and looking for some guidance.
>
> I have a very large global PBX network (NEC). I have mobile users, that for 
> many years we have had connecting through VPN. This has worked fine for us 
> until recently, various circumstances have made this unreliable. In searching 
> for a better way to handle this, I came across Kamailio. I want the mobile 
> users using TLS/SRTP, the NEC has various limitations on that, but more 
> importantly, I'm not putting my PBXes on the internet. I've been at this for 
> a few days now, learning and trying to see how I can do this with Kamailio, 
> when I eventually came across this: 
> https://www.fredposner.com/2309/kamailio-simple-tls-gateway/#:~:text=One%20feature%20that%20truly%20shines,aka%20to%20the%20PBX.
>
> Seems this is exactly what I need. The only difference really is that I have 
> an additional firewall between the internet and the Kamailio server, with the 
> external IP being NATed.
>
> In the famous last words of all users "it didn't work". Specifically, 
> registrations seem to work just fine (the NEC acts as the registrar). 
> However, when making a call:
>
> - The original INVITE from the mobile user (port 5061, TLS) reaches Kamailio 
> and is then proxied via UDP to the PBX.
> - The PBX behaves appears to behave as a B2BUA, creating a completely new 
> INVITE with a different Call-ID and sending it back to Kamailio.
> - Kamailio, seeing this as a completely new INVITE coming in on UDP (not 
> TLS), rejects it (403 Accepting TLS Only).
> - Likewise, if a call originates from the PBX side, same thing.
>
> I figure, if the INVITE is coming from the PBX, I can just skip the TLS 
> check, as it's coming from a trusted IP. However, the INVITE looks like:
> INFO: {1 udp 1 INVITE 7776d675@PBXIP} <script>: {R-MAIN] Incoming request 
> from PRIVATEIP using udp
>
> In other words, the INVITE is showing as coming from Kamailio.
>
> I can't use the alias tag that Kamailio sets initially as the PBX strips it 
> with the new INVITE. I figure to just explicitly send it to registered client 
> over TLS by just rewriting the destination URI, and then it goes into an 
> infinite loop.
>
> I got a bit lost at this point and if someone could help point me in the 
> right direction, I would be eternally grateful.
> __________________________________________________________
> Kamailio - Users Mailing List - Non Commercial Discussions -- 
> sr-users@lists.kamailio.org
> To unsubscribe send an email to sr-users-le...@lists.kamailio.org
> Important: keep the mailing list in the recipients, do not reply only to the 
> sender!
#!KAMAILIO
#
# Kamailio (OpenSER) SIP Server v5.3
# Kamailio as SIP Edge Router
# April 2020

# Need help?

# * Free/Official: 
#       -       Documentation: https://www.kamailio.org/w/documentation/
#       -       Wiki: https://www.kamailio.org/wiki/
#       -       Modules: https://www.kamailio.org/docs/modules/stable/
#       -       IRC: irc.freenode.net #kamailio
#       -       Mailing Lists: https://www.kamailio.org/w/mailing-lists/

# * Commercial:
#       -       LOD: https://lod.com f...@lod.com +1 (224) 334-3733
#       -       Business Directory: 
https://www.kamailio.org/w/business-directory/


####### Define Substitutions #######
# replace with values specific to your needs
#!substdef "/PRIVATEIP/10.20.20.137/"
#!substdef "/PUBLICIP/12.13.14.24/"
#!substdef "/PUBDOMAIN/call.now.today/"
#!substdef "/PBXIP/10.10.10.22/"
#!substdef "/APIKEY/thisismyvettedapikey/"

####### Define Code Blocks #########
#!define WITH_DEBUG
#!define WITH_ANTIFLOOD
#!define WITH_APIBAN
#       define WITH_SIPDEBUG

####### Global Parameters #########
### LOG Levels: 3=DBG, 2=INFO, 1=NOTICE, 0=WARN, -1=ERR
#!ifdef WITH_DEBUG
debug=4
log_stderror=yes
#!else
debug=2
log_stderror=no
#!endif

memdbg=5
memlog=5
pv_buffer_size=65536
pv_cache_limit=65536
log_facility=LOG_LOCAL0
log_prefix="{$mt $proto $hdr(CSeq) $ci} "
children=4
enable_tls=yes
tls_max_connections=2048
tcp_connection_lifetime=3605
dns=no
dns_cache_init=off
dns_srv_lb=no
dns_try_naptr=no
maxbuffer=65536
max_while_loops=250
rev_dns=no
use_dns_cache=no
auto_aliases=no
mhomed=0
listen=udp:PRIVATEIP:5060
listen=tls:PRIVATEIP:5061 advertise PUBLICIP:5061
alias=PRIVATEIP:5060
alias=PRIVATEIP:5061
alias=PUBLICIP:5061
alias=PUBDOMAIN:5061
port=5060
#!ifdef WITH_SIPDEBUG
onsend_route_reply=yes
#!endif
server_header="Server: kamailio 5.8"

####### Modules Section ########
loadmodule "tls.so"
loadmodule "jsonrpcs.so"
loadmodule "kex.so"
loadmodule "corex.so"
loadmodule "tm.so"
loadmodule "tmx.so"
loadmodule "sl.so"
loadmodule "rr.so"
loadmodule "pv.so"
loadmodule "maxfwd.so"
loadmodule "outbound.so"
loadmodule "textops.so"
loadmodule "siputils.so"
loadmodule "xlog.so"
loadmodule "sanity.so"
loadmodule "ctl.so"
loadmodule "cfg_rpc.so"
loadmodule "rtimer.so"
loadmodule "json.so"
loadmodule "http_client.so"
loadmodule "jansson.so"
loadmodule "htable.so"
loadmodule "textopsx.so"
loadmodule "rtpengine.so"
loadmodule "nathelper.so"
loadmodule "path.so"

#!ifdef WITH_ANTIFLOOD
loadmodule "pike.so"
#!endif

#!ifdef WITH_DEBUG
loadmodule "debugger.so"
#!endif

# ----------------- setting module-specific parameters ---------------
# ----- jsonrpcs params -----
modparam("jsonrpcs", "pretty_format", 1)

# ----- ctl params -----
modparam("ctl", "binrpc", "unix:/var/run/kamailio/kamailio_ctl")
modparam("ctl", "binrpc", "tcp:localhost:2046")

# ----- tm params -----
modparam("tm", "failure_reply_mode", 3)
modparam("tm", "noisy_ctimer", 1)
modparam("tm", "cancel_b_method", 1)

# ----- rr params -----
modparam("rr", "enable_full_lr", 1)
modparam("rr", "append_fromtag", 1)
modparam("rr", "ignore_sips", 1)
modparam("rr", "enable_double_rr", 2)

# ----- path -----
modparam("path", "enable_r2", 1)

# ----- rtimer params -----
#!ifdef WITH_APIBAN
modparam("rtimer", "timer", "name=apiban;interval=300;mode=1;")
modparam("rtimer", "exec", "timer=apiban;route=APIBAN")
modparam("htable", "htable", "apiban=>size=11;")
modparam("htable", "htable", "apibanctl=>size=1;initval=0;")
#!endif

# ----- tls params -----
modparam("tls", "tls_method", "TLSv1.2+")
modparam("tls", "verify_certificate", 0)
modparam("tls", "require_certificate", 0)
modparam("tls", "private_key", "/etc/kamailio/certs/kamailio.key")
modparam("tls", "certificate", "/etc/kamailio/certs/kamailio.crt")

# ----- rtpproxy params -----
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
modparam("rtpengine", "rtpengine_disable_tout", 15)
modparam("rtpengine", "rtpengine_tout_ms", 2000)
modparam("rtpengine", "rtpengine_retr", 2)

#!ifdef WITH_ANTIFLOOD
# ----- pike params -----
modparam("pike", "sampling_time_unit", 2)
modparam("pike", "reqs_density_per_unit", 16)
modparam("pike", "remove_latency", 4)

# ----- htable params -----
/* ip ban htable with autoexpire after 5 minutes */
modparam("htable", "htable", "ipban=>size=8;autoexpire=14400")

modparam("htable", "htable", "a=>size=256;autoexpire=14400")
#!endif

#!ifdef WITH_DEBUG
# ----- debugger params -----
modparam("debugger", "cfgtrace", 1)
modparam("debugger", "log_level_name", "exec")
#!endif

# do not follow redirects
modparam("http_client", "httpredirect", 0)
modparam("http_client", "connection_timeout", 4)
modparam("http_client", "verify_peer", 0)
modparam("http_client", "verify_host", 0)
modparam("http_client", "keep_connections", 1)

####### Routing Logic ########
request_route {
#!ifdef WITH_SIPDEBUG
        xlog("L_INFO","[R-MAIN] SIP DEBUG: $mb\n");
#!endif

        # per request initial checks
        route(REQINIT);

        # CANCEL processing
        if (is_method("CANCEL")) {
                rtpengine_manage();
                handle_ruri_alias();

                if (!t_relay_cancel()) {
                        xlog("L_INFO", "[MAIN] No matching transaction or other 
error on CANCEL\n");
                        sl_send_reply("500", "Internal Server Error M1");
                }

                exit;
        }

        # handle retransmissions
        if (!is_method("ACK")) {
                if (t_precheck_trans()) {
                        t_check_trans();
                        exit;
                }
                t_check_trans();
        }

        # handle requests within SIP dialogs
        route(WITHINDLG);

        if ($si=="PBXIP") {
                if (!loose_route()) {
                        switch($rc) {
                        case -2:
                                sl_send_reply("403", "Forbidden");
                                exit;
                        }
                }

                remove_hf("Route");
                force_rport();
                $fs = "tls:PRIVATEIP:5061";
        } else {
                xlog("L_INFO", "[R-MAIN] Incoming request from $si using 
$proto\n");
                if ($si == "PRIVATEIP") {
                        xlog("L_INFO", "[R-MAIN] Accepting internal relayed 
INVITE from self ($si)\n");
                } else {
                        if($si == "PBXIP") {
                                xlog("L_INFO", "[R-MAIN] Accepting INVITE from 
trusted PBX ($si)\n");
                        } else {
                                if(proto != TLS) {
                                        xlog("L_INFO", "[R-MAIN] rejecting non 
tls $ru from $si\n");
                                        sl_send_reply("403", "Accepting TLS 
Only");
                                        exit;
                                } 
                        }
                }

                if (is_method("REGISTER")) {
                        remove_hf("Route");
                        add_path();
                        $du = "sip:PBXIP:5060";
                } else {
                        if ($si == "PRIVATEIP") {
                                xlog("L_INFO", "[R-MAIN] Routing internal 
INVITE explicitly to TLS client\n");
                                $du = "sip:" + $rU + 
"@PRIVATEIP:5061;transport=tls";
                                $fs = "tls:PRIVATEIP:5061";
                        } else {
                                if ($rU == $null) {
                                        sl_send_reply("484", "Address 
Incomplete");
                                        exit;
                                }

                                $ru = "sip:" + $rU + "@PBXIP:5060";
                        }
                }

                if ($(ct{tobody.user})!=$null) {
                        $var(ctuser) = $(ct{tobody.user});
                        $var(cthost) = $si;
                        $var(ctport) = $sp;
                        remove_hf("Contact");
                        insert_hf("Contact: 
<sip:$var(ctuser)@PRIVATEIP:5060;alias=$var(cthost)~$var(ctport)~3>\r\n", 
"Call-ID");
                }

                t_on_failure("MANAGE_FAILURE");
                $fs = "udp:PRIVATEIP:5060";
        }

        route(RELAY);
        exit;
}

# Wrapper for relaying requests
route[RELAY] {
        handle_ruri_alias();

        if (is_method("INVITE|BYE|SUBSCRIBE|UPDATE")) {
                if (!t_is_set("branch_route")) t_on_branch("MANAGE_BRANCH");
        }

        if (is_method("INVITE|SUBSCRIBE|UPDATE|REGISTER")) {
                if (!t_is_set("onreply_route")) t_on_reply("MANAGE_REPLY");
        }

        if (is_method("INVITE")) {
                if (!t_is_set("failure_route")) t_on_failure("MANAGE_FAILURE");
        }

        xlog("L_INFO", "[RELAY] Relaying $ru\n");
        if (!t_relay()) {
                sl_reply_error();
        }

        exit;
}

# Per SIP request initial checks
route[REQINIT] {
#!ifdef WITH_ANTIFLOOD
        # flood detection from same IP and traffic ban for a while
        # be sure you exclude checking trusted peers, such as pstn gateways
        # - local host excluded (e.g., loop to self)
        if (src_ip!=myself && src_ip!=PBXIP) {
                if ($sht(ipban=>$si)!=$null) {
                        # ip is already blocked
                        xdbg("request from blocked IP - $rm from $fu 
(IP:$si:$sp)\n");
                        exit;
                }
                if ($sht(apiban=>$si)!=$null) {
                        # ip is blocked from apiban.org
                        xdbg("request from apiban.org blocked IP - $rm from $fu 
(IP:$si:$sp)\n");
                        exit;
                }
                if (!pike_check_req()) {
                        xlog("L_ALERT","[R-REQINIT]: pike blocking $rm from $fu 
(IP:$si:$sp)\n");
                        $sht(ipban=>$si) = 1;
                        exit;
                }
        }
#!endif

        if ($ua =~ "friendly-scanner|sipcli|VaxSIPUserAgent") {
                xlog("L_INFO","[R-REQINIT]: script kiddies from IP:$si:$sp - 
dropping and blocking\n");
                route(BLOCKIP);
                # sl_send_reply("200", "OK");
                exit;
        }

        if ($au =~ "(\=)|(\-\-)|(')|(\#)|(\%27)|(\%24)" and $au != $null) {
                xlog("L_INFO","[R-REQINIT]: sql injection from IP:$si:$sp - 
dropping\n");
                route(BLOCKIP);
                exit;
        }

        if (!mf_process_maxfwd_header("10")) {
                xlog("L_INFO","[R-REQINIT]: too many hops\n");
                sl_send_reply("483","R1 Too Many Hops");
                exit;
        }

        if (is_method("OPTIONS")){
                if (uri==myself && ($rU==$null || $rU=="kamailio")) {
                        sl_send_reply("200","Keep on Keeping on");
                        exit;
                }
        }

        if (!sanity_check("17895", "7")) {
                xlog("L_INFO","[R-REQINIT]: Malformed SIP message from 
$si:$sp\n");
                exit;
        }
}

# Handle requests within SIP dialogs
route[WITHINDLG] {
        if (!has_totag()) return;

        if ($si=="PBXIP") { # && $(ru{param.value,alias})!=$null) {
                route(RTPMANAGE);
                route(RELAY);
                exit;
        }

        if (loose_route()) {
                if ( is_method("NOTIFY") ) {
                        record_route();
                }

                route(RTPMANAGE);
                route(RELAY);
                exit;
        }

        if ( is_method("ACK|BYE") ) {
                route(RTPMANAGE);
                route(RELAY);
        }

        sl_send_reply("404","Not here WD1");
        exit;
}

# RTPProxy control and signaling updates for NAT traversal
route[RTPMANAGE] {
        if (has_totag()) {
                if (is_method("INVITE|UPDATE|ACK") || is_method("BYE")) {
                        if (proto==TLS) {
                                rtpengine_manage("RTP/AVP replace-origin 
replace-session-connection SIP-source-address ICE=remove");
                        } else {
                                rtpengine_manage("RTP/SAVP replace-origin 
replace-session-connection SIP-source-address ICE=remove");
                        }
                }

                t_on_reply("MANAGE_REPLY");
        } else {
                if (is_method("INVITE")) {
                        if (has_body("application/sdp")) {
                                if (proto==TLS) {
                                        rtpengine_manage("RTP/AVP 
replace-origin replace-session-connection SIP-source-address ICE=remove");
                                } else {
                                        rtpengine_manage("RTP/SAVP 
replace-origin replace-session-connection SIP-source-address ICE=remove");
                                }
                        }
                }
        }

        return;
}

route[BLOCKIP] {
        if (src_ip!=myself) {
                xlog("L_INFO","[R-BLOCKIP:$ci]: blocking $rm from $fu 
(IP:$si:$sp)\n");
                $sht(ipban=>$si) = 1;
        }

        return;
}

# Manage outgoing branches
branch_route[MANAGE_BRANCH] {
        xdbg("new branch [$T_branch_idx] to $ru\n");
        route(RTPMANAGE);
}

# Manage incoming replies
onreply_route[MANAGE_REPLY] {
        xdbg("incoming reply\n");
        if ($si!="PBXIP") {
                if ($(ct{tobody.user})!=$null) {
                        xlog("L_INFO","[R-M-REPLY] adding contact alias\n");
                        $var(ctuser) = $(ct{tobody.user});
                        $var(cthost) = $si;
                        $var(ctport) = $sp;
                        remove_hf("Contact");
                        insert_hf("Contact: 
<sip:$var(ctuser)@PRIVATEIP:5060;alias=$var(cthost)~$var(ctport)~3>\r\n", 
"Call-ID");
                }
        }

        if (!is_method("REGISTER")) {
                if (status=~"[12][0-9][0-9]") {
                        route(RTPMANAGE);
                }
        }
}

# Manage failure routing cases
failure_route[MANAGE_FAILURE] {
        xlog("L_INFO","[R-M-FAILURE] handling failure $rs\n");
        route(RTPMANAGE);
        if (t_is_canceled()) exit;
}

route[APIBAN] {
#!ifdef WITH_APIBAN
        // check if we already have an APIBAN id... if so, get the updates and
        // if not, get the full list of banned ips.

        $var(KEY) = "APIKEY";

        if ($sht(apibanctl=>ID) == 0) {
                $var(apiget) = "https://apiban.org/api/"; + $var(KEY) + 
"/banned";
        } else {
                $var(apiget) = "https://apiban.org/api/"; + $var(KEY) + 
"/banned/" + $sht(apibanctl=>ID);
        }

        xlog("L_INFO","API SEND: $var(apiget)\n");
        http_client_query("$var(apiget)", "$var(banned)");

        # if we dont get a 200 OK from the webserver, kick it back
        if ($rc!=200) {
                xlog("L_INFO","API ERR: No 200 Received. $var(banned)\n");
                exit;
        } else {
                xlog("L_INFO","API: $var(banned)\n");
        }

        $var(count) = 0;
        jansson_array_size("ipaddress", $var(banned), "$var(size)");
        while($var(count) < $var(size)) {
                jansson_get("ipaddress[$var(count)]", $var(banned), "$var(v)");
                $sht(apiban=>$var(v)) = 1;
                xlog("L_INFO","API: ipaddress[$var(count)] == $var(v)\n");

                $var(count) = $var(count) + 1;
        }
        jansson_get("ID", $var(banned), "$var(w)");
        xlog("L_INFO","ID: $var(w)\n");
        $sht(apibanctl=>ID) = $var(w);
#!endif
        xdbg("apiban complete\n");
}

event_route[htable:mod-init] {
        # pre load apiban
        route(APIBAN);
}

#!ifdef WITH_SIPDEBUG
onsend_route {
        xlog("L_INFO","[ONSEND_ROUTE] SIP DEBUG: \n$snd(buf)\n");
}
#!endif
__________________________________________________________
Kamailio - Users Mailing List - Non Commercial Discussions -- 
sr-users@lists.kamailio.org
To unsubscribe send an email to sr-users-le...@lists.kamailio.org
Important: keep the mailing list in the recipients, do not reply only to the 
sender!

Reply via email to