Apache 2.4 access control

How to optimise Apache configuration by understanding the default configurations.

A customer has an AWS EC2 server running two websites on Apache 2.4: a static public HTML for corporate website and a Ruby on Rails app meant for internal use. These two are configured using Apache VirtualHost with the RoR running using Passenger.

There are two issues that the customer would like us to fix:

  • The static public HTML site has directory listing enabled although they had not enabled it in the VirtualHost directive.
  • The RoR app is accessible from public and they would like to restrict it to certain IP ranges.

This was the original configuration in the conf.d/vhost.conf file:

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName app.example.com
    DocumentRoot /var/www/railsapp/public
    PassengerRuby /usr/bin/ruby

    <Directory /var/www/railsapp/public>
        Allow from all
        Options -MultiViews
        Require all granted
        RailsEnv production
    </Directory>
    ...
</VirtualHost>

<VirtualHost *:443>
    ServerName www.example.com    
    DocumentRoot /var/www/static

    <Directory "/var/www/static">
        AllowOverride All
        Require all granted
    </Directory>
    ...
</VirtualHost>
</IfModule>

Why directory listing was enabled

According to documentation the default for Options is FollowSymlinks if the directive is not set, and the directory listing should not be enabled.

On checking, I found that the customer has edited conf/httpd.conf as below:

#
# DocumentRoot: The directory out of which you will serve your
# documents. By default, all requests are taken from this directory, but
# symbolic links and aliases may be used to point to other locations.
#
DocumentRoot "/var/www/static"

#
# Relax access to content within /var/www/static.
#
<Directory "/var/www/static">
    AllowOverride None
    # Allow open access:
    Require all granted
</Directory>

# Further relax access to the default document root:
<Directory "/var/www/static">
    #
    # Possible values for the Options directive are "None", "All",
    # or any combination of:
    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
    #
    # Note that "MultiViews" must be named *explicitly* --- "Options All"
    # doesn't give it to you.
    #
    # The Options directive is both complicated and important.  Please see
    # http://httpd.apache.org/docs/2.4/mod/core.html#options
    # for more information.
    #
    Options Indexes FollowSymLinks

    #
    # AllowOverride controls what directives may be placed in .htaccess files.
    # It can be "All", "None", or any combination of the keywords:
    #   Options FileInfo AuthConfig Limit
    #
    AllowOverride None

    #
    # Controls who can get stuff from this server.
    #
    Require all granted
</Directory>

Thus the problem is that Options was actually specified in conf/httpd.conf. This comes from the default configuration when installing Apache. The customer had merely edited the Directory path. This could be an easy fix by just commenting the Options line but I find the entire chunk above necessary. This is because both VirtualHost directives are already using wildcard (*) IP matching which means it will definitely match one of them. As per the documentation, the match will be the first listed virtual host if there are no matching ServerName or ServerAlias. Hence I removed the entire chunk in conf/httpd.conf and moved the VirtualHost section of static website above the RoR instead.

Apache 2.2 versus 2.4 access control

I also reviewed the directives in the VirtualHost configurations and found that there are some unnecessary directives:

  • The first is that Allow from all is from Apache 2.2 and deprecated in Apache 2.4. The replacement is Require all granted.
  • Next is the line Options -MultiViews. It is unnecessary because the Options directive default is set to FollowSymLinks from 2.3.11 onwards. Furthermore MultiViews must be explicitly enabled. There is no need to disable it if it wasn't enabled anywhere.
  • Lastly I removed AllowOverride All since there is no .htaccess file in the static website.

Restricting Ruby on Rails access by IP

To limit access to RoR by IP addresses, I used the AllowOverrideList as it is more specific. The directive AllowOverride is set to None by default in version 2.3.9 onwards and therefore not necessary to include in the configuration.

The edited conf.d/vhost.conf is as below:

<IfModule mod_ssl.c>
<VirtualHost *:443>
    ServerName www.example.com    
    DocumentRoot /var/www/static

    <Directory "/var/www/static">
        Require all granted
    </Directory>
    ...
</VirtualHost>

<VirtualHost *:443>
    ServerName app.example.com
    DocumentRoot /var/www/railsapp/public
    PassengerRuby /usr/bin/ruby

    <Directory /var/www/railsapp/public>
        AllowOverrideList Require
        Require all granted
        RailsEnv production
    </Directory>
    ...
</VirtualHost>
</IfModule>

And all that is needed now is .htaccess to be placed in the public folder of the RoR app:

Require ip x.y.z.0/24