Mod_evasive est un module Apache pour contrer les attaques DDoS. Celui-ci est par exemple capable de détecter lorsqu’un utilisateur demande un trop grand nombre de pages sur un site web, sur un délai de temps très court. Voici comment l’installer et le configurer pour une utilisation basique. Lire la suite…
Last week during a casual conversation I overheard a colleague saying: “The Linux network stack is slow! You can’t expect it to do more than 50 thousand packets per second per core!”
That got me thinking. While I agree that 50kpps per core is probably the limit for any practical application, what is the Linux networking stack capable of? Let’s rephrase that to make it more fun:
On Linux, how hard is it to write a program that receives 1 million UDP packets per second?
Hopefully, answering this question will be a good lesson about the design of a modern networking stack.
First, let us assume:
Measuring packets per second (pps) is much more interesting than measuring bytes per second (Bps). You can achieve high Bps by better pipelining and sending longer packets. Improving pps is much harder.
Since we’re interested in pps, our experiments will use short UDP messages. To be precise: 32 bytes of UDP payload. That means 74 bytes on the Ethernet layer.
For the experiments we will use two physical servers: “receiver” and “sender”.
They both have two six core 2GHz Xeon processors. With hyperthreading (HT) enabled that counts to 24 processors on each box. The boxes have a multi-queue 10G network card by Solarflare, with 11 receive queues configured. More on that later.
A couple of explicitly defined IP addresses will later become handy:
receiver$ for i in `seq 1 20`; do
ip addr add 192.168.254.$i/24 dev eth2;
done
sender$ ip addr add 192.168.254.30/24 dev eth3
1. The naive approach
To start let’s do the simplest experiment. How many packets will be delivered for a naive send and receive?
The sender pseudo code:
fd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
fd.bind(("0.0.0.0", 65400)) # select source port to reduce nondeterminism
fd.connect(("192.168.254.1", 4321))
while True:
fd.sendmmsg(["x00" * 32] * 1024)
While we could have used the usual send syscall, it wouldn’t be efficient. Context switches to the kernel have a cost and it is be better to avoid it. Fortunately a handy syscall was recently added to Linux: sendmmsg. It allows us to send many packets in one go. Let’s do 1,024 packets at once.
With the naive approach we can do between 197k and 350k pps. Not too bad. Unfortunately there is quite a bit of variability. It is caused by the kernel shuffling our programs between cores. Pinning the processes to CPUs will help:
Now, the kernel scheduler keeps the processes on the defined CPUs. This improves processor cache locality and makes the numbers more consistent, just what we wanted.
Some basic iptables settings can prevent UDP flood from happening.
The Attacker
Here’s an example of the kinds of apps that were being used. This simple PHP app floods random UDP ports with very large packets continuously. This can degrade or cause failure for an entire subnet.
Generally speaking, there’s no need to allow UDP traffic other than DNS.
All non-essential UDP traffic can be completely blocked with the following settings:
# allow dns requests to google nameservers
iptables -A OUTPUT -p udp --dport 53 -d 8.8.8.8 -j ACCEPT
iptables -A OUTPUT -p udp --dport 53 -d 8.8.4.4 -j ACCEPT
# block all other udp
iptables -A OUTPUT -p udp -j DROP
ip6tables -A OUTPUT -p udp -j DROP
Alternatively, rate limiting can be employed as a more tolerant measure:
# Outbound UDP Flood protection in a user defined chain.
iptables -N udp-flood
iptables -A OUTPUT -p udp -j udp-flood
iptables -A udp-flood -p udp -m limit --limit 50/s -j RETURN
iptables -A udp-flood -j LOG --log-level 4 --log-prefix 'UDP-flood attempt: '
iptables -A udp-flood -j DROP
Gist: https://gist.github.com/thoward/6180165
Note: You’ll probably want to remove the log entry before this goes to production. Disks filling up with logs from rate limiting can crash your servers too!
We can use the iptables recent module to write some iptables rules that can block brute force attacks. In order to use this method you need a kernel and iptables installation that includesipt_recent. If your linux distribution doesn’t include the ipt_recent module or you are using a custom compiled kernel you might need to first include the iptables recent patch that can be found on the author’s website or in the iptables patch-o-matic area. If you are using Debian/Ubuntu you don’t need to do anything special as this is already included in your system.
Let’s see how we can use the iptables recent module to block brute force attacks agains ssh. Let’s see a simple example:
iptables -N SSHSCAN
iptables -A INPUT -p tcp --dport 22 -m state **--state NEW** -j SSHSCAN
iptables -A SSHSCAN -m recent --set --name SSH
iptables -A SSHSCAN -m recent --update **--seconds 300** **--hitcount 3** --name SSH -j DROP
This will basically allow only 3 NEW connections (as matched by the state NEW) in the timeframe of 300sec (5min). Any new connection will be automatically dropped.
The main disadvantage of using this method is that it will not make any distinction betweensuccessful and failed logins. If you are not careful and open too many connections yourself you might found yourself locked out. One walk-around for this issue is to whitelist our own administrative ips (still if we can do this for all the locations that need to connect to the system, then we can protect ourselves with simple firewall rules and we don’t need this added complexity). So at least for the hosts that we can (static ips) we should do this (replace with as many lines needed containing $WHITE_LIST_IP):
iptables -N SSHSCAN
** iptables -A INPUT -p tcp --dport 22 -s $WHITE_LIST_IP -j ACCEPT**
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j SSHSCAN
iptables -A SSHSCAN -m recent --set --name SSH
iptables -A SSHSCAN -m recent --update --seconds 300 --hitcount 3 --name SSH -j DROP
Even if we lock ourselves out, our existing connections will remain up since we are matching only on NEW connections. If needed we can take appropriate actions.
In case we want to have the blocked hosts logged, then we will have to add another iptables rule:
iptables -N SSHSCAN
iptables -A INPUT -p tcp --dport 22 -s $WHITE_LIST_IP -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j SSHSCAN
iptables -A SSHSCAN -m recent --set --name SSH
iptables -A SSHSCAN -m recent --update --seconds 300 --hitcount 3 --name SSH -j LOG --log-level info --log-prefix "SSH SCAN blocked: "
iptables -A SSHSCAN -m recent --update --seconds 300 --hitcount 3 --name SSH -j DROP
You can peek at the internal database kept by the module, by looking inside:/proc/net/ipt_recent/* (DEFAULT will contain default matches; in our example the name of the file is SSHSCAN):
cat /proc/net/ipt_recent/SSHSCAN
This solution is very effective and easy to implement. You just add the needed iptables rules to your existing firewall setup and you are set. Still, it has many limitations when compared with the other methods shown: like limited time frames, it will not differentiate against failed/successful logins, etc.
If you arrive on this page, is that you have already received a DDoS attack on your server or you want to protect it before this attack happens on your server. In this tutorial, we will install “No More DDoS” (replacing DDoS Deflate that is no longer maintained by its author) that lets you easily protect you against small DDoS attacks.
This script is available in 2 versions :
the Debian version, compatible with : Debian 6/7/8, Ubuntu Server 13.10, Ubuntu Server 14.04, Linux Mint 17 and distributions based on Debian.
the CentOs version, compatible with : CentOs 6/7, RHEL 6/7 (à venir dans la version 2.0), Fedora 20 (coming in version 2.0), and distributions based on CentOs.
wget -O- https://raw.githubusercontent.com/stylersnico/nmd/master/centos/install.sh | sh
2. Configurer No More DDoS
To configure No More DDoS, edit the “/usr/local/nmd/conf.d/agent.conf” file :
vim /usr/local/nmd/conf.d/agent.conf
In this file, you can edit the following information :
FREQ : Interval time between 2 launches of the script. By default, this script is run once per minute.
NO_OF_CONNECTIONS : Corresponds to the maximum number of established connections to an IP address. If an IP address has more than 500 connections established on your server, this IP will be banned.
APF_BAN : By default, the script blocks IP addresses in the firewall with iptables (APF_BAN=0). To use “APF”, specify 1 (APF_BAN=1).
EMAIL_TO : If you wish to be notified when blocking a DDoS attack, enter your email address at this line. If you leave this empty, then, no e-mail will be sent.
BAN_PERIOD : Period during an IP address is blocked. Default : 3600 seconds = 1 hour.