Apache Setup

The Apache webserver (httpd.apache.org) is by far the largest and most sophisticated program involved in out project (apart from the operating system itself.) It is fortunately also one of the most mature and it has been hardened significantly over its lifetime. There are still several things that can be done to reduce the possibility of an attacker exploiting Apache or accomplishing anything if they were to.

Chroot Apache

Chrooting. This is probably the most intensive hardening step that you can take. You will create a special directory structure to house Apache and its modules and then when it runs it will not be able to access files outside of that structure. Even if the server were somehow compromised it will not be able to access anything outside of the structure that you have created. I am not going to cover chrooting in this document because it has already been covered extensively in Securing Linux.

I will briefly list the commands I issued to setup the chroot jail on my system. (Which is creating a slightly different structure than the one in Securing Linux.)


          cd /home/www
          mkdir lib sbin tmp etc cgi dev bin var var/lock var/log var/run share files/home
          ln -s . usr
          for file in $(rpm -ql apache | grep sbin); do cp -v $file sbin; chmod -v a-x $file; done
          cp -av /usr/lib/apache modules
          chmod -v a-x /usr/lib/apache/*
          cp -av /usr/lib/php4 lib
          cp -av /usr/share/php share
          for file in bash ls more pwd strace cvs grep; do cp -v $(which --skip-alias --skip-dot --skip-tilde $file) bin; done
          for lib in $(ldd modules/* sbin/* bin/* lib/php4/* | perl -e 'while(<>) { print "$_\n" if ($_, $_) = (/(=>) (\S+).*/); }' | sort | uniq); do cp -v $lib lib/; done
          cp -v /lib/libnss_dns.so.2 /lib/libnss_files.so.2 lib/
          for file in localtime php.ini httpd/conf/* mime.types resolv.conf hosts; do cp -av /etc/$file etc; done
          for file in passwd group; do egrep ^\(apache\|root\|www\) /etc/$file > etc/$file; done
          mknod dev/null c 1 3
          mknod dev/random c 1 8
          chmod u=rwx,go=x etc dev lib modules cgi bin sbin
          chmod u=rwx,go= etc/ssl* var var/lock var/log var/run
        

Something that confused me very much is that I set everything up like I thought it was supposed to be but it didn't work. Apache would die when it was starting, complaining that the "user apache didn't exist." I used ldd to get all the libraries necessary to run everything but unbeknownst to me there were other libraries being loaded. To find them I first started a chrooted shell in the environment that Apache would be running in:


          /usr/sbin/chroot /home/www /bin/bash
        

Then I watched the system calls that Apache would be making using:


          strace /sbin/httpd
        

Specifically I was interested in seeing files that it attempted to open that failed:


          strace /sbin/httpd 2>&1 | grep "No such file"
        

And from that I found that libnss_files is needed for uid to username mapping and libnss_dns is needed for host lookups. This same basic process though could be used to diagnose other problems.

Once you have done any diagnostics that require a shell within the chrooted environment you can safely remove bash and the associated symlinks. This will make it more difficult for someone to start a shell in an exploit of the chrooted apache:

Having created the basic file structure necessary it takes now changing the configuration files to reflect the changes. So far as Apache is concerned when it is running the root of the filesystem, /, is /home/www and everything has to be relative to that.

Having done these things it should be possible to restart syslog with:


          /sbin/service syslog restart
        

And then restart Apache using:


          /sbin/service httpd restart
        

And then if it restarts properly you can test your setup with:


          for pid in $(/sbin/pidof httpd); do dir /proc/$pid/root; done;
        

And instead of seeing the normal root of your filesystem you should see the chroot environment you created.

Script Configuration

Dynamic and interactive content is the way of the future and allowing users to produce it is a good way to allow them to create more interesting and creative work. Also however it allows for a variety of security exploits since your webpages are in essence becoming programs and since your maintenance is remote it is letting people from all over run programs on your computer. There are ways to prevent the affects that these programs can have on your computer however. Chrooting is a major one, even if someone manages to get a malicious script onto your computer it will be restricted to /home/www. The main Apache process is running as root (in order to be able to bind port 80) but it does not serve content or run scripts itself, rather subprocesses are created that run as the apache user, so though root can possibly break out of a chroot jail the apache user (and thus scripts) cannot.

There are two primary ways that content is generated that I am going to deal with. One is a cgi program (often written in perl, python, or C) that runs and generates content. The other is preprocessed files (like php) where the code is often intermixed with html and the file is interpreted by a server module to generate the content.

CGI Setup

CGI programs are the most dangerous programs that you can run. They are usually written in languages that were designed to be powerful but not to be controlled. It is difficult to control the actions of cgi programs and it is a very good idea to restrict access to where they can be run from and people who can alter them as much as possible. In this setup the web root (/home/www/files/html) is on a partition that is mounted noexec so it is not possible to run cgi programs in the web root. It is a good idea to go ahead and set up Apache as though it were possible though since the mount options might change later and you would not want to be vulnerable.

There are a couple of apache directives to control how cgi is run. You should place these in the primary Directory directive in etc/httpd.conf:


            <Directory />
              Options SymLinksIfOwnerMatch IncludesNOEXEC
              AllowOverride None
              Order deny,allow
              Deny from all
            </Directory>
          

Then in the entry for your web root you can allow slightly more liberal access but still disallow cgi.


            <Directory "/files/html">
              Options +Indexes
              AllowOverride AuthConfig
              <Limit GET POST OPTIONS PROPFIND>
                Order allow,deny
                Allow from all
              </Limit>
            </Directory>
          

If you are going to run specific cgi programs like cvsweb, mailman, or awstats, I recommend creating a directory for them that is only accessible through an alias. For example, installing cvsweb: put the files in /home/www/cgi/cvsweb then add a directive to etc/httpd.conf like:


            ScriptAlias /cvsweb/ "/cgi/cvsweb/"
            <Directory "/cgi/cvsweb">
              AllowOverride None
              Options ExecCGI
              <Limit GET POST OPTIONS PROPFIND>
                Order allow,deny
                Allow from all
              </Limit>
            </Directory>
          

PHP Setup

Another method of generating content is server parsed files. These are languages that are written knowing that they will be executed on webservers and so many times considerations were made to allow tightened security. The most popular server parsed language for Apache is php and there are certain changes that you can make to etc/php.ini to restrict what php can do.

  • Set "open_basedir = /files/html" or "open_basedir = .". Files may not be opened from outside of the specified directory structure. For instance an attacker couldn't open("/etc/passwd") and print the entries in a webpage. The special value of . will limit access to within or below the directory housing the script.

  • Set "memory_limit = 204800". This will limit a script to 200K (1024 * 200) of memory. This could prevent a denial of service if someone put a script on the server that used lots of memory and then accessed it very quickly.

  • Set "max_execution_time = 30". This is the default value for this entry and it is a reasonable value. Used in conjunction with memory_limit this controls the detriment that malicious (or poorly written) scripts can have on the server.

  • Set "safe_mode = on". This enables several security restrictions. When opening files the owner of the script must be the same as the owner of the file. (Using the CVS setup that we are doing all files will be owned by apache so this is mute.) Connections to a MySQL database must be made using the same username as the owner of the file. The user id is prepended to the HTTP authentication realm (this "prevents someone from writing a password.")

  • Set "doc_root = /files/html". Enabled by "safe_mode = on", this will prevent php from serving any files from outside of that directory structure.

  • Set "safe_mode_exec_dir = /bin". Enabled by "safe_mode = on", this restricts programs that can be called to the specified directory. Since Apache is running in a chroot jail with limited programs in /bin, this is a safe place to allow programs from.

mod_vhost Configuration

The next bit of setup is not security related exactly, but it is a very convenient way to maintain the site. The server will be serving out a variety of dns aliases to the same ip. There will be one for each committee as well as a primary one for the site as a whole. Using the Apache vhost_alias you can just create a simple directory structure and then Apache will work out finding the proper directory.

The directory structure will have a primary directory for the honors program and then separate directories for each committee:


          /home/www/files/html/honors.tntech.edu/
          /home/www/files/html/honors.tntech.edu/computer
          /home/www/files/html/honors.tntech.edu/ecology
          /home/www/files/html/honors.tntech.edu/service
          /home/www/files/html/honors.tntech.edu/leadership
        

Apache then will contain a vhost directive:


          <VirtualHost 149.149.47.115>
            UseCanonicalName Off
            VirtualDocumentRoot /files/html/%3+/%1
          </VirtualHost>
        

When a request comes in (ecology.honors.tntech.edu) Apache tries to match a directory in DocumentRoot with the last 3 parts of the name (honors.tntech.edu) and then a subdirectory under that with the first part (ecology). In /home/www/files/html/honors.tntech.edu/ there is a symlink to . called www. This means that requests for www.honors.tntech.edu and honors.tntech.edu map to the same place. This also means that www.honors.tntech.edu/ecology, honors.tntech.edu/ecology and ecology.honors.tntech.edu all map to the same place.

This setup will not work correctly for honors.tntech.edu because the search for the first part fails. It is therefore necessary to have a single normal NameVirtualHost to catch that special case.


          NameVirtualHost 149.149.47.115
          <VirtualHost honors.tntech.edu>
            DocumentRoot /files/html/honors.tntech.edu
            ServerName www.honors.tntech.edu
          </VirtualHost>