Linux+Apache+MySQL+PHP HOWTO

The goal of this document is to provide assistance in building a Linux-based Apache, MySQL and PHP webserver, affectionately known as LAMP. We'll also cover the optional installation of bells and whistles including, but not limited to, secure imapd/ipop3d and cryptographic/hash libraries. Many documents assist you in building Apache with another single package, but for using more than one package the waters start to get a little murky and the instructions start to contradict.

Apache

Apache is an HTTP server designed as a plug-in replacement for the NCSA HTTP server. It fixes numerous bugs in the NCSA server and includes many frequently requested new features, and has an API which allows it to be extended to meet users' needs more easily. Apache is the most popular web server in the known universe; over half of the servers on the Internet are running Apache or one of its variants. Apache is available at http://httpd.apache.org.

You'll want to install the latest version of Apache 1.3.x. Currently, PHP support for Apache 2.x is experimental and not recommended by the PHP team. The main issue is thread safety, which requires a lot of code verification time and effort.

Next, we need to configure the source tree for Apache first before installing any of the other associated programs. You'll get a warning message about compiling with default settings; don't worry; we will do the actual configuration of apache later. This is just a preliminary configure, required just to get things going. Without this, some of your other packages won't install into the apache source tree correctly. At this stage in the game, we don't actually make the binaries as we haven't integrated all the other software bits (like PHP) yet. Simply run:

$ ./configure

Ignore any warnings you get. You'll also need to create an apache user and group if you want to run Apache as a user other than root, which is highly recommended:

# groupadd apache
# useradd -g apache -d /dev/null -s /bin/false apache

OpenSSL

OpenSSL is essentially a set of cryptographic routines and libraries for the Secure Sockets Layer (SSL) and Transport Layer Security (TLS) protocols. By itself, OpenSSL won't do much. Its chief benefit is that it provides these libraries for use by other applications which can take advantage of its functionality. OpenSSL is available at http://www.openssl.org/source/.

Note: If you have a SMP (multi-processor) machine, add the paramater threads to the config syntax line below to benefit from multi-threading support.

$ ./config --prefix=/usr --openssldir=/etc/ssl shared zlib
$ make
$ make test
# make install
# cp -fa /etc/ssl/man /usr && rm -rf /etc/ssl/man
# ldconfig -v

To run a SSL Server, you'll need to first generate your server's SSL Private Key. This is an RSA 1024 bit key. No one should have access to this file other than yourself. RSA is a public-key encryption technology developed by RSA Data Security, Inc. The acronym stands for Rivest, Shamir, and Adelman, the inventors of the technique. The RSA algorithm is based on the assumption that there is no efficient way to factor very large numbers. Deducing an RSA key therefore requires an extraordinary amount of computer processing power and time. You will never want to delete this key. To creatue it, use:

# cd /etc/ssl/certs
# openssl genrsa -des3 -out myserver.key 1024
# chmod 400 myserver.key

Next, generating your server's Certificate Signing Request:

# cd /etc/ssl/certs
# openssl -req -new -key myserver.key -out myserver.csr

Finally, you must submit the myserver.csr file to a valid Certificate Authority (CA) in order to receive your server's authorized SSL certificate. You can either submit this to a valid certificate authority (such as VeriSign or Thawte) or pretend to be your own Certificate Authority. If you plan on interacting with the rest of the world, you'll probably want to purchase a valid certificate rather than make your own. Otherwise, see Appendix 1 for additional details on becomming your own Certificate Authority. Regardless of the method you use to procure your server's SSL certificate, once you have it, store it in /etc/ssl/certs/myserver.crt with a chmod value of 400.

ModSSL

ModSSL allows you to add SSL support for Apache. Here, we will apply the ModSSL source extension and source patches to the Apache source tree. The actual installation of ModSSL is done when you install Apache. ModSSL is available at http://www.modssl.org/source/.

$ ./configure --with-apache=../apache-1.3.x \
     --with-crt=/etc/ssl/certs/myserver.crt \
     --with-key=/etc/ssl/certs/myserver.key

ModSecurity

ModSecurity is an open source intrusion detection and prevention engine for web applications. It operates embedded into the web server, acting as a powerful umbrella, shielding applications from attacks. ModSecurity is available from http://www.modsecurity.org/.

$ cp apache1/mod_security.c ../apache_1.3.x/src/modules/extra/

imapd

