Hi,
For some time I’ve been looking for a simple way to protect my Asterisk SIP pbx against attacks from bots, scanners which scans and trying dialing to premium numbers. Opening SIP port to the internet causes that there was no one minute without suspect requests hitting my Asterisk. The log was full of that attempts.
While analyzing this problem I noticed that bots, scanners, attackers using everywhere IP address of my server to trying break it. While my proper clients using domain of my Asterisk server. If user is using domain name in his SIP client/phone,this domain is used in further communication on SIP protocol.
Below I will show example of INVITE (INVITE is using to establish VoIP call) SIP request from user using domain name (sip.example.com), and from user using IP address (10.10.10.10) of Asterisk server.
With domain:
1 2 3 4 5 6 7 8 9 |
INVITE sip:202@sip.example.com:5060 SIP/2.0 Accept: application/conference-info+xml, application/sdp, message/sipfrag, multipart/mixed Via: SIP/2.0/UDP 192.168.50.118:5071;branch=z9hG4bKe0aa06e32b6841d20.8df0298f12545d84a;rport Route: <sip:sip.example.com:5060;lr> Max-Forwards: 70 From: "107" <sip:107@sip.example.com:5060>;tag=d04f22eca5 To: <sip:202@sip.example.com:5060>;tag=as5bf765cf Call-ID: 01f0d32f2156bb22 CSeq: 1027520776 INVITE |
With IP address:
1 2 3 4 5 6 7 8 9 |
INVITE sip:202@10.10.10.10:5060 SIP/2.0 Accept: application/conference-info+xml, application/sdp, message/sipfrag, multipart/mixed Via: SIP/2.0/UDP 192.168.50.118:5071;branch=z9hG4bKe0aa06e32b6841d20.8df0298f12545d84a;rport Route: <sip:10.10.10.10:5060;lr> Max-Forwards: 70 From: "107" <sip:107@10.10.10.10:5060>;tag=d04f22eca5 To: <sip:202@10.10.10.10:5060>;tag=as5bf765cf Call-ID: 01f0d32f2156bb22 CSeq: 1027520776 INVITE |
Knowing that, I want to block requests to Asterisk server which are NOT contains my domain name. In this point I want to clarify that I have special subdomain for telephones. Bots, scanners, attackers are not knowing about this domain.
Blocking unwanted requests can be done by iptables rules with string matching.
1 2 3 4 5 6 7 8 9 10 |
# UDP port 5060 rule # Allow for currently established SIP connection iptables -A INPUT -i eth0 -p udp --dport 5060 -m state --state ESTABLISHED,RELATED -j ACCEPT # Allow for packets witch contain my domain name iptables -A INPUT -i eth0 -p udp --dport 5060 -m string --string "sip.example.com" --algo bm -j ACCEPT # Drop other packets (without my domain) iptables -A INPUT -i eth0 -p udp --dport 5060 -m string --string "REGISTER sip:" --algo bm -j DROP iptables -A INPUT -i eth0 -p udp --dport 5060 -m string --string "INVITE sip:" --algo bm -j DROP iptables -A INPUT -i eth0 -p udp --dport 5060 -m string --string "OPTIONS sip:" --algo bm -j DROP iptables -A INPUT -i eth0 -p udp --dport 5060 -j DROP |
1 2 3 4 5 6 7 8 9 |
# TCP port 5060 rule # Allow for packet which contain my domain name iptables -A INPUT -i eth0 -p tcp --dport 5060 -m string --string "sip.example.com" --algo bm -j ACCEPT # Block other SIP request like REGISTER INVITE OPTION if they not contain my domain name iptables -A INPUT -i eth0 -p tcp --dport 5060 -m string --string "REGISTER sip:" --algo bm -j DROP iptables -A INPUT -i eth0 -p tcp --dport 5060 -m string --string "INVITE sip:" --algo bm -j DROP iptables -A INPUT -i eth0 -p tcp --dport 5060 -m string --string "OPTIONS sip:" --algo bm -j DROP # Allow to establish TCP connection and other packets then REGISTER INVITE OPTIONS (without domain) iptables -A INPUT -i eth0 -p tcp --dport 5060 -j ACCEPT |
After applying these rules, I did not see even one attack 🙂
At the and we can check increasing iptables counters:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
asterisk:~ # iptables -nvL Chain INPUT (policy ACCEPT 74700 packets, 24M bytes) pkts bytes target prot opt in out source destination 50 42859 ACCEPT tcp -- eth0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5060 STRING match "sip.example.com" 14 10038 DROP tcp -- eth0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5060 STRING match "REGISTER sip:" 0 0 DROP tcp -- eth0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5060 STRING match "INVITE sip:" 0 0 DROP tcp -- eth0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5060 STRING match "OPTIONS sip:" 685 215K ACCEPT tcp -- eth0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:5060 51 32475 ACCEPT udp -- eth0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:5060 state RELATED,ESTABLISHED 2 1487 ACCEPT udp -- eth0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:5060 STRING match "sip.example.com" 10 6930 DROP udp -- eth0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:5060 STRING match "REGISTER sip:" 0 0 DROP udp -- eth0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:5060 STRING match "INVITE sip:" 4 1725 DROP udp -- eth0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:5060 STRING match "OPTIONS sip:" 2 64 DROP udp -- eth0 * 0.0.0.0/0 0.0.0.0/0 udp dpt:5060 |
14 Comments
Hi Jan,
I implemented the rules for SIP over UDP. Almost everything works, but I have problems receiving INVITEs from DID providers (i.e., from servers on which my Asterisk is REGISTER’d). Did you assume that the rule for state “ESTABLISHED,RELATED” would catch them? In my case, it does not.
Hi Enzo,
Could you record INVITE packets from your SIP trunks (DID providers)? You can do it with tcpdump, example:
tcpdump -vvv -i eth0 -p udp and port 5060 -s 0 -A
Probably INVITES packets from SIP trunks, not contains yours domain name. But as you mentioned it should be catch by “ESTABLISHED,RELATED -j ACCEPT” rule.
I suspect that the connections entries in the conntrack table are expiring too fast in your case.
UDP connections are kept for short time in the conntrack table. On my machine with default kernel settings it only 180 seconds:
router:~ # cat /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream
180
I think you can solve this problem by configure asterisk to sending keepalives packets in your sip trunks. Option qualify=yes in trunk configuration do that. This should refreshing udp timeout for conntrack entry, connection to yours sip trunk should not expiring from conntrack table and then rule “ESTABLISHED,RELATED” should work properly 🙂
Hi Jan,
Yes, you are right: the “qualify=yes” does the trick. And the nf_conntrack_udp_timeout_stream is 180 also in my system. Pity Asterisk doesn’t allow to send simple keep-alive packets (CRLF/CRLF) as per RFC 6223 section 4.1, so I have to bother the peers with heavier Options requests.
I had provisionally solved the problem by adding ACCEPT rules for the domains referenced in the “From:” header of the INVITEs, but that’s obviously an inferior solution because those domains are relatively well-known, also by hackers who might use them to mount scanning attacks.
Thanks,
Enzo
I know this is 4 yrs old; does this still works?
If you are using iptables then yes, this still works and and it worked very well on my Asterisk server for years.
A few months ago I move my firewall from iptables to nftables for NAT performance reasons (my server also works as router for my local network, nftables has implemented flow offload mechanism)
and unfortunately on nftables it is impossible to implement rules described in this post.
Thanks Jan,
I see a lot of fail ‘REGISTERS’ in the LOG, but nothing showing up in iptables as DROP, why is this you think? Also these guy have gotten the server IP address, both local and ISP IPs how can this be? Is there anything I can do to prevent this?
Are you sure that the attackers are not using your dns domain to trying register to your server?
If your Asterisk is behind NAT I think you should use externaddr configuration parameter in sip.conf. I suspose that without configured externaddr Asterisk may sending information about its local IP addresses.
Regarding to the not increasing DROP counter – maybe other firewall rule is passing traffic to 5060 port without restrictions?
thousand and thousands of register requests from thousands of IP addresses aware overloading fail2ban, and this put a stop to it. Thank you this simple but ingenious filter helped. I guess I should thing about using a domain name a long time ago.
As to calls from DID providers, I simply added the IPs as ACCEPT.
Thank you again
I’m happy to hear that rules are working for you 🙂
Kind regards
First, thanks a lot to Jan and all the other contributors for this valuable blog post!
Du you think this block also make sense for SIP over TLS (port 5061)?
If so, how would you configure it for SIP over TLS?
First, thanks a lot to Jan and all the other contributors!
Du you think this block also makes sense for SIP over TLS (port 5061)?
If so, how would you configure it for SIP over TLS?
Hi Martin,
Rules posted above will not work, because iptables will se encrypted traffic.
I think there is two options to try in case of sip over tls.
1) Maybe module https://github.com/Lochnair/xt_tls will be usefully to check if client is using your sip domain. In case of TLS traffic you can check if client is using your domain by check SSL SNI (Server Name Indication) or rather block connection attempt which contains your server IP addr instead of your SIP domain.
I would try something like that:
iptables -A INPUT -p tcp –dport 5061 -m tls ! –tls-host “your.secret.sip.domainl” -j DROP
iptables -A INPUT -p tcp –dport 5061 -j ACCEPT
similar case is described here: https://github.com/Lochnair/xt_tls/issues/37
2) Another option is to use loadbalancer like HAproxy in front of your pbx. SSL in this case will be terminated on that loadbalancer. Traffic between loadbalancer and pbx will not encrypted and rules from my post should work 🙂
I just used your iptables rule method on one of our vici server, and it works as a ROCK!!! Perfectly securing our server now. ZERO attempt or attack for the past 3 hours. And inbound/outbound calls works very well.
Thank you for sharing your knowledge, Sir. Keep it up!
I’m glad to hear that rules are working for you 🙂
Kind regards