configuring a LAMP server on CentOS 6

In this first post I’ll explain how I configurated my VPS to run a basic LAMP server with multiple php versions, compiled by myself. You can get your own cheap VPS (~3€) and test a lot of things.

Dir structure

Firstly, we’ll create our dir structure. I’ll use jag.co as a domain for this example, you can choose the dir structure you want, but I recommend following this one:

/
/var/www/vhosts/jag.co
/var/www/vhosts/jag.co/logs
/var/www/vhosts/jag.co/httpdocs

We’ll also create a system user who will be running php, although at first we’ll run php under apache, just to test our configuration. You can create this dir structure and the system user with the following commands:

mkdir -p /var/www/vhosts/jag.co
useradd -d /var/www/vhosts/jag.co/ jag
usermod -g apache jag
chmod 710 /var/www/vhosts/jag.co

Now we’ll add an important repository to our system. We’ll need it here and you’ll need it later, for sure. After that, we are good to perform a basic httpd install, along with php.

yum install epel-release
yum install httpd
yum install mod_fcgid
yum install php

You should be able to test the default page of apache, visiting your vps ip through your preferred browser. But that’s too uncomfortable, so we’ll improve it. We are going to configure a virtual host, named jag.co. First, we need to activate virtual hosts. To do that, we’re going to edit the main httpd configuration file (/etc/httpd/conf/httpd.conf) and add/uncomment the following line:

NameVirtualHost *:80

Secondly, we’re going to create and edit the file /etc/httpd/conf.d/jag.co.conf and include the following content:

LoadModule suexec_module    lib/apache/mod_suexec.so
<VirtualHost *:80>
    ServerName jag.co
    DocumentRoot /var/www/vhosts/jag.co/httpdocs
    ServerAdmin hostmaster@jag.co

    ErrorLog /var/www/vhosts/jag.co/logs/error.log
    CustomLog /var/www/vhosts/jag.co/logs/access.log combined

    SuexecUserGroup jag jag
    ScriptAlias /cgi-bin/ "/var/www/vhosts/jag.co/.cgi-bin/"

    <Directory "/var/www/vhosts/jag.co/.cgi-bin/">
        AllowOverride None
        Order allow,deny
        Allow from all
    </Directory>

    <Directory "/var/www/vhosts/jag.co/httpdocs">
        Options Indexes Includes FollowSymLinks
        AllowOverride All
        #mod_php executes all php by default, so we need to use the Filesmatch directive to tell apache we want to execute php with our own cgi-bin.
        <FilesMatch ".+\.ph(p[345]?|t|tml)$">
            SetHandler php5-fastcgi
            Action php5-fastcgi /cgi-bin/php.fcgi
        </FilesMatch>
        Order allow,deny
        Allow from all
    </Directory>
	#allow 20M request, just to manage files
    FcgidMaxRequestLen 20000000

</VirtualHost>

Here we’re telling apache some basic directives to use with our virtualhost, like error logs (remember to create these files and set their permissions correctly) or how to execute php files. To execute php we’ll be using suexec_mod, which allow us to execute php under different users, thus improving security between virtual hosts. By default, mod_php will take all our php files and execute them under apache user. So if we don’t want that to happen under our virtualhost, we’ll use the directive SetHandler to change that. This way, we’re telling apache to execute .php files with an script we’ll create later. Now, we need to create log files:

mkdir -p /var/www/vhosts/jag.co/logs/
touch /var/www/vhosts/jag.co/logs/error.log
touch /var/www/vhosts/jag.co/logs/access.log

In next step we’ll create an script which will take care of php files, setting some environment variables and executing php. Create and edit the following file: /var/www/vhosts/jag.co/.cgi-bin/php.fcgi

#!/bin/bash
#
# php5.fcgi
# Shell Script to run PHP5 using mod_fastcgi under Apache 2.x
USER="jag"
PHPRC="/var/www/vhosts/jag.co/.cgi-bin/php.ini"
PHP_FCGI_CHILDREN=5
export PHP_FCGI_CHILDREN
exec /usr/bin/php-cgi

In this script we’re executing exactly the same php version mod_php would use, but later we’ll change that. Now you can set a customized php.ini for this virtual host. Also, remember to set permissions and ownership.

cp -fv /etc/php/cgi/php.ini /var/www/vhosts/jag.co/.cgi-bin/
chown -R jag:apache /var/www/vhosts/jag.co/
chown -R jag:jag .cgi-bin

You should add the open_basedir directive to your php.ini, so users can’t touch others users' files. Also, add the /tmp dir to this directive, you’ll need it if you want to upload files to your website ith your browser.

open_basedir = /var/www/vhosts/jag.co/httpdocs/:/tmp
Ok, you should be good to go. You can upload some php file and try to get it through your browser and see what happens. I recommend putting some phpinfo() function inside to get details about your php installation. You’ll notice it’s running under your user and not under apache.

Installing additional PHP versions

Ok, sooner or later you’ll find a client of yours or maybe yourself need an older/newer php version, so let’s take care of that before it happens. Firstly, we need to choose an additional php version and download it. I’ll choose 5.6.10 and will work from /script directory. You can choose any directory, this won’t affect at all. After downloading it, we’ll install some tools we’ll need to compile php.

