92 Adding New Virtual Host Accounts


#92 Adding New Virtual Host Accounts

This script is particularly useful for web administrators who serve a number of different domains and websites from a single server. A great way to accomplish this is by using virtual hosting, a capability of Apache (and many other web servers) to assign multiple domain names to the same IP address and then split them back into individual sites within the Apache configuration file.

Just as adding a new account on a private machine requires the creation of a new home directory, creating a new virtual host account requires creating a separate home for both the web pages themselves and the resultant log files. The material added is straightforward and quite consistent, so it's a great candidate for a shell script.

The Code

 #!/bin/sh # addvirtual - Adds a virtual host to an Apache configuration file. # You'll want to modify all of these to point to the proper directories docroot="/etc/httpd/html" logroot="/var/log/httpd/" httpconf="/etc/httpd/conf/httpd.conf" # Some sites use 'apachectl' rather than restart_apache: restart="/usr/local/bin/restart_apache" showonly=0; tempout="/tmp/addvirtual.$$" trap "rm -f $tempout $tempout.2" 0 if [ "" = "-n" ] ; then   showonly=1 ; shift fi if [ $# -ne 3 ] ; then   echo "Usage: $(basename 
 #!/bin/sh # addvirtual - Adds a virtual host to an Apache configuration file. # You'll want to modify all of these to point to the proper directories docroot="/etc/httpd/html" logroot="/var/log/httpd/" httpconf="/etc/httpd/conf/httpd.conf" # Some sites use 'apachectl' rather than restart_apache: restart="/usr/local/bin/restart_apache" showonly=0; tempout="/tmp/addvirtual.$$" trap "rm -f $tempout $tempout.2" 0 if [ "$1" = "-n" ] ; then showonly=1 ; shift fi if [ $# -ne 3 ] ; then echo "Usage: $(basename $0) [-n] domain admin-email owner-id" >&2 echo " Where -n shows what it would do, but doesn't do anything" >&2 exit 1 fi # Check for common and probable errors if [ $(id -u) != "root" -a $showonly = 0 ] ; then echo "Error: $(basename $0) can only be run as root." >&2 exit 1 fi if [ ! -z "$(echo $1  grep -E '^www\.')" ] ; then echo "Please omit the www. prefix on the domain name " >&2 exit 0 fi if [ "$(echo $1  sed 's/ //g')" != "$1" ] ; then echo "Error: Domain names cannot have spaces." >&2 exit 1 fi if [ -z "$(grep -E "^$3" /etc/passwd)" ] ; then echo "Account $3 not found in password file" >&2 exit 1 fi # Build the directory structure and drop a few files therein if [ $showonly -eq 1 ] ; then tempout="/dev/tty" # to output virtualhost to stdout echo "mkdir $docroot/$1 $logroot/$1" echo "chown $3 $docroot/$1 $logroot/$1" else if [ ! -d $docroot/$1 ] ; then if mkdir $docroot/$1 ; then echo "Failed on mkdir $docroot/$1: exiting." >&2 ; exit 1 fi fi if [ ! -d $logroot/$1 ] ; then mkdir $logroot/$1 if [ $? -ne 0 -a $? -ne 17 ] ; then # error code 17 = directory already exists echo "Failed on mkdir $docroot/$1: exiting." >&2 ; exit 1 fi fi chown $3 $docroot/$1 $logroot/$1 fi # Now let's drop the necessary block into the httpd.conf file cat << EOF > $tempout ####### Virtual Host setup for $1 ########### <VirtualHost www.$1 $1> ServerName www.$1 ServerAdmin $2 DocumentRoot $docroot/$1 ErrorLog logs/$1/error_log TransferLog logs/$1/access_log </VirtualHost> <Directory $docroot/$1> Options Indexes FollowSymLinks Includes AllowOverride All order allow,deny allow from all </Directory> EOF if [ $showonly -eq 1 ]; then echo "Tip: Copy the above block into $httpconf and" echo "restart the server with $restart and you're done." exit 0 fi # Let's hack the httpd.conf file date="$(date +%m%d%H%m)" # month day hour minute cp $httpconf $httpconf.$date # backup copy of config file # Figure out what line in the file has the last </VirtualHost> entry. # Yes, this means that the script won't work if there are NO virtualhost # entries already in the httpd.conf file. If there are no entries, just use # the -n flag and paste the material in manually... addafter="$(cat -n $httpconfgrep '</VirtualHost>'awk 'NR==1 {print $1}')" if [ -z "$addafter" ]; then echo "Error: Can't find a </VirtualHost> line in $httpconf" >&2 /bin/rm -f $httpconf.$date; exit 1 fi sed "${addafter}r $tempout" < $httpconf > $tempout.2 mv $tempout.2 $httpconf if $restart ; then mv $httpconf $httpconf.failed.$date mv $httpconf.$date $httpconf $restart echo "Configuration appears to have failed; restarted with old config" >&2 echo "Failed configuration is in $httpconf.failed.$date" >&2 exit 1 fi exit 0 
) [-n] domain admin-email owner-id" >&2 echo " Where -n shows what it would do, but doesn't do anything" >&2 exit 1 fi # Check for common and probable errors if [ $(id -u) != "root" -a $showonly = 0 ] ; then echo "Error: $(basename
 #!/bin/sh # addvirtual - Adds a virtual host to an Apache configuration file. # You'll want to modify all of these to point to the proper directories docroot="/etc/httpd/html" logroot="/var/log/httpd/" httpconf="/etc/httpd/conf/httpd.conf" # Some sites use 'apachectl' rather than restart_apache: restart="/usr/local/bin/restart_apache" showonly=0; tempout="/tmp/addvirtual.$$" trap "rm -f $tempout $tempout.2" 0 if [ "$1" = "-n" ] ; then showonly=1 ; shift fi if [ $# -ne 3 ] ; then echo "Usage: $(basename $0) [-n] domain admin-email owner-id" >&2 echo " Where -n shows what it would do, but doesn't do anything" >&2 exit 1 fi # Check for common and probable errors if [ $(id -u) != "root" -a $showonly = 0 ] ; then echo "Error: $(basename $0) can only be run as root." >&2 exit 1 fi if [ ! -z "$(echo $1  grep -E '^www\.')" ] ; then echo "Please omit the www. prefix on the domain name " >&2 exit 0 fi if [ "$(echo $1  sed 's/ //g')" != "$1" ] ; then echo "Error: Domain names cannot have spaces." >&2 exit 1 fi if [ -z "$(grep -E "^$3" /etc/passwd)" ] ; then echo "Account $3 not found in password file" >&2 exit 1 fi # Build the directory structure and drop a few files therein if [ $showonly -eq 1 ] ; then tempout="/dev/tty" # to output virtualhost to stdout echo "mkdir $docroot/$1 $logroot/$1" echo "chown $3 $docroot/$1 $logroot/$1" else if [ ! -d $docroot/$1 ] ; then if mkdir $docroot/$1 ; then echo "Failed on mkdir $docroot/$1: exiting." >&2 ; exit 1 fi fi if [ ! -d $logroot/$1 ] ; then mkdir $logroot/$1 if [ $? -ne 0 -a $? -ne 17 ] ; then # error code 17 = directory already exists echo "Failed on mkdir $docroot/$1: exiting." >&2 ; exit 1 fi fi chown $3 $docroot/$1 $logroot/$1 fi # Now let's drop the necessary block into the httpd.conf file cat << EOF > $tempout ####### Virtual Host setup for $1 ########### <VirtualHost www.$1 $1> ServerName www.$1 ServerAdmin $2 DocumentRoot $docroot/$1 ErrorLog logs/$1/error_log TransferLog logs/$1/access_log </VirtualHost> <Directory $docroot/$1> Options Indexes FollowSymLinks Includes AllowOverride All order allow,deny allow from all </Directory> EOF if [ $showonly -eq 1 ]; then echo "Tip: Copy the above block into $httpconf and" echo "restart the server with $restart and you're done." exit 0 fi # Let's hack the httpd.conf file date="$(date +%m%d%H%m)" # month day hour minute cp $httpconf $httpconf.$date # backup copy of config file # Figure out what line in the file has the last </VirtualHost> entry. # Yes, this means that the script won't work if there are NO virtualhost # entries already in the httpd.conf file. If there are no entries, just use # the -n flag and paste the material in manually... addafter="$(cat -n $httpconfgrep '</VirtualHost>'awk 'NR==1 {print $1}')" if [ -z "$addafter" ]; then echo "Error: Can't find a </VirtualHost> line in $httpconf" >&2 /bin/rm -f $httpconf.$date; exit 1 fi sed "${addafter}r $tempout" < $httpconf > $tempout.2 mv $tempout.2 $httpconf if $restart ; then mv $httpconf $httpconf.failed.$date mv $httpconf.$date $httpconf $restart echo "Configuration appears to have failed; restarted with old config" >&2 echo "Failed configuration is in $httpconf.failed.$date" >&2 exit 1 fi exit 0 
) can only be run as root." >&2 exit 1 fi if [ ! -z "$(echo grep -E '^www\.')" ] ; then echo "Please omit the www. prefix on the domain name" >&2 exit 0 fi if [ "$(echo sed 's/ //g')" != "" ] ; then echo "Error: Domain names cannot have spaces." >&2 exit 1 fi if [ -z "$(grep -E "^" /etc/passwd)" ] ; then echo "Account not found in password file" >&2 exit 1 fi # Build the directory structure and drop a few files therein if [ $showonly -eq 1 ] ; then tempout="/dev/tty" # to output virtualhost to stdout echo "mkdir $docroot/ $logroot/" echo "chown $docroot/ $logroot/" else if [ ! -d $docroot/ ] ; then if mkdir $docroot/ ; then echo "Failed on mkdir $docroot/: exiting." >&2 ; exit 1 fi fi if [ ! -d $logroot/ ] ; then mkdir $logroot/ if [ $? -ne 0 -a $? -ne 17 ] ; then # error code 17 = directory already exists echo "Failed on mkdir $docroot/: exiting." >&2 ; exit 1 fi fi chown $docroot/ $logroot/ fi # Now let's drop the necessary block into the httpd.conf file cat << EOF > $tempout ####### Virtual Host setup for ########### <VirtualHost www. > ServerName www. ServerAdmin DocumentRoot $docroot/ ErrorLog logs//error_log TransferLog logs//access_log </VirtualHost> <Directory $docroot/> Options Indexes FollowSymLinks Includes AllowOverride All order allow,deny allow from all </Directory> EOF if [ $showonly -eq 1 ]; then echo "Tip: Copy the above block into $httpconf and" echo "restart the server with $restart and you're done." exit 0 fi # Let's hack the httpd.conf file date="$(date +%m%d%H%m)" # month day hour minute cp $httpconf $httpconf.$date # backup copy of config file # Figure out what line in the file has the last </VirtualHost> entry. # Yes, this means that the script won't work if there are NO virtualhost # entries already in the httpd.conf file. If there are no entries, just use # the -n flag and paste the material in manually... addafter="$(cat -n $httpconfgrep '</VirtualHost>'awk 'NR==1 {print }')" if [ -z "$addafter" ]; then echo "Error: Can't find a </VirtualHost> line in $httpconf" >&2 /bin/rm -f $httpconf.$date; exit 1 fi sed "${addafter}r $tempout" < $httpconf > $tempout.2 mv $tempout.2 $httpconf if $restart ; then mv $httpconf $httpconf.failed.$date mv $httpconf.$date $httpconf $restart echo "Configuration appears to have failed; restarted with old config" >&2 echo "Failed configuration is in $httpconf.failed.$date" >&2 exit 1 fi exit 0

