mod_qos cannot only be used to implement QoS by controlling your Apache
web server's traffic but may also help you to protect
your web site against denial of service (DoS) attacks. The two features
of mod_qos which are particularly suitable to help you in doing this,
are the possibility to measure the bandwidth used for a TCP connection
(and the possibility to enforce that the requirements concerning the
minimum bandwidth is fulfilled) and the existence of event counters
per client's IP addresses (allowing you to limit the number of events
a client is allowed cause on your web site).
Note: Multiple users may share an IP addresses (IP address is not
unique per user) when using an intermediate proxy server or source NAT.
This is especially true for B2B applications.
This documentation shall give you an idea on how you could use mod_qos
within your Apache web server to defense against the following DoS attacks:
- Low-bandwidth DoS attacks
The attacker tries to keep many TCP connections to your web server open,
only sending partiality request data periodically (just enough not triggering
any inactivity timeouts). The target is to occupy all available connections
(running threads of your Apache server) so that nobody else can connect.
- HTTP GET/POST flood DoS attacks
The attacker sends a huge amount of HTTP requests to your web server without
awaiting the server's response. This requires only little traffic for the
attacker but may occupy your server's threads and consumes computing resources
(CPU, memory). Some attackers scan your web site prior an attack measuring
the duration it takes to answer the request in order to find out which one are
the "most expensive" HTTP requests (consuming most time while being processed
by your web server).
The following is a step-by-step checklist about potential configuration
options you want to apply to improve your Apache server's DoS resistance.
It shall give you an idea about which parameter you might want to set, to
build an additional protective layer against DoS attacks. Nevertheless,
I recommend you to read the detailed configuration options of each command
which you intend to use to learn all about the different possibilities.
Note: If you are under an attack, it's no a question whether someone
is blocked or not, but who. mod_qos helps your
web server to make the right decision. Your job is to set the thresholds
matching your infrastructure. You might either decide to have these
rules active all the time or just as part ot your incident response plan.
Basic Settings
Hardware is inexpensive. Today's CPUs offers many cores allowing you to run
many threads in parallel and prices for memory are as low as never before.
So please allow your Apache server to serve many requests / TCP connections in
parallel by setting a "high" value for its MaxClients resp. MaxRequestWorkers
parameter (and the corresponding ServerLimit /ThreadsPerChild
values). I'm going to use 896 in the following examples. This is not
an absolute value. Its probably not even a particular "high" value if you
are fighting against a low-bandwidth DoS attack. Maybe it is fine
for a quad-core Intel CPU serving several thousand concurrent users
(assuming you are running the Apache server as your web tier only and
not using more than half of the possible connections in the normal
case - the upper half is only a reserve for exceptional situations, better
if you have even more reserves as over-provisioning may be really helpful)
but you might choose a different value suitable for your environment
(note that each tread requires about 1 to 2 MB of memory). Just adapt
all other values mentioned on this page accordingly.
Other important Apache base settings concern timeouts.
One is the TimeOut
directive defining the idle timeout while waiting for data on the network
socket, the other directive is
KeepAliveTimeout , telling the Apache
server for how long to wait for a subsequent request before closing an idle
connection. Keep-Alive is an important feature to accelerate your web server
but you should disable it if the server runs out of free connections.
Disabling Keep-Alive will give more users a chance to connect to your
server and send a HTTP request if your server becomes too busy and don't
has any free slots anymore. mod_qos's QS_SrvMaxConnClose directive allows you to disable Keep-Alive
in such a situation automatically.
# maximum number of active TCP connections is limited to 896 (limited
# by the available memory, adjust the settings according to the used
# hardware):
MaxClients 896
# idle timeout (while the server is waiting for TCP packets):
TimeOut 5
# keep alive (enabled, but only until 80% of all connections are busy):
KeepAlive on
KeepAliveTimeout 2
MaxKeepAliveRequests 40
QS_SrvMaxConnClose 80%
|
Connections per IP
There is no reason to allow a single IP address to open an unlimited number of
TCP connections. The QS_SrvMaxConnPerIP
directive can be used to limit the connections a single IP is allowed to open.
# don't allow more than 30 TCP connections per client source address
# if the server has 500 or more open connections:
QS_SrvMaxConnPerIP 30 500
|
Minimum Data Rate
The definition of a minimum upload/download throughput a client must generate
(the bytes sent/received by the client per seconds) is a very important protection
mechanism against low-bandwidth DoS attacks.
The QS_SrvMinDataRate directive
can be used to implement this rule.
Syntax: QS_SrvMinDataRate <bytes per second> <max bytes per second> <connections>
It offers three parameters. The first defines the
minimum data rate a client must achieve if the server would be idle
(no connections) and the second parameter defines the throughput
a client must achieve in addition when the server reaches its MaxClients
setting (at maximum number of connections). The third parameter defines the
number of busy connections (at low number of connections) to enable this
restriction.
# minimum request/response data reate if the server has 500 or more
# open connections:
QS_SrvMinDataRate 120 1500 500
|
You might also want to have a look at the Apache module
mod_reqtimeout
(available since Apache 2.2.15) which may be used to set various timeouts for
receiving the request headers and the request body from the client.
Repeat Offender
A very effective means of protection is the possibility to block client
IP addresses automatically for a certain period if they violate a rule
multiple times respectively if they cause errors many times.
The QS_ClientEventBlockCount
directive can be used to do this.
It defines how often a "block event" (the Apache
process environment variable
QS_Block ) may occur during a
defined period of time. This enhances the effect of the above limitations.
Incoming TCP connections are rejected if a client IP address reaches
this threshold until the limitation expires.
The QS_SetEnvIfStatus is one of mod_qos's
directive which may be used to define an event which must not occur too frequently.
- 400 405 406 408 413 414 500
Status codes which may be caused by clients sending invalid or incomplete requests.
- QS_SrvMinDataRate
Slow clients violating the QS_SrvMinDataRate rule (see above).
- QS_SrvMaxConnPerIP
Clients opening too many TCP connections, see QS_SrvMaxConnPerIP above.
- BrokenConnection
Clients closing/aborting the TCP connection before reading the the HTTP response (BrokenConnection event).
These limitations do not only increase the DoS defense efficiency. BrokenConnection in particular can be used to detect clients performing a HTTP GET/POST flood DoS attack.
The following example blocks clients if they cause more than 20 events within 5 minutes.
This is just an example of events you might want to limit. Feel free to add
more or to ignore some events.
If you want to prevent from SSL DoS attacks as well (many SSL handshakes
initiated by the client), you might also want to block clients opening TCP
connections not sending any HTTP data. These clients may be marked using the
NullConnection event.
Requests per IP
Rules, limiting a clients number of requests to a resource, may be configured
using the
QS_ClientEventLimitCount
directive. This provides an event counter per IP address and you may specify
how often a client is allowed to
trigger this event within a defined period of
time. Such a limitation might defense HTTP GET/POST flood DoS attacks,
especially if you know which requests are the most expensive ones / which
URLs provide the greatest risk of being attacked.
You may use any request attributes to distinguish between "expensive"
(resources your server takes long to process, e.g. a search function
requiring a query running against a database) and "inexpensive" (like static
resource files which can be send to the client immediately). You can also
define different attribues and configure more than one counter per IP
(each counter is idenitfied by the specified name).
One attribute could be the request's URL and you may use the
SetEnvIfPlus
directive to detect them and to increment the counter.
You can use the qslog tool
(option -pu ) to analyze log data (or just use any other log data
management tool). You should not only search your Apache server's log about
"slow requests" but also check how many requests are issued by a single
IP address to decide what limitations you want to configure (which URLs
and how often).
For the following example, lets assume that requests to static resources
(jpg,gif,css,...) are cheap (quickly processed by your server) and other
requests expensive (takes long to be processed) while the most expensive
URL path is /generateReport.php . Therefore, you won't need
to set any penalty when accessing a static resource. Other resources
are marked by "1" (allowing a client to access them 20 times within two minutes)
while requests to the "most expensive" resource
/generateReport.php are burden by a
penalty of "2" (may be accessed only 10 times within two minutes).
It is also possible that you enable this limitation only if the server
reaches a predefined number of busy connections (no limitation if it is not
necessary).
You can achieve this by deleting the event variable (SlowRequest
in this example) if the server has less connections.
The QS_AllConn
variable tells you the number of busy TCP connections and the
QS_SetEnvIf directive
allows you to set or unset variables.
The following example unset the SlowRequest variable
as long as the server has less than 499 connections.
# don't allow a client IP to access a "handler" (not a static resource like
# a jpg, gif, ..) more than 20 times within two minutes:
QS_ClientEventLimitCount 20 120 SlowRequest
SetEnvIfPlus Request_URI / SlowRequest=1
SetEnvIfPlus Request_URI /generateReport.php SlowRequest=2
SetEnvIfPlus Request_URI .*\.(jpg)|(jpeg)|(gif)|(png)|(js)|(css)$ !SlowRequest
# disable any event counting if the server has less than 499 connections:
QS_SetEnvIf QS_AllConn=^[1234]?[0-9]{1,2}$ !SlowRequest
|
Clients violating these rules may times can be blocked for an even
longer period of time. This is implemented by a second counter, called
RepeatedlySlow in this example.
Alternatively, the QS_Block variable
could be set to incremented the
QS_ClientEventBlockCount
counter in the case of a repeated rule violation (to block clients at a
connection level extending the list of events defined
above).
Separation
Your site might hosts multiple web applications of different importance. An
unimportant application can be prone to a HTTP GET/POST flood DoS attack.
To minimize the influence of such an application on others, you can limit the
allocable resources using the
QS_LocRequestLimitMatch or
QS_LocRequestLimit
directive. These directives allow you to limit the number of
concurrent requests
to certain URLs.
You can either separate the entire URL namespaces of different applications or you configure
QS_LocRequestLimitMatch /
QS_LocRequestLimit
rules for those URLs which are particularly vulnerable to be exploited
(requests that require a long processing time).
Country Specific Rules
Some web sites may have content that is only of regional interest, e.g., a
site whose content is written in German has probably most visitors from
countries where German is a national language. This allows you
to deny clients connecting from other countries in the case
your server runs out of free TCP connections.
# loads the GEO IP database and allows only client connections from
# Germany, Austria or Switzerland if the number of busy connections
# server reaches 700:
QS_ClientGeoCountryDB conf/GeoIPCountryWhois.csv
QS_ClientGeoCountryPriv DE,AT,CH 700
|
The geolocation database file is a CSV file containing the following
fields: the double quoted integer number
defining the first IPv4 address in a netblock, the double quoted integer number
defining the last IPv4 address in a netblock, and the double quoted ISO 3166
country code.
Example (extract) of a database file:
"176.10.86.0","176.10.87.255","2953467392","2953467903","GB","United Kingdom"
"176.10.88.0","176.10.95.255","2953467904","2953469951","BE","Belgium"
"176.10.96.0","176.10.127.255","2953469952","2953478143","CH","Switzerland"
"176.10.128.0","176.10.227.63","2953478144","2953503551","SE","Sweden"
"176.10.227.64","176.10.227.71","2953503552","2953503559","NO","Norway"
"176.10.227.72","176.10.255.255","2953503560","2953510911","SE","Sweden"
|
Prefer Known Clients
mod_qos may prefer "known" (aka
VIP) client IP
addresses in the case that too many clients access the server.
"Known" clients are those which have once been identified by
the application by setting the corresponding HTTP response header.
Such identification may happen at successful user login. If your
application does not authenticate users (anonymous access only),
you might decide to identify known/friendly IP addresses by any
other attributes, e.g., if your application can confirm that
the client interprets JavaScript or by using a captcha.
Connections from clients which are not known to
mod_qos (never marked by
the corresponding response header) are denied
if the server runs on low TCP connection resources.
mod_qos prefers clients
which communicate with the server instantaneously and fast,
and denies access for slow clients sending data irregularly,
violating other rules or loading different content types than
the majority of the other clients do. If the threshold to block
unknown clients has been reached, the clients with the "worst"
behavior are denied first.
The directive to enable this is feature called
QS_ClientPrefer . When
using this feature, you should define how to detect the "good" clients
using the QS_VipIPHeaderName
directive (as mentioned above: let your application add a special response header
if a user successfully authenticates). Alternatively, you can use the
QS_VipIPUser directive if you are using
an Apache authentication module such as mod_auth_basic to mark IP addresses
from which someone has successfully been authenticated.
It is also recommended to configure a static value for the
QS_ClientContentTypes
directive in order to avoid falsification of the reference values during an attack.
# mark an IP address from which someone has authenticated
QS_VipIPUser
# specify the threshold (busy connections) when mod_qos starts to prefer some clients:
QS_ClientPrefer 80%
# define what content type "normal" clients do access (this is just an example!):
# html css/js images other 304
QS_ClientContentTypes 40 15 150 10 40
|
Preferring known clients also increases your server's resistance against
distributed denial of service (DDoS) attacks.
You might use the QS_CondEventLimitCount
directive in addtion to configure limitations only affecting unknown
clients while known clients might continue to access your server even
the configured threshold is reached.
Note: Clients marked as VIP
can pass the restrictions defined by the
QS_SrvMinDataRate and
QS_SrvMaxConnPerIP by
default. You can use
QS_SrvMinDataRateIgnoreVIP and
QS_SrvMaxConnPerIPIgnoreVIP
if you want to change this (which is recommended in most cases).
Size Restrictions
There are two reasons why you would like to limit some request attributes
when fighting against DoS attacks. Having request line or header limitations
may lay open clients exceeding these limits exposing attackers trying
to hide themslef. The other one is that you don't want allow a client to
send too much data.
Three Apache core
and one mod_qos directives may be used to set limitations.
# limits request line, header and body:
LimitRequestLine 7168
LimitRequestFields 35
LimitRequestFieldSize 7168
QS_LimitRequestBody 102400
|
Note: These parameters needs to be adapted to fit your web server's usage.
Especially the request body size limitation.
You might want to configure this limitation depending
on the request's content type or increase the values for some URLs.
Kernel and iptables
You should also ensure to have the kernel parameters of your server adjusted
to meet the requirements of a web server (many ports, short close timeouts).
Example:
# example settings
net.core.somaxconn = 32000
net.core.netdev_max_backlog = 32000
net.ipv4.ip_local_port_range = 9999 61000
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_slow_start_after_idle = 0
|
Your firewall in front or your Apache web server also performs important
tasks related to DoS prevention - but the configuration of your FW is out of
the scope of this article. However, iptables can also be fun...
# limits the number of new connections (active after reaching the burst limit only):
iptables -A INPUT -p tcp --dport 80,443 -m limit \
--state NEW --limit 60/minute --limit-burst 250 -j ACCEPT
# limits the number of established/concurrent connections:
iptables -A INPUT -m state --state RELATED,ESTABLISHED \
-m limit --limit 50/second --limit-burst 50 -j ACCEPT
# limits the connections from a single source IP to 100:
iptables -A INPUT -p tcp --syn --dport 80,443 -m connlimit \
--connlimit-above 100 -j REJECT
|
Inform Others
You may want to inform other systems about client IP addresses which shall
be blocked, e.g., to synchronize multiple Apache instances (using the
web console), to configure
iptables rules (to apply an IP address deny list for example),
or to inform your ISP about clients which shall no
longer be routed to your site. Watch your server's
logs! The directives mentioned on this
page may write event messages using the
following identifiers when blocking clients.
|