Apache or httpd is a strong and well tested webserver.

Installation

To install the Apache web server for this hardening guide on fedora run the following command as root:

[root@localhost] ~# yum install httpd mod_evasive mod_gnutls mod_security mod_selinux -y

A few modules I need to look into (TODO):

  • mod_log_post

Security Notes

Apache starts up using root privileges initially. Dropping the privileges is highly recommended as soon as possible (which it does if alternative credentials are supplied in it's configuration).

Additionally, the Apache web server can be used to remotely execute dynamic code if a vulnerability is found with the privileges the process is running as. This could be very dangerous... The languages allowed to be executed should be chosen wisely and hardened appropriately.

User credentials can be passed through the web server so it is strongly recommended that SSL certificates be used. If SSL certificates are available there are very few reasons not to use SSL everywhere.

The only exception I have been able to come up with is for an application that is extremely latency sensitive, contains no sensitive data, and have the data verified in a different way (through data signatures like HMAC or GPG). The latter is not necessarily required.

Firewall Adjustments

By default Apache only runs on port 80 using TCP, if SSL is enabled it will also use port 443 over SSL. Additional ports can be configured by the admin.

-A SERVICES -m tcp -p tcp --dport 80 -j ACCEPT
-A SERVICES -m tcp -p tcp --dport 443 -j ACCEPT

This service is very sensitive to the flood attack rules. It is recommended these be adjusted to allow twice the maximum number of connections expected during a time frame or alternatively to allow traffic on these two ports too bypass the anti-flood rules.

Performance Notes

If you use mod_security you should note that in my testing each Apache thread increases it's resident memory size by ~27Mb using the stock configuration. For servers that need to handle a high volume of requests this may not be acceptable. If using mod_security you DEFINITELY need to adjust the ServerLimit and MaxClients (they are aliases of each other so should always be the same).

Tuning ServerLimit/MaxClients with the worker MPM

  • Start up the apache server
  • Use the ps utility to determine the size of client threads (will be in Kb)
  • Determine that max amount of RAM the apache process should be allowed to use
  • Subtract the parent processes RAM from that
  • Divide the remaining RAM by the size of the client threads, this is your ServerLimit / MaxClient value

An example of this is provided below:

[root@localhost ~]# /etc/init.d/httpd start
Starting httpd:                                            [  OK  ]
[root@localhost ~]# ps aux -u apache | grep httpd
root      2238  0.7  0.4 177132  9312 ?        Ss   10:45   0:00 /usr/sbin/httpd
apache    2240  0.0  0.2 177132  5124 ?        S    10:45   0:00 /usr/sbin/httpd
apache    2241  0.0  0.2 177132  5124 ?        S    10:45   0:00 /usr/sbin/httpd
apache    2242  0.0  0.2 177132  5124 ?        S    10:45   0:00 /usr/sbin/httpd
apache    2243  0.0  0.2 177132  5124 ?        S    10:45   0:00 /usr/sbin/httpd
apache    2244  0.0  0.2 177132  5124 ?        S    10:45   0:00 /usr/sbin/httpd
apache    2245  0.0  0.2 177132  5124 ?        S    10:45   0:00 /usr/sbin/httpd
apache    2246  0.0  0.2 177132  5124 ?        S    10:45   0:00 /usr/sbin/httpd
apache    2247  0.0  0.2 177132  5124 ?        S    10:45   0:00 /usr/sbin/httpd
root      2249  0.0  0.0 103376   832 pts/0    S+   10:45   0:00 grep --color=auto httpd
[root@localhost ~]# PARENTMEMORY=$((9312/1024))
[root@localhost ~]# CHILDMEMORY=$((5124/1024))
[root@localhost ~]# APACHERAM=1024
[root@localhost ~]# echo $(($(($APACHERAM-$PARENTMEMORY))/$CHILDMEMORY))
203

The parent process will have a running user of 'root', all of the children processes will have 'apache'. In the above example the parent process is using 9312Kb of memory or ~9Mb, the child process is using 5124Kb or ~5Mb, and we're allowing Apache to use up to 1Gb of memory (1024Mb). With that knowledge we can see that we should set ServerLimit and MaxClients to "203" each.

Configuration

This is the main configuration file. This differs quite a bit from the configuration files that come with most distributions, most notably most of the modules are disabled and sections of the config are only applicable if certain modules are loaded. This preserves compatibility with any functionality I may need in the future while removing the bloat of the modules.

To use this configuration you'll need to create the directory /var/www/html/default. Any additional domains should be created in /var/www/html with their domain name as the folder name.

This file is being included here as it will be needed most places that I run Apache. Please refer to the PHP section below for more information.

mod_evasive is a crafty little module designed to limit the impact of DoS and DDoS attacks. This won't stop them but it will help defend against them. It will also send notices to an admin if desired (it's an optional feature which can be disabled).

