diff --git a/kamailio/dbtext/address b/kamailio/dbtext/address new file mode 100644 index 0000000..6d5674f --- /dev/null +++ b/kamailio/dbtext/address @@ -0,0 +1,2 @@ +id(int,auto) grp(int) ip_addr(string) mask(int) port(int) tag(string,null) +0:1:10.10.10.10:32:0:Known Gateway diff --git a/kamailio/dbtext/trusted b/kamailio/dbtext/trusted new file mode 100644 index 0000000..4ff5902 --- /dev/null +++ b/kamailio/dbtext/trusted @@ -0,0 +1 @@ +id(int,auto) src_ip(string) proto(string) from_pattern(string,null) tag(string,null) diff --git a/kamailio/dbtext/version b/kamailio/dbtext/version new file mode 100644 index 0000000..4f6be33 --- /dev/null +++ b/kamailio/dbtext/version @@ -0,0 +1,3 @@ +table_name(string) table_version(int) +address:6 +trusted:5 diff --git a/kamailio/default.cfg b/kamailio/default.cfg index f4f214c..fef2497 100644 --- a/kamailio/default.cfg +++ b/kamailio/default.cfg @@ -28,6 +28,8 @@ flags #!define FLB_NATSIPPING 2 #!define FLB_UAC_REDIRECT 3 +#!define TRUSTED_ADR_GROUP 1 + ####### Global Parameters ######### fork = yes children = 25 @@ -164,6 +166,9 @@ modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") ######## UAC Redirection module ######## loadmodule "uac_redirect.so" +######## DoS prevention mdule ######## +loadmodule "pike.so" + ####### Role Configurations ########## #!ifdef DISPATCHER-ROLE include_file "dispatcher-role.cfg" @@ -183,6 +188,13 @@ include_file "nat-traversal-role.cfg" loadmodule "db_kazoo.so" #!endif +loadmodule "db_text.so" +loadmodule "permissions.so" +modparam("db_text", "db_mode", 1) +modparam("permissions", "db_url", "text:///etc/kamailio/dbtext") +modparam("permissions", "db_mode", 1) + + ####### Routing Logic ######## route { @@ -198,6 +210,8 @@ route route(DISPATCHER_CLASSIFY_SOURCE); #!endif + route(DOS_PREVENTION); + route(HANDLE_OPTIONS); route(HANDLE_MOVE_REQUEST); @@ -231,7 +245,12 @@ route[SANITY_CHECK] { if (!mf_process_maxfwd_header("10")) { xlog("L_WARN", "$ci|end|Too much hops, not enough barley"); - sl_send_reply("483", "Too Many Hops"); + send_reply("483", "Too Many Hops"); + exit; + } + + if ( msg:len > 6144 ) { + send_reply("513", "Message too large"); exit; } @@ -246,6 +265,10 @@ route[HANDLE_OPTIONS] if (is_method("OPTIONS")) { if (isflagset(FLAG_INTERNALLY_SOURCED)) { route(INTERNAL_TO_EXTERNAL_RELAY); + } + else + if ($rd=~ "[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}\.[0-9]{1,3}") { + xlog("L_ERR", "Possible attack- Options: to $ru from $fu, UA $ua, IP $si\n"); } else { sl_send_reply("200", "Rawr!!"); } @@ -373,6 +396,37 @@ route[EXTERNAL_TO_INTERNAL_RELAY] t_relay(); } +route[DOS_PREVENTION] +{ + # allow request from internal network or from whitelist + if (isflagset(FLAG_INTERNALLY_SOURCED) || allow_source_address(TRUSTED_ADR_GROUP)) { + xlog("L_DBG", "Request from trusted IP $rm $si\n"); + return; + } + + # drop requests with no To domain or IP To domain (friendly-scanner) + if (is_method("REGISTER|SUBSCRIBE|OPTIONS") && + ($td == $null || $td=~ "[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}\.[0-9]{1,3}")) { + xlog("L_ERR", "Possible attack- wrong td: $rm to $ru from $fu, UA $ua, IP $si\n"); + exit; + } + + # drop Invite with IP auth realm + if (is_method("INVITE") && is_present_hf("Proxy-Authorization") && + $ar =~ "[0-9]{1,3}\.[0-9]{1,3}.[0-9]{1,3}\.[0-9]{1,3}" ) { + xlog("L_ERR", "Possible attack- Invite realm $ar: to $ru from $fu, UA $ua, IP $si\n"); + exit; + } + + # use pike check for the others + if (!pike_check_req()) { + if( $rc == -2) { + xlog("L_ERR", "DOS attack: $rm to $ru from $fu, UA $ua, IP $si\n"); + } + exit; + } +} + onreply_route[EXTERNAL_REPLY] { xlog("L_INFO", "$ci|log|external reply $T_reply_code"); @@ -467,4 +521,4 @@ onsend_route { xlog("L_INFO", "$ci|pass|$sndfrom(ip):$sndfrom(port) -> $sndto(ip):$sndto(port)"); } -## vim:set tabstop=4 softtabstop=4 shiftwidth=4 expandtab +# vim: tabstop=4 softtabstop=4 shiftwidth=4 expandtab diff --git a/kamailio/dispatcher-role.cfg b/kamailio/dispatcher-role.cfg index 713ac74..b9fd1fd 100644 --- a/kamailio/dispatcher-role.cfg +++ b/kamailio/dispatcher-role.cfg @@ -40,12 +40,49 @@ route[DISPATCHER_FIND_ROUTES] return; } - if (!ds_select_dst("1", "0")) { - xlog("L_ERR", "$ci|end|no servers avaliable"); + if (is_method("SUBSCRIBE")) { + $var(ds_group) = 20; + } else + if (is_method("REGISTER")) { + $var(ds_group) = 30; + } else { + $var(ds_group) = 1; + } + + if (!ds_select_dst("$var(ds_group)", "0")) { + xlog("L_ERR", "$ci|end|no servers avaliable in group $var(ds_group)"); + + # if we selected from group 1, try again in group 2 + if ($var(ds_group) == 1 ) { + + if (!ds_select_dst("2", "0")) { + xlog("L_ERR", "$ci|end|no servers avaliable in group 2"); + sl_send_reply("480", "All servers busy"); + exit; + } + + } else { + + sl_send_reply("480", "All servers busy"); + exit; - sl_send_reply("480", "All servers busy"); + } + } else { + + # if we selected from group 1 and there are less than 3 available servers, choose from group 2 + if ($var(ds_group) == 1 && $var(ds_cnt)< 3) { + + # clear $avp(ds_dst) and search in group 2 + $(avp(ds_dst)[*]) = $null; + + if (!ds_select_dst("2", "0")) { + xlog("L_ERR", "$ci|end|no servers avaliable in group 2"); + sl_send_reply("480", "All servers busy"); + exit; + } + + } - exit; } $var(contact_uri) = $(ct{tobody.user}) + "@" + $(ct{tobody.host}); diff --git a/kamailio/registrar-role.cfg b/kamailio/registrar-role.cfg index ef267ba..24d0697 100644 --- a/kamailio/registrar-role.cfg +++ b/kamailio/registrar-role.cfg @@ -2,6 +2,7 @@ ######## Generic Hash Table container in shared memory ######## modparam("htable", "htable", "auth_cache=>size=16;autoexpire=7200") +modparam("htable", "htable", "failed_auth_hash=>size=14;autoexpire=180;") ####### Authentication Interface module ########## loadmodule "auth.so" @@ -37,6 +38,9 @@ modparam("registrar", "received_avp", "$avp(AVP_RECV_PARAM)") route[HANDLE_REGISTER] { if (is_method("REGISTER")) { + + route(DOMAIN_FORMAT_CHECK); + #!ifdef NAT-TRAVERSAL-ROLE if (nat_uac_test("3")) { xlog("L_INFO", "$ci|log|Correcting NATed contact in registration\n"); @@ -50,11 +54,18 @@ route[HANDLE_REGISTER] #!endif if (is_present_hf("Authorization")) { + + if (!route(PREVENT_BRUTEFORCE)) { + auth_challenge("$fd", "0"); + exit; + } + if ($sht(auth_cache=>$Au) != $null && pv_auth_check("$fd", "$sht(auth_cache=>$Au)", "0", "0")) { xlog("L_INFO", "$ci|log|Authenticated $Au via cached SIP creds\n"); } else { ## RABBITMQ - Credentials fetch if (!auth_check("$fd", "subscriber", "1")) { + route(FAILED_AUTH_COUNT); auth_challenge("$fd", "0"); xlog("L_INFO", "$ci|log|Issued new auth challenge to failed registration attempt\n"); exit; @@ -77,4 +88,42 @@ route[HANDLE_REGISTER] } } -## vim:set tabstop=4 softtabstop=4 shiftwidth=4 expandtab +# AUTH: check to see if user if present in failed_auth_hash +route[PREVENT_BRUTEFORCE] +{ + if($sht(failed_auth_hash=>$Au::count) >= 2) { + $var(exp) = $Ts - 120; + if($sht(failed_auth_hash=>$Au::last) > $var(exp)){ + xlog("L_ERR", "Possible password brute force, from $ct on user $Au"); + return(-1); + } else { + $sht(failed_auth_hash=>$Au::count) = 0; + } + } + return(1); +} + + +#AUTH: add to failed_auth_hash in case of authentication password error +route[FAILED_AUTH_COUNT] +{ + if ($rc == -2) { + if($sht(failed_auth_hash=>$Au::count) == $null) { + $sht(failed_auth_hash=>$Au::count) = 0; + } + $sht(failed_auth_hash=>$Au::count) = $sht(failed_auth_hash=>$Au::count) + 1; + $sht(failed_auth_hash=>$Au::last) = $Ts; + } +} + +route[DOMAIN_FORMAT_CHECK] +{ + if ($rd =~ "([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})" || + $td =~ "([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3}" ) { + send_reply("403", "Forbidden"); + exit; + } + +} + +# vim:tabstop=4 softtabstop=4 shiftwidth=4 expandtab