Friday, April 18, 2008

Apache: How to enable CGI in one user's directory

Recently I had to configure Apache HTTPD server on a Fedora 7 box to allow CGI script execution in one particular user's public_html directory, yet not allowing it for all users.

Easy, I thought. First, Apache needs to know that a certain file is a CGI script and not a regular web page. Normally CGI scripts live in cgi-bin directory and Apache knows that anything in it is a CGI script by virtue of ScriptAlias directive. But how does it know a file outside of cgi-bin directory is a CGI script? Usually, by the file's extension. To tell Apache that any file with .cgi extension is a CGI script, uncomment the following line in the default httpd.conf (or add it if it's not there at all):


AddHandler cgi-script .cgi

Now Apache knows which files are CGI scripts. So far so good, but how do we tell it that it is permitted to run CGI scripts in a certain directory? We do it with ExecCGI option. Where do we add it? In Fedora, Apache configuration is modular. Instead of adding everything to the main httpd.conf file, and then struggling with it when you upgrade Apache, we add bits of local configuration to files in /etc/httpd/conf.d directory. So, I went ahead, created a new file usercgi.conf in that directory and entered the following directives in it:

<Directory /home/cgiuser/public_html>
  Options +ExecCGI
</Directory>

To my surprise, this didn't work. Apache refused to run CGI scripts in /home/cgiuser/public_html. Giving it more thought, I realized that the files in cond.d directory are included into the main httpd.conf before most of the directives there, and the main httpd.conf has the following part regarding users' public_html directories:

<Directory /home/*/public_html>
    Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
    ...  
</Directory>

The default directive comes after my custom directive and overrides it. What can we do? There are three approaches:
  1. Brute-force approach. Modify the main httpd.conf to include our custom directive after the default directive. I don't like this though, since it's easy to loose the configuration when upgrading to a newer version of Apache.
  2. Put the user's CGI scripts in a subdirectory of public_html, say public_html/cgi-bin. Then you will be able to turn ExecCGI on this directory on with the usual directives:
    
    <Directory /home/cgiuser/public_html/cgi-bin>
      Options +ExecCGI
    </Directory>
    
    
    Why does it work for subdirectory? Because a more specific Directory directive (the one with a longer path) always overrides the one that is less specific. Therefore options set for /home/cgiuser/public_html/cgi-bin will override those set for /home/*/public_html.
  3. Use Location directive instead of Directory:
    
    <Location ~/cgiuser/>
      Options +ExecCGI
    </Directory>
    
    
    Apache always processes Location directives after all Directory directives, thus a Location directive will take precedence.

No comments: