Apache

This page contains information about configuring the HTTP server software Apache.

There currently are two major versions of this software: a legacy 1.3 version and the current version 2. When I started using Apache I did so with the 1.3 version; at some point I was forced to switch over to version 2. Parts of this document may therefore still contain references to the 1.3 version, or describe a feature that works only with the 1.3 version.


References

  • Official documentation
  • Details about how Debian organizes Apache2 configuration: /usr/share/doc/apache2/README.Debian.gz


System installation

Debian packages

apache2
apache2-bin
apache2-data
apache2-utils

The packages above automatically install a large number of Apache modules which in earlier days needed to be installed separately. A notable example is the SSL module. Other modules still need to be installed as a separate package (e.g. PHP).


Security

On Debian the server runs as user www-data, group www-data.


Configuration

Overview

The directory that contains all the configuration files is

/etc/apache2

The main file that includes everything else is apache2.conf. It includes other files in the following order:

# Include module configuration:
IncludeOptional mods-enabled/*.load
IncludeOptional mods-enabled/*.conf

# Include list of ports to listen on
Include ports.conf

# Include generic snippets of statements
IncludeOptional conf-enabled/*.conf

# Include the virtual host configurations:
IncludeOptional sites-enabled/*.conf


I make use of this scheme in the following way:

  • Global configuration that must be present in all virtual hosts is in the following file. Note the "000" prefix, which makes sure that the file is loaded as early as possible so that it can set up defaults that can be overridden later on.
/etc/apache2/conf-available/000-pelargir.conf
  • SSL configuration that is the same for all virtual hosts that are subdomains of a given domain. These files are meant to be included by a vhost. For instance, the vhost for www.herzbube.ch would include pelargir-herzbube.ch-vhosts-ssl.conf.
/etc/apache2/conf-available/pelargir-herzbube.ch-vhosts-ssl.conf
/etc/apache2/conf-available/pelargir-grunzwanzling.ch-vhosts-ssl.conf
  • LDAP configuration if a directory requires HTTP authentication. This file is meant to be included inside a "Directory" directive.
/etc/apache2/conf-available/000-pelargir-ldap.conf
  • Drupal configuration. This file is meant to be included by each virtual host that is a Drupal site. See the Drupal wiki page for more information.
/etc/apache2/pelargir-drupalsites.conf
  • Virtual host configuration files in
/etc/apache2/sites-available


Note: The following command line utilities can be used to enable/disable various configuration snippets:

  • Modules: a2enmod / a2dismod
  • Configuration snippets: a2enconf / a2disconf
  • Virtual host configurations: a2ensite / a2dissite


Default access rights

My policy for default access rights can be roughly summarized like this:

  • By default disable and deny everything
  • Enable and allow only those things that are explicitly needed


This policy was formulated at a time when the default Debian configuration was more relaxed. With the introduction of Apache 2.4, the default Debian configuration was made much stricter so that it effectively is the same as my policy. The only folders that Debian allows access to are the folders /usr/share and /var/www, which is fine by me.


Here is the relevant excerpt from /etc/apache2/apache2.conf:

# Sets the default security model of the Apache2 HTTPD server. It does
# not allow access to the root filesystem outside of /usr/share and /var/www.
# The former is used by web applications packaged in Debian,
# the latter may be used for local directories served by the web server. If
# your system is serving content from a sub-directory in /srv you must allow
# access here, or in any related virtual host.
<Directory />
        Options FollowSymLinks
        AllowOverride None
        Require all denied
</Directory>

<Directory /usr/share>
        AllowOverride None
        Require all granted
</Directory>

<Directory /var/www/>
        Options Indexes FollowSymLinks
        AllowOverride None
        Require all granted
</Directory>


# AccessFileName: The name of the file to look for in each directory
# for additional configuration directives.  See also the AllowOverride
# directive.
#
AccessFileName .htaccess

#
# The following lines prevent .htaccess and .htpasswd files from being
# viewed by Web clients.
#
<FilesMatch "^\.ht">
        Require all denied
</FilesMatch>


/etc/apache2/conf-available/000-pelargir.conf

Disable CGI and PHP

With the following config snippet I turn off CGI execution and the PHP engine everywhere:

# ============================================================
# Configure the "default" to be a very restrictive set of
# permissions. Since Apache 2.4, Debian's default configuration
# denies all access to /, so we don't have to do much here
# anymore.
# ============================================================
<Directory />
  # Disable CGIs everywhere
  Options -ExecCGI
  # Disable directory listing everywhere. Important for, at least,
  # the cgi-bin folder
  Options -Indexes
</Directory>

# ============================================================
# PHP configuration
# - by default we turn PHP execution off
# - we want to turn it back on explicitly for all packages
#   that we install
# ============================================================
php_admin_flag engine off
php_flag register_globals off


An example how to explicitly allow access and enable CGI execution and the PHP engine for a specific directory:

<Directory /path/to/directory/>
  Require all granted
  Options +ExecCGI
  php_admin_flag engine on
</Directory>


SSI configuration

Server side includes (SSI) are disabled by default. The first thing to do is to enable the module:

a2enmod include


Next I turn SSI on for .shtml files, like this:

# ============================================================
# SSI configuration
# ============================================================
AddType text/html .shtml
AddHandler server-parsed .shtml
DirectoryIndex index.shtml


Finally, any directory that wants to allow SSI needs to say something like this:

<Directory /path/to/directory>
  Options Includes
</Directory>

Note: An alternative is IncludesNOEXEC, which means that SSI are permitted, but the #exec cmd and #exec cgi are disabled.


CGI scripts in /usr/lib/cgi-bin

Various Debian packages (e.g. awstats, mailman) operate as CGI scripts in the generic directory /usr/lib/cgi-bin. Apache must be configured so that it executes these scripts correctly. I choose to do this in the following way (as usual with options that are not site-specific I place them in /etc/apache2/conf-available/000-pelargir.conf):

# ============================================================
# CGI configuration
# - /usr/lib/cgi-bin should be available as /cgi-bin for all
#   Virtual Servers
# - however, we use the normal Alias instead of ScriptAlias
#   because we don't want every file in the directory to
#   automatically become a CGI
# - instead we add a CGI handler for some file extensions
#   and allow execution of CGIs in /cgi-bin with
#   "Options +ExecCGI"
# - some larger packages may require .htaccess files for
#   restricting access to parts of the package
# ============================================================
Alias /cgi-bin/ /usr/lib/cgi-bin/

<Directory /usr/lib/cgi-bin/>
  Require all granted
  Options +ExecCGI
  AllowOverride All
  AddHandler cgi-script .cgi .sh .pl
</Directory>

Notes:

  • Debian brings its own CGI configuration snippet, which conflicts with the above configuration. I disable the Debian config like this: a2disconf serve-cgi-bin.


Security settings

Here are a few additional security settings

# ============================================================
# Security settings
# ============================================================
# Unset the HTTP header "Proxy:". Mitigates the httpoxy attack
# (https://httpoxy.org/). Requires mod_headers.
RequestHeader unset Proxy early


Documentation

If you want the documentation in /usr/share/doc to be available via HTTP:

# ============================================================
# Documentation should be available for all Virtual Servers
# ============================================================
Alias /doc/ /usr/share/doc/
<Directory /usr/share/doc/>
  Require all granted
  Options FollowSymLinks Includes Indexes
</Directory>


Note: I no longer use this. When pelargir still was a MacMini and was part of my intranet, it was possible to add a restriction to the above config snippet that would allow access to the docs only from a computer in the local network (e.g. 192.168.0.0/16). Since pelargir's reincarnation as a Dedicated Server this restriction is no longer possible because pelargir is no longer part of my intranet. Because I don't want the whole world to access documentation on my server, I leave doc access disabled permanently.


Options directive

The Options directive controls which server features are available in a particular directory. Normally, if multiple Options could apply to a directory, then the most specific one is used and others are ignored; the options are not merged. However if all the options on the Options directive are preceded by a "+" or "-" symbol, the options are merged.

  • ExecCGI is useful, but should be set only in rare cases, where it is really necessary
  • FollowSymLinks is probably the most useful setting
  • Includes is useful only where SSI are required
  • Indexes exposes too much of the system and should be applied only with care
  • MultiViews is arcane (to me); it is useful if the same resource is present in more than one way (e.g. multiple languages) and the client should be able to say which type of resource it understands/prefers

See the Apache documentation for more details.


.htaccess files

While processing a request, Apache looks for the first existing .htaccess file in every directory of the path to the document, if distributed configuration files are enabled for that directory. It is possible to completely disable .htaccess files by saying

AllowOverride None

for a given directory. It is also possible to allow only certain directives within .htaccess files - in this case AllowOverride simply lists the allowed directives. See the Apache documentation for more details.


Note: .htaccess is only a default file name; if you wish you can define additional or completely different file names through the AccessFileName directive.


Evaluation order

Directories are evaluated in the following order:

  • <Directory> and .htaccess files simultaneously, but .htaccess files can override settings on the <Directory> directive
  • Directives <DirectoryMatch> and <Directory> with RegExps
  • Directives <Files> and <FilesMatch> simultaneously
  • Directives <Location> and <LocationMatch> simultaneously
  • Order of <Directory> directives: first the shorter directories (e.g. / before /doc); if directories are the same, then the order in which statements appear in the configuration
  • all other directives: the order in which they appear in the configuration
  • VirtualHosts are evaluated after the regular configuration

Generally one can say: "last wins".


Default character set / encoding

The following config snippet allows to set a default character set / encoding which is applied by Apache to resources that have the content types text/plain or text/html:

# ============================================================
# Here we specify that the default character set for text/plain
# and text/html content types is UTF-8. I know that when I write
# a text or HTML file nowadays, I am going to use UTF-8, so I
# can't be bothered to specify the charset for each new resource
# that I might add in the future.
#
# Note: Specific resources that need this are:
# - Root dir of herzbube.ch: Has a couple of text files
# - Software directory on herzbube.ch: ChangeLog, README, etc.
# ============================================================
<Directory />
  AddDefaultCharset utf-8
</Directory>


CGI scripts

Options +ExecCGI says that - in addition to any other options - execution of CGI scripts is allowed in a certain directory. Nothing works without this directive!!!

Once CGI execution has been allowed via ExecCGI, there are various methods how to mark files as being CGI scripts:

  • ScriptAlias works exactly the same as Alias, but it additionally "blesses" the directory so that all (!) files inside the directory become CGIs.
  • AddHandler uses file extensions to mark files as CGI scripts. Example:
AddHandler cgi-script cgi pl
  • SetHandler marks all matching files as CGI scripts. This is roughly equivalent to ScriptAlias, but SetHandler is probably more flexible because it is usable in more contexts than ScriptAlias.


mod_rewrite

References

http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html
http://httpd.apache.org/docs/2.2/misc/rewriteguide.html


Overview

In order to use the module, the following directive is necessary

RewriteEngine on

The engine is turned on/off per directory, or per virtual host.

The most commonly used directive is RewriteRule. It takes 2 arguments:

  • a regular expression that is used to match an address
  • a result to which the matching address should be rewritten

Example:

RewriteRule ^/$ /index.html

Usually a RewriteRule must refer to an existing path, i.e. it cannot refer to an alias. For instance, the following does not work:

Alias /xyz/ /abc/def/
RewriteRule ^/$ /xyz/index.html

The reason for this behaviour is explained at the end of the mod_rewrite documentation:

http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#RewriteBase

Note: The directive RewriteBase can only be used inside a .htaccess file or within a <Directory> directive.


Redirects

Currently I know of 3 ways how to perform a redirect:

  • inside an .html document
<META HTTP-EQUIV="refresh" CONTENT="0; URL=http://foo.bar/service">
  • with mod_alias
 Redirect /service http://foo.bar/service
  • with mod_rewrite
RewriteRule ^/$ /phpws/index.php [R,L]

Some explanations:

  • the flag R means "Redirect"; the server will send the HTTP status code 302 ("moved temporarily"); in order to send a different status code, one needs to specify R=<statuscode>
  • the flag L means "Last", i.e. no further RewriteRules are applied after the current one


Query strings

Query strings are the part at the end of an URL that starts with a question mark character ("?"). Apache processes query strings in a special way, they are not part of the URL! For this reason, query strings cannot be matched by a RewriteRule!

Apache stores query strings in the variable QUERY_STRING. In order to test for the presence of a query string, one has to use a RewriteCond directive that processes the variable QUERY_STRING. For instance:

RewriteCond %{QUERY_STRING} ^q=redirect_url=
RewriteRule <...>

Note: The "?" character is not part of the QUERY_STRING variable.

After a RewriteRule has performed its substitution task, the original query string is appended to the result. For instance:

RewriteCond %{QUERY_STRING} ^q=redirect_url=
RewriteRule .* /foobar/ [R,L]

This replaces everything (".*") by "/foobar/" and appends the query string, thus producing the URL /foobar/?q=redirect_url.

If the substitution part of a RewriteRule already contains a new query string, this new query string replaces the original query string. For instance:

RewriteCond %{QUERY_STRING} ^q=redirect_url=
RewriteRule .* /foo/?q=bar [R,L]

This rule produces the URL /foo/?q=bar.

In order to "combine" the old with the new query string the flag QSA can be used:

RewriteCond %{QUERY_STRING} ^q=redirect_url=
RewriteRule .* /foo/?q=bar [R,L,QSA]

This rule produces the URL /foo/?q=bar&q=redirect_url=. The quey strings are joined by an ampersand ("&") character.

To entirely remove a query string, the last charachter in the substitution string of a RewriteRule must be a "?" character. For instance:

RewriteCond %{QUERY_STRING} ^q=redirect_url=
RewriteRule .* /foobar/? [R,L]

This rule produces the URL /foobar/.

To re-use only parts of a query string you have to use backreferences:

RewriteCond %{QUERY_STRING} ^q=redirect_url=(.*)
RewriteRule .* %1? [R,L]

This rule replaces the old URL by a new one. The new URL consists of that part in the old query string that lies behind "redirect_url=". %1 is the backreference to the (.*) part in the RewriteCond directive.


Virtual Hosts

Virtual Host (or vhost) configuration is available in a separate page on this wiki: ApacheVirtualHost.


SSL

Overview

In order to allow https connections, Apache needs to listen on a different port than the default HTTP port 80. The default https port is 443. Apache also needs a server certificate that it can hand out to clients (e.g. web browsers) and that these clients can use to a) verify whether the server actually is who he claims, and b) to actually encrypt communication. The page ServiceEncryptionWithSSL contains instructions how to create and sign server certificates.

The most important directives are

  • SSLEngine on turns on SSL for a specific virtual host
  • SSLCertificateFile and SSLCertificateKeyFile are used to specify the location of the certificate file and the RSA key file
  • SSLCertificateChainFile is used to specify the location of a file that contains a chain of parent certificates, up to the root certificate


SSL and name-based virtual hosts

SSL and name-based virtual hosts are on somewhat "unfriendly terms", which means that normally it is not possible to define multiple name-based hosts all on the same port 443. A good explanation of the problem can be found at http://httpd.apache.org/docs-2.0/ssl/ssl_faq.html, although it should be obvious why the problem exists if you know

  1. About protocol layers
  2. That the HTTP layer comes on top of the SSL layer, and
  3. That name-based virtual hosts are based on the HTTP Host: header


The consequence is that normally you can have only one SSL enabled virtual host on any given combination of "IP address/TCP port". Instead of "normally", I should probably say "traditionally", because these days a solution to the problem exists: A TLS extension named SNI (Server Name Indication). Wikipedia has good information about the subject. The gist is that a client that uses the SNI extension will tell Apache which server name it connects to as part of the TLS negotiation phase, i.e. before the SSL protocol layer is established. This allows Apache to choose the correct name-based virtual host, whose configuration will indicate the correct SSL certificate to serve. All modern browsers and other web clients have SNI support, so in your Apache configuration it is safe to rely on SNI.


See the page ApacheVirtualHost for more information on how I configure SSL for my vhosts.


Debian default configuration for mod_ssl

In my current configuration I rely on Debian's default configuration for mod_ssl to provide the right "global" configuration options, i.e. options that are shared by all SSL virtual hosts.

I used to have my own global SSL configuration (in /etc/apache2/conf-available/000-pelargir-ssl.conf) which would define stuff like "SSLSessionCache", "SSLRandomSeed", "SSLMutex", or mime types for certificate/CRL downloads. But why bother if the Debian experts already do the right thing for me (and they are certainly much more knowledgable than I am about these things)?

A final note about the "SSLMutex" option: In Apache versions prior to 2.4 it used to be necessary to include this mod_ssl option: "SSLMutex file:/var/run/mod_ssl_mutex". In Apache 2.4 there is now a new general-purpose directive "Mutex" that accepts as parameters 1) the mutex type; 2) a mutex name. To replace "SSLMutex" it would be necessary to add a "Mutex" directive for the mutex name "ssl-cache". However, this is not necessary because the Debian default configuration already defines the default mutex type for all mutexes, including "ssl-cache". At the time of writing this definition is located in /etc/apache2/apache2.conf and looks like this: "Mutex file:${APACHE_LOCK_DIR} default"


Per-domain certificate configuration

I am using "Let's Encrypt" wildcard server certificates which allow me to have one certificate per domain, and therefore one certificate configuration per domain. All virtual hosts for a given domain can make use of the same domain-specific certificate configuration because the wildcard certificate will be valid for all sub-domains that any vhost might use.


At the time of writing I am maintaining four different domains, so I have the following four certificate configurations:

root@pelargir:~# l /etc/apache2/conf-available/*-vhosts-ssl.conf
-rw-r--r-- 1 root root 604 Jun  8 00:12 /etc/apache2/conf-available/pelargir-francescamoser.ch-vhosts-ssl.conf
-rw-r--r-- 1 root root 600 Jun  8 00:12 /etc/apache2/conf-available/pelargir-grunzwanzling.ch-vhosts-ssl.conf
-rw-r--r-- 1 root root 580 Jun  8 00:04 /etc/apache2/conf-available/pelargir-herzbube.ch-vhosts-ssl.conf
-rw-r--r-- 1 root root 588 Jun  8 00:13 /etc/apache2/conf-available/pelargir-moser-naef.ch-vhosts-ssl.conf

Important: These configuration files must NOT be generally enabled with a2enconf - they are included by a vhost configuration when needed.


Here is an example of how a certificate configuration looks like:

root@pelargir:~# cat /etc/apache2/conf-available/pelargir-herzbube.ch-vhosts-ssl.conf 
# --------------------------------------------------------------------------------
# SSL configuration to be included from a herzbube.ch Virtual Host
# configuration. Do NOT enable this configuration with a2enconf.
# --------------------------------------------------------------------------------
SSLEngine on
SSLCertificateFile      /etc/letsencrypt/live/herzbube.ch/cert.pem
SSLCertificateKeyFile   /etc/letsencrypt/live/herzbube.ch/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/herzbube.ch/chain.pem
SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown

Here is how the certificate configuration is included:

root@pelargir:~# cat /etc/apache2/sites-enabled/www.herzbube.ch.conf 
[...]
<VirtualHost *:443>
  [...]
  Include conf-available/pelargir-herzbube.ch-vhosts-ssl.conf
  [...]
</VirtualHost>
[...]


Authentication / Authorization

References

Overview 
http://httpd.apache.org/docs/2.2/howto/auth.html
mod_auth_basic 
http://httpd.apache.org/docs/2.2/mod/mod_auth_basic.html
mod_auth_digest 
http://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html
Require directive 
http://httpd.apache.org/docs/2.2/mod/core.html#require
mod_authnz_ldap 
http://httpd.apache.org/docs/2.2/mod/mod_authnz_ldap.html


Overview

Sometimes a resource made available by the web server needs to be protected from public access. These are the configuration steps necessary to do this:

  • Generate a password file (typically named .htpasswd) that contains a list of users that are allowed to access the resource, and the users' passwords
  • Configure Apache
    • Tell Apache that authentication is required to access the resource
    • Tell Apache where the password file is
    • Tell Apache which users in the password file are authorized to access the resource
  • This configuration is done through directives that are located
    • Either inside the main server configuration files
    • Or inside an access file (typically named .htaccess); in this case the directory where the access file is located needs to be configured with "AllowOverride AuthConfig"


When a web client tries to access a protected resource the following happens:

  • The web server sends a challenge to the web client to authenticate itself
  • If the web client is a web browser, it will usually ask the user for a username and a password
  • The username/password entered is sent back to the web server
    • If simple authentication is used, the password will be sent in clear text
    • If digest authentication is used, the password will be sent as an MD5 hash
    • If the connection between web client and web server is not encrypted, the clear text password or the password hash are visible to anyone who can view network traffic between server and client
  • The web server looks up the entry in the password file that matches the username submitted by the web client
  • If there is no entry, authentication fails and access is denied
  • If there is an entry, the web server compares the entry's password to the clear text password or password hash submitted by the web client
  • If the passwords do not match, authentication fails and access is denied
  • If the passwords match, authentication succeeds
  • As the final step, the web server checks whether the user that just authenticated itself is authorized to access the resource
  • If the user is not authorized, access is denied
  • If the user is authorized, access is granted


Framework used by Apache2

Each authentication / authorization use case is governed by the following framework

  • The configuration must specify which authentication type is to be used
    • Basic authentication = mod_auth_basic module
    • Digest authentication = mod_auth_digest module
  • Both modules are considered front-ends that delegate actual authentication and authorization to one of several possible authentication providers and authorization providers
  • Authentication providers
    • mod_authn_file module: if user names and passwords are located in a password file
    • mod_authnz_ldap module: if user names and passwords are located in an LDAP directory
  • Authorization providers
    • If authentication provider was mod_authn_file, there usually is no authorization provider because usually authorization is set to succeed automatically if authentication succeeds
    • mod_authnz_ldap module: can be used to fine-tune authorization by looking up rules located in an LDAP directory


mod_authn_file

TBD


LDAP

Why not PAM?

I already have configured PAM with LDAP support, so the best thing really would be if I could tell Apache to use PAM for authentication.

  • A Debian package libapache2-mod-auth-pam used to exist. The package was based on what can be found at http://pam.sourceforge.net/mod_auth_pam/. The web site says that the module is no longer maintained, and consequently the Debian package has disappeared and is no longer present in Debian jessie.
  • Another Debian package libapache2-mod-authnz-pam has popped up. This package is based on what can be found at https://www.adelton.com/apache/mod_authnz_pam/. The website looks healthy, but at the time of writing this the Debian package has not received any updates since October 2014, i.e. for more than 1.5 years. Not a good sign.


I therefore prefer using the well-maintained Apache core module mod_authnz_ldap.


mod_authnz_ldap

Note: mod_authnz_ldap does not support digest authentication!

mod_authnz_ldap does its job in two phases:

  • Authentication phase (aka search/bind phase); in this phase mod_authnz_ldap does the following things:
    • Connect to the LDAP directory using a special "search" DN
    • Search for an entry that matches the username submitted by the web client
    • If a single unique match is found, try to bind to the LDAP server using the entry and the password submitted by the web client
    • If binding is successful, authentication is considered to be successful
  • Authorization phase (aka compare phase); in this phase mod_authnz_ldap does the following things:
    • What happens in this phase depends on whether a Require directive is present in the server configuration or .htaccess file, and what the directive specifies
    • If no Require directive exists, authorization succeeds automatically without authentication
    • Require valid-user = authorization succeeds automatically if authentication was successful; for this to work, the mod_authz_user module must be loaded
    • Require ldap-user|ldap-group|ldap-dn|ldap-attribute|ldap-filter = authorization succeeds if a search of the LDAP directory using the specified user, group, DN, attribute or general LDAP filter produces the proper results
    • Consult the docs for mod_authnz_ldap for details


LDAP connection information contains a password that needs to be protected. For this reason, I store the information in a separate file that I can protect with a more restrictive set of permissions:

root@pelargir:/etc/apache2/conf-available# l 000-pelargir-ldap.conf 
-r-------- 1 root root 1173 Jan 12 15:23 000-pelargir-ldap.conf

root@pelargir:/etc/apache2/conf-available# cat 000-pelargir-ldap.conf 
# ============================================================
# One-time definition of LDAP connection and other information.
# To make use of this in a specific <Directory> block, add the
# following stuff:
#
# <Directory /foo/>
#   AuthName "bar"
#   AuthType Basic
#   AuthBasicProvider ldap
#   Require valid-user
# </Directory>
#
# Instead of "Require valid-user", any other authorization
# statement can be used. For complex authorization logic,
# RequireAll or RequireAny blocks may be necessary.
# ============================================================
<Directory />
  AuthLDAPUrl ldap://127.0.0.1:389/ou=users,dc=herzbube,dc=ch?uid?sub?(objectClass=*)
  AuthLDAPBindDN cn=readonly-users,ou=users,dc=herzbube,dc=ch
  AuthLDAPBindPassword secret
  # Tell mod_authnz_ldap to use attribute "memberUid" to check
  # for group membership
  AuthLDAPGroupAttribute memberUid
  # Tell mod_authnz_ldap that the group's memberUid attribute
  # contains simple user names (e.g. "foo"), not the whole user DN
  # (e.g. "cn=foo,ou=users,dc=herzbube,dc=ch")
  AuthLDAPGroupAttributeIsDN off
</Directory>

Notes:

  • Surprisingly, it is impossible to use ldapi or any other means to configure AuthLDAPUrl so that it connects to LDAP via Unix domain socket file. See this Apache bugtracker issue.
  • As mentioned in the comments, a directory can now be protected by adding a few configuration options either to the main server configuration, or to a .htaccess file.
  • In earlier versions of Apache (2.2.13 and earlier) it was impossible to specify the LDAP connection information for <Directory /> and let other directories inherit the information. Instead, it was necessary to specify the LDAP connection information for each directory that one wanted to protect. Issue 45946 at issues.apache.org describes the problem. A workaround was to use Apache's Include directive to prevent duplication of connection information.


mod_ldap

mod_ldap provides the actual LDAP support required by mod_authnz_ldap as well as a caching mechanism to improve web server performance. Basically the module's defaults are OK, but I like to add an URL that allows inspection of the cache. The following configuration block in pelargir.conf achieves this:

# ============================================================
# LDAP configuration
# - The default values for mod_ldap are OK (1024 cache entries,
#   TTL for entries = 600 seconds)
# - Add a cache information URL so that the cache status can be
#   inspected
#
# Notes:
# - During testing it might be useful to reduce the longevity
#   of cache entries by saying "LDAPCacheTTL 5" (entries expire
#   after 5 seconds) or "LDAPCacheEntries 0" (cache size is zero).
# ============================================================
<Location /ldapcache-info>
  SetHandler ldap-status
  Require all granted
</Location>


PHP

Debian packages

You only need to install the version-independent package

php

This will automatically pull in the most recent available PHP package (e.g. php-7.0) and the appropriate Apache module (e.g. libapache2-mod-php7.0). There is also a version-independent Apache module package libapache2-mod-php, but that's only used for other packages to formulate dependencies.


References


php-gd

Installing the php-gd package leads to a series of other Debian packages being installed. The biggest surprise here is that some of these packages are X11 packages. The reason is that php-gd depends via the version-specific package (e.g. php7.0-gd) on libx11-6. Another dependency is libxpm4, which also pulls in libx11-6.


php.ini

PHP exists in several "versions":

  • the command line (CLI) version
  • the Apache module version
  • possibly others


Depending on the version being executed, PHP locates its configuration in different directories:

/etc/php/x.y/cli
/etc/php/x.y/apache2

Within each directory, PHP looks for a central configuration file php.ini. In addition, PHP scans the sub-directory conf.d (if it exists) and treats any file found in the sub-directory ending in .ini as a configuration file. This scheme, which is similar to the Apache configuration scheme, makes it easy to modify the PHP configuration: Simply drop a new file into the conf.d directory instead of modifying the central configuration file php.ini. For instance, Debian packages for PHP modules drop a module-specific configuration file into conf.d. Each module's configuration file then at least "activates" the module through an "extension=" statement. For instance:

pelargir:/etc/php/7.0/apache2/conf.d# cat 20-gd.ini 
; configuration for php gd module
; priority=20
extension=gd.so

Configuration files can be enabled/disabled with these command line utilities:

phpenmod
phpdismod

Here is the configuration file that I am using to set server-specific PHP configuration options:

root@pelargir:~# cat /etc/php/7.0/apache2/conf.d/99-pelargir.ini 
; This file contains server-specific changes to the default PHP
; configuration.

; PHP applications that work with date functions (e.g. Roundcube) require a
; timezone. They can determine their own timezone, or they can rely on PHP
; to provide a default timezone. If no default timezone is set in the PHP
; configuration (php.ini), PHP versions prior to 5.4 tried to guess the
; timezone by querying the OS. PHP 5.4 and later, however, no longer play
; the unreliable guessing game. The result is that PHP applications that do
; not determine their own timezone may not get a timezone if none is set up
; in php.ini, and that date functions may therefore not work properly in
; those applications. The solution is to set an explicit default timezone in
; the PHP configuration.
date.timezone = "Europe/Zurich"


Web browser problems

Sometimes you have enabled PHP for a directory, and everything seems to be configured just right, but the web browser still tries to download the .php file instead of letting the web server "execute" it. If this happens, it might help to a) click the browser's "reload" button, or b) to clear the browser's cache.


Tracing

Install Debian package

php-xdebug

This adds the xdebug PHP module. Restart Apache to activate the module

apachectl restart

Add a line such as the following to the PHP file you want to debug:

xdebug_start_trace('/tmp/foo.trace');

For more information read this article.


Upgrades

Upgrading to Apache 2

References


Personal notes

The upgrade became necessary when I wanted to use the Subversion module for Apache, for which there only exists an Apache 2 module. The Debian package for the module is libapache2-svn. Installation of the module package forced the following dependencies to be installed:

apache2
apache2-common
apache2-mpm-worker

Apache 1.3 was still installed and running on port 80. This caused Apache 2 to become installed in a de-activated state. It was also not possible to manually start the Apache 2 daemon because of this conflict. In order to be able to test the new configuration while the old daemon was still running and serving requests from the Internet, I changed the port on which Apache 2 was listening to 8080. I did this by modifying

/etc/apache2/ports.conf

I was then able to gradually change and test the Apache 2 configuration until it was up to the state I had with Apache 1.3. This involved the following steps:

  • enable various modules with the command line utility a2enmod; I took some hints about which modules need to be enabled from the module configuration of Apache 1.3 in /etc/apache2/modules.conf
  • create sub-directories in /var/log/apache2 for the log files of the various virtual hosts
  • modify the various site configuration files in /etc/awstats so that the LogFile option points to the new Apache 2 log directories
  • make sure that already installed packages that are based on Apache have their configuration updated for Apache 2
    • sometimes this was as simple as saying dpkg-reconfigure <package>; the DebConf process then detected that Apache 2 was available and prompted me for the version of Apache that I wanted to install a configuration for
    • sometimes I had to manually create a symlink, such as
ln -s /etc/gallery/apache.conf /etc/apache2/conf.d/gallery.conf
  • install the Debian package libapache2-mod-php4; this caused apache2-mpm-worker to be replaced by apache2-mpm-prefork
  • modify /etc/php4/apache2/php.ini so that it contains the following lines
extension=ldap.so
extension=mysql.so
extension=domxml.so
extension=gd.so
extension=mcrypt.so
extension=imap.so

When I was finished with the upgrade process, I reset the port in /etc/apache2/ports.conf to 80, changed the file /etc/default/apache2 to contain the string NO_START=0 and uninstalled the old Apache 1.3 package.


Upgrading to Apache 2.4

  • Upgrading the system was relatively painless, i.e. packages were installed correctly and the server started normally. All I had to do was fix the configuration, which took some time time because there were quite a few changes. All in all I did not run into any shocking failures, though, mostly because I was warned by apt-listchanges what was going to happen.
  • The main reference for upgrading to Apache 2.4 from 2.2 is this: http://httpd.apache.org/docs/2.4/upgrading.html
  • One of the main changes is that access control and authorization now follow a different model. For instance, "Allow from all" no longer exists and must be replaced with "Require all granted". Similarly, "Deny from all" must be replaced with "Require all denied", while the "Order" directive no longer exists at all (e.g. "Order deny,allow" can be removed).
  • In addition, I was also affected by these changes
    • SSLMutex is replaced by Mutex
    • NameVirtualHost no longer exists
  • Debian uses the opportunity to also make some changes
    • The "conf.d" folder no longer exists, instead configuration snippets are now stored in "conf-available" and must be enabled/disabled with a2enconf2 / a2disconf (places a symlink to the original configuration file in "conf-enabled"). This follows the same mechanism as modules and sites that can be enabled/disabled.
    • Debian now includes its own useful default configuration for mod_ssl. My own mod_ssl configuration becomes obsolete through this.
    • Debian now includes its own CGI configuration, which conflicts with my own configuration. In this case I prefer my own configuration, so I disable the Debian configuration with a2disconf serve-cgi-bin.
  • Packages that still had problems after I modified the Apache configuration
    • Mediawiki: Displayed an ugly header line at the top of each page. To fix I had to uninstall the package "php-wikidiff2".
    • phpLdapAdmin: Unusable because the current package version contains a PHP error (Fatal error: Cannot redeclare password_hash() in /usr/share/phpldapadmin/lib/functions.php on line 2225)


Troubleshooting

Address already in use

The following error message on startup

(98)Address already in use: make_sock: could not bind to address [::]:443

means that the daemon tries to bind to the same address/port several times. I encountered this problem during an upgrade where the "Listen 443" directive appeared twice, once in /etc/apache2/ports.conf and once in /etc/apache2/conf.d/pelargir-ssl.conf


NameVirtualHost *:80 has no VirtualHosts

When the message "NameVirtualHost *:80 has no VirtualHosts" appears on startup, there may be two up to two causes for the problem:

  • the directive "NameVirtualHost *:80" appears multiple times in the configuration
  • the VirtualHost "*:80" does not appear anywhere in the configuration; a VirtualHost "*", i.e. without port number, is not sufficient