imapd is a POP3/IMAP server from the folks at the University of Washington. As an added bonus, it provides access via SSL if configured correctly for use with OpenSSL. It is also commonly referred to as the "c-client library". It is available for download at ftp://ftp.cac.washington.edu/imap/.

6.1 Brief Rant

Setting up imapd can be really frustrating. Actually, I take that back; setting up secure imapd is the frustrating bit. This is mostly due to the apparent lack of any customer-service talent from the folks who produce imapd. Reading the documentation is akin to an experience with a lurking singular, sinister attitude of mind. For example:

"We can NOT provide you with support in building/installing OpenSSL, or in obtaining certificates. If you need help in doing this, try the contacts mentioned in the OpenSSL README." -- docs/SSLBUILD

Never mind the vagueness with the contacts or the lack of said references in the OpenSSL README. Granted, most of what they implement, they implement well. But where it doesn't work on the client end, or where it relies on outside sources (i.e., the not us syndrome), they deflect, deny, and blame others for not following the "official IMAP standards". Given that type of tone, I have to agree with the following philosophy regarding such things:

"Take TCP for example. The TCP protocol is specified in a series of documents. If you make a formally correct implementation of the base TCP RFC you won't even make connections. Much of the flow control behaviour, the queueing and the detail is learned only by being directly part of the TCP implementing community. You can read all the scientific papers you like, it will not make you a good TCP implementor." -- Linux-kernel posting by Alan Cox

6.2 Installing imapd

Current versions of imapd are now built with TLS/SSL encryption support by default. Since imapd incorrectly assumes we installed OpenSSL in /usr/local/openssl, we need to pass on a few other arguments to our make command. Assuming you're building for a generic Linux system, you can run:

# make slx SSLDIR=/usr SSLCERTS=/etc/ssl/certs SSLINCLUDE=/usr/include/openssl
# cp -fa imapd/imapd /usr/sbin/
# cp -fa ipopd/ipop3d /usr/sbin/

Modify your mail spool permissions. We'll assume your mail spool is located in /var/spool/mail; if not, adjust the following command to fit:

# chmod 1777 /var/spool/mail

Next, update /etc/services if necessary with:

imaps 993/tcp
pop3s 995/tcp

Finally, update /etc/xinetd.conf with:

service pop3s
{
     socket_type = stream
     protocol = tcp
     wait = no
     user = root
     server = /usr/sbin/ipop3d
}

service imaps
{
     socket_type = stream
     protocol = tcp
     wait = no
     user = root
     server = /usr/sbin/imapd
}

If you're using inetd instead of xinetd, update /etc/inetd.conf with:

pop3s stream tcp nowait root /usr/sbin/ipop3d ipop3d
imaps stream tcp nowait root /usr/sbin/imapd imapd

You'll need to run a "kill -HUP inetd" or "kill -HUP xinetd" to activate your changes.

6.3 Generating Your SSL Mail Certificates

Generate your PEM Private Key. Essentially, this is the same file as your webserver's Private Key saved in a different format. PEM stands for Privacy Enhanced Mail.

# cd /etc/ssl/certs
# openssl rsa -outform PEM -in myserver.key -out temp.pem

# chmod 400 temp.pem

Next, generate your PEM Public Key. This is actually just a copy of your PEM Private Key and your CRT Public Key concatenated into a single file. Without this, you can't do SSL email. You will never want to delete this key

# cd /etc/ssl/certs
# cat temp.pem myserver.crt > imapd.pem
# chmod 400 imapd.pem
# ln -sf imapd.pem ipop3d.pem
# rm -f temp.pem

Integrating imap into PHP

To integrate POP/IMAP support into PHP, you'll need to copy the following file to your system. The commands below work for imap-2002e. Note: This gets more tricky each time there's a new revision of imapd, so your mileage may vary.

# mkdir -p /usr/include/imap/{lib,include}
# cp c-client/linkage.h c-client/osdep.h /usr/include/imap/include/
# cp c-client/auths.c /usr/include/imap/lib/ # cp src/c-client/*.h /usr/include/imap/include/ # cp src/osdep/unix/*.h /usr/include/imap/include/
# cp src/c-client/*.c /usr/include/imap/lib/ # cp src/osdep/unix/*.c /usr/include/imap/lib/ # cp c-client/c-client.a /usr/include/imap/lib/libc-client.a
# ln -s /usr/include/imap/include/os_slx.h /usr/include/imap/include/osdep.h

MySQL

