Configuring IP tables from Asterisk dial plan

I have an Asterisk server running with autocreatepeer and I managed to count te number of times an attempt from an IP address to make a call is made. If within 60 seconds three attempts are made from the same IP address the idea is to block the IP number in IP-tables.

I use this line
exten => s,n, System(iptables -A INPUT -s ${IP_NUMBER} -j DROP)

If I do this command on the linux prompt it shows that the rule is added to the rule table

iptables -L --line-numbers |grep 158.69.88.54 (where 158.69.88.54 is the IP number that tried three times within a minute to set up a call)

25 DROP all – 158.69.88.54 anywhere

I expected that from the moment the rule was added the IP number would be blocked and not reach Asterisk anymore but that is not what is happening. 158.69.88.54 is still trying to set up phone calls using different sip peer names (because of the use of autocreatepeer ) It is perhaps a bit off topic for this forum, but what is needed to get an IPtables rule into action? If I got this to work it is much easier then fai2ban to block annoying IP numbers.

Try the following
exten => s,n, System(iptables -w -I INPUT -s ${IP_NUMBER} -j DROP)

-w will wait for others to finish before adding the rule.
-I will add the rule to the top of the rules, so it will be DROPped prior to an ACCEPT instead of at the end of the rules.

MAKE SURE YOU NEVER BLOCK YOUR ADMIN IP or you will not be able to get onto your server remotely.

1 Like

I would suggest creating a table specifically for these rules, which will avoid the risk of killing admin access.

I’m locking IP numbers so if I have a less briliant moment I can always get access from another IP number to delete the rue that is blocking me at the office.

It is easy to test with this endless attempts of intruding my Asterisk server. I don’t have a trunk and the dial plan is prepared for it but it still is a waste of resources.

Pay123: with the -w what or who is it that IPtables is waiting for before writing the rules?

I got it up and running and it is working like a sharm. For those who think this can be useful for their Asterisk box I copied the script. It is very straightforward and for particular use some adjustments might be needed. A white-list would be nice to add and instead of storing the info in separate files I can imagine that a database will be handy.

[max_inbount_trials]

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; created by Erik de Wild
; erik@wildsound.nl
; 5th november 2016
;
; free use of this script is allowed as long as no money is charged for
; using this script as it is and credits for the the auteur of the script are granted.
; if you need adjustments or extensions just send an e-mail with the request
;
; happy coding!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;;;;;;;;;;;;
; add a derectory to /var/spool/asterisk with the name “inbound_count” and
; an empty file in this directory with the name “banned-ip-numbers”

exten => s,1, Set(FILE_EXISTS=0)
exten => s,n, Set(IP_NUMBER=${SHELL(asterisk -rx “sip show channels” | grep ${CALLERID(num)})})
exten => s,n, Set(IP_NUMBER=${IP_NUMBER:0:15}) ; the first 15 position of the grepped lin
; which is the maximum length of an
; IP-number. Might need adjustment for IPv6
; numbers

exten => s,n, Set(IP_NUMBER=${CUT(IP_NUMBER, ,1)}) ; delete the spaces at the end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; make this file for the white list
; /var/spool/asterisk/inbound_count/white_list
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;!!!
exten => s,n, Set(WHITE_LISTED=${SHELL(cat /var/spool/asterisk/inbound_count/white_list | grep ${IP_NUMBER})})
exten => s,n, Set(WHITE_LISTED=${CUT(WHITE_LISTED, ,1)}) ; cut spaces off
exten => s,n, NoOp(${WHITE_LISTED}" de lengte van het IP-nummer is " ${LEN(${WHITE_LISTED})})
exten => s,n, SET(LEN_WHITE_LISTED=${LEN(${WHITE_LISTED})})
exten => s,n, GotoIf($[ “${LEN_WHITE_LISTED}” : “0”]?not_white_listed)
exten => s,n, Return()

;!!!
exten => s,n(not_white_listed), SET(PAD=/var/spool/asterisk/inbound_count/${IP_NUMBER})