How It Works

Though long, this script is quite straightforward, as most of it is focused on various output messages. The error condition checks in the first section are complex conditionals that are worth exploring. The most complex of them checks the ID of the user running the script:

 if [ $(id -u) != 0 -a $showonly = 0 ]; then 

This test can be paraphrased as, If you aren't root , and you haven't specified that you want only the commands displayed on the terminal, then ...

After each Unix command, this script checks the return code to ensure that things went well, which catches most of the common errors. The one error not caught this way occurs if there's no chown command or if the chown command can be run only by root . If that's the case, simply comment out the following line, or alter it to work properly:

 chown  $docroot/ $logroot/ 

In a similar way, many web hosting companies have their own preferred set of entries in a VirtualHost block, and perhaps a more restrictive Directory privilege set than the one specified in this script. In both cases, fine-tuning the script once ensures that all subsequent accounts are created with exactly the right permissions and configuration.

The script takes particular pains to avoid leaving you with a corrupted httpd.conf file (which could be disastrous): It copies the content in the current httpd.conf file to a temporary file ( http.conf.MMDDHHMM , e.g., http.conf.10031118 ), injects the new VirtualHost and Directory blocks into the live httpd.conf file, and then restarts the web server. If the server restart returns without an error, all is well, and the old config file is kept for archival purposes. If the restart fails, however, the following code is executed:

 if $restart ; then   mv $httpconf $httpconf.failed.$date   mv $httpconf.$date $httpconf   $restart   echo "Configuration appears to have failed; restarted with old config" >&2   echo "Failed configuration is in $httpconf.failed.$date" >&2   exit 1 fi 

