Setting up an Ubuntu Web Server

Files

Download the necessary files for the article here.

Background

A request came in at work for a web server for students on the web design modules to host their work on. Since they might realistically break stuff with dodgy scripts, I decided that it would be better to set up a whole new server for them to use, rather than letting them use an existing, live server.

The request that came in was for the following for each student:

I decided that I would give Ubuntu a whirl seeing as though it’s the flavour of the month, and since I am required to document setup procedures I thought this one was worth sharing here, as the end result is quick to install and easy to manage.

These instructions are based on Ubuntu Server 6.10 Edgy Eft, but may work equally well for later versions.

It’s worth noting that LAMPing a normal Ubuntu installation, rather than using the server version really isn’t that hard. I have used the PowerPC version of Ubuntu on a G4 iMac, and whilst the Mac would boot fine from 6.10 Desktop it wouldn’t boot at all from 6.10 Server media. I added LAMP (well, AMP) just with:

sudo apt-get install mysql-server php5 php5-mysql apache2 && shutdown -r now

And that seems to have worked fine. Apache works, PHP files work, MySQL works and PHP was built with MySQL support.

Installation

  1. Download and burn the Ubuntu 6.10 Server (not Desktop) ISO to CD and boot from the CD.
  2. Choose the ‘install to hard disk’ option and work through the setup choosing sensible options for keyboard etc.
  3. When the option to install DNS or LAMP appears, select LAMP (make sure there is an X in the box by pressing the space bar) and continue. LAMP stands for Linux, Apache, MySQL, PHP and will make sure these services are setup, all of which we will be using for this project.
  4. When the system finishes installing and re-boots (make sure to take out the CD or set the system not to boot from CD first) you will just be prompted with a text based login, because this is the server version. Log in using the account that you created during setup.
  5. By default the root password is disabled for security reasons; you are expected to do everything using ‘sudo’. If you want to use sudo, then all further instructions marked here to be run ‘as root’ must be run with sudo instead. Personally, I like to enable the root account as it makes life easier, but it depends on how critical the system is. You can set the password for root with: sudo passwd root
  6. You do not have to install X (the graphical environment) but I would recommend you do so as you will be able to use more tools which may help you in the future. Plus, the update manager for Ubuntu is graphical and by all accounts using that is more sensible than using apt on the command line (especially with dist upgrades). None of the rest of this tutorial relies on X. If you wish, change to the root account (in all future steps marked ‘as root’ use the ‘su’ command to change to a root shell if you are not already in one) and then install X with the following commands: su
    apt-get update
    apt-get install ubuntu-desktop
  7. Once the X installation finishes (it will take a while and may ask for a monitor resolution part way through), re-boot the server as root with: shutdown -r now
  8. The server will re-boot into X (assuming the X installation went OK and graphics cards were detected etc.). Open a terminal and install any further software that may be needed as root, for example: apt-get install nmap vim ssh openssh-server
  9. Then get all software up to date by running the following command as root: apt-get upgrade This may require a re-boot if there are kernel updates. Re-run this step a couple of times to check everything is up to date.
  10. At this point, set the IP address for the server either using the graphical tool or by editing /etc/network/interfaces
  11. As root, delete the folder /etc/skel/Examples, this contains various bits of media that we don’t need.
  12. As root in a terminal, type the command ‘cpan’ to enter CPAN configuarion. Work through accepting defaults until you get to the proxy config, then enter proxy settings if needed. Continue through again accepting defaults until you come to the location options, then choose the correct, geographically close locations. Let the installation complete to a cpan< prompt.
  13. Enter the command install String::MkPasswd to install this Perl library (used later for generating random passwords), then exit when complete by typing ‘exit’. I would ignore any prompts to upgrade CPAN, even if it mentions that it should be seamless, as it didn’t work very nicely when I tried it!
  14. Finally, set the MySQL root password with the command mysql -u root mysql and then giving the following query to MySQL: SET PASSWORD FOR root@localhost=PASSWORD('sqlpassword'); Obviously replacing sqlpassword there for whatever password you want to use (make it secure!).

Apache Setup

  1. Move the userdir config files into the mods-enabled directory as root. This will allow the userdir mod to be run which will give each of our users a website at http://yourserver.yourdomain/~username/ mv /etc/apache2/mods-available/userdir* /etc/apache2/mods-enabled/
  2. Add index.htm to the DirectoryIndex directive in /etc/apache2/apache2.conf to allow index.htm files to act as indexes (only index.html is enabled by default)
  3. Edit /etc/apache2/mods-enabled/userdir.conf and add a second Directory directive to enable a cgi-bin for each user, so that the whole file looks as follows: <IfModule mod_userdir.c>
      UserDir public_html
      UserDir disabled root

      <Directory /home/*/public_html>
        AllowOverride FileInfo AuthConfig Limit
        Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
      </Directory>

      <Directory /home/*/public_html/cgi-bin/>
        AllowOverride None
        Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
        Order allow,deny
        Allow from all
        AddHandler cgi-script .cgi .pl
      </Directory>
    </IfModule>
    You would need to add additional extensions to the AddHandler directive if your users are going to use any file extensions other that .cgi or .pl
  4. Re-start apache as root to load new settings with apache2ctl restart

FTP Setup

For this project we are going to use VSFTP as it is a more secure product than ProFTPD.

  1. Install vsftp as root with the following command: apt-get install vsftpd
  2. Edit the file /etc/vsftpd.conf and make the following changes:
    1. Change ‘YES’ to ‘NO’ in the line Anonymous_enable = YES
    2. Uncomment the following lines by removing the #:
      1. #local_enable = YES
      2. #write_enable = YES
      3. #local_umask = 022
      4. #chroot_local_user = YES
  3. Re-start the server as root to load the new settings with /etc/init.d/vsftpd restart

