Limit Annoying Connection Sources That Try to Access to Our Server With Iptables + Hashlimit

I will introduce the method to limit the number of connections per fixed time with using iptables. For example, you can restrict the brute force attack on your server to reduce the efficiency of the attack. This is effective when the attacker cannot be identified by the IP address, when it is not possible to limit the IP address due to the nature of the service such as SMTP, or when you do not want to affect the legitimate user.

Example scenario:

Explanation:

If you want to restrict the whole (eg, if you want to restrict the communication to port 22 all at once) then use limit, but if you want to control individually based on the client IP address then use hashlimit

The hashlimit control flow looks something like this:

  • Clients that communicate are grouped by source IP address and destination port to form a target group how to put together is specified by --hashlimit-mode srcip, dstport
  • Tickets will be assigned to each target group . If you have a ticket , the target group can communicate. Tickets will be consumed each time you communicate.
  • Initially you have 30 tickets. The number is specified by--hashlimit-burst 30
  • One ticket will be added every minute. The additional pace is specified by --hash limit 1/m
  • You can have up to 30 tickets . Overflowed tickets will be deleted. The maximum number is specified by--hashlimit-burst 30 (same as the initial number)
  • The target group of the ticket information will be deleted from the lost communication after 2 minutes. Information retention period is specified by --hashlimit-htable-expire 120000

Remarks: Words such as ticket and target group are added for explanation, not general names.

Example configuration:

  • Create chain HASHCHECK
root@vagrant:/home/vagrant# iptables -N HASHCHECK
  • Use HASHLIMIT to enforce and limit the number of connections
root@vagrant:/home/vagrant# iptables -A HASHCHECK -m hashlimit --hashlimit-name hashcheck_t \
--hashlimit 1/m --hashlimit-burst 30 --hashlimit-mode srcip,dstport \
--hashlimit-htable-expire 120000 -j ACCEPT

Explanation:

-m hashlimit: use the hashlimit module

--hashlimit-name hashcheck_t:set the hash table name to hashcheck_t

--hashlimit 1/m: set the number of connections per hour to “1/min”

--hashlimit-burst 30 : set the burst value (number of connections that can ignore the above — hashlimit value and connect) to 30

--hashlimit-mode srcip,dstport: identify the restriction target by source IP address and destination port

--hashlimit-htable-expire 120000 :set the record retention period in the hash table to 120000 milliseconds

  • Log unauthorized communications
root@vagrant:/home/vagrant# iptables -A HASHCHECK -m limit --limit 1/s -j LOG --log-prefix '[IPTABLES HASH DROP] : '
  • DROP unauthorized communications
root@vagrant:/home/vagrant# iptables -A HASHCHECK -j DROP
  • Adapt the chain that limit ICMP
root@vagrant:/home/vagrant# iptables -A INPUT -p icmp -j HASHCHECK

When actually using it, it is better to perform the following processing before limiting with HASHLIMIT.

  • Allow communication from reliable IP address/IP address band (prevent accidental explosion)
  • Permission of established communication [ELATED, ESTABLISHED] (It seems that there are few hash limits for established communication)

Checking configuration

root@vagrant:/home/vagrant# iptables -L -vn
Chain INPUT (policy ACCEPT 7 packets, 520 bytes)
pkts bytes target prot opt in out source destination
0 0 HASHCHECK icmp -- * * 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 4 packets, 544 bytes)
pkts bytes target prot opt in out source destination
Chain HASHCHECK (1 references)
pkts bytes target prot opt in out source destination
0 0 ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 limit: up to 1/min burst 30 mode srcip-dstport htable-expire 120000
0 0 LOG all -- * * 0.0.0.0/0 0.0.0.0/0 limit: avg 1/sec burst 5 LOG flags 0 level 4 prefix "[IPTABLES HASH DROP] : "
0 0 DROP all -- * * 0.0.0.0/0 0.0.0.0/0

Testing

Time to test our configuration 😊

  • Start ping our server