The live httpd.conf file is moved to http.conf.failed.MMDDHHMM , and the old http.conf file, now saved as http.conf.MMDDHHMM , is moved back into place. The web server is started once again, and an error message is output.

These hoops, as shown in the snippet just given, ensure that, whether the VirtualHost addition is successful or not, a copy of both the original and edited http.conf files remains in the directory. The only stumbling block with this technique occurs if the restart command doesn't return a nonzero return code upon failure. If this is the case, it's well worth lobbying the developer to have it fixed, but in the meantime, if the script thinks that the restart went fine but it didn't, you can jump into the conf directory, move the new http.conf file to http.conf.failed.MMDDHHMM , move the old version of the configuration file, now saved as httpd.conf.MMDDHHMM , back to httpd.conf , and then restart by hand.

Running the Script

This script requires three arguments: the name of the new domain, the email address of the administrator for Apache error message pages, and the account name of the user who is going to own the resultant directories. To have the output displayed onscreen rather than actually modifying your httpd.conf file, include the -n flag. Note that you will doubtless need to modify the value of the first few variables in the script to match your own configuration before you proceed.

The Results

Because the script doesn't have any interesting output when no errors are encountered , let's look at the "show, but don't do" output instead by specifying the -n flag to addvirtual :

 $  addvirtual -n baby.net admin@baby.net taylor  mkdir /etc/httpd/html/baby.net /var/log/httpd//baby.net chown taylor /etc/httpd/html/baby.net /var/log/httpd//baby.net ####### Virtual Host setup for baby.net ########### <VirtualHost www.baby.net baby.net> ServerName www.baby.net ServerAdmin admin@baby.net DocumentRoot /etc/httpd/html/baby.net ErrorLog logs/baby.net/error_log TransferLog logs/baby.net/access_log </VirtualHost> <Directory /etc/httpd/html/baby.net> Options Indexes FollowSymLinks Includes AllowOverride All order allow,deny allow from all </Directory> Tip: Copy the above block into /etc/httpd/conf/httpd.conf and restart the server with /usr/local/bin/restart_apache and you're done. 

Hacking the Script

There are two additions to the script that would be quite useful: First, create a new website directory and automatically copy in an index.html and perhaps a custom 404 error page, replacing in the 404 error page a specific string like %%domain%% with the new domain name, and %%admin email%% with the email address of the administrator.

A second useful addition would be to test and possibly refine the restart testing; if your restart program doesn't return a nonzero value on failure, you could capture the output and search for specific words (like "failed" or "error") to ascertain success or failure. Or immediately after restarting, use psgrep to see if httpd is running, and respond appropriately.




Wicked Cool Shell Scripts. 101 Scripts for Linux, Mac OS X, and Unix Systems
Wicked Cool Shell Scripts
ISBN: 1593270127
EAN: 2147483647
Year: 2004
Pages: 150
Authors: Dave Taylor

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net