Simplifying repetitive Apache configuration with mod_macro
Apache is one of the more flexible webservers, and most of this is achieved via the use of various extension modules bundled with it, or externally available. mod_macro is a great module allowing you to simplifying configuration of multiple virtual hosts enormously.
Some of the available Apache modules, such as as mod_rewrite, are well-known, but others seem to be little-loved. In my opinion mod_macro is one module that should be much more popular than it seems to be!
The goal of mod_macro is two-fold:
- To allow you to define macros.
- To allow you to expand previously defined macros.
Why might you do this? Just to cut down on configuration inside your virtual hosts. This is particularly useful if you've started using the Let's Encrypt SSL provider, for example, but even without that if you have many virtual host definitions that contain repetition it is worth taking a look at it.
Imagine you have a webserver hosting multiple sites, you probably have a bunch of files beneath /etc/apache2/sites-enabled/, each one looking like this:
<VirtualHost 1.2.3.4:443> ServerName www.example.com SSLEngine On SSLProtocol all -SSLv2 -SSLv3 SSLHonorCipherOrder On SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4" SSLCertificateFile /etc/apache2/ssl/example.com.crt SSLCertificateKeyFile /etc/apache2/ssl/example.com.key SSLCertificateChainFile /etc/apache2/ssl/le/bundle.pem Redirect / https://example.com/ </VirtualHost> <VirtualHost 1.2.3.4:443> ServerName example.com SSLEngine On SSLProtocol all -SSLv2 -SSLv3 SSLHonorCipherOrder On SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4" SSLCertificateFile /etc/apache2/ssl/example.com.crt SSLCertificateKeyFile /etc/apache2/ssl/example.com.key SSLCertificateChainFile /etc/apache2/ssl/le/bundle.pem DocumentRoot /srv/example.com/public/htdocs </VirtualHost>
Wouldn't it be simpler if you could replace those big blocks of SSL configuration with just a single line? After all the only thing that would vary across sites is the path to the certificate file(s).
mod_macro allows you to do that exact thing, and can be used pretty simply, we just need to enable the module, then define the macros we're going to use. To enable the module run:
# a2enmod macro
Then define our macros in a suitable configuration-file:
- For Apache 2.2.x – on wheezy:
- /etc/apache2/conf.d/macro.conf
- For Apache 2.4.x – on jessie:
- /etc/apache2/conf.enabled/macro.conf
Here's a simple definition of an SSL-macro:
<Macro SSL $file> SSLEngine On SSLProtocol all -SSLv2 -SSLv3 SSLHonorCipherOrder On SSLCipherSuite "EECDH+ECDSA+AESGCM EECDH+aRSA+AESGCM EECDH+ECDSA+SHA384 EECDH+ECDSA+SHA256 EECDH+aRSA+SHA384 EECDH+aRSA+SHA256 EECDH EDH+aRSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4" SSLCertificateFile /etc/apache2/ssl/$file.crt SSLCertificateKeyFile /etc/apache2/ssl/$file.key SSLCertificateChainFile /etc/apache2/ssl/le/bundle.pem </Macro>
Notice how the macro definition has the argument "$file" used in the definition, and then inline in the body? That's how you pass "variables" to your macro.
With this defined we can now simplify our previous configuration file to read:
<VirtualHost 1.2.3.4:443> ServerName www.example.com use SSL example.com Redirect / https://example.com/ </VirtualHost3> <VirtualHost 1.2.3.4:443> ServerName example.com use SSL example.com DocumentRoot /srv/example.com/public/htdocs </VirtualHost3>
My personal Apache deployment tends to have a bunch of virtual hosts which just proxy to servers running on localhost, for that I've defined the following macro:
<Macro Proxy $port> <Proxy *> Order allow,deny Allow from all </Proxy> ProxyPass / https://localhost:$port/ ProxyPassReverse / https://localhost:$port/ <Location /> Order allow,deny Allow from all </Location> ProxyPreserveHost on ProxyBadHeader ignore </Macro>
Now I can define some hosts using it like so:
# All traffic goes to 127.0.0.1:2013 <VirtualHost 80.68.84.111:80 [2001:41c8:10b:103::111]:80> ServerName blog.steve.org.uk Use Proxy 2013 </VirtualHost> # All traffic goes to 127.0.0.1:2004 <VirtualHost 80.68.84.111:80> ServerName ipv4.steve.org.uk Use Proxy 2004 </VirtualHost>
This setup is very simple to understand, use, and cuts down enormously on configuration.
For further details on using the module please do consult the Apache documentation.