Quota Setup

This step is only needed if you want to apply a quota to your users. If you do not apply quotas then be sure to remove the relevant lines from the user setup script.

  1. Install quota tools as root with: apt-get install quota
  2. Edit /etc/fstab and add usrquota and grpquota options to the mount point which contains the home directories. For me this was the root mount point, but it will depends on how you did your partitoning (it could be that you made a separate mount point for /home). The entry should read something like: # /dev/hda1
    UUID=4ea80ca3-37b7-447e-831a-700a38627029 / ext3 defaults,errors=remount-ro,usrquota,grpquota 0 1
  3. Finish setup with the following series of commands as root: touch /quota.user /quota.group
    chmod 600 /quota.*
    mount -o remount /
    quotacheck -avugm
    quotaon -avug

Note: it may be necessary to load the ‘services’ graphical tool and explicitly tick ‘quota’ as I noticed after a couple of re-boots that the quotaing seemed to have turned itself off. That may have been me cocking it up at some point though!

Resources Setup

Ubuntu has a folder /etc/skel which contains items that will be copied into new users’ home folders, so this it a good place to put any resources that new users should have.

In this setup the following resources are copied into a public_html folder inside the skel folder:

  1. tips.htm: (a file containing info for the users, this is duplicated as index.htm and personalised later on)
  2. phptest.php: <?php
       echo "Hello from PHP!";
    ?>
  3. cgi-bin directory containing:

    • bashtest.cgi: #!/bin/bash

      echo -e "Content-type: text/html\n\n";
      echo -n "Hello from Bash!";
    • perltest.pl: #!/usr/bin/perl

      print "Content-type: text/html\n\n";
      print "Hello from Perl!";
  4. Wordpress directory (you can get the latest version of this from the Wordpress website).

These resources are laid out in the following fashion: skel
`-- public_html
    |-- cgi-bin
    |   |-- bashtest.pl
    |   `-- perltest.pl
    |-- phptest.php
    |-- tips.htm
    `-- wordpress

You can put anything else you like in here, it will all be copied to every new user’s home directory.

User Setup

Finally, here is the script for user setup. This script will create a new user, apply their quota, modify their skel files and permissions, create a mysql database for their blog and then setup the config files for their blog.

You should set the following variables to suit your setup:

#!/bin/bash
#
# User setup script for web server, 
#
# Ian Atkinson, Leeds College of Art and Design
# March 2007
#
# Designed for Ubuntu Server 6.10
# Usage %createuser.sh <username>

usernamePrefix=""
username="$usernamePrefix$1"
quota=20000
dbname="wordpress_$username"

userLen=`perl -e "print length(\"$username\")"`

if [ $userLen -gt 16 ]; then
   echo "Username: $username is too long ($userLen chars > 16), exiting"
   exit
fi

password=`perl -e "use String::MkPasswd qw(mkpasswd); print mkpasswd(-length => 8, -minnum=> 2,-minlower => 4, -minupper => 2, -minspecial => 0);"`

cryptPassword=`perl -e "print crypt(\"$password\",\"xx\")"`

echo "Username = $username Password = $password"
echo "-----------------------------------------"

# create the group and user

echo "* Creating group $username"
groupadd $username

echo "* Creating user $username"
useradd --create-home --password $cryptPassword --shell /bin/bash --comment $username -g $username $username

# personalise skel files

echo "* Copying helper files and personalising"
sed -e "s/<h1>Tips/<h1>Site for $username/g" /home/$username/public_html/tips.htm > /home/$username/public_html/index.htm

# set quota

echo "* Applying quota"
setquota -u $username $quota $quota 0 0 -a

# create database

echo "* Creating MySQL database for Wordpress blog"
echo "CREATE DATABASE $dbname; GRANT ALL ON $dbname.* to '$username'@'localhost' identified by '$password';" | mysql -u root -psqlpassword

# set up the wordpress config file

echo "* Creating Wordpress config file"

echo "<?php
define('DB_NAME'    , '$dbname'  );
define('DB_USER'    , '$username');
define('DB_PASSWORD', '$password');
define('DB_HOST'    , 'localhost');
\$table_prefix  = 'wp_';
define ('WPLANG', '');
define('ABSPATH', dirname(__FILE__).'/');
require_once(ABSPATH.'wp-settings.php');
?>" > /home/$username/public_html/wordpress/wp-config.php

# setting permissions

echo "* Setting permissions and ownership"

chmod 755 /home/$username/public_html
chmod 644 /home/$username/public_html/*
chmod -R 755 /home/$username/public_html/cgi-bin
chown -R $username.www-data /home/$username/public_html/wordpress
chmod -R 775 /home/$username/public_html/wordpress

echo

If you create a list of users in a text file, one per line, you can install them in a batch by using the following command as root: for i in `cat users.txt` ; do sh ./createuser.sh $i ; done

Finally, here is a Perl script that you may use to check a big list of usernames and make sure that none of them are too long (when using the above batch method):

#! /usr/bin/perl

if (($#ARGV + 1) != 2)
{
        print "\n\nUsage checklen.pl <list file> <prefix>\n\n";
        exit(1);
}

else
{
        my ($list) = $ARGV[0];
        my ($prefix) = $ARGV[1];

        my $prefLen = length($prefix);
        my $remLen  = 16 - $prefLen;

        print "list file : $list\n";
        print "prefix    : $prefix ($prefLen chars, $remLen remaining)\n\n";


        open(IN, "$list") || die "Can't open file!";
        my @data = <IN>;
        close(IN);

        foreach $line(@data)
        {
                chomp($line);

                if (length($line) > $remLen) 
                        { print "Too Long --> $line\n"; }

                else 
                        { print "OK       --> $line\n"; }
        }
}