httpd.conf
via virtual hosts. To do so, I planned to
use suEXEC.
Apache suEXEC is a feature of the Apache Web server. It allows users to run CGI and SSI applications as a different user - normally, all web server processes run as the default web server user (often wwwrun, Apache or nobody). The suEXEC feature consists of a module for the web server and a binary executable which acts as a wrapper. suEXEC was introduced in Apache 1.2 and is often included in the default Apache package provided by most Linux distributions.
If a client requests a CGI and suEXEC is activated, it will call the suEXEC binary which then wraps the CGI scripts and executes it under the user account of the server process (virtual host) defined in the virtual host directive.
Additionally, suEXEC perform a multi-step check on the executed CGI to ensure security for the server (including path-checks, a limit of permitted commands, etc.)
It took me quote some time to get it working, but eventually I succeeded.
I verifed that Apache on the system was compled with suexec support.
httpd -V | grep suexec -D SUEXEC_BIN="/usr/sbin/suexec"
I checked the suexec settings on the system.
# suexec -V
-D AP_DOC_ROOT="/var/www"
-D AP_GID_MIN=100
-D AP_HTTPD_USER="apache"
-D AP_LOG_EXEC="/var/log/httpd/suexec.log"
-D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
-D AP_UID_MIN=500
-D AP_USERDIR_SUFFIX="public_html"
# ls -l /usr/sbin/suexec
-r-s--x--- 1 root apache 14264 Aug 30 12:32 /usr/sbin/suexec
httpd.conf
file:
SuexecUserGroup jdoe jdoe
I also added the line below to the same virtualhost section:
ScriptAlias /cgi-bin/ "/home/jdoe/public_html/examplesite/cgi-bin/"
I then needed to copy the php-cgi
file located in
/usr/bin
in the user's cgi-bin
directory and
verify that the ownership of the file was set for the user's account.
[jdoe@frodo cgi-bin]$ pwd /home/jdoe/public_html/moonwillowsrealm/cgi-bin [jdoe@frodo cgi-bin]$ cp /usr/bin/php-cgi . [amy@frostdragon cgi-bin]$ ls -l php-cgi -rwx------ 1 jdoe jdoe 2872768 Feb 21 22:57 php-cgi
I placed a PHP file, whoami.php, and a Perl
file whoami.pl, in the cgi-bin
directory to use for testing. Both files were owned by the user's
account and had the group set to the user's group. The .php file had
permissions of 644 and the .pl file had permissions of 700.
Whenever I checked whoami.php in the
cgi-bin
directory, I saw the following:
uid=48(apache) gid=48(apache) groups=48(apache)
Whenever I used whoami.pl, I saw the following:
Internal Server Error
The server encountered an internal error or misconfiguration and was unable to complete your request.
When I checked the error log file for the website, I saw the following:
[Fri Feb 11 21:45:26 2011] [error] [client 72.45.13.244] suexec policy violation: see suexec log for more details
[Fri Feb 11 21:45:26 2011] [error] [client 72.45.13.244] Premature end of script headers: whoami.pl
When I checked the /var/log/httpd/suexec.log
, I saw the
following entries corresponding to times I attempted to access whoami.pl
[2011-02-11 21:45:26]: uid: (501/jdoe) gid: (501/501) cmd: whoami.pl
[2011-02-11 21:45:26]: command not in docroot (/home/jdoe/public_html/examplesite/cgi-bin/whoami.pl)
From the error message I realized the problem was due to the fact that
suexec had been compiled to use /var/www
for
AP_DOC_ROOT
as could be seen by issuing the command
suexec -V
[Note: suexec -V
can only be run as
root].
# suexec -V -D AP_DOC_ROOT="/var/www" -D AP_GID_MIN=100 -D AP_HTTPD_USER="apache" -D AP_LOG_EXEC="/var/log/httpd/suexec.log" -D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin" -D AP_UID_MIN=500 -D AP_USERDIR_SUFFIX="public_html"
Any program that will be run by suexec must reside within suEXEC's document
root. On this system, user's websites are within
/home/username/public_html
. So I needed the document root to
be /home
, instead. Unfortunately, the only way to change an
suexec option is to recompile the software.
So I followed the instructions found at
Setting up suEXEC for changing the value of AP_DOC_ROOT
.
I first determined the RPM for Apache that was installed on the system
with rpm -qi httpd
. You can also get that information
by using httpd -V
.
# httpd -V | grep "Server version" Server version: Apache/2.2.3
Since the system was running CentOS, I downloaded the approprate source RPM from http://mirror.centos.org/centos/5/updates/SRPMS/. Update: (2012-02-21) Check the readme.txt file at this location for the current location of SRPMS files. On February 21, 2012, the SRPMS are at http://vault.centos.org/5.7/updates/SRPMS/. The latest SRPM file as of February 21, 2012 for CentOS 5.7 is httpd-2.2.3-53.el5.centos.3.src.rpm. You would need to get the appropriate file for whatever distribution and version of Linux you are using.
Update for CentOS 7 on 2015-07-25: For CentOS 7, the source file for
Apache 2.4.6 can be found at
CentOS Mirror. I checked the version of the httpd package on the system
and found it was 2.4.6-31.el7
.
# rpm -qi httpd | grep "Source RPM"
Source RPM : httpd-2.4.6-31.el7.centos.src.rpm
There were httpd-2.4.6-18.el7.centos.src.rpm
and
httpd-2.4.6-19.el7.centos.src.rpm
at the CentoS Mirror site,
vault.centos.org. I downloaded the "-19" one.
I then extracted the files from the source package using
rpm -ivh package
.
# rpm -ivh httpd-2.2.3-43.el5.centos.3.src.rpm 1:httpd warning: user mockbuild does not exist - using root warning: group mockbuild does not exist - using root
I received a lot of "warning: group mockbuild does not exist - using root" messages, but those weren't of concern to me.
With CentOS 5.7, I found the source
files in /usr/src/redhat/SOURCES
. Within that directory, I
found the file httpd-2.2.3.tar.gz
. I unzipped and untarred
it. I then made the working directory the one created when I extracted
the files from the .rpm.gz file.
I used the following command to extract the files with CentOS 5.7:
# tar -zxvf httpd-2.2.3.tar.gz # cd httpd-2.2.3
With CentOS 7, there was no /usr/src/redhat/SOURCES
directory,
but I found the file I needed, which was a .bz2 file in
/root/rpmbuild/SOURCES/
, instead. With the bz2 file on CentOS 7,
I had to subsitute a "j" for the "z" in the command to extract the contents of
the .bz2 file:
[root@localhost temp]# ls /root/rpmbuild/SOURCES/httpd-2.4.6.tar.bz2 /root/rpmbuild/SOURCES/httpd-2.4.6.tar.bz2 [root@localhost temp]# cp /root/rpmbuild/SOURCES/httpd-2.4.6.tar.bz2 . [root@localhost temp]# tar -jxvf httpd-2.4.6.tar.bz2
I needed to edit the suexec.h
file, so I looked for it
and found it within the support
subdirectory.
# find . -name suexec.h ./support/suexec.h
I then looked for the location where AP_DOC_ROOT
was set
within suexec.h
and found the following:
/*
* DOC_ROOT -- Define as the DocumentRoot set for Apache. This
* will be the only hierarchy (aside from UserDirs)
* that can be used for suEXEC behavior.
*/
#ifndef AP_DOC_ROOT
#define AP_DOC_ROOT DEFAULT_EXP_HTDOCSDIR
#endif
I changed the define AP_DOC_ROOT
line so it read as follows:
#define AP_DOC_ROOT "/home"
Since the default value in suexec.h
for
HTTPD_USER
I had seen when
I ran suexec -V
earlier was not what it
was for suexec on the system, I changed that value as well, so it would be
as it was before for the system when I recompiled.
/*
* HTTPD_USER -- Define as the username under which Apache normally
* runs. This is the only user allowed to execute
* this program.
*/
#ifndef AP_HTTPD_USER
#define AP_HTTPD_USER "www"
#endif
I changed #define AP_HTTPD_USER "www"
to be
#define AP_HTTPD_USER "apache"
, since that was the user under
which Apache was running on the system and was the value I had seen when
I ran suexec -V
.
I also changed the value for AP_UID_MIN
from the default
value in suexec.h
to the one that was present for suexec on
the system.
/*
* UID_MIN -- Define this as the lowest UID allowed to be a target user
* for suEXEC. For most systems, 500 or 100 is common.
*/
#ifndef AP_UID_MIN
#define AP_UID_MIN 100
#endif
I changed the value for AUP_UID_MIN
from 100
to 500
.
I also needed to change the variable that points to the location of suexec's log file.
/*
* LOG_EXEC -- Define this as a filename if you want all suEXEC
* transactions and errors logged for auditing and
* debugging purposes.
*/
#ifndef AP_LOG_EXEC
#define AP_LOG_EXEC DEFAULT_EXP_LOGFILEDIR "/suexec_log" /* Need me? */
#endif
I changed the value of AP_LOG_EXEC
to be
"/var/log/httpd/suexec.log"
, so it matched the file where
logging was shown when I issued the suexec -V
command when
I was running CentOS 5.7. I didn't see any value for AP_LOG_EXEC
when I ran the command on the CentOS 7 system:
# suexec -V -D AP_DOC_ROOT="/var/www" -D AP_GID_MIN=100 -D AP_HTTPD_USER="apache" -D AP_LOG_SYSLOG -D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin" -D AP_UID_MIN=500 -D AP_USERDIR_SUFFIX="public_html"
Note: this value is derived from the include/ap_config_layout.h
,
so it could also be changed there.
I then ran configure
. Note: the instructions at
Setting up
suEXEC suggest using ./configure --prefix=/usr/local/apache2
,
but Apache isn't installed in /usr/local/apache2
on a CentOS
system. Instead, it is installed in /usr/sbin
.
# ./configure
Note: run ./configure
from the top-level directory in which
you extraced the contents of the .gz file or .bz2 file, not the
support
subdirectory where suexec.h
is
located.
On the CentOS 7 system, when I ran ./configure
, I encountered
the error message "configure: error: APR not found. Please read the
documentation." I was able
to resolve the problem by downloading Apache Portable Runtime Project
files.
I then ran make suexec
. Note: If later you change any of the
options in suexec.h
and need to recompile suexec again,
you will need to use make --always-make suexec
to have any
subsequent changes incorporated after running make suexec
the
first time.
# make suexec
I then had a new binary version of suexec in the support
subdirectory with AP_DOC_ROOT
now set to /home
.
I then needed to change permissions on the suexec file thst was just created.
# chmod 4510 support/suexec
I replaced the existing version of suexec
in /usr/sbin
with the new version.
[root@frodo httpd-2.4.6]# cp support/suexec /usr/sbin/suexec
cp: overwrite `/usr/sbin/suexec'? y
[root@frodo httpd-2.4.6]# ls -l support/suexec
-r-s--x---. 1 root root 35347 Jul 25 22:17 support/suexec
[root@frodo httpd-2.4.6]# support/suexec -V
-D AP_DOC_ROOT="/home"
-D AP_GID_MIN=100
-D AP_HTTPD_USER="apache"
-D AP_LOG_EXEC="/usr/local/apache2/logs/var/log/httpd/suexec_log"
-D AP_SAFE_PATH="/usr/local/bin:/usr/bin:/bin"
-D AP_UID_MIN=500
-D AP_USERDIR_SUFFIX="public_html"
I noticed that the AP_LOG_EXEC
path was
/usr/local/apache2/logs/var/log/httpd/suexec_log
, though
I had the following line in support/suexec.h
:
#define AP_LOG_EXEC DEFAULT_EXP_LOGFILEDIR "/var/log/httpd/suexec_log"
I found the following line in include/ap_config_layout.h
beneath
the directory where I had extracted the files in the .bz2 file:
#define DEFAULT_EXP_LOGFILEDIR "/usr/local/apache2/logs"
Since there is no /usr/local/apache2
directory on the
system, I modified that line as shown below, instead:
#define DEFAULT_EXP_LOGFILEDIR "/var/log/httpd/"
I then changed the AP_LOG_EXEC
line in
support/suexec.h
to be as follows:
#define AP_LOG_EXEC DEFAULT_EXP_LOGFILEDIR "suexec_log"
I then reran ./configure
and make suexec
.
Since when I had previously checked for suexec support within Apache, I
had found it was already loaded, I didn't have to modify the Apache
etc/httpd/conf/httpd.conf
file by adding
LoadModule suexec_module modules/mod_suexec.so
to
include suexec support.
httpd -V | grep suexec -D SUEXEC_BIN="/usr/sbin/suexec"
And I had already added SuexecUserGroup username groupname
to the virtualhost section for the website in Apache's httpd.conf
file.
But when I attempted to access a Perl script in the website's cgi-bin directory, I got a "500 Internal Server Error" message with "The server encountered an internal error or misconfiguration and was unable to complete your request."
When I checked the website's error log, I saw the following:
[Sun Feb 13 16:54:12 2011] [error] [client 72.45.13.244] (13)Permission denied: exec of '/usr/sbin/suexec' failed
[Sun Feb 13 16:54:12 2011] [error] [client 72.45.13.244] Premature end of script headers: whoami.pl
I checked the suexec file's permissions again and found what what appeared to be the source of the problem:
# ls -l /usr/sbin/suexec
-r-s--x--- 1 root root 29343 Feb 12 22:31 /usr/sbin/suexec
I then realized that the group for suexec previously had been apache, so I changed it from root to apache.
# chgrp apache /usr/sbin/suexec
Attempting to access the Perl script again resulted in the same error, so I restarted apache, but then saw another error.
# apachectl restart Warning: SuexecUserGroup directive requires SUEXEC wrapper
Looking at the permissions on suexec again, I saw that they had been
reset to 710
. I changed the permissions to what was needed
and restarted Apache.
# ls -l /usr/sbin/suexec -r-x--x--- 1 root apache 29343 Feb 12 22:31 /usr/sbin/suexec # chmod 4510 /usr/sbin/suexec # ls -l /usr/sbin/suexec -r-s--x--- 1 root apache 29343 Feb 12 22:31 /usr/sbin/suexec # apachectl restart
But the "500 Internal Server Error" message reappeared when I tried accessing the Perl script again. Checking the website's error log file, I saw the following:
[Sun Feb 13 19:21:09 2011] [error] [client 72.45.13.244] suexec policy violation: see suexec log for more details
[Sun Feb 13 19:21:09 2011] [error] [client 72.45.13.244] Premature end of script headers: whoami.pl
Checking the /var/log/httpd/suexec.log
file, I saw the
following:
[2011-02-13 19:21:09]: uid: (501/jdoe) gid: (501/501) cmd: whoami.pl
[2011-02-13 19:21:09]: directory is writable by others: (/home/jdoe/public_html/examplesite/cgi-bin)
Checking the permissions on the cgi-bin
directory, I saw
the following:
$ ls -ld cgi-bin drwxrwxr-x 2 jdoe jdoe 4096 Feb 13 15:51 cgi-bin
I changed the permissions to be 700
.
$ chmod 700 cgi-bin
But that resulted in a "403 Forbidden" message with "You don't have permission to access /cgi-bin/whoami.pl on this server." when I atttmpted to access the webpage for the Perl script via a browser.
I changed the permissions on the directory to 711
and the
permissions on the whoami.pl
file to be 700
. I
was then able to access the Perl script, after correcting a coding error in
it, in the cgi-bin
directory,
which was defined in the virutalhosts section for that particular website
in httpd.conf
with ScriptAlias /cgi-bin/
"/home/jdoe/public_html/examplesite/cgi-bin/"
.
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
ScriptAlias /cgi-bin/ "/home/jdoe/public_html/examplesite/cgi-bin/"
ServerAdmin webmaster@example.com
DocumentRoot /home/jdoe/public_html/examplesite
ErrorLog /home/jdoe/public_html/examplesite/logs/example-error.log
CustomLog /home/jdoe/public_html/examplesite/logs/example-transfer.log common
SuexecUserGroup jdoe jdoe
</VirtualHost>
The Perl script, whoami.pl, which had a userid
and groupid that matched the user's account, had permissions 700
and showed that it was running under the user's account. The script
whoami.pl
contained the following lines:
#!/usr/bin/perl -w
print "Content-type: text/html\n\n";
print "<html><head><title>Whoami Perl Test</title></head>\n<body>\n";
system ("id -a");
print "</body>\n</html>"
It produced the following output showing me that Apache was running under the user's account for the relevant website:
uid=501(jdoe) gid=501(jdoe) groups=501(jdoe),509(test)
A whoami.php file I put in the same directory with similar code, produced the same output.
The code for whoami.php
was as follows:
<html>
<body>
<?php
system("id -a");
?>
</body>
</html>
Note: When I much later upgraded Apache on the system, I had to go
through the same steps for recompiling suexec
again and
replacing the one in usr/bin
with the one I created after
again modifying suexec.h
.
References: