Linux+Apache+MySQL+PHP HOWTO

David Lechnyr <davidrl * at * comcast * dot * net>
April 01, 2004 13:10:20

It was all so different before everything changed.


The goal of this document is to provide assistance in building a Linux-based, SSL-enabled Apache, MySQL and PHP webserver, affectionately known as LAMP. 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.

Configuring Apache, Part 1

Apache is an open-source web server. Currently, there are three main flavors of Apache available: Apache 1.3.x, Apache 2.x, and Apache-SSL. Since it is not recommended to run Apache 2.x with PHP, and Apache-SSL tends to drag behind in version releases, we'll be building Apache 1.3.x. However, don't worry -- we'll be adding SSL support into our Apache build shortly. Apache is available for download from http://httpd.apache.org/.

To start, 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. Again, we won't actually build Apache yet; we just want to prepare it so that we can graft in our other programs first.


OpenSSL

OpenSSL is essentially a set of cryptographic routines and libraries for the Secure Sockets Layer (SSL) and Transport Layer Security (TLS) protocols. Programs that typically make use of these routines and libraries include anything that can use SSL, including Apache, imapd/ipop3d, and OpenSSH. 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 for download from http://www.openssl.org/source/.

$ ./config --prefix=/usr/local/openssl --openssldir=/etc/ssl shared zlib threads
$ make
$ make test
# make install
# echo "/usr/local/openssl/lib" >> /etc/ld.so.conf
# 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 /usr/local/openssl/bin
# ./openssl genrsa -des3 -out /etc/ssl/certs/myserver.key 1024
# chmod 400 /etc/ssl/certs/myserver.key

Next, generating your server's Certificate Signing Request:

# ./openssl -req -new -key /etc/ssl/certs/myserver.key -out /etc/ssl/certs/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 the Appendix 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.

$ ./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. Operating as an Apache Web server module, the purpose of ModSecurity is to increase web application security, protecting web applications from known and unknown attacks. Some of its features include:

Download the program from http://www.modsecurity.org/ and verify that it hasn't been tampered with:

$ wget http://www.modsecurity.org/download/mod_security-1.7.6.tar.gz
$ wget http://www.modsecurity.org/download/mod_security-1.7.6.tar.gz.asc
$ wget http://www.webkreator.com/download/ivanr_webkreator_com.pub
$ gpg --import ivanr_webkreator_com.pub
$ gpg --verify mod_security-1.7.6.tar.gz.asc

Uncompress it and copy the module directly into your Apache source code:

$ tar xzvf mod_security-1.7.6.tar.gz
$ cp mod_security-1.7.6/apache1/mod_security.c ../apache_1.3.x/src/modules/extra/

This will allow mod_security to be installed statically into Apache. When we finish compiling Apache later on, we'll be using two new directives (--activate-module=src/modules/extra/mod_security and --enable-module=security) to enable this new code.

Of course, you will ultimately need to add specific directives to your Apache configuration file to make mod_security actually do anything. Take a look at files httpd.conf.example-minimal or httpd.conf.example-full in the mod_security tarball to get some idea of what to do. Or even better, read the PDF manual included in the tarball.


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/.

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

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/local/openssl \
	SSLCERTS=/etc/ssl/certs \
	SSLINCLUDE=/usr/local/openssl/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.

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

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/local/mysql \
	--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-medium.cnf /etc/my.cnf
# echo "/usr/local/mysql/lib" >> /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.

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>'

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


PHP

PHP is a general-purpose scripting language that is especially suited for creating dynamic web pages. 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.

Download PHP from http://www.php.net/ and verify that it hasn't been tampered with. Since they only provide the MD5 signatures, and not a PGP key, we'll need to compare the md5 signature of our downloaded file with what is listed on their website download page.

$ wget http://us1.php.net/distributions/php-4.3.5.tar.bz2
$ md5sum php-4.3.5.tar.bz2

Uncompress the PHP source code and run the configuration instructions. Note that some of these configuration parameters require some common sense. For example, if you specify "--with-mysql=/usr/local/mysql", then you'd better have already installed MySQL!

$ tar xjvf php-4.3.5.tar.bz2
$ cd php-4.3.5 
$ ./configure --prefix=/usr \
	--sysconfdir=/etc \
	--with-config-file-path=/etc \
	--with-mysql=/usr/local/mysql \
	--with-apache=../apache_x.y.z \
	--with-openssl \
	--with-imap=/usr/include/imap \
	--with-imap-ssl \
	--enable-memory-limit \
	--enable-inline-optimization \
	--disable-debug \
	--enable-track-vars \
	--disable-cgi \
	--with-gettext \
	--enable-ftp
$ make
# make install
# cp php.ini-recommended /etc/php.ini

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/local/mysql 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
--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
--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

Configuring Apache, Part 2

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.

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

$ SSL_BASE=SYSTEM ./configure --prefix=/usr/local/apache \
	--sysconfdir=/etc/apache \
	--disable-module=all \
	--enable-module=access --enable-module=actions \
	--enable-module=alias --enable-module=asis \
	--enable-module=auth --enable-module=autoindex \
	--enable-module=cgi --enable-module=dir \
	--enable-module=env --enable-module=setenvif \
	--enable-module=log_config --enable-module=mime \
	--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/local/apache/bin/httpd

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
--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 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


Appendix: 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


Additional Resources


Copyright 2004 David Lechnyr
This document is distributed in the hope it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation. A copy of the license is available at http://www.gnu.org/licenses/fdl.txt