The one potential problem I can see cropping up is with AJAX requests. Those can come in fast and hard, and they are intentional and necessary, if that becomes a problem (you'll get a 403 response when you trip and it will last for DOSBlockingPeriod seconds) then play with the DOSPageCount setting and the DOSSiteCount setting. Alternatively you can just program your AJAX clients to understand what happened and to react accordingly.

The GnuTLS module provides SSL encryption for web requests. It's quite a bit more flexible than mod_ssl though it hasn't been audited as thoroughly. It is also considered an 'experimental' module for apache even though it's been in the wild for two years.

  • /etc/httpd/conf.d/mod_security.conf

WARNING: While this is a good module to have loaded it sextuples the amount of memory used by child processes. You will need to tune the server's performance settings accordingly. This may have a huge impact on high-traffic sites.

With that out of the way this is a very good application firewall to have as part of the defense-in-depth doctrine, though it's configuration can be a burden. The mod_security module gives your Apache Web server increased ability to inspect and process input from Web clients before it's acted on by the scripts or processes waiting for the input.

The mod_security module even lets you inspect Web server output before it's transmitted back to clients. I love this feature: it allows you to watch out for server responses that might indicate that other filters have failed and an attack has succeeded!

With that said this isn't the file you should actually be looking at. Yes this is where everything will get loaded but the stock Fedora mod_security for the most parts loads up mod_security rules in other places including the Core ModSecurity Rule Set which will get updated with the rest of your server.

The variables that you'll want to play around with are located in /etc/httpd/modsecurity.d/modsecurity_crs_10_config.conf and if you want to write your own rules in /etc/httpd/modsecurity.d/modsecurity_localrules.conf.

Some changes I'd recommend in /etc/httpd/modsecurity.d/modsecurity_crs_10_config.conf:

  • Uncomment the rule that limits argument name length to 100 characters
  • Uncomment the rule that limits the value of an argument's length to 400 characters
  • Uncomment the rule that limits the total argument length to 64000 characters
  • Review the restricted extension types

After keeping an eye on the logs for a few weeks and ensuring that there aren't any false positives, you should change the line SecDefaultAction "phase:2,pass to SecDefaultAction "phase:2,deny,log,status:403".

The mod_selinux module allows us to extend SELinux contexts into individual web applications or virtual hosts without impacting the memory usage of individual child processes (The parent process seems to use about 100Kb of memory more but this is negligible).

Since I don't use the built in apache authentication I'm limited to restrictions based on virtual hosts, which is still a rather large gain of security.

You can additionally adjust contexts based on the IP the user is connecting from.

If you enable the environment module (env_module) in apache the domain may be available for use within the application (it would be SELINUX_DOMAIN). This hasn't been tested. With PHP you may need to add the E to the string variable_order, and adjust auto_globals_jit in the php.ini file.

Changing contexts with the stock SELinux rules is denied! You will need to create a SELinux policy to allow it.

Virtual Host Contexts

To make use of this feature you need to define contexts within each of the virtual host configuration directives after enabling mod_selinux by adding the following line:

selinuxDomainVal    *:s0:c1

The trailing domain should be different for virtual host (the next one would be c2).

IP Based Contexts

You can set contexts based on the IP address being connected from by adding the following line within a Directory definition:

SetEnvIf Remote_Addr "10.13.37.(25[0-5]|2[0-4][0-9]|[1-9]?[0-9])$" SELINUX_DOMAIN=*:s0:c1

This does not exist upon the default installation and will need to be created. No certificates are automatically generated when you install the GnuTLS module, if you need them you will need to generate them yourself.

  • /etc/httpd/conf.d/welcome.conf

Delete this file, it has no use and should be removed.

Virtual Hosts

Please note that name based virtual hosts are not supported on SSL connections when using mod_ssl. They work quite well with mod_gnutls and browsers that support SNI (Server Name Indication, as described in section 3.1 of RFC3546). Compatibility with older browsers may be impacted.

Name Based

Name based virtual hosts allow you to re-use an IP to host multiple websites. This is only officially supported for unencrypted connections, however by using the GnuTLS module you can easily set this up with SSL based hosts as well. An example of how to use this can be seen in the virtual_hosts.conf file on this page.

IP Based

I don't really use these so I'm not including documentation, this section is mostly so other people referencing this documentation can be aware that they can have different virtual hosts bound to specific IP addresses rather than hostnames.

PHP

[root@localhost] ~# yum install php php-suhosin

Additional PHP packages that may be needed and that I've found very useful:

  • php-mcrypt

  • php-bcmath

  • php-ldap

  • php-snmp

  • php-pdo

  • php-mysql

  • php-pecl-xdebug

  • /etc/php.ini

The following is for production use, display_errors, display_startup_errors, mysql.trace_mode may be useful in development.

This is the config file for the suhosin extension. If an application isn't working check the logs generated by suhosin to see if it is the issue.

Basic Authentication

Sometimes it's just needed to prevent access to something for a little while, HTTP basic authentication can help! If you're using the hardened configuration above you'll need to enable a few modules to actually use it and make sure that a .htaccess file has permission to override AuthConfig. Add the following lines if they aren't already there in the section where you load modules:

LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule authn_file_module modules/mod_authn_file.so
LoadModule authz_user_module modules/mod_authz_user.so

Additionally if you want to use group based authentication add:

LoadModule authz_groupfile_module modules/mod_authz_groupfile.so

First create a user to use for the login:

htpasswd -c .htpasswd demouser

Additional users can be added by omitting the -c.

Create or append the following to a .htaccess file in the directory you want to protect:

AuthType Basic
AuthName "Some Name Describing the site"
AuthUserFile /path/to/.htpasswd
Require valid-user

That's it you'll have HTTP Basic authentication. If you want to use a group file you'll need to change the setting in the .htaccess file to match:

AuthType Basic
AuthName "Some Name Describing the site"
AuthUserFile /path/to/.htpasswd
AuthGroupFile /path/to/.htgroups
Require group demogroup

And create a group file .htgroups with the following:

demogroup: demouser someotheruser

Kerberos Authentication

Passenger

Before going through this you need to ensure that the tools for compiling ruby are installed on the system these are listed in Ruby.

Ensure that ruby is installed on the system as well as the development headers needed to compile the passenger module, this is all done as root:

yum install ruby curl-devel ruby-devel httpd-devel apr-devel apr-util-devel -y
gem install passenger
passenger-install-apache2-module

I wasn't able to get this working at the current time as there is a bug in passenger 3.0.12 that prevents it from being compiled with GCC 4.6.