mkdir /script
cd /script
wget http://us1.php.net/get/php-5.6.10.tar.gz/from/this/mirror
tar -zxvf mirror
yum install libxml2-devel libXpm-devel gmp-devel libicu-devel t1lib-devel aspell-devel openssl-devel bzip2-devel libcurl-devel libjpeg-devel libvpx-devel libpng-devel freetype-devel readline-devel libtidy-devel libxslt-devel libmcrypt-devel pcre-devel curl-devel mysql-devel ncurses-devel gettext-devel net-snmp-devel libevent-devel libtool-ltdl-devel libc-client-devel postgresql-devel
yum groupinstall 'Development Tools'

Ok, time to configure our php install. You should think about what needs you have and find out which libraries you’ll need in your php instalation. I’ve used this one, you should be able to go with it. Also, it’s pretty straight forward to change this in future, so don’t worry a lot about it. After configuring it, we’ll install it. This will be an slow process, so sit and wait patiently.

./configure --prefix=/usr/bin/php56 --with-config-file-path=/etc/php56 --with-config-file-scan-dir=/etc/php56/php.d --with-libdir=lib64 --with-mysql --with-mysqli --enable-mbstring --disable-debug --disable-rpath --with-bz2 --with-curl --with-gettext --with-iconv --with-openssl --with-gd --with-mcrypt --with-pcre-regex --with-zlib --enable-cgi --enable-pdo=shared --with-pdo-mysql=shared --with-pdo-sqlite=shared --enable-zip --with-jpeg-dir=/usr/lib64 --with-freetype-dir=/usr/ --enable-gd-native-ttf

make
make test
make install

Ok, now you can copy your new php.ini

cp php.ini-production /etc/php56/php.ini
vim /etc/php56/php.ini
#add this
cgi.fix_pathinfo = 1

Remember the script we wrote to execute php files? Ok, now it’s the time to tell that script to execute php files with our new php install. We’ll need to change the exec line, like this:

exec /usr/bin/php56/bin/php-cgi

Now you can repeat this operation as many times as you want, and you’ll have different php versions, choosing which one you want to use with each virtual host. Also, you can configure and install php again with other params, overwriting the old configuration with no problems.

Troubleshooting

If you have trouble, I recommend to check log files, specially apache and suexec log. This last file will tell you if there are some wrong set permissions.

Installing mysql and phpmyadmin

Installing mysql and phpmyadmin is easy, just type in the following commands and follow the instructions on screen:

yum install mysql-server
service mysqld start
mysql_secure_installation

You should limit the access to phpmyadmin, so I recommend adding this to /etc/httpd/conf.d/phpMyAdmin.conf. Just change x.x.x.x for your ip. This way, only you can connect to phpmyadmin

# phpMyAdmin - Web based MySQL browser written in php
#
# Allows only localhost by default
#
# But allowing phpMyAdmin to anyone other than localhost should be considered
# dangerous unless properly secured by SSL

Alias /phpMyAdmin /usr/share/phpMyAdmin
Alias /phpmyadmin /usr/share/phpMyAdmin

<Directory /usr/share/phpMyAdmin/>
   AddDefaultCharset UTF-8

   <IfModule mod_authz_core.c>
     # Apache 2.4
     <RequireAny>
       Require ip 127.0.0.1
       Require ip ::1
       Require x.x.x.x
     </RequireAny>
   </IfModule>
   <IfModule !mod_authz_core.c>
     # Apache 2.2
     Order Deny,Allow
     Deny from All
     Allow from 127.0.0.1
     Allow from ::1
     Allow from x.x.x.x
   </IfModule>
</Directory>

<Directory /usr/share/phpMyAdmin/setup/>
   <IfModule mod_authz_core.c>
     # Apache 2.4
     <RequireAny>
       Require ip 127.0.0.1
       Require ip ::1
       Require x.x.x.x
     </RequireAny>
   </IfModule>
   <IfModule !mod_authz_core.c>
     # Apache 2.2
     Order Deny,Allow
     Deny from All
     Allow from 127.0.0.1
     Allow from ::1
     Allow from x.x.x.x
   </IfModule>
</Directory>

# These directories do not require access over HTTP - taken from the original
# phpMyAdmin upstream tarball
#
<Directory /usr/share/phpMyAdmin/libraries/>
    Order Deny,Allow
    Deny from All
    Allow from None
</Directory>

<Directory /usr/share/phpMyAdmin/setup/lib/>
    Order Deny,Allow
    Deny from All
    Allow from None
</Directory>

<Directory /usr/share/phpMyAdmin/setup/frames/>
    Order Deny,Allow
    Deny from All
    Allow from None
</Directory>

# This configuration prevents mod_security at phpMyAdmin directories from
# filtering SQL etc.  This may break your mod_security implementation.
#
#<IfModule mod_security.c>
#    <Directory /usr/share/phpMyAdmin/>
#        SecRuleInheritance Off
#    </Directory>
#</IfModule>

Finally, you should auto start apache and mysql, just in case you need to reboot your server. Use the following commands to achieve this

chkconfig httpd on
chkconfig mysqld on

Resources