Tag Archive for 'opensips'

OpenSIPS: ratelimit with dynamically changeable value

This note will instruct you how to protect each DID number connected to your OpenSIPS from SIP DDoS, limiting not the total amount of INVITE requests going to your OpenSIPS server, but only INVITEs to some certain RURI.

Check it out, I hope you like it!

This is useful when you have a plenty of SIP numbers (DIDs) connected to your server and each one accepts inbound calls, e.g. a call centre or a taxi ordering service, etc. And you have to check each destination and drop too much requests, without degradation of any other incoming calls.

This is a nice solution to prevent the situation seen on the graph in the previous post.

PS: clustering support is not described here.

    loadmodule "ratelimit.so"		     # no deps
    modparam("ratelimit", "window_size", 2)  # ban timeout, sec

Add start limit value to the startup_route:

    startup_route {
    	cache_store("local", "incoming:ratelimit", "8");
    }

Somewhere in the initial INVITE section:

# AntiDDoS for each inbound call
if($si !~ "^10\..*") {
    cache_fetch("local", "incoming:ratelimit", $var(rl));
        # $var(rl) invites/sec going to each $rU.
        # SBT is the most precise policy.
        if (!rl_check("pipe_$rU", "$(var(rl){s.int})", "SBT")) {          
            sl_send_reply("503", "Service Unavailable. AntiDDoS");
            xlog("L_INFO", "call $ci from $fU@$si:$sp to $oU@$Ri drp by rl");
            exit;
        };
};

Live statistics:

voip-sipgw01 opensips # opensipsctl fifo rl_list
PIPE::  id=pipe_9618688830 algorithm=SBT limit=8 counter=0
PIPE::  id=pipe_9020578345 algorithm=SBT limit=8 counter=0
PIPE::  id=pipe_9611157347 algorithm=SBT limit=8 counter=0
PIPE::  id=pipe_79190224444 algorithm=SBT limit=8 counter=0
...
PIPE::  drop_rate=581

Change the limit on the fly up to 10 INVITES to each $rU:

opensipsctl fifo cache_store local incoming:ratelimit 10

OpenSIPS: batch dialogs killer one-liner

Situation: VoIP ISP server suddenly became unavailable and you have some thousands of dialogs like this:

dialog::  ID=17580696317398
	state:: 3
	user_flags:: 0
	timestart:: 1556697836
	datestart:: 2019-05-01 13:03:56
	timeout:: 1556701436
	dateout:: 2019-05-01 14:03:56
	callid:: 7EB7F3F9-6B1E11E9-90F3C2ED-81742C80@XX.YY.168.28
	from_uri:: sip:ZZZXXX7146@XX.YY.168.28
	to_uri:: sip:NNNN902290@XX.YY.169.130
	caller_tag:: 8999291C-450
	caller_contact:: sip:ZZZXXX7146@XX.YY.168.28:5060
	callee_cseq:: 0
	caller_route_set:: <sip:XX.YY.169.130;lr=on;ftag=8999291C-450;did=dff3.a92b;nat=yes>
	caller_bind_addr:: udp:EE.FF.116.74:5060
	caller_sdp:: 
	CALLEES:: 
		callee:: 
			callee_tag:: as3d7dee61
			callee_contact:: sip:abc@EE.FF.116.62:5060;transport=udp
			caller_cseq:: 101
			callee_route_set:: 
			callee_bind_addr:: udp:EE.FF.116.74:5060
			callee_sdp:: 

To kill them in some seconds (not to wait when OpenSIPS will terminate them according to SIP timers), do:

for i in `opensipsctl fifo dlg_list | grep callid | grep \@XX.YY.168.28 | awk '{print $2}'` ; do opensipsctl fifo dlg_end_dlg $i ; done

In fact, the situation was as follows: VoIP software on the ISP side (we have a DID from it connected to our OpenSIPS cluster) had some problems and they suddenly started sending us a bunch of unique INVITES (with different Call-IDs) for any certain inbound call.

Very soon the total number of dialogs dramatically increased up to enormous values. And this is a theme of my next article!

CentOS: disable /tmp/opensips_fifo cleanup

dpz2-pbx ~ # cat /usr/lib/tmpfiles.d/tmp.conf 
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

# See tmpfiles.d(5) for details

# Clear tmp directories separately, to make them easier to override
v /tmp 1777 root root 10d
v /var/tmp 1777 root root 30d

# Exclude namespace mountpoints created with PrivateTmp=yes
x /tmp/systemd-private-%b-*
X /tmp/systemd-private-%b-*/tmp
x /var/tmp/systemd-private-%b-*
X /var/tmp/systemd-private-%b-*/tmp

# Disable auto-remove of /tmp/opensips_fifo
x /tmp/opensips_fifo   # add this add this add this add this add this add this add this 

OpenSIPS battle: REGISTER requests vs permissions module

Sutuation: you have to check the source address of REGISTER messages, going to your OpenSIPS server and decide wether to allow them or to deny.

Use permissions module for this.

You can use it in two variants:

