Workaround for negated (exception) iptables rules with multiple sources or destinations

Written by - 0 comments

Published on - Listed in Network Linux Security


To protect a public DNS nameserver from massive DNS requests/attacks I created a rate limit (connlimit) rule a while ago:

root@dns:~# iptables -I INPUT -p tcp --dport 53 -m connlimit --connlimit-above 10 -j REJECT
root@dns:~# iptables -I INPUT -p udp --dport 53 -m connlimit --connlimit-above 10 -j REJECT

This connection limit rule is triggered as soon as a single source is using more than 10 connections. Connections 11 and more are then automatically rejected.

This results in the following rules shown in the INPUT chain:

root@dns:~# iptables -nvL
Chain INPUT (policy ACCEPT 24925 packets, 1951K bytes)
 pkts bytes target     prot opt in     out     source               destination         
  122  9068 REJECT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:53 #conn src/32 > 10 reject-with icmp-port-unreachable
    0     0 REJECT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:53 #conn src/32 > 10 reject-with icmp-port-unreachable 
[...]

Although this worked (and proofed to be great helper during attacks), this caused issues with internal DNS nameservers which required frequent mass queries for DNS replication. This means: The created rate limit rule needs an exception for a couple of sources.

iptables error on negate rule with multiple sources or destinations

To create a an exception to a rule the iptables command supports the exclamation mark (!) which can be used in combination with -s (source) or -d (destination). The goal would be to create the same rate limit rule as seen above, however not for requests coming from 192.168.100.53 and 192.168.200.53, hence create an exclusion.

However when using multiple sources (or destinations), the negated rule cannot be created:

root@dns:~# iptables -I INPUT -p tcp --dport 53 ! -s 192.168.100.53,192.168.200.53 -m connlimit --connlimit-above 10 -j REJECT
iptables v1.8.4 (legacy): ! not allowed with multiple source or destination IP addresses

Instead of trying to combine rate limit and exception rule into one, the rule can be split into a REJECT and an ACCEPT rule as a workaround.

Create separate ACCEPT and REJECT rules as workaround

First let's create the general rate limit rules which are applied on all sources:

root@dns:~# iptables -A INPUT -p tcp --dport 53 -m connlimit --connlimit-above 10 -j REJECT
root@dns:~# iptables -A INPUT -p udp --dport 53 -m connlimit --connlimit-above 10 -j REJECT

Then create the exception rule (with -j ACCEPT action) with the allowed sources BEFORE the general rate limit rule (using -I INPUT to insert this rule at the top of the INPUT chain):

root@dns:~# iptables -I INPUT -p tcp --dport 53 -s 192.168.100.53,192.168.200.53 -m connlimit --connlimit-above 10 -j ACCEPT
root@dns:~# iptables -I INPUT -p udp --dport 53 -s 192.168.100.53,192.168.200.53 -m connlimit --connlimit-above 10 -j ACCEPT

This results in the following rules in the INPUT chain:

root@dns:~# iptables -nvL
Chain INPUT (policy ACCEPT 120 packets, 11895 bytes)
 pkts bytes target     prot opt in     out     source               destination        
    0     0 ACCEPT     udp  --  *      *       192.168.200.53        0.0.0.0/0            udp dpt:53 #conn src/32 > 10
    0     0 ACCEPT     udp  --  *      *       192.168.100.53        0.0.0.0/0            udp dpt:53 #conn src/32 > 10
    0     0 ACCEPT     tcp  --  *      *       192.168.200.53        0.0.0.0/0            tcp dpt:53 #conn src/32 > 10
    0     0 ACCEPT     tcp  --  *      *       192.168.100.53        0.0.0.0/0            tcp dpt:53 #conn src/32 > 10
    0     0 REJECT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:53 #conn src/32 > 10 reject-with icmp-port-unreachable
    0     0 REJECT     udp  --  *      *       0.0.0.0/0            0.0.0.0/0            udp dpt:53 #conn src/32 > 10 reject-with icmp-port-unreachable
[...]

With this workaround, the rate limit is in place for all sources except the ones defined in the -j ACCEPT rules.

Note: Another potential workaround would be to use iptables ipset, but I did not follow up on that.


Add a comment

Show form to leave a comment

Comments (newest first)

No comments yet.

RSS feed

Blog Tags:

  AWS   Android   Ansible   Apache   Apple   Atlassian   BSD   Backup   Bash   Bluecoat   CMS   Chef   Cloud   Coding   Consul   Containers   CouchDB   DB   DNS   Database   Databases   Docker   ELK   Elasticsearch   Filebeat   FreeBSD   Galera   Git   GlusterFS   Grafana   Graphics   HAProxy   HTML   Hacks   Hardware   Icinga   Influx   Internet   Java   KVM   Kibana   Kodi   Kubernetes   LVM   LXC   Linux   Logstash   Mac   Macintosh   Mail   MariaDB   Minio   MongoDB   Monitoring   Multimedia   MySQL   NFS   Nagios   Network   Nginx   OSSEC   OTRS   Office   OpenSearch   PGSQL   PHP   Perl   Personal   PostgreSQL   Postgres   PowerDNS   Proxmox   Proxy   Python   Rancher   Rant   Redis   Roundcube   SSL   Samba   Seafile   Security   Shell   SmartOS   Solaris   Surveillance   Systemd   TLS   Tomcat   Ubuntu   Unix   VMWare   VMware   Varnish   Virtualization   Windows   Wireless   Wordpress   Wyse   ZFS   Zoneminder