MySQL is a fast, multi-threaded, multi-user and robust SQL (Structured Query Language) database server. It comes with a nice API which makes it easy to integrate into other applications, including PHP. Version 4 has additional advantages such as faster transactions and the default use of InnoDB tables. If you don't install MySQL, PHP will instead use a scaled-down version of SQL. This is probably not what you want if you plan to do web-based database integration with PHP

7.1 Building MySQL

Prior to installing MySQL, you must first create a mysql user and group:

# groupadd mysql
# useradd -g mysql -d /dev/null -s /bin/false mysql

Then, build MySQL with:

$ CXXFLAGS="-O3 -felide-constructors -fno-exceptions -fno-rtti" \
     CFLAGS="-O3" CC=gcc CXX=gcc \
     ./configure --prefix=/usr \
     --localstatedir=/var/lib/mysql \
     --with-extra-charsets=none \
     --without-bench \
     --without-debug \
     --without-readline \
     --with-mysqld-user=mysql \
     --enable-assembler \
     --enable-thread-safe-client \
     --with-client-ldflags=-all-static
$ make
# make install
# cp support-files/my-huge.cnf /etc/my.cnf
# echo "/usr/lib/mysql" >> /etc/ld.so.conf
# ldconfig -v

The meaning of the configuration switches are:

To have MySQL start automatically when your system boots, copy support-files/mysql.server to the location where your system has its startup files and make it executable with chmod 755. If you plan to run MySQL and Apache on the same server, you can disable the networking options in MySQL since you'll never need them (everything's on the same server). This is generally considered a good idea since it cuts down on security issues across the network, and is probably what you want. Edit the mysql.server startup script and make sure to add --skip-networking to the safe_mysqld command.

7.2 Creating the initial MySQL database

To prepare the necessary database format for MySQL, run:

# mkdir -p /var/lib/mysql
# chown -R mysql /var/lib/mysql
# chgrp -R mysql /var/lib/mysql
# scripts/mysql_install_db
$ mysqladmin -u root -p password '<new-password>'
$ mysqladmin -u root -h <hostname> -p password '<new-password>'

7.3 Maintaining your MySQL database

To backup your MySQL database, you should run something similar to this:

$ mysqldump --all-databases -u root -p > all_databases.sql
$ chmod 400 all_databases.sql

To restore your database from a backup file, you can use:

mysql -u root -p < all_databases.sql

To analyze, check and optimize (respectively) your tables, you can run:

$ mysqlcheck --all-databases -u root -avp
$ mysqlcheck --all-databases -u root -cvp
$ mysqlcheck --all-databases -u root -ovp

Mhash

Mhash is a library which provides a uniform interface to a large number of hash algorithms. These algorithms can be used to compute checksums, message digests, and other signatures. It's available at http://mhash.sourceforge.net.

$ ./configure --prefix=/usr
$ make
$ make check
# make install
# ldconfig -v

Mcrypt

Mcrypt enables add-on support of string encryption and decryption, which is much broader and more efficient than PHP's own cryptographic code. This is mainly used for saving sensitive data to disk that you need to decrypt at a later time. If you only want to encrypt (and not decrypt) strings, or aren't interested in the advanced cryptographic functions this module profides, skip this feature and use the built-in PHP function crypt() instead. Installation of mcrypt requires both mhash and libmcrypt. Libmcrypt is a library that provides uniform interface to access several encryption algorithms, and is used primarily by mcrypt. Both MCrypt and Libmcrypt are available at http://sourceforge.net/projects/mcrypt.

$ ./configure --prefix=/usr
$ make
$ make check
# make install
# ldconfig -v

PHP

PHP (a recursive acronym for "PHP: Hypertext Preprocessor") is a widely-used Open Source general-purpose scripting language that is especially suited for Web development and can be embedded into HTML. What distinguishes PHP from something like client-side JavaScript is that the code is executed on the server. The best things in using PHP are that it is extremely simple for a newcomer, but offers many advanced features for a professional programmer.

10.1 Installing PHP

Note that some of these configuration parameters require some common sense. For example, if you specify "--with-mysql=/usr", then you'd better have already installed MySQL!

$ ./configure --prefix=/usr \
    
--sysconfdir=/etc \
     --with-config-file-path=/etc \
     --with-mysql=/usr \
     --with-apache=../apache_x.y.z \
     --with-openssl \
     --with-imap=/usr/include/imap \
     --with-imap-ssl \
     --with-mhash \
     --with-mcrypt \
     --enable-memory-limit \
     --enable-inline-optimization \
     --enable-sysvsem \
     --disable-debug \
     --enable-track-vars \
     --disable-cgi \
     --with-gettext \
     --enable-ftp \
     --enable-safe-mode