root@vagrant:/home/vagrant# ping 192.168.123.123
PING 192.168.123.123 (192.168.123.123) 56(84) bytes of data.
64 bytes from 192.168.123.123: icmp_seq=1 ttl=64 time=0.030 ms
64 bytes from 192.168.123.123: icmp_seq=2 ttl=64 time=0.053 ms
64 bytes from 192.168.123.123: icmp_seq=3 ttl=64 time=0.064 ms
64 bytes from 192.168.123.123: icmp_seq=4 ttl=64 time=0.062 ms
64 bytes from 192.168.123.123: icmp_seq=5 ttl=64 time=0.063 ms
64 bytes from 192.168.123.123: icmp_seq=6 ttl=64 time=0.063 ms
64 bytes from 192.168.123.123: icmp_seq=7 ttl=64 time=0.051 ms
64 bytes from 192.168.123.123: icmp_seq=8 ttl=64 time=0.072 ms
64 bytes from 192.168.123.123: icmp_seq=9 ttl=64 time=0.062 ms
64 bytes from 192.168.123.123: icmp_seq=10 ttl=64 time=0.045 ms
64 bytes from 192.168.123.123: icmp_seq=11 ttl=64 time=0.062 ms
64 bytes from 192.168.123.123: icmp_seq=12 ttl=64 time=0.062 ms
64 bytes from 192.168.123.123: icmp_seq=13 ttl=64 time=0.062 ms
64 bytes from 192.168.123.123: icmp_seq=14 ttl=64 time=0.062 ms
64 bytes from 192.168.123.123: icmp_seq=15 ttl=64 time=0.044 ms
<- Ticket (burst) is exhausted here

As we can see in the log, connections are dropped

root@vagrant:/home/vagrant# tail -f /var/log/syslog
Jul 20 17:37:43 vagrant kernel: [ 217.601323] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=40086 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=16
Jul 20 17:37:44 vagrant kernel: [ 218.624511] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=40220 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=17
Jul 20 17:37:45 vagrant kernel: [ 219.648188] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=40342 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=18
Jul 20 17:37:46 vagrant kernel: [ 220.672312] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=40590 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=19
Jul 20 17:37:47 vagrant kernel: [ 221.696527] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=40781 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=20
Jul 20 17:37:48 vagrant kernel: [ 222.721058] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=40835 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=21
Jul 20 17:37:49 vagrant kernel: [ 223.744844] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=41079 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=22
Jul 20 17:37:50 vagrant kernel: [ 224.768166] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=41217 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=23
Jul 20 17:37:51 vagrant kernel: [ 225.792474] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=41347 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=24
Jul 20 17:37:52 vagrant kernel: [ 226.816439] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=41390 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=25
Jul 20 17:37:53 vagrant kernel: [ 227.840532] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=41469 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=26
Jul 20 17:37:54 vagrant kernel: [ 228.864942] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=41696 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=27
Jul 20 17:37:55 vagrant kernel: [ 229.888339] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=41827 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=28
Jul 20 17:37:56 vagrant kernel: [ 230.912771] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=41998 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=29
Jul 20 17:37:57 vagrant kernel: [ 231.936437] [IPTABLES HASH DROP] : IN=lo OUT= MAC=11:11:11:11:11:11:11:11:11:11:11:11:11:11 SRC=192.168.123.123 DST=192.168.123.123 LEN=84 TOS=0x00 PREC=0x00 TTL=64 ID=42208 DF PROTO=ICMP TYPE=8 CODE=0 ID=750 SEQ=30

2 minute has passed since the start of new communication, 1 ticket was added and communication was possible.

64 bytes from 192.168.123.123: icmp_seq=62 ttl=64 time=0.051 ms
64 bytes from 192.168.123.123: icmp_seq=63 ttl=64 time=0.072 ms
64 bytes from 192.168.123.123: icmp_seq=64 ttl=64 time=0.062 ms
64 bytes from 192.168.123.123: icmp_seq=65 ttl=64 time=0.045 ms
64 bytes from 192.168.123.123: icmp_seq=66 ttl=64 time=0.062 ms
64 bytes from 192.168.123.123: icmp_seq=67 ttl=64 time=0.062 ms
64 bytes from 192.168.123.123: icmp_seq=68 ttl=64 time=0.062 ms
64 bytes from 192.168.123.123: icmp_seq=69 ttl=64 time=0.062 ms
64 bytes from 192.168.123.123: icmp_seq=70 ttl=64 time=0.044 ms

Thanks for reading!

DevOps Consultant. I’m strongly focused on automation, security, and reliability.