Also Known As: Blake Forgets Things A Lot So He Put Them Here
As a part-time small business operator, I run Linux servers for the stability and reliability. However, because most of my day-to-day work is done in Windows, I often find myself getting very rusty on the ins-and-outs of administrating the various parts of the servers I work with. Once I get something set up, I rarely, if ever, have to come back to change it. In October of 2007 I made a decision to migrate one of my existing servers, and old Redhat 8.0 box that was past its prime and falling too far out of date. This server had been running for four and a half years, but with all the changes Redhat had been through (leaving this box unsupported), it was time to move on. I made the decision to move to a CentOS 4 box at the same hosting company.
Before phoning to order the new server, I started my adventures by building a local box next to my desk from the CentOS CDs. I have always kept notes on my server activities, mostly so that I can come back later and figure out why the hell I used that specific set of ./configure options, or where I put some sort of important file. I dug out my notes from nigh on a half-decade ago, and it became apparent after awhile that some of them needed updating. As you might guess, with a little work, that ended up turning into this guide.
Anyone who wants to see how I manage to get my LAMP (Linux/Apache/MySQL/PHP) servers up and running. I generally run small-scale database-driven web applications and static HTML sites off of these boxes, and manage them remotely via SSH. The primary services I configure include Apache, MySQL, PHP, Postfix, Dovecot, SSL and BIND. My boxes are generally limited to one or two local users, so I don't require a lot of user-level security or user/group management.
In a nutshell, this guide will help you set up a LAMP server that is remotely-administered via SSH.
The guide assumes you are familiar with the following things:
bash, which I use exclusively throughout this guide. You may have to troubleshoot any differences between shells if you use something else.Entropy runs rampant throughout this guide, but I tried to stick to some basic conventions while writing it up.
Random commands, file names or code snippets will look like this throughout the document.
Keep in mind that this guide grew out of a stack of notes. It's really just a place that I wanted to put the procedures that I personally use. The way I set stuff up may not work for you… in fact, it could be totally wrong!
Moreso, I am a minimalist at heart. My philosophy is "If I don't need it, I don't want it installed". I find that several of the normal LAMP applications that I install in this guide (such as Apache and PHP) will often be distributed in a binary package (RPM) with a lot of extra options built in. For this reason, I will sometimes choose to compile from source to keep things slim.
The servers I describe in this guide are pruned down, bare-bones installs. I have no test data to compare performance to standard installations (I'm too busy!), so please take this guide with a hefty dose of salt. If you don't like what I'm doing, by all means, install things with extra options, or from base RPMs, or whatever way you want. The goal here is to get it working with a minimum of effort. I've spent plenty of time fussing with this stuff, and the end result is this guide, which I'm passing along to you in the hopes that it will save you some time, or teach you something.
If I'm doing something wrong (likely), or there's a better way to do it (very likely), please enlighten me! If you have something you'd like to see included in this guide, I can do my best to accomodate, although if it's not something that doesn't fit the theme of "LAMP for us little guys", I probably won't include it.
That being said, I hope you find it useful!
I have only ever installed CentOS from CD for machines on my local network. I usually pay for dedicated hosting, so generally when I order a server, it comes with whatever the going package is at the hosting company. A lot of the time they will perform a base install, which loads the server with all sorts of things that I will never use (such as entire X-windows packages). If this is the case with you, skip down to Clean Up Packages section. If you're installing from CD, read on.
CentOS helpfully groups a bunch of software packages into groups for us. I generally choose 'Customize software selection' at this point and pick the packages I want as follows:
If you don't need to access this machine via Samba, you probably don't want the Windows file server option.
Hit 'OK' when you're done, then 'OK' again to begin installing. Sit back and relax, it could take a few minutes. When it's done, you'll get a happy "Congratulations" message to lift your spirits. Remove the CD and reboot, and we'll get the real fun started.
Now, if you're installing from CD and you want to get away from the console so you can go sit on the couch and work from your laptop, at this point you might want to configure the firewall, add some users and groups, set up sudoers, and then set up SSH at this point so you can do everything else from the comfort of your couch. Come back here when you're done, otherwise, continue reading and we'll get there soon.
Note that up until you've completed the sections up to Sudoers, you'll need to be logged in as root. After that, sudo will be the best way to go.
Time to delete some crap! Now, if you installed via CD, you shouldn't have a ton of extra stuff on there if you only selected the package groups I mentioned earlier. However, if you ordered your server from somewhere, it might be piled high with junk. A log of installed pacakages can be found in /root/install.log, you may want to give it a browse. You can always use yum info [package name] to help figure out what something does.
Tip: If you are not familiar with the yum command, it might behoove you to take a moment and read the man page. It's your primary tool for installing, removing and updating software on your system.
That being said, take a look below for a huge list of things to uninstall. I probably didn't even manage to write all of them down by the time I was finished with my last production server. The base install put so much on there I was sort of glazing over as I ran endless 'yum remove' commands. That being said, here's a list of things you're not either not going to need on your remotely-administered Linux server, or that we'll install later. I formatted it as a single yum remove command in a giant block of text for easy copy-and-paste; you can do the whole thing at once.
WARNING: I have only tested this on an old version of CentOS 4! I've had reports of this causing really bad problems due to widely varying dependency removals on other distros and versions, or even between initial install methods. Use at your own risk, and if you do, please review the yum changes carefully to be sure you're not removing some vital system component!
yum remove them all! If you remove something accidentally that it turns out you need later, then you can just do a yum install and get it back. Mmm, yum. You may have noted that I'm removing some core LAMP packages such as MySQL. I mentioned this earlier. If you're concerned about a particular LAMP package, take a look at the installation section of this guide and see what I've done with it.
With some of the extras gone, we need to update the rest. Run yum update to update everything else on the system. That's it! Depending on how recent your install is, you might have to wait a few minutes for all the updates to download. You might get some kernel upgrades here as well, which is fine, now is the best time to be installing those. You may also get asked to import some GPG keys for package verification. This is generally ok to do.
I compile a lot of stuff from source, so I generally install the following packages. Note that some of them are devel libraries.
Confirm any dependencies they need and install those as well.
Noob tip: If you are unfamiliar with devel libraries, here's the short version: they are libraries of code that other programs can integrate into themselves to gain some sort of functionality. Example: if you build your PHP module from source, and you want to be able to use the PHP functions related to SSL in your PHP scripts, you need to compile PHP with a reference to the OpenSSL devel libraries so that it includes the latest OpenSSL code. This is easier than it sounds, and all it requires is that you have installed the '-devel' package of whatever you're working with when you go to compile PHP, ie. openssl-devel.
If you ever get errors when compiling something from source, one of the first things to check is whether you have installed the proper devel libraries for your configuration options.
We're all done with packages (for now)!
If any of the following default users exist, you should get rid of them for security. Use userdel [username]:
This is an optional step depending on your setup. If you're building a production box, you might already have a decent firewall setup going. If you're putting a new box on an unprotected home network, you might want to have the firewall running. Depending on what you want running will depending on which ports you want to open.
Run:
Configure the following options once you're in the super hi-tech interface:
You can configure your system to automatically download and install updates from various sources using up2date. If you choose to do this, you can download and install up2date using yum install up2date, then configure it by running up2date --configure.
If you don't want your system updating automatically, you can periodically run yum update and review what needs updating first. This is my preferred option, as I like to keep informed as to what the latest updates are and what they fix, and sometimes automatic package updates will overwrite an important configuration file and I end up spending an hour trying to figure out why a service that worked perfectly yesterday isn't working today. Your mileage may vary.
If you do utilize up2date, it's a common option to leave out any kernel packages from the automatic updates.
Now to get off the root account if you haven't done so already. Create your own user and whatever groups you like. I generally create a 'webdev' group and put apache in it (later), which allows my web developers to create areas in their web trees where apache can write to if necessary.
Sudo is the way to go when you're doing stuff that requires root privileges. You shouldn't run around the system logged in as root, as that can be dangerous. Sudo helps keep you out of that habit by allowing you to run single commands as root as required.
The easiest way to work with sudo is to configure it to allow everyone in the 'wheel' group to have sudo access. This is a historical group, traditionally used for this sort of purpose. Run:
to edit the sudoers file. Activate the wheel group by uncommenting the following line (delete the '#' at the front of the line):
Use :wq to save and exit vi. Your wheel group is all set for sudo access, so just add yourself to it:
See http://fedorasolved.org/post-install-solutions/sudo for more on sudo.
Now, if you're on a remote server, open a new session and log in with your new personal account. If you're on a local box, just log out (CTRL-D) and log back in. Check that you can become root using sudo:
The '-' option sets your environment to the root account (as if you logged in normally using root), without it you'll still have your own environment (paths, etc). Once you confirm sudo is working, close down any other root sessions you might have open and continue working with from your personal user id. This is the way you should log in from now on.
This is an easy one. Check that the timezone is correct by running date. If it's not, look at the timezone files in /usr/share/zoneinfo. Find your timezone, then copy it to /etc/localtime:
It's a good idea to set the ZONE in /etc/sysconfig/clock as well, as I've noticed some yum upgrades have a nasty habit of defaulting to this timezone and changing your system time when you least expect it.
Run date again to make sure things are ok. Some services won't pick up any system time changes automatically, and you may want to reboot the machine or restart these services. The cron daemon for certain will require a restart (sudo /sbin/service crond restart).
Make sure you sync any changes to the hardware clock as well, or the system time might be off the next time you reboot:
If you've changed your mind on the host name, or weren't there for the base installation, you can change the hostname with the following steps:
I used to use cron jobs and ntpdate to sync the system time, but all that has been replaced with a nifty service called ntpd. It's way more complicated than setting your time needs to be, but it is rather interesting, so if you want to know more about it, Google for ntpd and read up. In the meantime, to keep your clock synced, all you need to do is start the service. We'll also use chkconfig to make sure it starts at boot time.
Set your clock manually, close to the current time, with the following command:
Now turn on ntpd:
ntpd may take awhile at first to establish appropriate readings before syncing your time exactly. You can see ntpd notifications in /var/log/messages.
Speaking of chkconfig, there's a whole list of services installed on your machine that you don't necessarily need. We un-installed some of them back in our Post-installation package removal frenzy, which is good (take off, gpm mouse server, eh hoser?) Others we can turn off. You'll need to be root, or sudo these commands.
A good reference for configuring your SSH server is here: http://fedorasolved.org/post-install-solutions/securing-ssh/
I got annoyed with seeing all the random log-in attempts from bots and script kiddies on port 22, so I ended up moving my SSH port to somewhere else. The options below will help tighten up your SSH a little bit without doing anything too crazy. Using key authentication instead of password authentication can be a good idea for the security-concious, but I generally set up password auth for ease-of-use, as I never know when or where I might need to log in from.
I edit the following options in the SSH daemon config file at /etc/ssh/sshd_config.
The MaxStartups option is in the format start:rate:full. sshd will begin to refuse connections randomly at rate/100 (ie. 50% here) once there are more than start connections. This refusal probability increases linearly until full number of connections is reached, whereby all connections are refused until some open connections clear up (time out or finish).
Be careful with your SSH config! Any screwups in the config file might cost you your running SSH server, and if it's remotely hosted, you might have to make an embarrassing call to tech support to ask them to fix your config so you can log in. If in doubt, read some docs or leave it alone.
Now, do a sudo /sbin/service sshd reload (NOT restart), and open a new session to the server on the new port BEFORE closing your old connection. Make sure it works!
I have some web applications that use GeoIP. If you don't know what it is, or don't care about it, skip this step. It's not important unless you have applications that will need it.
I was going to install GeoIP from yum, as there is a GeoIP package available. However, I could not find one for mod_geoip2, which is what you need to integrate it with Apache. It's an easy install, and I like the database to be up to date, so rather than fool with RPMs, I just downloaded the source and compiled it. The latest version of the C API can be found at http://www.maxmind.com/download/geoip/api/c/.
We'll also need mod_geoip2, but we need apache first so that we have the apxs tool. So read on to Apache, then we'll revisit mod_geoip2.
The backbone of any LAMP box is the Apache HTTP server. As I explained earlier, there are some packages I like to compile from source, and Apache is one of them. It's one of the core features of your box, and I like to make sure it's not full of cruft. Compiling from source can occasionally create some problems, however. One problem I've run into before is that other software packages that rely on Apache may be tough to install from yum if yum can't find the 'httpd' package (which is the standard Apache package distributed through yum). On the other hand, when the fine team at Apache fixes a security bug, you can get the latest version from source whenever you want, without the (sometimes extremely long) wait time associated with getting the official distribution. Joining the mailing lists for projects such as Apache is a good way to keep up on the latest releases.
If you have problems or don't care about slimming down Apache, you can simply run yum install httpd. Otherwise, the instructions below will guide you through the relatively painless process of installing from source. We'll do all this as root:
Go to http://httpd.apache.org/download.cgi to find the download link for the latest version of Apache 2.2 (it'll be something like "Unix Source: httpd-2.2.8.tar.gz"). Copy the link and paste it into wget:
Next to the download link on the apache page will be a link to the MD5 checksum of the package you're downloading. Click on it, and compare it to the results of:
If they aren't the same, something's wrong with your tarball, so re-download it. Otherwise, continue:
The following configuration command will set up the executable code in /usr/sbin, conf files in /etc/httpd, and the rest in /usr/local/apache. It also enables dynamic module loading, mod_rewrite, mod_geoip2, mod_ssl, and mod_deflate. Remove or add whichever configuration directives you may or may not need.
Add an apache system account for the daemon to run under, and make it part of our web development group:
Edit the Apache config file at /etc/httpd/httpd.conf to use these credentials to run under. We want to set the following options in this file:
Save and close. Now, Apache provides a handy script called apachectl, found at /usr/sbin/apachectl, that we can use to start and stop the server. This script is so handy, I just copy it to the SysV control script directory and modify it slightly to make it work with chkconfig (SysV is a system of 'runlevels' that controls what gets started and stopped at boot or init time… Google around on it if you're interested). By copying and editing the apachectl script slightly, we can easily set up Apache to work with the service command, and have it started at boot time.
Add the following lines immediately after the very first line (the 'shebang' line that says #!/bin/sh):
If you're curious about the inner workings of what we just did, see http://spiralbound.net/2006/11/15/controlling-services-with-chkconfig/ for more on chkconfig.
Now we can use apache as a service!
Great! Now all we have to do to start and stop apache is use commands like service apache stop/start/restart. It's also configured to load at boot time. Give it a try:
Now hit your IP address or hostname in your browser (http://x.x.x.x). Apache should be serving up documents from it's default directory, which contains a basic HTML page. You should see a page that says "It works!". Nice job! Log out of your root session (CTRL-D) and take a break.
All that's left to do is work with the configuration file. Now, everyone who uses this guide is likely to have significantly different requirements in the way they host their websites, so I won't go into a lot of detail about each configuration option. It's really in your best interests to learn how to carefully configure Apache if you're going to be serving web pages, and it doesn't take much googling to figure out the configs. Start here if you're stuck.
I use name-based virtual hosting, so that I can host multiple web sites at a single IP address. I serve all of my files out of /srv/www/[sitename], put all my logs into a separate directory for each web site under /var/log/httpd/[sitename], and keep separate virtual host configuration files for each site in /etc/httpd/vhosts/[sitename].conf. This gives me the flexibility I need for per-site configuration and log analysis. So, to set this all up:
With that all done, there's not much to do in the main configuration file. Change the following configuration options in /etc/httpd/httpd.conf. You'll need to sudo to edit it.
Look for the second <Directory> configuration and change it to /srv/www. It might look something like this:
Near the bottom where all the Include directives are, add this:
Enable GeoIP if you're installing it (see the GeoIP section) by placing the following at the bottom of the file:
</IfModule>
We'd also like to use mod_deflate to compress as much served content as possible. This is better than using PHP's built-in compression, as you can easily compress static content as well. See http://httpd.apache.org/docs/2.2/mod/mod_deflate.html for more on mod_deflate, and be sure you understand what the config options we're about to set are going to do.
Add this to the bottom of the config file:
Save your edits, then restart apache:
If you'd like to test it again now, your documents are being served out of /srv/www. So place a file called index.html into that directory with the line "hello" in it, and hit your IP address with a web browser. You should see your basic page appear! If you don't, check the error log in /var/log/httpd/error_log.
Not so bad, eh? Now you've got a lean and mean web server!
Log out of your root session with exit or CTRL-D.
mod_geoip2 is the module that allows Apache to gather GeoIP data. You'll need GeoIP installed to use mod_geoip2.
Use your web browser to check out http://www.maxmind.com/download/geoip/api/mod_geoip2/ and find the latest version number. Copy the link so you can paste it into wget.
Done! apxs automatically enables it in the Apache config file for us (although we already did that above). If you run the following command you should see geoip_module listed in the module list.
Once we get PHP installed, you can use the following test script (inside a web page, this won't work from the command line) to see the GeoIP data in the $_SERVER global variable.
<?php
print_r($_SERVER);
?>
I install MySQL from the rpms released on the MySQL website as they are the most up to date. Installing from source has little advantage for MySQL as it is already heavily optimized.
Create a place to put our downloaded RPMs:
Now head over to http://dev.mysql.com/downloads/mysql to find the download links for the three packages you'll want. Since we're working with CentOS 4 here, you'll want the Redhat Enterprise Linux 4 RPMs. You have to go through a couple of annoying screens to get to each mirrored download.
Download the Server, Client and Headers and libraries packages. The rpms you should end up with are named MySQL-server-community-[version].rhel4.[architecture].rpm, MySQL-client-community-[etc], and MySQL-devel-community-[etc]. Place them all in /usr/src/mysql.
Now, we need to get the MySQL GPG key to verify these RPMs before we can install them. Go to the MySQL manual at http://dev.mysql.com/doc/refman/4.1/en/checking-gpg-signature.html, and copy the key text on the page in the blue block (they point it out). Paste it into a new file, /usr/src/mysql/mysql_gpg_public_key.asc and save it. Now we'll import the key to our RPM keyring and verify the RPMs:
Make sure they all say 'OK' after each check. If so, install them with yum:
The install creates the mysql user on the system, and places a handy startup script in /etc/init.d/mysql. It also installs MySQL as a service, which means we can start it using service mysql start. Easy as pie.
Now run:
Follow the prompts to set a MySQL root user password and whatever else you'd like to do. Once you have a root password set, you'll want to modify the MySQL config files. There are some example configs found in /usr/share/mysql, called my-large.cnf, my-huge.cnf, etc. Take a look at them. Any half-decent machine will generally fit at least the my-large.cnf file these days. I'm not going to tell exactly you how configure your server, as it's important that you figure out what you need based on what your hardware is like. My general approach is to take the my-huge.cnf file and make slight modifications:
Add or change the following:
#Slow query logging
log-slow-queries
long_query_time=2
#Kill threads after they've been idle this long
wait_timeout = 300
#Max number of connected clients. Keep it in-line with Apache's MaxClients setting.
#A good rule of thumb is that Total Physical Memory = max_connections * (sort_buffer_size + read_buffer_size) -- key_buffer
max_connections = 160
#This is a per-user connection maximum.
max_user_connections = 160
All done? Start 'er up and get out of your root session.
Now you can connect to MySQL with mysql -u root -p and start making databases and users. Remember to not use the root user for your web applications!
PHP is the other key LAMP component that I insist on installing from source. A lot of PHP's functionality can be added in as modules, and a base PHP distribution installed via yum or RPM will include most or all of these modules for compatibility reasons. Some of them I'll never use, and some I've never even heard of. I definitely do not want the memory overhead of unused modules.
Make sure you have the following packages installed.
Download the latest PHP tarball from http://www.php.net/downloads.php into /usr/src/php.
This is the configure line I use. Your mileage may vary, but I'm fond of it. Please note that this is a very minimal set of options! If you start getting errors from PHP saying such-and-such function doesn't exist, you're missing a module, and you'll need to come back here, re-run the configure script with your module added, and recompile the whole thing.
Copy the example .ini file to a place where PHP will read it:
Verify some of the following settings. I'll give you the settings I use for a development box. You may want to change the error reporting level and/or logging for a production box. For zlib.output_compression, remember that we set up apache with mod_deflate,
so we don't want to compress output in PHP as well.
For a development machine, I use the following error settings:
On a production machine, you might want this instead:
The PHP install script may have added some or all of the necessary configuration lines in the Apache config file. The following options need to be set inside, so double-check each one in /etc/httpd/httpd.conf:
Restart apache. Try making a test page! If you've been following this guide straight through, just go to /srv/www and create a test.php page, and hit it with your browser.
It's nice to make a SSL certificate for your server, so you can use it for serving secure web pages. As an added benefit, you can use it to provide a secure log in for your mail users! Most POP3/IMAP programs support an SSL layer, so if you want to run mail server, you can set it up to only accept secure connections. This is a real benefit, and I highly recommend doing so.
I generally make self-signed certificates, because I don't do anything secure that isn't for my own personal use, or something that I can easily explain to my users. The whole "pay money to get a signed certificate" thing is a scam in my opinion. If people only knew that the Certificate Signing Authority (CSA) companies don't care who you are as long as you pay 'em the cash (*cough* VeriSign), then they'd realize that the whole accept-certificate rigamarole that browsers give you is a joke. If you're doing business on my server, you're already trusting me, so there's no reason you shouldn't accept a certificate SIGNED BY ME.
*Ahem*. I'll get on with it.
If you want more detail on what we're doing here, look at http://www.jm-solutions.com/OpenSSL/Setup/setup02.php, or do some googling.
Make the private key and a Certificate Signing Request (CSR):
Remove the passphrase from the keyfile we just made, so Apache won't ask for it on boot:
Create a self-signed certificate good for 10 years:
Protect everything you just made:
When you're ready to use the certificate, you may have to adjust the permissions a bit depending on your needs. I generate a separate set of keys and certificates for my mailserver (named mailserver.csr, etc) and store them in /etc/ssl beside any webserver key I might have.
Set up any log rotations you want by putting configure files into /etc/logrotate.d. The only logs I need to configure are the Apache web logs for each website I run. The following logrotate setup handles them all at once:
Add the following:
# rotate log files daily
daily
# keep x days of backlogs
rotate 14
endscript
}
Save and exit. Check the man pages of logrotate for more info on each option.
Awstats is a web log analyzer that produces some good info. You have to modify your websites to load a javascript file if you want the full reports, but I've done that by placing the script on the footer of every page. You also have to ensure that your web logs are logging in Apache "combined" format.
Visit the AWStats home page to get the download link for the latest tarball. Then do the standard wget/unpack:
AWStats is Perl-based, so you'll need to enable cgi scripting under Apache for a particular directory in order to view output. We'll make that directory and copy our configs there, then set up scripting in Apache in a few minutes:
Now set up your awstats conf files from this base model file.
I use the GeoIP analysis option with AWStats, so I need to install the Perl GeoIP plugin in addition to the normal GeoIP libraries I've already installed previously.
Run CPAN by typing:
If you haven't used cpan up until now, you'll have to fill in all the questions until you get to the actual cpan> prompt. I just type "no" at the first prompt to allow CPAN to try to automatically configure itself. Then:
Done! Now AWStats can use GeoIP.
I generally keep one configuration file per site I wish to have analyzed, ie. /etc/awstats/awstats.[site name].conf. Copy and modify the awstats.model.conf file as needed:
That should be enough to get you up and running. If you want to analyze your first log, run /srv/www/awstats/wwwroot/cgi-bin/awstats.pl -config=[conf name] -update, where [conf name] is the awstats.[conf name].conf file of the configuration file in /etc/awstats.
What I normally do now is have Logrotate configured to rotate all my website log files each day (remember how we set them up in separate log directories on a per-site basis?) Once they've been rotated, I have an AWStats cron entry set up to analyze the day-old log. The cron entry looks like this (I usually have a user named "webcron" or something to handle all my website-related cron jobs):
So at 4:30 every morning, awstats analyzes the log specified in the config file awstats.sitename.conf (which in my naming scheme will be /var/log/httpd/[sitename]/access_log.1).
Now I set up an Apache virtual host so I can view the stats. Put this into one of your virtual host sites files. (p.s. I also create a DNS entry to get a stats.yoursite.com address that I can use to access this virtual host. You can just use an Alias directive instead if you don't feel like having a separate virtual host).
Here's the vhost entry I use:
<Directory "/srv/www/awstats/wwwroot">
</Directory>
</VirtualHost>
The aliases are to tell your webserver to run those URLs as scripts (AWStats is written in Perl, so Apache needs to be told to execute any URL inside that directory as a script).
Now you can go to http://stats.yoursite.com/awstats/awstats.pl?config=yoursite and get some web stats. Neat! As a final step, I generally use an Apache .htaccess file to put some basic HTTP Authentication on my AWStats area, just so people can't snoop my web stats.
Postfix is an excellent and simple way to run your mail system. I switched from an old version of Exim, as the configurations were just too much to deal with. I hear the newer Exims are a bit better, but I haven't tried them. Postfix is fairly standard these days, so unless you have specific mail system requirements, you can't go wrong with it.
Choose postfix, then 'OK'. Postfix is now installed, with all the necessary configuration files in /etc/postfix. It's also been installed as a service, which I love so much. Start 'er up:
Log out of your root session.
Since I run multiple web sites, but only have a few people who actually use the system for mail, I set up both virtual aliases and virtual mailboxes. For instance, I might host a small-business website, and they'll want a bunch of email addresses for their store, but I don't want them to have a user account on the machine. Virtual mailboxes to the rescue. Virtual aliases allow me to have the same address at different domains go to different mailboxes as well, which is handy from a user and maintenance perspective, and also for reducing spam (bob@domain1.com will go to a different place than bob@domain2.com). The key directives to set are in /etc/postfix/main.cf:
#Blank, for virtual domains
mydestination =
#Blank, for virtual domains
local_recipient_maps =
#Some helpful spam settings
smtpd_helo_required = yes
smtpd_helo_restrictions =
smtpd_sender_restrictions =
# hash:/etc/postfix/access
smtpd_data_restrictions =
#Other tweaks
max_idle = 300s
anvil_status_update_time = 1h
anvil_rate_time_unit = 300s
export_environment = TZ MAIL_CONFIG SENDER
Virtual mailboxes and aliases are acheived by adding all the following directives (at the end of main.cf):
With all those virtual settings, we'd better set up what it needs in the real system. Still as root, run the following:
For each domain you want to accept mail at:
Each virtual user should have a spool file, and then you can point all the email addresses you want at it. Edit /etc/postfix/vmailbox
That puts the spool file for 'blake' into /var/mail/vhosts/mydomain.com/blake. If I want aliases to go to that spool, point them at my main email address by editing /etc/postfix/virtual.hash:
When you're all done setting up mailboxes and aliases, run postmap on each file:
This was a bit complex and it took me a long time to get right for my particular set up. Remember, this guide is pretty much a giant notepad for me to keep track of all my linux sysadmin fun! You can probably safely ignore this part if you want, unless you also need special piping procedures. While ensuring that I have virtual aliases and mailboxes, I also had to set up a special transport to allow incoming mail to a special address to be piped to a web script. That's the transport_maps setting below, which also goes into the main.cf file:
Now, at the very bottom of the ever-so-fun /etc/postfix/master.cf (not main.cf), I created a transport named "mycatch":
Almost there. Now we just have to set up the specific address in /etc/postfix/transport, like so:
Note that these addresses also have to be set up in the virtual mailbox file as well, as the addresses need to be recognized. Transports happen after mail acceptance, but before delivery, so the email needs to be valid (ie. in the mailbox file) in order for it to be accepted. Edit /etc/postfix/vmailbox:
Note that the mail will never be delivered to the devnull mailbox, as the transport we just set up will intercept it. You get the idea. Now run:
and try out your pipe scripts by sending an email.
Postgrey is a charming little piece of work by David Schweikert that cut down my email spam by 90%. For real. Maybe 95%. The first time I installed it, I actually spent the better part of an entire day watching the mail logs scroll by, rejecting spam after spam. It was a beautiful sight. The theory is simple: A good portion of spammers use distributed bot networks (ie. infected Windows machines) to send out spam. By sending out a control signal, the "bot-master" can have an infected computer process and send a piece of spam. You could describe these bots as using a "pump and dump" method. They connect to a mail server, dump out a spam, and disappear into the void. With a network of thousands of machines, it's easy for a spammer to send a large number of spams very quickly without using a centralized location that can be blocked.
The way Postgrey works is that every time your server receives an incoming email connection, it says "Hey, I'm busy here. Try again later", even if it's not busy. This is a perfectly acceptable response in the gloomy world of mail protocols. Any valid email server doing the sending will simply think "Hmm, looks like Blake's server is busy. I'll queue that piece of mail and try it again in a few minutes." Here's the key point: spam bots aren't valid email servers. Most don't even wait around for a response after they've hurled their spam at you. They won't try again. In the meantime, when Yahoo's valid server comes back and says "Hey, I'm back, can you take this mail yet?", Postgrey recognizes that Yahoo tried a previous delivery, and thinks "Odds are you're not a spammer. Sure, I'll take that."
It's genius. Some spammers are adapting to this technique, but by doing so, they have to sacrifice a significant portion of their spamming resources in order to get their mail to you (they have to queue mail, wait for re-trys, etc). So it's win-win, even if you do still get the occasional spam. I'm serious when it cuts back 90% though. It's an amazing difference.
There's a downside of course, and that is that all mail deliveries are delayed, even valid ones. This means that Postgrey may not be a viable solution for a large business or one that deals with extremely time-sensitive email. If that's the case, more traditional filtering methods will have to be used. For me however, it works and it works well.
So here we go. See http://www.howtoforge.com/greylisting_postfix_postgrey for more.
First we configure yum to be able to work with the Dag package repository, as the postgrey packages can be found there.
Paste the following and save:
Now:
You may have to install a GPG key for some of the Perl packages that come as dependencies.
This install happily creates the 'postgrey' user to run itself under, and once again to my delight, includes a chkconfig startup script for us in /etc/init.d/postgrey, so we can use my ever-favourite service commands to start and stop postgrey.
At this point I'll disable the Dag repository again so that yum doesn't check it every time. I'd rather just use the official repositories for most things. Do a sudo vi /etc/yum.repos.d/Dag.repo and set:
The service script references a source file for run-time options, located in /etc/sysconfig/postgrey. You may want to adjust the default greylisting delay of 10 minutes to something smaller. This reduces the effectiveness of greylisting, but also reduces delivery delay. Find your balance. I also tweak the default greylist text that gets logged in the mail log. Therefore, the entire contents of my /etc/sysconfig/postgrey file contains:
We also need to tell Postfix to consult the Postgrey service whenever it received an email. The following option set in the Postfix main configuration file /etc/postfix/main.cf will do it (note that it's only the last line that activates Postgrey, the rest are other restrictions you may or may not want):
That's it! Start up Postgrey and reload Postfix's configuration file:
Don't forget to turn the Postgrey service on as well.
Run a tail -f /var/log/maillog and send yourself an email. Note the rejection message that scrolls by. Send yourself another one and it'll get through instantly, as Postgrey will recognize the combination of to/from addresses and the sending email server signature. Don't despair about the first email… it'll get through soon, generally within an hour, usually less.
Suck on it, spammers.
Dovecot provides POP3, IMAP, or secure versions of either service.
The dovecot package that comes with CentOS has MySQL 4 and postgres package dependencies, which I didn't want, which is why it was included at the top of my guide as one of the packages to uninstall. We'll get the latest source from http://www.dovecot.org/download.html and compile it from there.
Note that this configure doesn't add any SQL drivers, and auto-detects the SSL libraries.
Dovecot is now installed under /usr/local/dovecot, with the conf file in /etc/dovecot/dovecot-example.conf, which must be renamed to dovecot.conf
See http://www.howtoforge.com/linux_postfix_virtual_hosting_2 and http://wiki.dovecot.org/QuickConfiguration for more configuration fun. Keep in mind that my set-up uses virtual hosting, so you might not want to follow my configs!
I only serve secure POP3 at the moment, and since I have "virtual" users, I don't need the PAM authentication parts of the configuration. However, because the users are virtual and not real UNIX accounts, we need to set up a users file and a passwords file to verify them when they log in. You'll see all this in the configuration changes I make below:
# passdb pam {
# }
passdb passwd-file {
# Path for passwd-file
args = /etc/dovecot/passwd
}
userdb passwd-file {
# Path for passwd-file
args = /etc/dovecot/users
}
You'll note that I commented out the passdb pam { section, including the closing brace, and enabled the passdb passwd-file and userdb passwd-file sections. You'll also notice that I set up some SSL options. See the SSL certificate section earlier in the guide where I generated some keys for the mailserver to use.
Now, the following needs to be done to bring our system up to speed with our config file:
The users file needs to be in the same format as the standard system passwd file (/etc/passwd). The dovecot passwd file also uses the standard system passwd file format to encrypt passwords. This means that in order to generate passwords, you need a tool that utilizes the basic crypt() system call. I couldn't find anything to do it because I'm a total noob, so I wrote a php script called make_md5crypt_password that just called it:
#!/usr/local/bin/php
<?php
echo crypt($argv[1]);
?>
I'm sure there's a better way, but that's what I did for now. Simply run it like ./make_md5crypt_password.php mypassword, and it'll output a password suitable for copying and pasting into the dovecot passwords file.
When you're done, you'll have a dovecot users file that looks something like this:
And a dovecot passwd file that looks something like this:
Finally, to set dovecot up as a service, we'll apply a standard init.d script to it. A copy of such a script can be found at http://wiki.dovecot.org/DovecotInit. Copy it to /etc/init.d/dovecot, and change the DAEMON=/usr/local/sbin/dovecot line to DAEMON=/usr/sbin/dovecot. Then put the following at the top of the file:
Hooray! Another service controlled via the service command. Add it to chkconfig (so it'll start at boot time), then start 'er up:
Log out of your root session and take a break.
BIND is a popular package for running DNS services. I like to run my own DNS servers, just so I can be in control of the records if I ever need to make a quick change.
This one's easy. I like using the bind-chroot package with this one as well, which places our BIND daemon into a chroot jail. Given the number of BIND exploits over the years, it's a nice security precaution.
A control script has been placed at /etc/init.d/named, so we've got service functionality again. An entry for named has even been added to chkconfig for us, although it is set to be off at all runlevels. Do a
to turn it on at levels 2345. Now, for each domain you want to set up, you'll have to place a zone entry into /var/named/chroot/etc/named.conf, and put the DNS record in /var/named/chroot/var/named/mydomain.com.zone. I set the zone entries up to point to /var/named/mydomain.conf, so I will create a symlink to each zone file from that directory.
Don't forget to email your ISP and get a reverse DNS entry set up for your mail server!
Subversion is great, but you probably don't want it on a production webserver. Keep it on a development box in-house, or a production server separate from any with essential web services.
The svnserve daemon needs a control script and a chkconfig entry to run as a service. I found a script at http://www.fedoraforum.org/forum/archive/index.php/t-165130.html, but it doesn't work (for CentOS anyway). I modified it to a version that works. Copy it to /etc/init.d/svnserve, then create a file at /etc/sysconfig/subversion that contains:
The init script will use these options when launching the svnserve daemon. Now:
Assuming you're just setting up a fresh repository named svn under /srv, you can just import your old repository:
Set up at least some basic authentication options in /srv/svn/conf/svnserve.conf file:
And if you're setting up a new repository, you'll have to create /srv/svn/conf/passwd to go along with that conf file:
Don't forget to set the permissions on this clear-text password file:
If you've just moved the repository between servers, make sure to set your SVN clients to use the new repo!
Samba you almost definitely don't want on a production web server. It's quite useful on a home network so that you can transfer data easily between file shares however, and it also provides the service required for a windows machine to recognize a linux machine by hostname only.
Give yourself a samba password, and that's it!
Edit your Samba config to set up whatever shares you want. I change/add the following options as well:
Start samba up:
I use JpGraph for some websites I run. It's a PHP package that produces good-looking graphs.
You have to do a fun bunch of setup to get JpGraph all the stuff it needs to work with. We're going to need to be able to extract some stuff from some CAB files for starters. Re-enable the yum Dag repository by setting enabled=1 in /etc/yum.repos.d/Dag.repo (see the Postgrey section where I set this repository up).
Once that's done, continue on. First we need to install cabextract, then get some Microsoft TT fonts. See http://corefonts.sourceforge.net/ for updated instructions on that, otherwise:
The fonts should be downloaded from SourceForge and compiled into an RPM. The new RPM will reside under the standard RPM build architecture. Install it:
Now for Jpgraph. Go to http://www.aditus.nu/jpgraph/jpdownload.php and get the download link for the latest version.
The JpGraph code sits above any website document roots I might have set up. I can include Jpgraph into my web code and use it there, but the code itself is not directly accessible from the web.
Disable the Dag repository again by setting enabled=0 in /etc/yum.repos.d/Dag.repo
Edit /srv/www/jpgraph/src/jpg-config.inc.php and set the following line:
Note: If fonts aren't appearing in your graphs, make sure that you compiled PHP with the "-dir" options pointing to the right lib*.so files (/usr/lib)
Xdebug is a profiling and debugging tool for PHP. It's quite handy. However, you definitely do not want this enabled on a production server! My own tests have shown that it slows down the execution of a PHP script simply by being loaded with Apache (it overrides some of the internal PHP functions with its own). Put it on your development machines though, by all means! It's good for what ails your code.
Go to xdebug.org and download the source tarball to /usr/src/xdebug:
Edit /usr/local/lib/php.ini and add the following under the "Dynamic Extensions" section:
Restart Apache for the changes to take effect.
You can also check to see if Xdebug shows up under php -m from the command line.
The following is some of the options I used with Xdebug. They have to go into the main Apache config files, under a VirtualHost entry or in the main configuration, as they are php_admin_value settings and can't be used in .htaccess files. When I want to do profiling, I'll toggle the xdebug.profiler_enable flag to 1, otherwise it's best to leave it off as it will generate a lot of output. Make sure to create the directory you're putting the output files into as well.
Mono is an open-source platform designed to bring the .NET architecture to non-Windows platforms. If you want to run any .NET code on Linux, Mono is your key. This is a great project! Check out http://www.mono-project.com/ for more.
Copy the following into a yum repository information file at /etc/yum.repos.d/mono.repo:
We also need a couple of packages, due to some fun issues I found with Mono during one of my original installs. These packages are needed for systems without the 'inotify' patch on the kernel, as they provided a FileSystemWatcher called FAM that is more efficient for the mono server to use than it's default polling mechanism. Without it, on CentOS4.4 anyway, the mono server would use 10% of the CPU even while idle. So:
Now the real stuff:
Due to httpd package dependency issues (see notes under Install Apache for more on that), I compiled mod_mono from source. Get it from http://go-mono.com/sources-stable/, and download it to /usr/src/mod_mono:
Edit the main Apache config file (/etc/httpd/httpd.conf) and add:
MonoServerPath "/usr/bin/mod-mono-server2"
Alias /demo /usr/lib/xsp/test
<Directory "/usr/lib/xsp/test">
</Directory>
Restart apache and exit your root session:
Check the server at http://localhost/demo/index.aspx; you're running .NET code!
I'll leave it at that for Mono… it's a whole 'nother ball of worms. To get applications up and running, you'll want to check out the Mod mono docs as well as the general Mono docs. Mono is still a work in progress for me, and I don't run any websites with it yet, but I might at some point very soon!