$ make
# make install

These configuration options roughly translate to:

--prefix=/usr Install library, header, pear & manpage files in /usr instead of /usr/local
--sysconfdir=/etc Install PEAR system configuration file in /etc instead of /usr/etc
--with-config-file-path=/etc Store the main php.ini configuration file in /etc rather than /usr/lib
--with-mysql=/usr Include PHP code for integrating with MySQL
--with-apache=../apache_x.y.z Include PHP code for integrating with Apache. Since this is generally considered a good idea, just say yes.
--with-openssl Include PHP code for integrating generic SSL support
--with-imap=/usr/include/imap Include PHP code for integrating with POP/IMAP
--with-imap-ssl Include PHP code for integrating with secure (SSL) POP/IMAP
--with-mhash Include PHP code for integrating with mhash
--with-mcrypt Include PHP code for integrating with mcrypt. This is much broader and more efficient than PHP's built-in cryptographic code. This is mainly used for saving sensitive data to disk that you need to decrypt at a later time. If you only want to encrypt (and not decrypt) strings, or aren't interested in the advanced cryptographic functions this module profides, skip this feature and use the built-in PHP function crypt() instead.
--enable-memory-limit Allows you to control the maximum amount of memory that a script can use. This helps to prevent buffer overflows (security).
--enable-inline-optimization Recommended by the PHP team if you have "lots of memory" and are using gcc as your compiler
--enable-sysvsem Enable System V sempahore (specialized file locking to prevent simultaneous access) support.
--disable-debug Do not compile in debugging symbols
--enable-track-vars Just say yes.
--disable-cgi Do not create a stand-alone PHP cgi-binary (security).
--with-gettext Include PHP code to support internationalization
--enable-ftp Include PHP code to support built-in ftp functionality

10.2 PHP Configuration File

The PHP Configuration file is located in /etc/php.ini and is not created by default. To create your initial PHP configuration file, run:

# cp php.ini-recommended /etc/php.ini

Next, make the following adjustments to your /etc/php.ini file:

safe_mode = On
safe_mode_gid = Off
safe_mode_exec_dir = /web/exec
safe_mode_include_dir = /web/include
open_basedir = /web
expose_php = Off
display_errors = Off
log_errors = On
error_log = /web/log/php.log
register_globals = Off

Apache Final Configuration

This is where we tie all our various and assorted parts into the final product. Our philosophy is simple: Only compile in what you need, and nothing more! We use static modules for the added speed benefit.

$ SSL_BASE=SYSTEM ./configure --prefix=/usr \
     --sysconfdir=/etc/apache \
     --sbindir=/usr/sbin \
     --logfiledir=/var/log \
     --runtimedir=/var/run \
     --datadir=/web \
     --iconsdir=/web/icons \
     --htdocsdir=/web/default \
     --disable-module=all \
     --enable-module=access \

     --enable-module=log_config \
     --enable-module=dir \
     --enable-module=mime \
     --enable-module=auth
     --enable-module=ssl \
     --enable-module=speling \
     --disable-rule=SSL_COMPAT \
     --enable-rule=SSL_SDBM \
     --activate-module=src/modules/php4/libphp4.a \
     --activate-module=src/modules/extra/mod_security \
     --enable-module=security
     --server-uid=apache
     --server-gid=apache
$ make
# make install
# chmod 0511 /usr/sbin/httpd
# mkdir -p /web/secure

These configuration options roughly translate to:

