Apache configuration with mod_macro

After my brief brush with Lighttpd, I found that it couldn’t handle group authentication. So, instead, I decided to rewrite my Apache configuration, using mod_macro to obtain a more compact configuration. In the process, I discovered that I actually wanted that from the start, and only went to Lighttpd because it appeared to offer a simpler way to that. After a little poking around with it, its templating capabilities turned out to be quite weak compared to that of a properly equipped Apache.

The short version: My configuration is now 50% smaller, even with the addition of an extra virtual host or two. mod_macro is really kinda cool.

If you don’t know, mod_macro is an Apache module that lets you define macros. Astounding and surprising, isn’t it? In web programming, you’d be more correct in calling them templates: a named chunk of configuration directives, in which you can perform limited variable expansion. As a simple example, here is a macro that restricts access to a path:

<Macro RestrictAccess $url $userfile $username>
  <Location $url>
    AuthType basic
    AuthName "Restricted access"
    AuthUserFile $userfile
    Require user $username
  </Location>
</Macro>

You then invoke this macro and pass it parameters to inject into the template:

Use RestrictAccess /private /srv/www/.htpasswd dave

Using this mechanism, you can dramatically reduce the size of your apache configuration, especially with several virtual hosts with redundant statements. I used macros to do just that. Let’s construct a set of macros to aid us in configuring virtual hosts.

First of all, let’s standardize and fold away all the tedious boilerplate of virtual hosts: defining the server name, the logging configuration, and default access directives.

<Macro DocrootLoggingAccess $name $vhost_dir>
  ServerName $name
  ServerAdmin dave+httpd_$name@...
  DocumentRoot ${APACHE_WWWROOT}/$vhost_dir
  CustomLog ${APACHE_LOGROOT}/$vhost_dir/access.log combined
  ErrorLog ${APACHE_LOGROOT}/$vhost_dir/error.log
  LogLevel warn
  ServerSignature off
  <Directory ${APACHE_WWWROOT}/$vhost_dir>
    AllowOverride None
    Order allow,deny
    Allow from all
  </Directory>
</Macro>

Note the use of APACHE_WWWROOT and APACHE_LOGROOT variables. These are environment variables, which Debian’s apache package helpfully lets us define in /etc/apache2/envvars. Debian uses this mechanism to define things like the PID file path, which gets pushed into the configuration, but also gets included by debian helper scripts, like the start-stop daemon. I subverted the mechanism to define my own global configuration variables:

export APACHE_WWWROOT=/srv/www
export APACHE_LOGROOT=/srv/logs/www

This lets me define paths relative to these global roots, which makes the configuration look much nicer. With this macro alone, a virtual host for serving static files becomes as simple as:

<VirtualHost *:80>
  Use DocrootLoggingAccess static.natulte.net natulte.net/static
</VirtualHost>

That will serve the content of /srv/www/natulte.net/static on http://static.natulte.net . Don’t try it, it was just an example, I’m not actually serving anything there :-)

I won’t go over the other macros I have in detail, but here is a quick overview:

# Configure a FastCGI dynamic instance to handle all .php files through PHP 5
Use PHP5

# Serve the Trac environment 'nxos' under URL path /nxos, via FastCGI.
# Logins to the environment should use authentication data for
# domain natulte.net, group 'nxos-dev'
#
# This macro makes use of an APACHE_TRACROOT envvar.
Use Trac nxos /nxos natulte.net nxos-dev

# Serve a read-only copy of the nxos mercurial repositories under /hg
#
# Uses the APACHE_HGROOT envvar.
Use MercurialReadOnly nxos /hg

# Same as above, but authenticate write access using natulte.net's
# authentication data (access to specific repositories is configured in
# each repo directly, apache needs only to identify potentially acceptable
# users).
Use Mercurial nxos /authenticated-hg natulte.net

# Configure the given URL path as a Dokuwiki instance (directory access and
# rewrite rules).
Use Dokuwiki /wiki

# Activate /server-status and /server-info on this vhost. Useful for
# debugging broken macro expansions.
Use ServerStatus
Use ServerInfo

# Small helper template: given a domain name, include configuration directives
# for HTTP Basic authentication using user/group data global to that domain.
# Reused in all the above templates that do authentication.
Use AuthData natulte.net

I won’t bore you with any more gruelling details. Suffice it to say that, with this set of macros, the configuration for, say, nxt.natulte.net goes from a 50 line monstrosity of boilerplate and weird FastCGI directives, to:

# Notice how I can still add snippets of configuration. I just
# don't have boilerplate rubbish any more!
<VirtualHost *:80>
  Use DocrootLoggingAccess nxt.natulte.net natulte.net/nxt
  Use TracEnv nxos /nxos/trac natulte.net nxos
  Use MercurialReadOnly nxos /nxos/hg

  <Location /nxos/docs>
    Options Indexes
  </Location>
</VirtualHost>

Thanks to mod_macro, I’m happy with my web server configuration for the first time in a while. It’s as code should be: compact, common elements refactored, no duplication, and above all, clear and easy to understand.