Suexec

From DreamHost
Jump to: navigation, search

suEXEC allows Apache to run CGI programs as a different user than that of the Apache process. suEXEC significantly reduces many of the security risks associated with running CGIs. suEXEC is enabled for all DreamHost users and cannot be disabled. suEXEC performs checks on the executed CGI to ensure security for the server. It has strict permission checking that will result in a "500 Internal Server Error" if the permissions of the script are not setup correctly, and it cleans the environment by only passing through those environment variables considered safe.

Script Permissions

Because of the way suEXEC handles security, the directory in which your CGI script lives and the script itself must not be writable by anyone but the user. If it is writable, you will receive a "500 Internal Server Error". The script must also be executable. The easiest way to set permissions is with chmod:

$ chmod 755 script.cgi

Apache Module mod_env

Because suEXEC cleans the environment by wiping out all the environment variables except those deemed safe by the server's configuration, the directives provided by mod_env such as SetEnv, PassEnv, and SetEnvIf will not work as expected. Some third-party applications use environment variables for directory-specific configuration. For example, Movable Type allows you to host multiple instances of it from a single source base. You supply the path to an instance-specific configuration file via the environment variable MT_CONFIG.

Say you have foobar.cgi

#!/usr/bin/perl

use strict;
use cgi;

$ENV{FOOBAR} ||= 'default';
my $q = CGI->new;
print $q->header();
print $ENV{FOOBAR};

installed at both /home/username/yourdomain.com and /home/username/yourotherdomain.com. In your first domain's directory, you drop an .htaccess with the directive

SetEnv FOOBAR foo

and

SetEnv FOOBAR bar

into your second domain's directory. When you visit yourdomain.com/foobar.cgi in your browser, you'd expect it to print foo, and when you visit yourotherdomain.com/foobar.cgi, you'd expect bar. Instead, default would be printed both times.

At first, you might assume mod_env is not installed or that the SetEnv directive is not allowed from .htaccess. In fact, SetEnv is working just fine. It is setting the environment variable; suEXEC just isn't passing it to your CGI script. On the one hand, if you're developing your own application, you can just keep this in mind while developing it and not design your application to use non-standard environment variables. If, on the other hand, you are using a third-party application and modifying the source code would be prohibitively difficult and/or time-consuming (or if you just gotta have those custom environment variables in the application you're developing yourself), there are a few workarounds.

Workaround A: Prepend HTTP_ to Variable Name

The way DreamHost has suEXEC configured, it allows any environment variable that begins with HTTP_ through. So, if you change your .htaccess to SetEnv HTTP_FOOBAR foobar and use $ENV{HTTP_FOOBAR} in your CGI script, it will work as expected.

This workaround is probably best suited to applications you're developing yourself. Digging through the source code of third-party applications (especially large applications, which may have hundreds of thousands of lines) and changing each instance of $ENV{FOOBAR} to $ENV{HTTP_FOOBAR} would be both prohibitively complex and time-consuming, not to mention you'd have to redo all that work every time you upgraded the application. This solution is a bit hacky, but it works.

Workaround B: Edit the CGI Script to Set the Variable Itself

You can place $ENV{FOOBAR} = 'foo'; at the top of your script. Like Workaround A, this workaround would be best suited to applications you're developing yourself. Its usefulness would be limited even then though. The purpose of using environment variables is to alter the behavior of your script based on the context (or environment) its executed from. If you set the variable from within the script itself, the behavior of the script will be the same regardless of the context its executed from.

Workaround C: Create a Wrapper Script

Create wrapper.cgi.

#!/bin/sh

export FOOBAR=foo
exec /home/username/yourdomain.com$SCRIPT_URL

If you just have one CGI script you want to set an environment variable for, you can change the last line of the wrapper to exec /home/username/yourdomain.com/foobar.cgi. If you need to set an environment variable for dozens or even hundreds of files of a third-party application (such as Movable Type), $SCRIPT_URL allows you to determine which script was requested and set the environment variable for that script.

Then use .htaccess to redirect requests for the script to wrapper.cgi.

RewriteEngine On
RewriteRule ^application/.*\.cgi /wrapper.cgi
  • application is the directory where your (third-party or in-house developed) app lives.
  • If you're only setting an environment variable for one (or a few) CGI scripts, you can specify the script in the RewriteRule (e.g. RewriteRule ^script.cgi /wrapper.cgi). Just add additional RewriteRules for additional scripts.
  • As you can imagine, just continuing to add RewriteRules past a few scripts would become unwieldy. Then it would be best to match all files ending with a specific file extension. By matching only files ending with .cgi, .pl, .py, .rb, etc., static files such as HTML documents and images are unaffected.
  • If your wrapper script is in the same directory as the scripts you want wrapped and you're matching by pattern (this doesn't matter if your matching a specific file name), your wrapper must have a different file extension than your scripts. For example, if your wrapper ends in .cgi, you might want to use .pl (RewriteRule .*\.pl wrapper.cgi) or .py (RewriteRule .*\.py wrapper.cgi) for your scripts.
  • wrapper.cgi can call a script outside your domain's directory. This is actually what you probably want to do. That way the wrapper in both /home/username/yourdomain.com and /home/username/yourotherdomain.com can refer to the same script(s) in /home/shared.

Workaround C is a kludge but is suitable for a far wider range of cases than either Workaround A or Workaround B. Unlike Workaround A, you can set an environmental variable for hundreds (or thousands!) of files by editing just two files (wrapper.cgi and .htaccess). And if setting an environmental variable for a third-party application, you don't have to modify any files when you upgrade the application. You can have multiple wrapper scripts, so unlike Workaround B, the context (or environment) can actually alter the behavior of the script(s). Just copy wrapper.cgi and .htaccess from yourdomain.com to yourotherdomain.com, edit wrapper.cgi and change export FOOBAR=foo to export FOOBAR=bar, and voilà!

See Also

External Links