1. with OpenSIPS’ text config files register.allow and register.deny (similar to Unix hosts.allow and hosts.deny).
In this case you should use module’s function ‘allow_register

Example of blocking REGISTERs from 10.145.13.49 IP address:

register.deny file:

ALL : "^sip:.*10\.145\.13\.49"
ALL : "^sip:.*0*10\.145\.0*13\.0*49"   # this is to prevent bypassing
                                       # by the insertion of one or more '0' in the IP address

register.allow file is empty (allow everything except those in .deny file).

OpenSIPS script snippet:

	if ( is_method("REGISTER") ) {
		if (allow_register("register")) {
			save("location");
			exit;
		} else {
			sl_send_reply("403", "Forbidden registration from your IP v2");
			exit;
		}
	}

But this method has one big disadvantage – you need to restart OpenSIPS each time you edit register.allow/register.deny.
OpenSIPS ‘permissions’ module has a MI function ‘address_reload‘ but it reloads the table (see below), not the allow/deny files.
So, it’s more cool to use the second variant, go on reading!..

2. with DB table ‘address‘.
In this case you should use modules’ function ‘check_address

– register.allow and register.deny files are empty.
– add entries to ‘address’ table. In our case we’re using not real SQL DB but dbtext. So, this is how ‘/etc/opensips/dbtext/address’ file looks like:

voip-pbx-sbc ~ # cat /etc/opensips/dbtext/address 
id(int,auto) grp(int) ip(string) mask(int) port(int) proto(string) pattern(string,null) context_info(string,null)
1:0:10.84.2.0:24:0:any
2:0:10.145.13.5:32:0:any
3:0:10.145.13.49:32:0:any
4:0:10.145.14.0:24:0:any

WARNING: every time you add any new table, do not forget to add it’s version to another table ‘version’:

voip-pbx-sbc ~ # cat /etc/opensips/dbtext/version 
table_name(string) table_version(int) 
dispatcher:8
load_balancer:2
address:5

Firstly, I haven’t done it, and that’s why OpenSIPS could not start and I had this message in the system log:

ERROR:core:db_check_table_version: invalid version 0 for table address found, expected 5

So, the script snippet with the ‘check_address’ function:

	if ( is_method("REGISTER") ) {

		if(check_address("0","$si","0","any")) {
			save("location");
			exit;
		} else {
			sl_send_reply("403", "Forbidden registration from your IP v2");
			exit;
		}

	}

And here’s the magic! You may add IP-addresses or subnets to your DB or dbtext file and then run a MI command ‘address_reload‘ without restarting your high-loaded OpenSIPS.

Now the policy is “if address is in the table – allow it, otherwise block”. Look at the images below.

IP is not in the table – REGISTER is forbidden:

IP has been added to dbtext table and table reloaded – registrations passed successfully:

You can also look the table’s contents with MI commands ‘opensipsctl fifo address_dump‘ and ‘opensipsctl fifo subnet_dump‘.

UPD: OpenSIPS core developer’s answer to my question http://lists.opensips.org/pipermail/users/2017-October/038169.html .

OpenSIPS: protecting from undesired requests

Original: http://lists.opensips.org/pipermail/users/2013-March/024887.html

A few suggestions (mostly already suggested by many guys in this thread, i
am only arranging their order to a secure setup), opensips log level should
be at least 2.

1. I usually filter out all known nasty users / attackers right in sanity
check section of default request route. My sanity check section structured
something like this,

a). check max forwards.
b). check message size.
c). check user-agent string against filter list, you can use
permissions module for this as well as hard code user-agents as Nick
suggested.

############################################
route {
if (!mf_process_maxfwd_header("10")) {
     sl_send_reply("483","Too Many Hops");
     exit;
};

if (msg:len > max_len) {
     sl_send_reply("513","Message Too Big");
     exit;
};

if ($ua =~ "friendly-scanner") {
     xlog("L_WARN", "[$pr:$fU@$si:$sp]: Rejecting '$rm' request from bogus device '$ua' \n");
     exit;
};
...
#####################################

2. Then in authentication section, i make sure to authenticate both INVITE
and REGISTER requests, you check ret-code for both www-authorize and
proxy-authorize methods and if it is -1 or -2 then do xlog to print log on
intruder which is picked by fail2ban to block the user (make sure text
pattern in your xlog matches failregex in fail2ban! ).

Negative code meanings: http://www.opensips.org/html/docs/modules/2.1.x/auth_db.html#id293676

#####################################
...
if (!www_authorize("","subscriber")) {

     switch ($retcode) {
     case -1:
          xlog("L_NOTICE", "[$pr:$fU@$si:$sp]: Auth error for '$tU' from '$si',
          peer not found - User-Agent: '$ua' \n");
          break;
     case -2:
          xlog("L_NOTICE", "[$pr:$fU@$si:$sp]: Auth error for '$tU' from '$si',
          wrongpassword - User-Agent: '$ua' \n");
          break;
          ...
     };

www_challenge("", "1");
exit;
};
...
#######################################