Installing PHP5.3 MySQL5.5 and FastCGI on CentOS 5.7

Trying to create a development environment that matches our live servers required a little inginuity, as the server software versions are not directly available for the operating system in question

Tagged in:

 

After having set up this development environment, I decided that someone else might benefit from the same information, so I've written this little guide for you

OK, first things first: I have just done a fresh net-install of CentOS 5.7 64-bit, which is the exact operating system on our latest servers. No other configuration has been done to this instance. The only package selection that was made was Server so otherwise it's clean, with no additional software installed.

Note that I use Nano as my editor in these examples. You may prefer other editors like Emacs, Vi, Pico etc. Simply replace the word nano with your choice of editor as necessary

Onwards!

Upgrading CentOS 5.7 to Apache 2.2

The first step is to ensure that SELinux is disabled, as it gets in the way somewhat

system-config-securitylevel

Ensure that it is disabled and save settings. If you needed to change any settings here then you need to force them to take effect

setenforce 0

OK. Next we need to see what versions of Apache, if any, are available to us

yum list httpd*

Hopefully you see updates available. I see I can install to 2.2.3, so I'm going to do that

yum update httpd.x86_64 httpd-manual.x86_64

If that all completes without any errors, then we just need to tell Apache to start every time the system comes up

chkconfig --levels 235 httpd on

And that's Apache upgraded. Simple, eh?

Upgrading CentOS 5.7 to PHP 5.3

PHP 5.3 is not directly available for CentOS 5.7, where you only get PHP 5.1. So we're going to use the CentOSPlus repository to look for it

yum --enablerepo=centosplus list php5*

Ah, there it is! Currently I see PHP 5.3.3 available, which is not the latest version, but it's adequate for my purposes.

Now, installing it directly causes dependency issues with other installed PHP5.1 packages, so I'm going to uninstall PHP5.1 before I go any further. In fact, I'm just going to uninstall any PHP related packages

yum remove php*

Bye bye PHP 5.1 and all related packages! Now let's install all PHP 5.3 packages. Note that in this example I do a very broad "install all packages starting php53". You may want to be a little more specific

yum --enablerepo=centosplus install php53*

Hopefully that will complete with no errors and we can check that it's installed with this simple command

php -v
PHP 5.3.3 (cli) (built: Jan 11 2012 13:54:48)
Copyright (c) 1997-2010 The PHP Group
Zend Engine v2.3.0, Copyright (c) 1998-2010 Zend Technologies

So far so good! Now lets test that PHP is installed and working OK by creating a simple test script

nano /var/www/html/index.php

This is going to be as simple as it gets. Open PHP tag and call the phpinfo() function

<?php phpinfo();

Save that file and close the editor. We should start Apache now too

service httpd start

Now we'll visit the webpage and hopefully see the same PHP version displayed at the top of the page.

Note that the Server API will currently read Apache 2.0 Handler

Installing FastCGI (mod_fcgid) on CentOS 5.7

There are a few different ways of running PHP in Apache. Usually you have the option of running as an Apache module (which we are doing currently), via CGI or the newer (and not-suprisingly, faster) FastCGI.

Running as an Apache module (mod_php) is generally considered to be faster, and simpler. As we see, it's also the default in this case. However, I need to run it in FastCGI (mod_fcgid). The main reason being to replicate our live server configuration, but the reason we run in FastCGI on the live server is to protect websites from each other.

With the Apache module, you run every site in a single Apache instance that is owned by the specified Apache user (defaults to apache, a member of the group apache). This means that the same user will own the files of every website that you operate on this server, which therefore means that one website can cross over to the other's files and do things you probably don't want them to (like read config files and steal passwords). If you control the server completely yourself then this may not be a problem, but in our case we need to separate the various clients, and FastCGI allows us to do that. I've known some servers run the process as root, and I've been able to easily navigate and modify the entire filesystem via a simple PHP script. This is bad!

FastCGI runs a process for the user of the website that you're requesting. This user is restricted then to their own sites, of which there could still be many. They cannot see other sites on the same server, and therefore cannot steal any data. This is what we want.

It should be noted that FastCGI, while faster than standard CGI, is still slower than simply using mod_php.

Anyway, I want to use FastCGI, and I also want to configure the server to allow me to create multiple vhosts so that I can run many sites off of the same server with ease.

FastCGI isn't available directly from any CentOS repo, so we need to install the EPEL repository, which is a Fedora Special Interest Group's repo.

rpm -Uvh http://dl.fedoraproject.org/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm

The repository above recently moved from the old location at http://download.fedora.redhat.com/pub/epel/5/x86_64/epel-release-5-4.noarch.rpm to the location above. Be aware!

Next, we want to install the YUM Priorities

yum install yum-priorities

Now we can install FastCGI

yum install mod_fcgid

Hopefully that's gone smoothly, and now we can make some config changes to PHP. Load up php.ini in to your editor of choice

nano /etc/php.ini

Now head to the end of the file and enter this line:

cgi.fix_pathinfo = 1

Save the file and exit. Now we need to stop mod_php:

nano /etc/httpd/conf.d/php.conf

Comment out every line (# at the start), save the file and close the editor

Now restart Apache and hopefully it still starts OK

service httpd restart

If we now load up the test page again (ie http://hostname) then we should no longer see the PHP test script. This is because PHP support has been removed and Apache is no longer configured to handle PHP files

So, now we're going to prepare Apache to handle multiple vhosts. The way that I prefer to do this is to set up a vhosts directory within /etc/httpd/conf.d/ and every directory will have it's only configuration file. It's just easier to manage this way. You don't have to do it this way if you don't want to, but I recommend it, because even if you're only creating one site now, you might end up adding loads more at a later date and it's better to be safe than sorry. If you want to remove a site then all you have to do is to remove the config file for that site. Easy.

So, load up the Apache config file and let's tell it to look in this vhosts directory

nano /etc/httpd/conf/httpd.conf

Now search the file for a section like this:

#
# Load config files from the config directory "/etc/httpd/conf.d".
#
Include conf.d/*.conf

Underneath it, add these lines:

#
# Load vhost config files from the vhost config directory "/etc/httpd/conf.d/vhosts".
#
Include conf.d/vhosts/*

Next, we need to give priority to index.php over index.html etc. Find "DirectoryIndex" and add index.php to the start of the following list, so it looks something like this:

DirectoryIndex index.php index.html index.html.var

Next, search for a reference to "NameVirtualHost" and uncomment it so that it reads as follows:

NameVirtualHost *:80

Save the file and exit your editor

Now we create the vhosts directory that all of your site's config files will go into

mkdir /etc/httpd/conf.d/vhosts

Now, we're going to call FastCGI via suexec. However, suexec cannot use symlinks and it should have a document root of /var/www. We'll check that before we proceed:

/usr/sbin/suexec -V

Now we're looking for the AP_DOC_ROOT value, which should read like this:

-D AP_DOC_ROOT="/var/www"

Excellent! So, within /var/www we will create a directory where we can put in some wrapper scripts that will create the instance of FastCGI running as your chosen user

mkdir -p /var/www/fcgi/

Now we shall create the first vhost. It will be called web1. We shall also create a new group called web1 and a user called web1, who will be a member of the web1 group. Following that?

groupadd web1
useradd -s /bin/false -d /var/www/vhosts/web1 -m -g web1 web1

We now have a web1 group and a web1 user who has no shell access and his home directory is /var/www/vhosts/web1

You'll probably have had an error about the home directory after that last command. We're going to create it now and deal with any permissions and ownership necessary. Because web1:web1 will be the user that this website runs under, web1:web1 needs to have ownership of the directory

mkdir -p /var/www/vhosts/web1/httpdocs
chmod -R 755 /var/www/vhosts/web1
chown -R web1:web1 /var/www/vhosts/web1

So, we now have the /var/www/vhosts home directory for the web1 user, and within that we have created the httpdocs directory for the web files to go in. Why the extra directory? Well it's always wise to allow some space outside of the public web root for secure files to be stored. It might even be wise, if you're the one designing the web site, to put all of your code in /var/www/vhosts/web1/codebase for example, and have /var/www/vhosts/web1/httpdocs/index.php refer back to ../codebase/controller.php... if you see where I'm going with that?

Anyway, that bit out of the way, we now need to create the FastCGI wrapper script, that will initiate the instance of FastCGI when necessary

mkdir /var/www/fcgi/web1
nano /var/www/fcgi/web1/fcgi

So, within the /var/www/fcgi directory that we created earlier, we have created a web1 directory and an fcgi wrapper script within it. This is like the vhosts directory that we created earlier, with the web1 config file in. Making it easy to administer, remember?

Anyway, in this script, enter the following:

#!/bin/sh
PHPRC=/etc/
export PHPRC
export PHP_FCGI_MAX_REQUESTS=500
export PHP_FCGI_CHILDREN=2
exec /usr/bin/php-cgi

The PHP_FCGI_MAX_REQUESTS value refers to the maximum number of requests that can be made before a FastCGI process is stopped and a new one is launched. PHP_FCGI_CHILDREN defines the number of PHP children that will be launched. Edit both as necessary, but this should be fine for now

The wrapper script that we just created needs to be executable, and the web1 user needs to have ownership. This is different from the vhosts config as they are all loaded by a single process

chmod 755 /var/www/fcgi/web1/fcgi
chown -R web1:web1 /var/www/fcgi/web1

Now we'll create the vhost entry for this site

nano /etc/httpd/conf.d/vhosts/web1

Enter this information, editing for your exact directories/filenames as necessary:

<VirtualHost *:80>
	ServerName www.web1
	ServerAlias web1
	ServerAdmin webmaster@localhost
	DocumentRoot /var/www/vhosts/web1/httpdocs/
		<IfModule mod_fcgid.c>
		SuexecUserGroup web1 web1
		PHP_Fix_Pathinfo_Enable 1
		<Directory /var/www/vhosts/web1/httpdocs/>
			Options +ExecCGI
			AllowOverride All
			AddHandler fcgid-script .php
			FCGIWrapper /var/www/fcgi/web1/fcgi .php
			Order allow,deny
			Allow from all
		</Directory>
	</IfModule>

	# ErrorLog /var/log/apache2/error.log
	# CustomLog /var/log/apache2/access.log combined
	# ServerSignature Off
</VirtualHost>

Now restart Apache and hopefully there will be no errors

service httpd restart

Now, let's copy over that test script that we used in the original installation and see whether we're now running web1 in FastCGI mode or not

cp /var/www/html/index.php /var/www/vhosts/web1/httpdocs/

Now we load this page in to the browser. Bear in mind that unless you've set your new site up in DNS then you'll need to add the IP to your HOSTS file. In my case I just added:

192.168.10.18 web1
192.168.10.18 www.web1

So I load the page up no problem, I see PHP Version 5.3.3 at the top, and I see the server API says...

CGI/FastCGI

Result!

Installing MySQL 5.5 on CentOS 5.7

The next, and final stage, is to install MySQL Server v5.5 on to this operating system. The default client version is 5.0.77 which is pretty old and I don't have any MySQL Server installed. We can soon have that sorted!

Firstly we need to get the Remi repository

rpm -Uvh http://rpms.famillecollet.com/enterprise/remi-release-5.rpm

Now let's take a look at what's available

yum --enablerepo=remi list mysql mysql-server

Notice that in a clean install that yum reports that mysql_x86_64 is installed? As I mentioned a moment ago, this is the client. It's not the server daemon, so we need to get that. The latest version I see at the moment is 5.5.20, which is exactly what I'm looking for!

yum --enablerepo=remi install mysql.x86_64 mysql-server.x86_64

Now notice that I specifically instructed yum to install the x86_64 versions? The reason being that in the 32-bit version, some of the language files are missing, and if you install that then you end up with an error like this:

Error message file '/usr/share/mysql/english/errmsg.sys' had only 480 error messages

So make sure you specify the 64-bit version if you're running a 64-bit version of CentOS, which I am here

OK, so hopefully MySQL is all installed now. Let's start it up

service mysqld start

No problems, hopefully. Though you'll probably get a first start message about securing your installation. Wise words! So lets do it:

/usr/bin/mysql_secure_installation

Now, follow the instructions carefully. These are my recommendations:

  1. Set a root password
  2. Remove anonymous users
  3. Disallow root login remotely
  4. Remove test database
  5. Reload privileges

So, basically, go along with everything that the script suggests

Now let's log in to MySQL and check that everything is as it should be

mysql -uroot -p

Enter the root password that you just set

Now, if you do want to allow root access from your remote workstation, ie where you are right now, do so but with a diffent password, like so:

mysql> CREATE USER 'root'@'192.168.10.3' IDENTIFIED BY 'remoterootpassword';

Replacing 192.168.10.3 with your IP and remoterootpassword with whatever password you want to use

Note that now, you can't log in to MySQL from this session with this username/password combination, but you can log in from a client on your remote machine. Note how the passwords can be different for connecting from the server, and from your workstation? You can have a password per IP if you really want, but personally I tend to only ever allow one valid IP at a time for myself when connecting remotely. If my IP changes then I dial in via SSH, delete that user, and create a new one with the new IP

And that's it. All that's left to do now is to set MySQL server to start when your server reboots. Easy:

mysql> exit;
chkconfig --levels 235 mysqld on

The end!

If you've found this article of any use at all, please get in touch and let me know what you think. I'm always pleased to take on feedback; positive and negative