SSL_BASE=SYSTEM This variable tells Apache that the OpenSSL libraries are already installed on the system.
--prefix=/usr Install Apache in /usr rather than /usr/local/apache
--sysconfdir=/etc/apache Install Apache's configuration files in /etc/apache rather than /usr/local/apache/conf or /usr/etc
--sbindir=/usr/sbin Install Apache's system binaries in /usr/sbin rather than /usr/local/apache/bin or /usr/bin
--logfiledir=/var/log/ Install Apache log files in the /var/log directory rather than /usr/local/apache/log
--runtimedir=/var/run Store Apache's runtime files in /var/run
--datadir=/web Apache's cgi-bin directory will be installed in the /web/cgi-bin directory (no, this is not a typo).
--iconsdir=/web/icons Apache's graphic files will be installed in the /web/icons directory.
--htdocsdir=/web/default Apache's web pages will be located in the /web/default directory.
--enable-module=ssl Compile Apache with SSL support.
--enable-module=speling This module attempts to correct misspellings of URLs that users might have entered, by ignoring capitalisation and by allowing up to one misspelling (and yes, it's spelled correctly).
--disable-rule=SSL_COMPAT Build the final code of mod_ssl without backward compatibility code for Apache-SSL 1.x, mod_ssl 2.0.x, Sioux 1.x and Stronghold 2.x.
--enable-rule=SSL_SDBM This controls whether the built-in SDBM library should be used instead of a custom defined or vendor supplied DBM library. Enable this to force the use of SDBM in case the vendor DBM library is buggy or restricts the data sizes too dramatically.
--activate-module=src/modules/php4/libphp4.a Build Apache with PHP support. This file doesn't yet exist; this is normal.
--server-uid=apache The username to run Apache as (security).
--server-gid=apache The group to run Apache as (security).

Make sure your /etc/apache/httpd.conf file includes the following entries to support PHP:

AddType application/x-httpd-php .php .inc .class
AddType application/x-httpd-php-source .phps

Make sure your /etc/apache/httpd.conf file includes the following entries to support ModSecurity:

AddModule mod_security.c
<IfModule mod_security.c>
     SecFilterEngine On
     SecFilterCheckURLEncoding On
     SecFilterForceByteRange 32 126
     SecAuditEngine RelevantOnly
     SecAuditLog /web/log/audit.log
     SecFilterDebugLog /web/log/audit-debug.log
     SecFilterDebugLevel 0

     SecFilterDefaultAction "deny,log,status:406"
     SecFilter "<( |\n)*script>"
     SecFilter "<(.|\n)+>"
     SecFilter "\.\./"

</IfModule>

Make sure your /etc/apache/httpd.conf file includes the following entries to support SSL:

AddType application/x-x509-ca-cert .crt
AddType application/x-pkcs7-crl .crl

SSLProtocol all
SSLPassPhraseDialog builtin
SSLMutex file:/var/run/ssl_mutex
SSLSessionCache dbm:/var/run/ssl_scache
SSLSessionCacheTimeout 300
SSLRandomSeed startup file:/dev/urandom 1024
SSLRandomSeed connect file:/dev/urandom 1024
SSLLog /var/log/apache_ssl.log
SSLLogLevel warn
SSLEngine on
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
SSLCertificateFile /etc/ssl/certs/myserver.crt
SSLCertificateKeyFile /etc/ssl/certs/myserver.key

To start Apache, without SSL, use:

apachectl start

Or, to start Apache with SSL support, run:

apachectl startssl

To abruptly stop Apache, which will affect anyone currently connected to your web page, run:

apachectl stop

And finally, to gracefully restart Apache without adversely affecting anyone connected to your web page, use:

apachectl graceful

Pretending to be your own Certificate Authority

# openssl genrsa -des3 -out /etc/ssl/certs/ca.key 1024
# chmod 400 /etc/ssl/certs/ca.key
# openssl req -new -x509 -days 365 -key /etc/ssl/certs/ca.key -out /etc/ssl/certs/ca.crt
# mkdir /etc/ssl/certs/ca.db.certs
# echo '01' > /etc/ssl/certs/ca.db.serial
# touch /etc/ssl/certs/ca.db.index

You'll also need to create the file /etc/ssl/certs/ca.config with:

[ ca ]
default_ca              = CA_own

[ CA_own ]
dir                     = .
certs                   = $dir
new_certs_dir           = $dir/ca.db.certs
database                = $dir/ca.db.index
serial                  = $dir/ca.db.serial
RANDFILE                = $dir/ca.db.rand
certificate             = $dir/ca.crt
private_key             = $dir/ca.key
default_days            = 365
default_crl_days        = 30
default_md              = md5
preserve                = no
policy                  = policy_anything

[ policy_anything ]
countryName             = optional
stateOrProvinceName     = optional
localityName            = optional
organizationName        = optional
organizationalUnitName  = optional
commonName              = supplied
emailAddress            = optional

Finally, pretending to be a Certificate Authority, we sign our own server's request for its own certificate:

# cd /etc/ssl/certs
# openssl ca -config ca.config -out myserver.crt -infiles myserver.csr
# openssl verify -CAfile ca.crt myserver.crt