exten => s,n, NoOp(“XXXXXXXXXXXXXXXXXXXXXXXX”)
exten => s,n, NoOp("the IP-number of SIP-peer " ${CALLERID(num)} "is " ${IP_NUMBER})
exten => s,n, NoOp(“XXXXXXXXXXXXXXXXXXXXXXXX”)
exten => s,n, SET(FILE_EXISTS=${STAT(e,${PAD})}) ; check if file already exists
exten => s,n, GotoIf($[ “${FILE_EXISTS}” : “1”]?check_last_read)
exten => s,n, System(echo -E “xx” > ${PAD}) ; create new file and add 1 line with xx
exten => s,n, Return() ; if everything is ok the call is routed to normal
; procedure. The context has be started with a
; Gosub.

exten => s,n(check_last_read),Set(LAST_READ=${STAT(A,${PAD})}) ;EPOCH of last read
exten => s,n, NoOp(XXXXXXXXXXXXXXXXX ${LAST_READ}) ; in seconds since the start
; of epoch time
exten => s,n, SET(SECONDS_AGO=$[${STRFTIME(${EPOCH},GMT-1,%s)} - ${LAST_READ}])

exten => s,n, Set(TIME_WINDOW=180) ; if an attempt is made after more then 180 seconds
; a new file is generated and the counting start
; over again
exten => s,n, GotoIf($[ ${SECONDS_AGO}<${TIME_WINDOW}]?add_line)
exten => s,n, NoOp(number of seconds between the last attempt and this attempt ${SECONDS_AGO})
exten => s,n, System(echo “xx” > ${PAD}) ; create new file, last attempt was longer ago then
; the set time window

exten => s,n, Return() ; if everything is ok the call is routed to normal
; procedure. The context has be started with a
; Gosub.

exten => s,n(add_line),System(echo “xx” >> ${PAD}) ; add line to file of this specific IP-number
exten => s,n, Set(SIZE_FILE=${STAT(s,${PAD})}) ; check for number of bytes in file
exten => s,n, Set(MAX_SIZE=9) ; three attempts with in the timeframe,
; every attempt adds 3 bytes to the file
; (xx and a line return)
exten => s,n, GotoIf($[ ${SIZE_FILE}>${MAX_SIZE}]?hang_up) ;
exten => s,n, Return() ; if everything is ok the call is routed to normal
; procedure. The context has be started with a
; Gosub.

exten => s,n(hang_up),Playback(beep)
exten => s,n, System(echo "IP-number "${IP_NUMBER} “is blocked by IPtables” > /var/spool/asterisk/inbound_count/banned-ip-numbers)
exten => s,n, System(iptables -w -I INPUT -s ${IP_NUMBER} -j DROP)
;exten => s,n, System(iptables -A INPUT -s ${IP_NUMBER} -j DROP)
;exten => s,n, System(/sbin/iptables-save)
exten => s,n, NoOp(#######################################)
exten => s,n, NoOp(IP-nummer ${IP_NUMBER} is now blocked in IPTables)
exten => s,n, NoOp(#######################################)
exten => s,n, Playback(beep)
exten => s,n, Playback(beep)
exten => s,n, Hangup()

When multiple programs (threads in Asterisk) try to access iptables at the same time things might go wrong (I learned that the hard way).
The -w option serialises calls to iptables so that everything is executed as it is suposed to be.

Under normal circumstances you would never call iptables from different programs/threads at the same time, but in your case it is more then likely.
I found out as I had some scripts access iptables on boot when iptables was still loading, as a result I had inconsistent results on boot. Digging deeper I found the -w option and that fixed the issue (took me quite some time before the penny dropped)

By the way with the -I parameter you can specify a line number to insert the rule, so if you have specific rules that always need to be executed (e.g. allways accepting your ip address)
For example :
iptables -w -I INPUT -s ${SAFE_IP_NUMBER} -j ACCEPT (this would be the 1st rule)
and then drop other addresses as follows :
iptables -w -I 1 INPUT -s ${IP_NUMBER} -j DROP
this would insert the rule after the first rule.

1 Like