#!KAMAILIO # # Simple/sample kamailio.cfg for running a proxy/registrar with TLS and # WebSockets support. #!substdef "!DBURL!sqlite:///etc/kamailio/db.sqlite!g" #!substdef "!MY_IP_ADDR!a.b.c.d!g" #!substdef "!MY_DOMAIN!example.com!g" #!substdef "!MY_WS_PORT!80!g" #!substdef "!MY_WSS_PORT!443!g" #!substdef "!MY_MSRP_PORT!9000!g" #!substdef "!MY_WS_ADDR!tcp:MY_IP_ADDR:MY_WS_PORT!g" #!substdef "!MY_WSS_ADDR!tls:MY_IP_ADDR:MY_WSS_PORT!g" #!substdef "!MY_MSRP_ADDR!tls:MY_IP_ADDR:MY_MSRP_PORT!g" #!substdef "!MSRP_MIN_EXPIRES!1800!g" #!substdef "!MSRP_MAX_EXPIRES!3600!g" ##!define LOCAL_TEST_RUN #!define WITH_TLS #!define WITH_WEBSOCKETS #!define WITH_MSRP ####### Global Parameters ######### fork=yes children=4 #!ifdef WITH_TLS enable_tls=1 #!endif listen=MY_IP_ADDR #!ifdef WITH_WEBSOCKETS listen=MY_WS_ADDR #!ifdef WITH_TLS listen=MY_WSS_ADDR #!endif #!endif #!ifdef WITH_MSRP listen=MY_MSRP_ADDR #!endif tcp_connection_lifetime=3604 tcp_accept_no_cl=yes tcp_rd_buf_size=16384 #!ifdef LOCAL_TEST_RUN debug=2 mpath="modules" #!else debug=0 mpath="/usr/lib64/kamailio/modules/" #!endif loadmodule "db_sqlite.so" loadmodule "tm.so" loadmodule "sl.so" loadmodule "rr.so" loadmodule "pv.so" loadmodule "maxfwd.so" loadmodule "usrloc.so" loadmodule "registrar.so" loadmodule "textops.so" loadmodule "siputils.so" loadmodule "xlog.so" loadmodule "sanity.so" loadmodule "ctl.so" loadmodule "auth.so" loadmodule "auth_db.so" loadmodule "kex.so" loadmodule "mi_rpc.so" loadmodule "corex.so" #!ifdef WITH_TLS loadmodule "tls.so" #!endif #!ifdef WITH_MSRP loadmodule "msrp.so" loadmodule "htable.so" loadmodule "cfgutils.so" #!endif #!ifdef WITH_WEBSOCKETS loadmodule "xhttp.so" loadmodule "websocket.so" loadmodule "nathelper.so" #!endif # ----------------- setting module-specific parameters --------------- # ----- tm params ----- # auto-discard branches from previous serial forking leg modparam("tm", "failure_reply_mode", 3) # default retransmission timeout: 30sec modparam("tm", "fr_timer", 30000) # default invite retransmission timeout after 1xx: 120sec modparam("tm", "fr_inv_timer", 120000) # ----- rr params ----- # add value to ;lr param to cope with most of the UAs modparam("rr", "enable_full_lr", 1) # do not append from tag to the RR (no need for this script) modparam("rr", "append_fromtag", 0) # ----- registrar params ----- modparam("registrar", "method_filtering", 1) modparam("registrar", "max_expires", 3600) modparam("registrar", "gruu_enabled", 0) # ----- usrloc params ----- modparam("usrloc", "db_url", "DBURL") modparam("usrloc", "db_mode", 0) # ----- auth params ----- modparam("auth", "nonce_count", 1) modparam("auth", "qop", "auth") # ----- auth_db params ----- modparam("auth_db", "db_url", "DBURL") modparam("auth_db", "calculate_ha1", yes) modparam("auth_db", "password_column", "password") modparam("auth_db", "load_credentials", "id") # ----- corex params ----- modparam("corex", "alias_subdomains", "MY_DOMAIN") #!ifdef WITH_TLS # ----- tls params ----- modparam("tls", "tls_method", "TLSv1.2+") modparam("tls", "certificate", "/etc/pki/CA/ser1_cert.pem") modparam("tls", "private_key", "/etc/pki/CA/privkey.pem") modparam("tls", "ca_list", "/etc/pki/CA/calist.pem") #!endif #!ifdef WITH_WEBSOCKETS # ----- nathelper params ----- modparam("nathelper|registrar", "received_avp", "$avp(RECEIVED)") # Note: leaving NAT pings turned off here as nathelper is _only_ being used for # WebSocket connections. NAT pings are not needed as WebSockets have # their own keep-alives. #!endif #!ifdef WITH_MSRP # ----- htable params ----- modparam("htable", "htable", "msrp=>size=8;autoexpire=MSRP_MAX_EXPIRES;") #!endif ####### Routing Logic ######## # Main SIP request routing logic # - processing of any incoming SIP request starts with this route # - note: this is the same as route { ... } request_route { if ((($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT) && !(proto == WS || proto == WSS)) || $Rp == MY_MSRP_PORT) { xlog("L_WARN", "SIP request received on $Rp\n"); sl_send_reply("403", "Forbidden"); exit; } # per request initial checks route(REQINIT); #!ifdef WITH_WEBSOCKETS if (nat_uac_test(64)) { # Do NAT traversal stuff for requests from a WebSocket # connection - even if it is not behind a NAT! # This won't be needed in the future if Kamailio and the # WebSocket client support Outbound and Path. force_rport(); if (is_method("REGISTER")) { fix_nated_register(); } else { if (!add_contact_alias()) { xlog("L_ERR", "Error aliasing contact <$ct>\n"); sl_send_reply("400", "Bad Request"); exit; } } } #!endif # handle requests within SIP dialogs route(WITHINDLG); ### only initial requests (no To tag) # CANCEL processing if (is_method("CANCEL")) { if (t_check_trans()) { t_relay(); } exit; } t_check_trans(); # authentication route(AUTH); # record routing for dialog forming requests (in case they are routed) # - remove preloaded route headers remove_hf("Route"); if (is_method("INVITE")) { record_route(); } # handle registrations route(REGISTRAR); if ($rU==$null) { # request with no Username in RURI sl_send_reply("484", "Address Incomplete"); exit; } # user location service route(LOCATION); route(RELAY); } route[RELAY] { if (!t_relay()) { sl_reply_error(); } exit; } # Per SIP request initial checks route[REQINIT] { if (!mf_process_maxfwd_header("10")) { sl_send_reply("483", "Too Many Hops"); exit; } if (!sanity_check("1511", "7")) { xlog("Malformed SIP message from $si:$sp\n"); exit; } if (uri == myself && is_method("OPTIONS") && !(uri=~"sip:.*[@]+.*")) { options_reply(); exit; } } # Handle requests within SIP dialogs route[WITHINDLG] { if (has_totag()) { # sequential request within a dialog should # take the path determined by record-routing if (loose_route()) { #!ifdef WITH_WEBSOCKETS if ($du == "") { if (!handle_ruri_alias()) { xlog("L_ERR", "Bad alias <$ru>\n"); sl_send_reply("400", "Bad Request"); exit; } } #!endif route(RELAY); } else { if ( is_method("ACK") ) { if ( t_check_trans() ) { # no loose-route, but stateful ACK; # must be an ACK after a 487 # or e.g. 404 from upstream server t_relay(); exit; } else { # ACK without matching transaction... # ignore and discard exit; } } sl_send_reply("404", "Not Found"); } exit; } } # Handle SIP registrations route[REGISTRAR] { if (is_method("REGISTER")) { if (!save("location")) { sl_reply_error(); } exit; } } # USER location service route[LOCATION] { if (!is_subscriber("$ru", "subscriber", "1")) { t_newtran(); send_reply("404", "Not Found"); exit; } if (!lookup("location")) { $var(rc) = $rc; t_newtran(); switch ($var(rc)) { case -1: send_reply("480", "Temporarily Unavailable"); exit; case -2: send_reply("405", "Method Not Allowed"); exit; case -3: send_reply("500", "Server Internal Error"); exit; } } } # Authentication route route[AUTH] { if (is_method("REGISTER") || from_uri==myself) { # authenticate requests if (!auth_check("$fd", "subscriber", "1")) { auth_challenge("$fd", "0"); exit; } # user authenticated - remove auth header if(!is_method("REGISTER")) { consume_credentials(); } } # if caller is not local subscriber, then check if it calls # a local destination, otherwise deny, not an open relay here if (from_uri!=myself && uri!=myself) { sl_send_reply("403", "Forbidden"); exit; } } #!ifdef WITH_WEBSOCKETS onreply_route { if ((($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT) && !(proto == WS || proto == WSS)) || $Rp == MY_MSRP_PORT) { xlog("L_WARN", "SIP response received on $Rp\n"); drop; } if (nat_uac_test(64)) { # Do NAT traversal stuff for replies to a WebSocket connection # - even if it is not behind a NAT! # This won't be needed in the future if Kamailio and the # WebSocket client support Outbound and Path. add_contact_alias(); } } event_route[xhttp:request] { set_reply_close(); set_reply_no_connect(); if ($Rp != MY_WS_PORT #!ifdef WITH_TLS && $Rp != MY_WSS_PORT #!endif ) { xlog("L_WARN", "HTTP request received on $Rp\n"); xhttp_reply("403", "Forbidden", "", ""); exit; } xlog("L_DBG", "HTTP Request Received\n"); if ($hdr(Upgrade)=~"websocket" && $hdr(Connection)=~"Upgrade" && $rm=~"GET") { # Validate Host - make sure the client is using the correct # alias for WebSockets if ($hdr(Host) == $null || !is_myself("sip:" + $hdr(Host))) { xlog("L_WARN", "Bad host $hdr(Host)\n"); xhttp_reply("403", "Forbidden", "", ""); exit; } # Optional... validate Origin - make sure the client is from an # authorised website. For example, # # if ($hdr(Origin) != "http://communicator.MY_DOMAIN" # && $hdr(Origin) != "https://communicator.MY_DOMAIN") { # xlog("L_WARN", "Unauthorised client $hdr(Origin)\n"); # xhttp_reply("403", "Forbidden", "", ""); # exit; # } # Optional... perform HTTP authentication # ws_handle_handshake() exits (no further configuration file # processing of the request) when complete. if (ws_handle_handshake()) { # Optional... cache some information about the # successful connection exit; } } xhttp_reply("404", "Not Found", "", ""); } event_route[websocket:closed] { xlog("L_INFO", "WebSocket connection from $si:$sp has closed\n"); } #!endif #!ifdef WITH_MSRP event_route[msrp:frame-in] { msrp_reply_flags("1"); if ((($Rp == MY_WS_PORT || $Rp == MY_WSS_PORT) && !(proto == WS || proto == WSS)) && $Rp != MY_MSRP_PORT) { xlog("L_WARN", "MSRP request received on $Rp\n"); msrp_reply("403", "Action-not-allowed"); exit; } if (msrp_is_reply()) { msrp_relay(); } else if($msrp(method)=="AUTH") { if($msrp(nexthops)>0) { msrp_relay(); exit; } if (!www_authenticate("MY_DOMAIN", "subscriber", "$msrp(method)")) { if (auth_get_www_authenticate("MY_DOMAIN", "1", "$var(wauth)")) { msrp_reply("401", "Unauthorized", "$var(wauth)"); } else { msrp_reply("500", "Server Error"); } exit; } if ($hdr(Expires) != $null) { $var(expires) = (int) $hdr(Expires); if ($var(expires) < MSRP_MIN_EXPIRES) { msrp_reply("423", "Interval Out-of-Bounds", "Min-Expires: MSRP_MIN_EXPIRES\r\n"); exit; } else if ($var(expires) > MSRP_MAX_EXPIRES) { msrp_reply("423", "Interval Out-of-Bounds", "Max-Expires: MSRP_MAX_EXPIRES\r\n"); exit; } } else { $var(expires) = MSRP_MAX_EXPIRES; } $var(cnt) = $var(cnt) + 1; pv_printf("$var(sessid)", "s.$(pp).$(var(cnt)).$(RANDOM)"); $sht(msrp=>$var(sessid)::srcaddr) = $msrp(srcaddr); $sht(msrp=>$var(sessid)::srcsock) = $msrp(srcsock); $shtex(msrp=>$var(sessid)) = $var(expires) + 5; # - Use-Path: the MSRP address for server + session id $var(hdrs) = "Use-Path: msrps://MY_IP_ADDR:MY_MSRP_PORT/" + $var(sessid) + ";tcp\r\n" + "Expires: " + $var(expires) + "\r\n"; msrp_reply("200", "OK", "$var(hdrs)"); } else if ($msrp(method)=="SEND" || $msrp(method)=="REPORT") { if ($msrp(nexthops)>1) { if ($msrp(method)!="REPORT") { msrp_reply("200", "OK"); } msrp_relay(); exit; } $var(sessid) = $msrp(sessid); if ($sht(msrp=>$var(sessid)::srcaddr) == $null) { # one more hop, but we don't have address in htable msrp_reply("481", "Session-does-not-exist"); exit; } else if ($msrp(method)!="REPORT") { msrp_reply("200", "OK"); } msrp_relay_flags("1"); msrp_set_dst("$sht(msrp=>$var(sessid)::srcaddr)", "$sht(msrp=>$var(sessid)::srcsock)"); msrp_relay(); } else { msrp_reply("501", "Request-method-not-understood"); } } #!endif