Cover V12, I02

Article

feb2003.tar

Managing IP Access Restrictions

Peter Dintelmann

Configuring IP access restrictions with Apache is simple -- just load the mod_access module and put a few Allow or Deny directives in your httpd.conf config file:

[httpd.conf]
<Location /restricted/area/>
    Order Deny,Allow
    Deny from all

    # localhost is for testing
    Allow from 127.0.0.1

    # Tina's workstation
    Allow from 192.168.10.8

    # Robert's workstation
    Allow from 192.168.10.17
</Location>
This provides a simple means to protect resources. Requests from hosts, which are not permitted for this Location, will receive an 403 (Forbidden) error.

After entering some IP addresses into the config file, you will notice that it is a good idea to separate access restrictions from your main server configuration.

Mod_perl lets you configure Apache using Perl code, which allows for great flexibility. You must load the mod_perl module in httpd.conf and put your code inside a <Perl> directive. The values of your global variables with names matching Apache's configuration directives are fed into Apache's configuration mechanism. A detailed discussion of how to configure Apache with Perl can be found in Writing Apache Modules with Perl and C by Doug McEachern and Lincoln Stein (O'Reilly & Associates, Inc., 1999).

Thus, our Allow directives from above may be rewritten as:

[httpd.conf]
<Location /restricted/area/>
    Order Deny,Allow
    Deny from all

    # localhost is for testing
    Allow from 127.0.0.1

    <Perl>
        @Allow = ( ["from", "192.168.10.8"],   # Tina's workstation
                   ["from", "192.168.10.17"],  # Robert's workstation
                 );
    </Perl>
</Location>
Here you see how multiple arguments can easily be passed by using array (refs). Clearly, our next step will contain something like:

@Allow = map {["from",$_]} qw(192.168.10.8 192.168.10.17);
to get rid of the repetitive "from" keyword.

Now, to put the access restrictions aside from the server configuration, we need some sort of external storage. Perl offers various ways to this end such as databases, LDAP servers, and many others. Here we use flat text files for simplicity. A database approach may be more convenient and appropriate when you need to (simultaneously) configure a load-balanced server farm.

Within a <Perl> section, the special variable $0 contains the name of the configuration file. We use this feature to locate the text file containing the IP addresses to be permitted for the <Location>.

Here is the complete code of the <Perl> section:

<Perl>
    use File::Basename;
    my $confDir = File::Basename::dirname $0;
    open my($fh), "$confDir/allow.conf" or warn $!;
    @Allow = map {/([^#\s]+)/;["from",$1]} grep !/^\s*(#|$)/ => <$fh>;
    close $fh;
</Perl>
The File::Basename module is used to determine the directory of the configuration file, which may be included into Apache's main httd.conf config file using an Include directive. We open the file named allow.conf in the same directory emitting a warning in case of a failure. You will find this warning both on the console and in the error log when starting the server. We use a lexical variable for the handle, which requires at least Perl 5.6.

The next line of code contains the complete text processing. The grep first filters out all empty lines and those starting with optional white space, followed by the # character. These lines are regarded as comments. In the remaining lines, we filter out everything from the # character to the end of line using a regular expression, and pass the result to @Allow as above.

The allow.conf file may thus look like this:

[allow.conf]
#
# @(#) allow.conf - access restrictions for /restricted/area
#
# History:  <add on top>
#           Oct 10, 2002    added Robert
#           Oct 01, 2002    created

# --- workstations ---
192.168.10.8    # Tina
192.168.10.17   # Robert
Because we use a simple wrapper around the mod_access module, you can put anything into allow.conf understood by this module. See the mod_access manpages for details. In particular, full or partial IP addresses, network/netmask pairs, and (partial) DNS names may be used. Be careful with the latter option because name lookups may be slow. If you cannot avoid them, use the name service cache daemon of your machine (e.g., see the nscd(1M) manpage for details).

This article has illustrated how the power of Perl can be used to simplify the configuration of your Apache Web server with just a few lines of code.

Reference

Doug McEachern and Lincoln Stein, Writing Apache Modules with Perl and C. O'Reilly & Associates, Inc., 1999.

Dr. Peter Dintelmann holds a PhD degree in mathematics and works for Dresdner Bank, one of Germany's major banks, where he develops and administers Web applications for financial market data. Perl is his favorite programming language.