Block HTTP traffic based on XFF IP behind ELB

iptables can only work with IP and we cannot make it use the values from a HTTP header

OS : Amazon Linux

#yum install mod_security
#vim /etc/httpd/conf.d/mod_security.conf

Following rule is added to block Traffic [ELB used]:

SecRule REQUEST_HEADERS:X-Forwarded-For "@Contains 11.222.333.44" "phase:1,log,deny,id:1001"

Following rule is added to block traffic [ELB not used]

SecRule REMOTE_ADDR "^111.222.333.444" "phase:1,log,deny,id:1004"
#/etc/init.d/httpd restart

XFF IP is used since the server is behind ELB.

Similarly following rule can be used to Whitelist an IP:

SecRule REQUEST_HEADERS:X-Forwarded-For "@Contains 111.222.333.444" phase:1,log,allow,ctl:ruleEngine=off,id:10012

The rule above disables ModSecurity scanning for the IP address 111.222.333.444

htaccess for Apache

OS -> Ubuntu

Install apache

#apt-get install apache2

Enable rewrite_module

#a2enmod rewrite

Create the htaccess file

vim /var/www/unni/.htaccess

AuthUserFile /var/www/unni/.htpasswd
AuthGroupFile /www.null
AuthName "Authorization Required"
AuthType Basic
require user abc

Create the htpasswd encrypted to store user credentials

htpasswd -c /var/www/unni/.htpasswd abc

Make an entry in the apache virtualhost file for this directory

vim /etc/apache2/sites-enabled/000-default

<Directory "/var/www/unni">
AllowOverride AuthConfig
</Directory>

Setting Prefork parameters – Apache

80/20 Rule of Thumb : Overall performance depends on – 80% Application Tunning and 20% Infra Tuning

Apache Default configuration on Amazon Linux

IfModule prefork.c>
StartServers       8
MinSpareServers    5
MaxSpareServers   20
ServerLimit      256
MaxClients       256
MaxRequestsPerChild  4000

Lets Load Test with ab tool

#ab -n 100 -c 30 http://ec2-public-domain-name/

Result with Default settings

Concurrency Level:      30
Time taken for tests:   103.505 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      8985594 bytes
HTML transferred:       8935090 bytes
Requests per second:    0.97 [#/sec] (mean)
Time per request:       31051.358 [ms] (mean)
Time per request:       1035.045 [ms] (mean, across all concurrent requests)
Transfer rate:          84.78 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      238  254   8.1    254     272
Processing: 16920 27832 4341.7  28074   36874
Waiting:    16318 26355 4130.9  26518   35428
Total:      17166 28086 4343.4  28318   37144

The server by luckily did not produce any failed requests.

Server should not be swapping

In order to suit that rule, Apache should not try to allocate more RAM than available. Let’s get the average memory used by an Apache thread (don’t forget to relaunch ab load during calculation) :

ps aux | grep 'http' | awk '{print $6/1024;}' | awk '{avg += ($1 - avg) / NR;} END {print avg " MB";}'

Output = 86 MB

Server says that 86 Mb of RAM are used for every dynamic request (we aren’t testing static files there).

Get the RAM used by the system and other processes :

ps aux | awk '{sum1 +=$4}; END {print sum1}'

Remaining system usage of memory = 63 MB

So :

MaxClients = (Total RAM – System RAM) / RAM per Apache thread
MaxClients = (3754 – 63 ) / 86 = 42 ie ~40

I’ll keep a little margin and consider 40 to be a much more suited value.
So let’s configure Apache with our new values :

StartServers       8
MinSpareServers    5
MaxSpareServers   20
ServerLimit      256
MaxClients       40
MaxRequestsPerChild  4000

Relaunch the tests and get these results :

Concurrency Level:      30
Time taken for tests:   105.033 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      8588139 bytes
HTML transferred:       8539995 bytes
Requests per second:    0.95 [#/sec] (mean)
Time per request:       31509.752 [ms] (mean)
Time per request:       1050.325 [ms] (mean, across all concurrent requests)
Transfer rate:          79.85 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      238  256  11.5    255     288
Processing: 15610 28706 5467.0  28298   39657
Waiting:    14150 27296 5346.7  27130   38121
Total:      15857 28962 5471.6  28539   39944

Inference : The Time has increased!
Loading time is really bad. Let’s reconfigure Min/Max servers according to MaxClients :

StartServers       11
MinSpareServers    11
MaxSpareServers    42
ServerLimit      256
MaxClients       40
MaxRequestsPerChild  0

New results are a bit better :

Concurrency Level:      30
Time taken for tests:   98.985 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      8468700 bytes
HTML transferred:       8421500 bytes
Requests per second:    1.01 [#/sec] (mean)
Time per request:       29695.580 [ms] (mean)
Time per request:       989.853 [ms] (mean, across all concurrent requests)
Transfer rate:          83.55 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      237  254   9.5    254     277
Processing: 15206 27228 5040.1  28182   36289
Waiting:    14425 25832 4829.0  26688   34533
Total:      15462 27482 5041.7  28430   36566

Inference : Time has decreased.

Server Details : m1.medium AWS Instance with Magento installed.

SOURCE : http://nls.io/how-do-i-calculate-apache-maxclients/

PHP-FPM Tuning : http://nls.io/optimize-nginx-and-php-fpm-max_children/