Apache and PHP Course
Apache and PHP Course
chrooting........................................................................................................................................29
CGI.....................................................................................................................................................30
Apache and CGI.............................................................................................................................30
Improving security with suEXEC and FastCGI............................................................................31
SSL.....................................................................................................................................................32
Creating a self-signed certificate...................................................................................................32
Configuring Apache to use SSL.....................................................................................................33
Adding PHP........................................................................................................................................36
Pre-installation...............................................................................................................................36
Preparation.....................................................................................................................................36
Compiling PHP..............................................................................................................................37
A note on SELinux....................................................................................................................39
Removing PHP..............................................................................................................................39
Extensions......................................................................................................................................39
Recompiling PHP..........................................................................................................................40
1. Adding a new extension........................................................................................................40
2. Recompiling the PHP binary.................................................................................................40
Configuring PHP............................................................................................................................40
Testing PHP + MySQL..................................................................................................................42
Testing PHP's GD extension..........................................................................................................43
.htaccess files......................................................................................................................................45
Setting up authentication by username and password...................................................................45
Authorisation by group..................................................................................................................46
Rewriting URLs.............................................................................................................................46
Virtual hosts........................................................................................................................................47
Setting up jelica.com......................................................................................................................47
Setting up logging and CGI for a virtual host...........................................................................49
Allow following of symlinks....................................................................................................50
Allowing directive overrides.....................................................................................................50
Virtual host PHP configuration.................................................................................................50
The final configuration file for our virtual host.............................................................................51
Fixing localhost..............................................................................................................................52
Troubleshooting..................................................................................................................................55
Logs...............................................................................................................................................55
Status reports..................................................................................................................................55
Standard tools................................................................................................................................56
More advanced tools......................................................................................................................56
License................................................................................................................................................57
Introduction
This document outlines how to compile, install, and configure Apache and PHP on Linux. It is not a
complete manual to the process, but goes through the process step by step, explaining the decisions
to be made along the way.
We are working towards the following scenario:
A secure, custom built and configured Apache web server with support for PHP 5 (including
the MySQL and GD extensions) plus virtual hosts
Some PHP scripts to prove we can connect to the MySQL server from PHP, and that we can
use the GD graphics toolkit
A layout for virtual hosts: we're going to assume one client, with their own website at
jelica.com
A user account for the virtual host, isolated from the main Apache configuration, allowing
the user to login and edit their website
Note that I wrote these instructions based on Ubuntu, but they should be portable to other Linux
distributions. In particular, I have outlined Fedora-specific issues, as the materials were written for a
training course run using machines installed with Fedora.
Compiling Apache
Pre-compilation decisions
Which version of Apache?
1.x
Has been around for years, and is a known quantity. A safe choice.
2.x
Code is much improved, and many of the modules have been revamped. Configuration is
also more consistent, and the format for directives improved. However, some people have
reservations about using it. Although it is possible to run in a hybrid multi-process/multithread mode (using the worker MPM), many of the libraries you're likely to use with it may
not be (e.g. PHP extensions). However, under normal conditions (i.e. up to tens of thousands
of hits per day, rather than millions), this version of Apache is likely to be a better solution
than Apache 1.x.
Binary or source?
Source = more control; you can patch when you want; you can add features when you like
via package management tool (using individual components), e.g. Apt on Debian, RPM
on Fedora
via a pre-packaged stack, with optional certification and support, e.g. SpikeSource
(http://www.spikesource.com/downloads.html), Sourcelabs
(http://sourcelabs.com/?page=software&sub=amp)
Preparation
Preparing the machine you're going to install on
gcc
OpenSSL
Perl 5 allows you to use some of the support scripts like apxs (for building and installing
shared modules)
Download the source and check the archive's integrity using md5sum like this:
root@lily:/home/ell/download# md5sum httpd-2.2.2.tar.bz2
9c759a9744436de6a6aa2ddbc49d6e81
httpd-2.2.2.tar.bz2
Compare the string on the left to the MD5 hash listed on the Apache download site. They should
match. If they don't, the download has been corrupted, so do it again.
Preparation on Fedora
On Fedora, I found I needed to install the following via "Add/Remove Software":
Compiling
Unpack the tarball
Need to get apr up and running first:
cd httpd-2.2.2/srclib/apr
./configure --prefix=/opt/apache-apr
make
make install
Then apr-util:
cd httpd-2.2.2/srclib/apr-util
./configure --prefix=/opt/apache-apr-util --with-apr=/opt/apache-apr
make
4
make install
Then Apache:
cd httpd-2.2.2
./configure --prefix=/opt/apache --with-apr=/opt/apache-apr --with-apr-util=/opt/apache-apr-util
make
make install
Test:
/opt/apache/bin/apachectl start (as root)
NB you need to be root if the port Apache listens on (Listen directive) is below 1024; default
is port 80
Test by visiting http://localhost/ in a web browser
Controlling Apache
ps to see the processes Apache starts
When Apache starts, it establishes a parent process as the original user (e.g. root in our case); it then
spawns child processes to handle requests. The number of children is configurable (see later).
The PID file stores the ID of the parent process. It can be sent a variety of standard POSIX signals
to control it directly; or (better) it can be controlled through the apachectl script.
The files in the log directory are the default Apache logs, as specified by the auto-generated config.
file. error_log is useful for debugging, and at the moment contains start/stop info.; access_log
records requests served.
The apachectl script takes a variety of switches
start = start the parent process
stop (TERM signal) = tell the parent to kill its children; it does this immediately; then once
they've exited, the parent kills itself
graceful (USR1 signal) = instruct the parent process to advise the children to exit; they
allow all requests being served to complete; then they stop; then the parent stops; then the parent
restarts itself; the parent process then starts new children with the latest version of the configuration
file
graceful-stop (WINCH signal) = as graceful, but no restart after everything stops
restart (HUP signal) = this restarts its children (as in TERM), but doesn't stop the parent
process; the parent process just rereads its configuration file and carries on running
status = show short status report (NB this needs lynx installed to work, and mod_status to
be enabled)
configtest = test whether the config. file is readable and correctly formatted
Modules
Modules add extra functionality to Apache. Their functionality is managed via Apache
configuration directives; and each module makes different directives available.
Static- vs. dynamically-loaded modules?
Static = whole server + modules in one binary; slightly faster; harder to compromise as you
can't just link new modules into it; must recompile whole thing each time you update; uses
more memory
Dynamic: you need to have mod_so enabled (NB mod_perl should not be compiled as a
shared module, according to http://www.faqs.org/docs/apache-compile/apache.html)
We'll do as many as we can as dynamic modules, while keeping the core static
To see the list of modules compiled into the httpd binary:
/opt/apache/bin/httpd -l
Here's what I got:
core.c (yes - essential for the server to operate)
mod_authn_file.c (yes - essential for Basic authentication)
mod_authn_default.c (yes - essential for authentication)
mod_authz_host.c (yes - authorization by hostname/IP)
mod_authz_groupfile.c (yes - authorization by groups defined in a file)
mod_authz_user.c (yes - authorization by users defined in a file)
mod_authz_default.c (yes - essential for authorization)
mod_auth_basic.c (yes - support for Basic authentication)
mod_include.c (no - unless you need server-side includes)
mod_filter.c (no - provides filtering of resources before they are returned in the response, e.g.
zipping the response body, downsampling every image sent back from the server)
mod_log_config.c (yes - allows customisation of log output)
mod_env.c (no - unless need to set and clear environment variables for use with CGI scripts - e.g.
essential if running Ruby on Rails applications with FastCGI)
mod_setenvif.c (yes - supports a lot of other modules)
prefork.c (yes)
http_core.c (yes)
mod_mime.c (yes - allows Apache to correctly deliver content based on MIME type)
mod_status.c (no - shows server status page)
mod_autoindex.c (no - unless you want directory indexes to be shown for directories with no
index file)
mod_asis.c (no - used to send a file without appending response headers to it - so you could have a
file which contains a whole HTTP response, including headers)
mod_cgi.c (no - unless you want CGI script support)
6
mod_negotiation.c (no - it provides a method for negotiating the best content type to suit the
client's capabilities)
mod_dir.c (yes - controls the DirectoryIndex directive, used to set the default file to serve for a
directory, e.g. index.php)
mod_actions.c (no - triggers CGI scripts based on the MIME type of a resource requested - e.g. all
requests for image/jpeg are handed off to a specific CGI script)
mod_userdir.c (no - unless you want ~/public_html directories for user home sites)
mod_alias.c (yes - handles aliasing of URLs to directories)
mod_so.c (yes - shared object support for dynamic extension loading)
Disabling modules
Any modules we want turned off have to be explictly disabled with this syntax:
--disable-MODULE
For our purposes:
--disable-userdir
--disable-actions
--disable-negotiation
--disable-cgi
--disable-asis
--disable-autoindex
--disable-status
--disable-env
--disable-filter
--disable-include
BUT we can also remove the remaining modules and make them dynamically-loaded:
--disable-mod_authn_file
--disable-mod_authn_default
--disable-mod_authz_host
--disable-mod_authz_groupfile
--disable-mod_authz_user
--disable-mod_authz_default
--disable-mod_auth_basic
--disable-mod_log_config
--disable-mod_mime
--disable-mod_dir
--disable-mod_alias
Note we didn't disable a few of the modules, as we do want them statically compiled (e.g. mod_so,
which enables shared modules to be loaded)
7
Enabling modules
Extra modules we want:
ssl (support for SSL - we'll put this in statically)
setenvif (set environmental variables conditional upon modules being loaded)
headers (enable modification of request/response headers)
rewrite (for rewriting requests - used for search-engine friendly URLs, for example)
deflate (for zipping content before it is sent to client [useful if client supported gzipped
streams, e.g. Firefox])
cgi (for running CGI scripts)
The typical method (the one we'll use) is to use shared modules rather than static ones
We do this by adding this option to ./configure, with the names of the modules we want to enable:
--enable-mods-shared='setenvif headers rewrite deflate cgi'
But we will enable SSL as a static module, to ensure it is always used and to minimise the
possibility of the library being trojaned.
--enable-ssl
We can also add back in the modules which were previously statically-compiled but which we are
converting to dynamically-loaded modules:
--enable-mods-shared='authn_file authn_default authz_host authz_groupfile authz_user
authz_default auth_basic log_config mime dir alias'
mod_ldap (base module to support other modules, e.g LDAP authentication modules)
Recompiling
Recompiling a new version of Apache (given an old version already exists) isn't too arduous. There
are several things we might want to do:
9
2. /opt/apache/bin/apxs -c -i mod_echo.c
3. Edit /opt/apache/conf/httpd.conf and add these lines:
LoadModule echo_module modules/mod_echo.so
ProtocolEcho On
4. Restart Apache
11
Patching
Occasionally, between releases of Apache versions, official patches may be released for the current
version. These patches will typically implement important security updates which are too vital to
wait until the next full release. They are fairly rare, but you should check for applicable patches
before compiling.
To get the patches, go to the source download directory on one of the mirror sites, via the Apache
Downloads link. Inside the main distribution directory is a patches directory, e.g.
http://www.mirrorservice.org/sites/ftp.apache.org/httpd/patches/
This contains a series of directories with names in this format:
apply_to_2.2.0/
Inside these directories are a series of patches for each released version of Apache. To apply a
patch:
1. Download the patch file
2. Place it in the source directory for Apache
3. Apply it to your source with:
patch -s < file.patch
where file.patch is the name of the patch file you downloaded
4. configure/make/make install (see the Upgrading section above for more details about the
effect of this)
12
Configuring Apache
Default configuration
The default configuration for our compiled Apache is in /opt/apache/conf/httpd.conf. There are
other useful files containing sample configuration "fragments" in the /opt/apache/conf/extra
directory. They can be used for reference or pulled into your main config. file as they are, with little
modification.
NB this also checks the syntax of the config. file; can do this without displaying modules using:
/opt/apache/bin/httpd -t
Initial configuration
We have the option to use a single config. file OR spread it out over multiple files. Pros and cons:
We'll use a single file for the main config.; plus a separate file for the SSL config., and one for each
virtual host.
We are also writing a config. file which only works for our compiled version of Apache. The default
generated file provided as an example in the Apache distribution contains a variety of conditional
statements. These apply different configuration directives depending on the underlying operating
system, but we are going to dispense with these as much as possible to get a streamlined
configuration file.
Start with a blank config file in
/opt/apache/conf/httpd.conf
Then add:
# base of the web server install
ServerRoot /opt/apache
# name of the web server (can help prevent
# startup problems)
ServerName localhost
# email address of the administrator
# (shown in error messages)
ServerAdmin ell@localhost
# location of the root of the web server document tree
DocumentRoot /var/www/htdocs
# path to the process ID (PID) file, which
# stores the ID of the main Apache process
PidFile /var/run/apache/httpd.pid
# which port to listen on
Listen 80
# do not resolve client IP addresses to names (reduces overhead)
14
HostNameLookups Off
# effective user and group
User apache
Group apache
We will need to create the "effective user" under which Apache will run on the system. Apache will
run with the permissions of this user and group:
groupadd apache
useradd apache -g apache -d /dev/null -s /bin/false
(-d = home directory, -g = main group, -s = shell)
And we'll need to create the appropriate directories:
mkdir /var/www/htdocs (for the resources Apache will serve)
mkdir /var/run/apache (for the process file)
mkdir /var/log/apache (for logs)
There are also several directories lying around which we can safely remove:
rm -Rf /opt/apache/manual (the Apache manual)
rm -Rf /opt/apache/cgi-bin (we'll put the cgi-bin into individual host configurations)
rm -Rf /opt/apache/icons (icons used when listing the content of directories - we're not going
to be doing this anyway, so we may as well remove them)
It's necessary to keep the default logs directory, even though we're not using it directly, as some
modules use it to store files, while not providing a directive to customise the log directory location.
If it's useful you can also keep the man directory. You can access the files in this directory using the
man command like this:
man ./htdigest.1
for example, if you need to find out more about the commands Apache provides.
We can now try restarting to ensure our config. works:
/opt/apache/bin/apachectl restart
We won't be able to see our site anymore, but at least we should be able to see if the server starts
OK.
Starting/stopping automatically
Symlink into appropriate run-level
On Ubuntu, I suggest run-level 2
ln -s /opt/apache/bin/apachectl /etc/rc2.d/S85apache
15
ln -s /opt/apache/bin/apachectl /etc/rc2.d/K20apache
You also need to make sure that the network is up and the hostname set before you start the Apache
server, so a high number like 85 is suitable.
3:on
4:on
5:on
6:off
apache
apache
apache
apache
start
stop
restart
graceful
etc.
MPM settings
We also need some directives to control the activity of the MPM. For the prefork MPM (which
we're using) we can specify the following:
# number of spare servers to keep running to
# handle potential incoming requests
MinSpareServers 5
# max. number of servers to leave idling
MaxSpareServers 10
17
File layout
We've already started making decisions about how our Apache server will be laid out. Let's
consolidate this now.
Suggested directory layout:
Default web site: /var/www/htdocs (we can eventually remove this if we don't want to use it)
We're going to use a separate layout for each virtual host (to be covered later).
Although we have an apache user and group, they shouldn't own the Apache executables: these
must start as root to run on port 80; so if someone cracked the apache user account and replaced the
httpd binary with a Trojan'ed version, the next time httpd runs it would run the Trojan code as root.
/opt/apache
root:root
755
644
/opt/apache/bin
root:root
u+x
/opt/apache/build/*.sh
root:root
u+x
18
Path
/opt/apache/conf
root:root
700
/var/log/apache
root:root
700
/var/run/apache
root:root
700
/var/www/htdocs
root:root
755
/var/www/cgi-bin
root:root
755
Logging
[To enable logging, the log_config module has to be loaded first.]
Apache has two separate logs:
1. Error log
The first stop for diagnosing errors with the server; by default will also contain CGI error
output. Example message:
[Wed Oct 11 14:32:52 2000] [error] [client 127.0.0.1] client
denied by server configuration: /opt/apache/htdocs/test
Date and time of the message
Type of message
IP address of client which triggered the error
19
Error message
The level of logging is set in Apache config. using the LogLevel directive. The possible
settings are (in order of decreasing significance):
emerg Emergencies - system is unusable. "Child cannot open lock file. Exiting"
alert Action must be taken immediately. "getpwuid: couldn't determine user name from
uid"
crit Critical Conditions. "socket: Failed to get a socket, exiting child"
error Error conditions. "Premature end of script headers"
warn Warning conditions. "child process 1234 did not exit, sending another SIGHUP"
notice Normal but significant condition. "httpd: caught SIGBUS, attempting to dump
core in ..."
info Informational. "Server seems busy, (you may need to increase StartServers, or
Min/MaxSpareServers)..."
debug Debug-level messages "Opening config file ..."
Setting the LogLevel tells Apache to log all messages of that severity or higher. Setting the
LogLevel to crit, for example, will report emerg, alert and crit messages. The standard
setting is error.
The log is written to the file specified by the ErrorLog directive, which specifies the path for
the log file, e.g. ErrorLog /var/log/apache/error_log
2. Access log
This logs requests made to the server. It is set up by defining two directives:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\"
\"%{User-Agent}i\"" combined
CustomLog /var/log/apache/access_log combined
Here I am using a standard log format commonly known as "combined". Note that you can
reference any request header using the %{Header}i syntax. You can also record response
headers with %{Header}o.
%>s is the status sent in the response (e.g. 200, 404, 302). If you specify %>s, the final
status is recorded; if you specify %<s, the initial status message sent to the request is
recorded.
<IfModule log_config_module>
# access log
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\"
\"%{User-Agent}i\"" combined
CustomLog "/var/log/apache/access_log" combined
</IfModule>
Note I sneaked in a directive to load a shared module here (mod_log_config.so). This is necessary
before we can start using the directives which that module makes available in our config..
I also put the directives which depend on this module inside a conditional <IfModule> directive.
This means that if we decide to turn off this module at some point, the directives relating to it are
ignored. This makes the config. file more stable, and also makes it easier to track dependencies
between modules and directives.
error_log = '/var/log/apache/error_log'
archived_error_log = error_log + suffix
rename(access_log, archived_access_log)
rename(error_log, archived_error_log)
# do a graceful restart
call(['/opt/apache/bin/apachectl', 'graceful'])
While saving some CPU cycles, this approach also has the advantage of keeping log file names
simple (just access_log and error_log), as logrotate does. This makes configuration easier later on
(e.g. if we want multiple virtual hosts to write to the same access_log, we can just specify the
filename access_log).
The old log files are renamed by appending a date suffix onto the end of the original file name. You
could refine this by removing really old logs, or zipping the archived logs.
[NB there appears to be a bug with the graceful restart command for Apache 2.2 (it is recorded on
the Apache bug tracker), which causes an error to appear in the logs when running the above script.
However, this appears to have no effect on the server's operation.]
23
Options on directories
The Options directive covers file access configuration for individual <Directory> directives. It
24
allows you to govern features like execution of files, following symlinks, and showing indexes of
files in a directory. Here are the options available:
SymLinksIfOwnerMatch: only follow symlinks if the owner of the link is the same as the
owner of the file pointed to
IncludeNOEXEC: allows server-side includes, but prevents the exec command being used
in SSIs
Indexes: when on, the server will generate an index of files in a directory if no default
resource (liked index.html) is specified
MultiViews: allows content negotiation (i.e. serve files based on user's language preference)
25
This now becomes the default setting for any directories below the root of the filesystem, including
/var/www/htdocs.
AuthConfig
FileInfo
Indexes
Limit
Option
All
None
The Options override is probably the most confusing: if AllowOverride Options is specified, then
the default Options setting for the directory can be overridden by a .htaccess file in that directory!
You can specify which directive types can be overridden like so:
AllowOverride AuthConfig Limit
In our case, for the root directory, we don't want to allow anything to be overridden:
<Directory />
Order Deny,Allow
Deny from all
AllowOverride None
Options None
</Directory>
Try adding a .htaccess file to /var/www/htdocs, then fetch it in your web browser. It should work
OK, which isn't what we want.
We can globally turn off access to these files like this by putting a FilesMatch directive at the top
level directive of our httpd.conf file:
<FilesMatch "(^\.ht|~$|\.bak$)">
Order Deny,Allow
Deny from All
</FilesMatch>
This directive can also be applied to individual virtual hosts or directories, and can be set in
.htaccess files if AllowOverride is set to All for that directory.
Now try getting your .htaccess file. It should be protected.
There is also a DirectoryMatch directive, which can be used to prevent serving of directories whose
name matches a specified regular expression.
DefaultType text/plain
<IfModule mime_module>
# location of the MIME types configuration file
TypesConfig conf/mime.types
</IfModule>
The mime.types file maps file suffixes (.html, .php etc.) to MIME types (a MIME type just
describes the kind of content a file contains, and is used by the client to determine how to handle the
file, e.g. display in the browser, download, display in a helper application). Note that the
TypesConfig directive is implicit and doesn't have to be specified as we have here, and it will still
work. But it's worth being explicit, again to remind us of the dependency between the module and
the mime.types file in the conf directory.
There is another MIME module called mod_mime_magic, which uses hints in the file to determine
its MIME type, as well as the filename suffix. This could be helpful in cases where you have many
unusual and esoteric file types, or have files without suffixes or incorrect suffixes.
It is also possible to add your own custom MIME types on top of the default ones using mod_mime.
28
chrooting
Chroot'ing Apache is another way to add more security, by constricting Apache to running in a
specific directory. No files outside the chroot directory are accessible to Apache once running.
The traditional method for chroot'ing Apache is complex; however, mod_chroot is an easier way to
chroot Apache which keep things simple: http://core.segfault.pl/~hobbit/mod_chroot/
29
CGI
"The Common Gateway Interface (CGI) is a standard for interfacing external applications with
information servers, such as HTTP or Web servers. A plain HTML document that the Web daemon
retrieves is static, which means it exists in a constant state: a text file that doesn't change. A CGI
program, on the other hand, is executed in real-time, so that it can output dynamic information."
(from http://hoohoo.ncsa.uiuc.edu/cgi/intro.html)
suEXEC only really makes sense in a shared hosting environment with virtual hosts. It
allows execution of CGI scripts as a user different from the Apache effective user. To use it,
Apache must be compiled with suEXEC support (it isn't compiled in by default). Once in
place, a virtual host can be configured to run any CGI scripts under a different user and
group, as specified by the SuexecUserGroup directory (only allowed inside a VirtualHost
directive). suEXEC puts a stringent series of checks in place every time a CGI script is
requested, such as checking whether the suEXEC user exists, whether they have permissions
to execute the script, permissions on the directory containing the script, ownership of the
script, and so on.
FastCGI creates a separate process for CGI scripts, and allows requests for specified
resources to be routed to that process. Because the FastCGI process is persistent across
requests (so multiple requests for a CGI script can be handled by a single process), it is more
efficient than standard CGI (close to Apache module speeds). In addition, FastCGI can also
be configured to run under suEXEC, so the external FastCGI process runs as a user different
from the Apache effective user.
The only downside to FastCGI is that it is not in active development, and is widely
considered "abandoned" (for example, the README has not been updated for 2 years, and
the current version is not compatible with Apache 2.2 without manual patching of the
source).
31
SSL
SSL is a protocol for communicating securely between a client and a server. Originally developed to
secure HTTP communications, it has been extended to cover SMTP, IMAP and other protocols.
SSL is based around Public Key Cryptography. In this type of encryption, there isn't a single key (as
there is in symmetrical encryption); there are two keys, one public and one private. Anything
encrypted with the public key can be decrypted only if the private key is known; and anything
encrypted with the private key can be decrypted only with the public key.
SSL works in two phases:
1. Handshake
The server sends the client a digital certificate, which contains its public key. The certificate
can either be signed by:
1. The owner of the certificate (a.k.a. self-signed, shouldn't be trusted)
2. A private certificate authority (e.g. an organisation might create a certificate for use on
its intranet)
3. A public certificate authority (i.e. an organisation which exists only to sign certificates
and verify identities)
A certificate received by a client may have been signed directly by one of the above, or by
an intermediary between the certificate's owner and a certificate authority. The client
verifies the certificate by following the signing chain of the certificate back to its root
authority, and makes a decision to either trust or reject the certificate. Optionally, the server
may require that the client send its own certificate before it will allow communication.
If the certificate is trusted, the client and the server then negotiate the encryption protocol to
use and a set of symmetrical keys. Symmetrical keys are faster than using public key
encryption.
2. Data exchange
The client and server exchange data using the agreed symmetrical key.
There is a good document explaining the ins and outs of SSL and Apache at:
http://www.modssl.org/docs/2.8/
32
33
ls /opt/apache/conf/ssl
You should see server.key and server.crt.
4. Set permissions on the directory:
chown root.root /opt/apache/conf/ssl
chmod 700 /opt/apache/conf/ssl
5. Set permissions on the certificate and the key:
chmod 600 /opt/apache/conf/ssl/server.*
6. Make a new file to hold Apache's SSL configuration:
touch /opt/apache/conf/ssl.conf
chmod 600 /opt/apache/conf/ssl.conf
7. Make a directory to store the SSL session cache (this improves performance as it caches
session data and prevents unnecessary handshakes, e.g. if a single client creates multiple
parallel connections to the server):
mkdir /opt/apache/cache
chown root.root /opt/apache/cache
chmod 700 /opt/apache/cache
8. Put together a minimal SSL configuration in ssl.conf:
Listen 443
SSLCertificateFile conf/ssl/server.crt
SSLCertificateKeyFile conf/ssl/server.key
# switch off SSLv2 (which is flawed)
SSLProtocol All -SSLv2
# only support high-grade encryption
SSLCipherSuite ALL:!EXP:!NULL:!ADH:!LOW
# session cache: type:location(max_size)
SSLSessionCache shmcb:/opt/apache/cache/sslcache(51200)
SSLSessionCacheTimeout 300
# configuration to handle broken SSL implementation
# in IE
SetEnvIf User-Agent ".*MSIE.*" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
# configure the default site to be available over SSL
# as well as standard HTTP
<VirtualHost localhost:443>
SSLEngine on
ServerName localhost:443
DocumentRoot /var/www/htdocs
CustomLog /var/log/apache/access_log combined
ErrorLog /var/log/apache/error_log
</VirtualHost>
34
35
Adding PHP
Pre-installation
There are several choices to make:
1. Which version: 4 or 5 or both?
PHP 5 is stable, and superior to version 4 in its support for object-oriented programming. It
is also possible to run PHP 5 in version 4 compatibility mode, which should provide nearperfect support for PHP 4 scripts.
An alternative is to install both, and select the version to use as follows:
1. per-host (by setting an AddHandler directive for a whole virtual host which specifies the
PHP version to use)
2. per-directory (by setting an AddHandler directive inside a directory, either in a .htaccess
file or in httpd.conf)
3. per-file (by setting a handler for files with a specific file suffix, e.g. .php4, in httpd.conf
or .htaccess)
We are going to install PHP 5.
2. Do you want web, command line, and/or GUI?
If you don't need command line or GUI support, leave them out when compiling PHP.
3. Will it be used by untrusted users?
In situations where the server will only be used by trusted users, PHP can safely be run as a
module. In this situation, PHP runs inside the main server process, under the Apache
effective user. Where some untrusted users may be using the server to run PHP scripts, a
safer setup is to use standard CGI, CGI with an execution wrapper like suEXEC, or PHP
under FastCGI. This isolates the PHP process from Apache and is safer; it also means that
Apache is potentially faster, as it isn't also running PHP, so static file delivery may be
speeded up.
We are going to install as a module, as this is the simplest approach, and good for most
general purpose use.
Preparation
You will need the following pieces of software to compile PHP on Ubuntu:
flex
bison
autoconf
MySQL
36
Compiling PHP
Download from php.net
Compare with the md5sum (as we did for Apache)
Unpack
Connect to the unpacked directory
To compile PHP, we need to reference a couple of graphics library files. On Ubuntu, this isn't a
problem; but on Fedora (at least in version 5), the graphics libraries have non-standard names which
cause compilation to fail. We can fix this by symlinking the real graphics libraries to correctlynamed files like this:
ln -s /usr/lib/libjpeg.so.62 /usr/lib/libjpeg.so
ln -s /usr/lib/libXpm.so.4 /usr/lib/libXpm.so
Run the configure script like this:
./configure --prefix=/opt/apache/php --with-apxs2=/opt/apache/bin/apxs --with-config-filepath=/opt/apache/conf --enable-memory-limit --with-pear=/opt/apache/php/pear --withoutpgsql --with-mysql=shared --with-mysqli=shared --with-pdo-mysql=shared --with-gd=shared
--with-zlib=shared --with-freetype-dir=/usr/lib --with-xpm-dir=/usr/lib --with-jpeg-dir=/usr/lib
--with-gettext=/usr/lib
The options I've used here specify the following:
--prefix = where to install
--with-apxs2 = location of the apxs binary (for installing the PHP module into Apache)
--with-config-file-path = where the php.ini file will be
--enable-memory-limit = compile with memory limit support
--with-pear = install pear (packaging mechanism for PHP extensions)
--without-pgsql = disable support for PostgreSQL
--with-EXTENSION=shared = enable the following extensions as shared
mysql = include support for MySQL
mysqli = improved MySQL extension
pdo-mysql = enable PDO support for MySQL (PDO is a new database interface in PHP 5)
zlib = enable support for the zlib extension (stream compression support)
gd = enable PHP GD support (for image manipulation and creation)
--with-freetype-dir, --with-xpm-dir, --with-jpeg-dir = path to Freetype/XPM/JPEG handling
libraries (NB compiling against Freetype is the easiest way to enable PHP font-rendering functions
within GD)
--with-gettext = location of the GNU gettext libraries; useful for internationalisation
37
Note that there is a default set of extensions installed with PHP which is fairly sane, so we will
leave them as is. If you want to turn any of them off, use:
--disable-EXTENSION
OR
--without-EXTENSION
(use ./configure --help to work out which you'll need for a given extension)
Then run these commands to compile and install:
make
make install
Next we need to copy the recommended PHP config. file to the location where we told our
compiled PHP it would be:
cp php.ini-recommended /opt/apache/conf/php.ini
chown root:root /opt/apache/conf/php.ini
chmod 600 /opt/apache/conf/php.ini
When we ran make install, it added this line to /opt/apache/conf/httpd.conf:
LoadModule php5_module modules/libphp5.so
(If you recompile PHP and do make install, it may add another line like this to httpd.conf,
which will break Apache. You can fix it by just removing the repeated line.)
Tell Apache which files to treat as PHP scripts:
AddHandler application/x-httpd-php .php
And to treat index.php as a possible default home page when a website root is requested:
DirectoryIndex index.html index.php
To test our installation:
1. cd /var/www/htdocs
2. create a new file called info.php with this content:
<?php
phpinfo();
?>
3. Test at http://localhost/info.php
You should see a screen with information about your PHP settings, loaded modules, etc.
38
A note on SELinux
If you follow these instructions on a default Fedora install, you may find that you are unable to
restart Apache once you've installed PHP, and get an error something like:
Cannot restore segment prot after reloc: Permission denied
This can be caused if you have SELinux enabled, which is the default on Fedora. (On Ubuntu, it
isn't a problem (by default).)
The easiest way round this (I'm not going into the intricacies of SELinux policies here!) is to disable
SELinux in /etc/selinux/config, by setting:
SELINUX=disabled
Removing PHP
If for any reason you want to junk your installation, you can remove all traces of PHP like this:
1. Stop Apache
2. rm /opt/apache/php
3. rm /opt/apache/modules/libphp5.so
4. Delete or comment out this line:
LoadModule php5_module
modules/libphp5.so
in /opt/apache/conf/httpd.conf
5. The lines which set up the PHP handler and specify index.php as a DirectoryIndex can be
left in, as they won't affect Apache's operation; remove them if you really want to get rid of
PHP altogether
6. You can leave /opt/apache/conf/php.ini where it is: it won't affect Apache's operation.
7. Start Apache
NB this is one of the advantages of not spreading PHP and PEAR across the filesystem: it makes it
easy to completely strip it out of the server. I found this really useful when experimenting with
different configure switches.
Extensions
Compiling extensions as static or shared is similar to Apache.
By default, the php.ini file doesn't load extensions; you have to tell it where they are and which
ones to load.
Edit php.ini:
1. Set the directory containing PHP extension .so files:
extension_dir = /opt/apache/php/lib/php/extensions/no-debugnon-zts-20050922
2. Add one directive for each extension:
extension=mysql.so
extension=mysqli.so
39
extension=pdo_mysql.so
extension=gd.so
extension=zlib.so
If you want to check the extensions which have been compiled in as shared, have a look in the
extension_dir (defined above). You should see a .so file for each shared module.
You can also see a list of all extensions by doing:
/opt/apache/php/bin/php -m
though this doesn't discriminate between shared and static extensions.
Recompiling PHP
1. Adding a new extension
We can compile new extensions into our PHP installation using the phpize tool. This is similar to
apxs, but intended for installing PHP extensions. We'll install the mbstring extension this way:
1. Go to the PHP source tree
2. cd ext/mbstring
3. /opt/apache/php/bin/phpize
This prepares the source in the current directory for compilation as a PHP extension
4. ./configure --with-php-config=/opt/apache/php/bin/php-config
5. make
6. make install
7. Edit /opt/apache/conf/php.ini and add this line:
extension=mbstring.so
8. Check the extension is loaded using:
/opt/apache/php/bin/php -m
or by using phpinfo().
Configuring PHP
We've already checked our PHP configuration using the phpinfo() command.
The config. file consists of a bunch of directives; if the directive is commented with a semi-colon,
40
safe_mode: When safe_mode is on, PHP does a check when a script calls a function
which tries to access a file on the filesystem. If the owner of the script and the owner
of the file are different, PHP does not allow the operation. (NB this can be relaxed
using the safe_mode_gid directive.)
ii.
expose_php: turn it to "Off" if you don't want PHP to add itself to the Apache
response headers.
iii.
memory_limit: 8M is quite low, and may cause problems with certain scripts; a
setting of 64M is more realistic.
iv.
display_errors: Leave this off on a production server and log errors to a file instead.
You can turn it on in individual scripts if you need it with:
ini_set("display_errors", 1);
You should also make sure display_startup_errors is set to Off.
v.
error_log: log errors into a file, rather than displaying them in the response:
error_log = "/var/log/php/php_log"
NB log_errors must be set to On for this to work.
vi.
register_globals: set to Off. Do not turn it on: it is very dangerous and can open
vulnerabilities in poorly-written scripts.
vii.
allow_url_fopen: set to Off. If On, this allows PHP scripts to open files on remote
servers via ftp or http.
viii. magic_quotes_gpc: set this to Off. It is confusing if it's turned on, as it automatically
escapes quotes in POST data.
ix.
file_uploads: turn on if you want to globally allow file uploads via PHP scripts.
x.
enable_dl: turn this Off; if On, it allows users to load their own extensions from
within a PHP script.
xi.
session.save_path: the path to the directory into which session data is saved; set it to
/var/www/sessions
xiii. session.referer_check: set to the domain name for the Apache server; this ensures
that session cookies are only accepted if the client's referer contains this string; in our
case, we can set it to localhost.
As PHP runs as the apache user, and we have tightened access to /var/log/apache, we will setup a
separate log directory for PHP. This directory will be writeable by the apache user (/var/log/apache
isn't, for security reasons, and rather than make /var/log/apache writeable, it's better to put PHP logs
into a different, less-secure directory):
mkdir /var/log/php
chown apache.apache /var/log/php
chmod 700 /var/log/php
(We could also apply log rotation to these logs, as we did for the Apache logs.)
We also need a separate directory to save session data:
mkdir /var/www/sessions
chown apache.apache /var/www/sessions
chmod 700 /var/www/sessions
while($row = mysql_fetch_assoc($result)) {
echo $row['name'] . '<br/>';
}
?>
And a short script using PDO's MySQL functionality:
<?php
$dbh = new PDO('mysql:host=localhost;dbname=test', 'root');
foreach ($dbh->query('SELECT * FROM people') as $row) {
echo $row['name'] . '<br/>';
}
$dbh = null;
?>
header('Content-Type: image/png');
imagepng($im);
?>
You may need a different font path: use
locate ttf
or
43
44
.htaccess files
These files can be used to set local configuration for a directory (and its subdirectories). They are
commonly used to specify authentication and authorisation setup, but can also be used to set custom
handlers, rewrite rules, PHP configuration, and so on (in fact, you can set any directives enabled for
the directory, as specified by AllowOverride).
Note that any configuration you can do in a .htaccess file can also be done inside the main Apache
configuration files. If you have control over the main config. files, use them instead of doing
configuration inside .htaccess files, as it means your config. will be centralised and easier to
manage.
authn_file_module modules/mod_authn_file.so
auth_basic_module modules/mod_auth_basic.so
authz_user_module modules/mod_authz_user.so
authz_groupfile_module modules/mod_authz_groupfile.so
6. Create a data directory which will contain the configuration files for authentication:
mkdir /opt/apache/data
chown root:root /opt/apache/data
chmod 711 /opt/apache/data
7. Create the file with the user data using the htpasswd program:
/opt/apache/bin/htpasswd -c /opt/apache/data/passwords elliot
The -c switch tells the command where to create the passwords file; elliot is the user we are
creating. You will be prompted to enter a password then confirm it.
8. Create a .htaccess file in /var/www/htdocs/secure/.htaccess to protect the secure directory:
AuthType Basic
45
Authorisation by group
The above can be easily extended to do group authentication:
1. Create a groups file in /opt/apache/data/groups with this content:
administrators: elliot
2. Modify /var/www/htdocs/secure/.htaccess to authorise by group:
AuthType Basic
AuthName "Restricted Files"
AuthUserFile /opt/apache/data/passwords
AuthGroupFile /opt/apache/data/groups
Require group administrators
Rewriting URLs
To demonstrate the use of other directives in .htaccess files, let's add a rewrite rule which redirects
any request for files in the secure directory to the index page.
1. Load the rewrite module in httpd.conf:
LoadModule rewrite_module modules/mod_rewrite.so
2. Configure the secure directory for rewriting by modifying /opt/apache/conf/httpd.conf:
<Directory /var/www/htdocs/secure>
Options SymLinksIfOwnerMatch
AllowOverride FileInfo AuthConfig Limit
</Directory>
3. Add a rewrite directive to /var/www/htdocs/secure/.htaccess:
RewriteEngine On
RewriteRule .* index.php [L]
This maps any URL of the form http://localhost/secure/xxxxx to
http://localhost/secure/index.php.
4. Test in your browser.
There is a full guide to using mod_rewrite at:
http://httpd.apache.org/docs/2.2/misc/rewriteguide.html
46
Virtual hosts
[Only about 1000 virtual hosts are possible per Apache instance using the approach detailed in this
section. Beyond this limit, it is better to use an optimised solution like mod_vhost_alias instead.]
Virtual hosting allows "Running multiple websites on a single machine".
Two methods: IP-based or name-based
1. Name-based is simplest and requires fewer IP addresses (which are a scarce resource).
2. IP-based is more complex and needs one IP address for each host. For SSL sites on different
hosts, must use IP-based hosting (can't have multiple SSL sites on a single IP address).
We're going to use name-based virtual hosts.
Our aim is to keep files related to an individual virtual host in one location reserved for that host;
any core Apache log files etc. remain in a central location. This is the layout for each host:
In cases where we are using chrooting, we might also have the following:
We'll miss this last one out of our virtual host configuration, for simplicity's sake.
We are also going to store each virtual host configuration in its own configuration file, named after
the host. For example, for our jelica.com and oceanarea.com hosts, we will put their configuration
in these two files:
1. /opt/apache/conf/jelica.com.conf
2. /opt/apache/conf/oceanarea.com.conf
I am not going to cover how to setup a machine to restrict a user to their own virtual host
directories, with no access to the rest of the filesystem. (See the earlier section on chroot.)
Setting up jelica.com
1. Create the user in charge of the domain:
useradd --home /var/www/jelica.com jelicacom
2. Make the user's home directory accessible to Apache:
chgrp apache /var/www/jelica.com
chmod g+x /var/www/jelica.com
47
3. Create an htdocs directory for the user inside their home directory:
mkdir /var/www/jelica.com/htdocs
chown jelicacom:apache /var/www/jelica.com/htdocs
chmod 2750 /var/www/jelica.com/htdocs
Note that the last command also changes the sticky bit on the directory (the '2' at the start of
the argument to chmod), so that any files added to the directory end up being owned by the
apache group.
4. Make an index file for the domain in /var/www/jelica.com/index.php
5. Create the configuration file for the domain in /opt/apache/conf/jelica.com.conf
<VirtualHost *:80>
DocumentRoot /var/www/jelica.com/htdocs
ServerName jelica.com
<Directory /var/www/jelica.com/htdocs>
Order Allow,Deny
Allow from all
</Directory>
</VirtualHost>
6. Set permissions:
chmod 600 /opt/apache/conf/jelica.com.conf
7. Add the directive to make Apache attach virtual host definitions to all IP addresses of the
server:
NameVirtualHost *:80
If you had a machine with multiple IP addresses, you could just set up one or two of these to
serve virtual hosts from, e.g.
NameVirtualHost 11.12.13.14:80
8. Pull the jelica.com configuration file into httpd.conf:
Include /opt/apache/conf/jelica.com.conf
9. Create a file in /home/jelicacom/htdocs for testing called index.php
10. Add an entry to /etc/hosts to map the domain name jelica.com to the localhost IP address.
This enables to test our new virtual host without having to register the domain name etc..
127.0.0.1 jelica.com
11. Test at http://jelica.com/
12. Test user login by attempting to login via ssh:
ssh jelicacom@localhost
Make sure the logged in user ends up in the /var/www/jelica.com directory.
48
Note that log rotation will need to take the new log location into account; alternatively, you can
leave it up to users to do their own log rotation.
Put any session information into a separate sessions directory specific to this virtual host.
This prevents users from other virtual hosts spying on this host's session data.
50
These settings allow users to upload files using their PHP scripts.
Again, you will need a tmp directory for the virtual host:
mkdir /var/www/jelica.com/tmp
chown jelicacom.apache /var/www/jelica.com/tmp
chmod 2770 /var/www/jelica.com/tmp
5. One more useful trick is to enable users to create files from inside their PHP scripts. For
now, we will enable PHP to write only into the htdocs directory.
The first step is to allow the apache user to write to the htdocs directory:
chmod g+w /var/www/jelica.com/htdocs
The only issue with allowing apache to create files inside htdocs is that the files created this
way will not be editable by the virtual host's owner (in this case, jelicacom). One solution is
to add the user to the apache group:
usermod -G jelicacom,apache jelicacom
However, this could potentially give the user access to files in other virtual hosts (i.e. any
file owned by the apache group).
51
Fixing localhost
While this approach sets up jelica.com, it simultaneously destroys the configuration for localhost.
To keep our current localhost settings, we need to remove the settings specific to the localhost
website into a separate config. file called /opt/apache/conf/localhost.conf (remember to chmod to
600).
<VirtualHost *:80>
ServerName localhost
52
Order Allow,Deny
Allow from all
</Directory>
54
Troubleshooting
Occasionally, we may run into problems with our Apache and PHP configuration. Here are a few
ways to track down those problems.
Logs
The log files should be your first port of call.
/var/log/apache/error_log
This will record any Apache-specific error messages, and also some other info. (e.g. SSL
initialisation info.).
/var/log/apache/access_log
This records requests made to the web server. It is not as useful as the error_log, but can
enable you to determine what was happening when an error occurred.
/var/log/php/php_log
Contains messages specific to PHP applications.
If the Apache log files are not sufficiently verbose, you can turn up the level of error reporting in
httpd.conf by setting the LogLevel directive to "debug" (you need to restart for this to take effect).
Status reports
Apache has a status module which enables you to get some general information about the server.
However, it can be a security liability, as once the module is active, any user can add a status report
to their virtual host via a .htaccess file. So we have disabled it.
Here's a sample configuration for reference:
### server status
LoadModule status_module modules/mod_status.so
<IfModule status_module>
ExtendedStatus On
<Location /status>
SetHandler server-status
Order Deny,Allow
Deny from all
Allow from 127.0.0.1
</Location>
</IfModule>
55
This makes the status report available at http://localhost/status, but only to users on the local
machine.
For more information, see:
http://httpd.apache.org/docs/2.2/mod/mod_status.html
Standard tools
Some standard command line tools can give you a view of the system processes, which might help
you find memory hogs and other system-level problems:
top
View processes and their memory and CPU usage in real time.
ps
View a snapshot of processes currently running.
ethereal
A general network monitoring tool, which can be useful for viewing the responses returned
by Apache.
strace
This gives a low-level view of the activity taking place when a binary executes, but its
output is difficult to decipher. I've never found a need to use a tool with this level of
complexity when debugging.
As an example, you can monitor Apache startup using:
strace /opt/apache/bin/httpd -k start
This shows the files Apache attempts to read/write, and may help identify issues like
missing system libraries.
56
License
This work is licenced under the Creative Commons Attribution-NonCommercial-ShareAlike 2.5
License. To view a copy of this licence, visit http://creativecommons.org/licenses/by-nc-sa/2.5/ or
send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
If you have any corrections/comments on this text, please feel free to contact me (elliot at
moochlabs.com).
57