Creative Commons License
This work is licenced under a Creative Commons Licence.

Apache + PHP + JSP + SQL + Perl on FreeBSD

Introduction

FIX ME

MySQL 5.0.x Client Installation

Note! You will need to mount devfs or at least mknod a /dev/random inside this jail, otherwise MySQL will only throw out “SSL Connection Error” when you are trying to establish a SSL encrypted connection! The same goes for SSHd.

In order to be able to connect to our MySQL server, which has its own jail, we’ll need to install the mysql-client in each user’s chroot. First, let’s compile MySQL without the server (i.e. client only). This is really easy:

$ cd /usr/ports/databases/mysql50-client/
# make WITH_OPENSSL=yes all install clean

On Ubuntu you can simply do:

# apt-get install mysql-client-5.0 libmysqlclient15-dev

Remember those SSL certificates we created earlier? We’ll need them again. Copy them over from the host system to the jailcell:

# mkdir /var/jail/10.0.1.1/etc/ssl/certs
# cp /root/openssl/cacert.pem /var/jail/10.0.1.1/etc/ssl/certs/
# cp /root/openssl/client-cert.pem /var/jail/10.0.1.1/etc/ssl/certs/
# cp /root/openssl/client-key.pem /var/jail/10.0.1.1/etc/ssl/certs/

Edit the jailcell’s /etc/my.cnf. Define the following:

[client]
port            = 3306
host            = 10.0.1.0
ssl-ca          = /etc/ssl/certs/cacert.pem
ssl-cert        = /etc/ssl/certs/client-cert.pem
ssl-key         = /etc/ssl/certs/client-key.pem

You can safely delete the part under [mysqld] of the config file, it’s somewhat irrelevant when we only have a MySQL client installed in this jail.

nsvsd Installation

Installing nsvsd in the jail even easier than on the host system (you need at least make, gcc, g++ installed):

$ tar xfvz nsvsd-0.5.tar.gz
$ cd nsvsd-0.5
# ./configure; make; make install

After that, edit /etc/nsswitch.conf and /etc/nsvsd.conf. One might be paranoid and use a separate MySQL user account for the jail then the host system. After that, supervise nsvsd:

$ mkdir -p /package; chmod 1755 /package; cd /package
$ wget http://cr.yp.to/daemontools/daemontools-0.76.tar.gz
$ tar xfvz daemontools-0.76.tar.gz

$ cd admin/daemontools-0.76

Edit src/conf-cc and add ‘-include /usr/include/errno.h’ (without the quotes) to the compile line.

# package/install
# mkdir -p /service/nsvsd/log/{main,supervise} /service/nsvsd/supervise

/service/nsvsd/run is simple:

#!/bin/sh
exec /sbin/nsvsd -d info 2>&1

/service/nsvsd/log/run is not much more complicated:

#!/bin/sh
exec setuidgid nsvsd multilog t ./main
# chmod +x /service/nsvsd/run /service/nsvsd/log/run
# chown nsvsd /service/nsvsd/log/main

# svscanboot &

This way the daemon will start whenever the jail is started.

PostgreSQL client installation

This is required if we wish to compile PHP with PostgreSQL support. I’m using the ports, so that I can tune the settings.

# cd /usr/ports/databases/postgresql80-client/; make all install clean

This installs the client portion of PostgreSQL. No need to install the server portion here, as it’s already in another jail.

Perl 5.8.x and DBI-modules Installation

We’ll need to install Perl before PHP and mod_perl (and thus also before Apache) if we want a newer version than Perl 5.6.x, as PHP’s libmcrypt has a dependency to Perl. Execute the following:

# pkg_add -r perl wget

Or, on Ubuntu:

# apt-get install wget perl libperl-dev

Next up was the DBI installation. The jail will probably not have job control, so you’ll want to use this instead of the CPAN shell:

# perl -MCPAN -e ‘install Bundle::DBI’

Always answer “yes” when you get the following question:

Shall I follow them and prepend them to the queue
of modules we are processing right now? [yes]

Since I wanted SSL support in my DBD::mysql, I decided to compile my DBD::mysql by hand. For some reason ‘make’ will not find the MySQL libraries and headers unless specified, so I executed the following while inside the jailcell:

# cd /root/
# fetch http://www.perl.com/CPAN/modules/by-module/DBD/DBD-mysql-2.9004.tar.gz
# tar xfvz DBD-mysql-2.9004.tar.gz
# cd DBD-mysql-2.9004
# perl Makefile.PL –ssl
# make
# make install

Warnings about mysql_config can safely be ignored. Notice especially the ‘–ssl’ argument to Makefile.PL. If you’re going to compile this on the host system, you’d probably want to do something like this instead:

# perl Makefile.PL –ssl –cflags=”-I/var/jail/10.0.1.0/usr/local/include/mysql/” \
–libs=”-L/usr/lib-lz -lcrypt -lm -L/usr/lib -lssl -lcrypto \
-L/var/jail/10.0.1.0/usr/local/lib/mysql/ -lmysqlclient”

SSH Installation

The ‘commercial’ SSHD supports chrooting of certain groups using the ChRootGroup feature and certain users using the ChRootUser feature. OpenSSH can be patched to support these. OpenSSH can also be patched to chroot with the magic token /./ in a user’s home directory (http://chrootssh.sourceforge.net/), while the commercial SSHd cannot. However, the ‘commercial’ version of SSH does not read obey login.conf, which is something I definitely want. Thus, I’m patching OpenSSH to support the /./ magic token.

# cd /usr/ports/security/openssh-portable; make -DWITH_OPENSSH_CHROOT install clean

Or, if you prefer to compile it yourself (Ubuntu):

$ tar xfvz openssh-4.7p1-chroot.tar.gz
$ cd openssh-4.7p1-chroot
# apt-get install openssl libssl-dev libwrap0-dev
$ ./configure –with-md5-passwords –with-tcp-wrappers
$ make
# make install

After the installation, edit the jail’s /usr/local/etc/ssh/sshd_config. If you’re going to run SSHd on the host system (for administrative purposes or whatnot), be sure to limit the allowed users using the ‘AllowedUsers’ directive in sshd_config. Otherwise, people could just connect to that server and slip past our chroot/jail!

SSHd requires that you have devfs mounted in the webserver jail. To mount it at boot time, insert the following in the host system’s /etc/rc.conf:

jail_cherry_devfs_enable="YES"
jail_cherry_devfs_ruleset="devfsrules_jail"

To start SSHd automatically in the jail, define the following in the jail’s /etc/rc.conf:

sshd_enable="YES"
sshd_program="/usr/local/sbin/sshd"

The following might be good options to put in the jail’s /usr/local/etc/ssh/sshd_config:

ListenAddress 10.0.1.1
PermitRootLogin no
PasswordAuthentication yes
PermitEmptyPasswords no

Note that you don’t have to define UseLogin yes; the limits in login.conf are enforced anyway. Actually, setting UseLogin yes will break the chroot! Depending on the user’s shell, a maxproc limit would yield a following error message in Bash: “fork: Resource temporarily unavailable”.

I tried breaking out of the chroot in various ways, but it seemed secure. mknod is disabled in a jail. The chdir(“..”); trick does not work. traceroute and ping will not work inside the chroot (or inside the jail for that matter), as they are not allowed open raw sockets. Later we’ll install Perl and PHP along with the suexec wrapper, so that the scripts will be executed using the user’s UID/GID instead of the server’s UID/GID.

Now, you’ll still need to create a directory structure for the chrooted SSH/SFTP users. I wrote a small Perl script for the task. It’s meant to be executed on the host system, probably as root (might even work as a regular user if the permissions are right. Haven’t tested that though.) The script requires the DBI and an appropriate DBD Perl module. I’ve tested the script with Perl 5.6.0 and 5.8.2.

Denyhosts

Denyhosts is a really good idea to protect your SSH installation against brute force attacks, worms and alike.

# apt-get install denyhosts

dtach installation

I wanted to offer my chrooted users the GNU screen’s ‘detach’ feature, but installing full screen just for the deatch feature seemed too bloated for this kind of use. Then I found a small program called ‘dtach’ (sic) which emulates screen’s ‘detach’ feature. Get it at http://dtach.sf.net/.

However, we need to make some modifications to dtach for it to work on FreeBSD 5.1-RELEASE. After decompressing the sources, remove the libutil.h ifdef from detach.h. Otherwise, you’ll get an error such as this when running ‘make’:

gcc -g -O2 -W -Wall -I. -c ./attach.c
In file included from detach.h:40,
                 from attach.c:19:
/usr/include/libutil.h:76: syntax error before "uid_t"
*** Error code 1

Stop in /var/jail/10.0.1.1/root/dtach-0.5.

I had the problem of getting some garbage data in my console when detaching or exiting from a program, namely the string ‘1;2c’. I solved this by commenting out (by adding /* and */) the following in attach.c (line 49 in version 0.5):

printf("\033[?25h\033[?0c");

You could optionally remove the whole line from attach.c. Now you can proceed to build dtach as you normally would:

$ ./configure

$ make

This will build a 'dtach' binary you can copy into the user's chroot, along with /usr/lib/libutil.so.4. You will need to create appropriate pseudo-terminals in the user's /dev, if you do not with to have devfs mounted inside the user's chroot. I didn't, so I took the MAKEDEV script from the last distribution it was included in, i.e. FreeBSD 5.0-RELEASE, and basically included the functionality in my mkchroot.pl Perl script.

dtach also needs to be able to read /var/run/ld-elf.so.hints inside the chroot!

Apache 1.x Installation

Download and install Apache. I am aware that version 2.x is available, and even considered stable by the Apache team, but I'm not upgrading just yet, due to lack of third-party modules and (currently) slightly worse performance than the 1.x series, at least on my system. I'm going to use the 'www' user, which exists by default on FreeBSD.

$ fetch http://www.tux.org/pub/net/apache/dist/httpd/apache_1.3.39.tar.gz
$ tar xfvz apache-1.3.39.tar.gz

Before we can compile Apache, we need to add mod_ssl support:

$ fetch http://www.modssl.org/source/mod_ssl-2.8.30-1.3.39.tar.gz
$ tar xfvz mod_ssl-2.8.30-1.3.39.tar.gz
$ cd mod_ssl-2.8.30-1.3.39

$ ./configure --with-apache=../apache_1.3.39
$ cd ..

We still need to add mod_perl support. Note: Do not compile mod_perl as DSO (Dynamic Shared Object)! According to various sources, Apache will crash (I never tried).:

$ fetch http://perl.apache.org/dist/mod_perl-1.0-current.tar.gz
$ tar xfvz mod_perl-1.0-current.tar.gz
$ cd mod_perl-1.30
$ perl Makefile.PL \

EVERYTHING=1 \
APACHE_SRC=../apache_1.3.39/src \
USE_APACI=1 \
PREP_HTTPD=1 \
DO_HTTPD=1
# make && make install
$ cd ..

Next, add mod_jk support (for Java/Tomcat). Note the 'make' will not work, you'll need to use 'gmake' (otherwise, you'll run into the 'gcc: No input files specified' error)!:

$ fetch jakarta-tomcat-connectors-1.2.25-src.tar.gz
$ tar xfvz jakarta-tomcat-connectors-1.2.25-src.tar.gz
$ cd jakarta-tomcat-connectors-1.2.25-src/jk/native
$ CFLAGS=-fPIC ./configure --with-apache=../../apache_1.3.39 --enable-EAPI
$ gmake
# gmake install

Finally, mod_auth_mysql support:

$ fetch http://switch.dl.sourceforge.net/sourceforge/modauthmysql/mod_auth_mysql-3.0.0.tar.gz
$ tar xfvz mod_auth_mysql-3.0.0.tar.gz
$ cd mod_auth_mysql-3.0.0
$ /usr/local/apache/bin/apxs -c -L/usr/local/lib/mysql/ -I/usr/local/include/mysql/ -lmysqlclient -lm -lz mod_auth_mysql.c

On Ubuntu you might want to use this instead:

$ /usr/local/apache/bin/apxs -c -L/usr/lib/mysql -I/usr/include/mysql -lmysqlclient -lm -lz mod_auth_mysql.c

Finally, install the module:

# /usr/local/apache/bin/apxs -i mod_auth_mysql.so

Now we're ready to configure Apache. Apache comes with a ready layout for FreeBSD (see config.layout), which I will use here. No reason to break hier(8):

$ cd ../apache_1.3.39
$ ./configure --with-layout=config.layout:FreeBSD \
--enable-module=ssl \
--enable-shared=ssl \
--enable-module=rewrite \
--enable-shared=rewrite \

--disable-module=imap \
--with-perl=/usr/bin/perl \
--enable-module=so \
--server-uid=www-data \
--server-gid=www-data \
--without-execstrip \

--enable-suexec \
--suexec-caller=www-data \
--suexec-uidmin=1000 \
--suexec-gidmin=1000 \
--suexec-docroot=/home \
--suexec-logfile=/var/log/apache/suexec_log \

--activate-module=src/modules/perl/libperl.a \
--enable-module=perl \
--enable-module=dir \
--enable-shared=dir \
--activate-module=src/modules/jk/libjk.a \
--enable-shared=jk

The output of this command should be similar to this:

Configuring for Apache, Version 1.3.39
 + using installation path layout: FreeBSD (config.layout)
 + activated perl module (modules/perl/libperl.a)
 + activated jk module (modules/jk/libjk.a)
Creating Makefile
Creating Configuration.apaci in src
Creating Makefile in src
 + configured for FreeBSD 5.3 platform
 + setting C compiler to gcc
 + setting C pre-processor to gcc -E
 + using "tr [a-z] [A-Z]" to uppercase
 + checking for system header files
 + adding selected modules
    o rewrite_module uses ConfigStart/End
      enabling DBM support for mod_rewrite
    o ssl_module uses ConfigStart/End
      + SSL interface: mod_ssl/2.8.22
      + SSL interface build type: DSO
      + SSL interface compatibility: enabled
      + SSL interface experimental code: disabled
      + SSL interface conservative code: disabled
      + SSL interface vendor extensions: disabled
      + SSL interface plugin: Vendor DBM (libc)
      + SSL library path: [SYSTEM]
      + SSL library version: OpenSSL 0.9.7d 17 Mar 2004
      + SSL library type: installed package (system-wide)
    o perl_module uses ConfigStart/End
      + mod_perl build type: OBJ
      + setting up mod_perl build environment
      + id: mod_perl/1.30
      + id: Perl/v5.8.5 (freebsd) [perl]
      + adjusting Apache build environment
      + enabling Perl support for SSI (mod_include)
 + enabling Extended API (EAPI)
 + using builtin Expat
 + checking sizeof various data types
 + doing sanity check on compiler and options
Creating Makefile in src/support
Creating Makefile in src/os/unix
Creating Makefile in src/ap
Creating Makefile in src/main
Creating Makefile in src/lib/expat-lite
Creating Makefile in src/modules/standard
Creating Makefile in src/modules/ssl
Creating Makefile in src/modules/perl
Creating Makefile in src/modules/jk

Continue the installation:

$ make

Assuming no errors, install Apache:

# make install

Output should be similar to:

+--------------------------------------------------------+
| You now have successfully built and installed the      |
| Apache 1.3 HTTP server. To verify that Apache actually |
| works correctly you now should first check the         |
| (initially created or preserved) configuration files   |
|                                                        |
|   /usr/local/etc/apache/httpd.conf
|                                                        |
| and then you should be able to immediately fire up     |
| Apache the first time by running:                      |
|                                                        |
|   /usr/local/sbin/apachectl start
|                                                        |
| Or when you want to run it with SSL enabled use:       |
|                                                        |
|   /usr/local/sbin/apachectl startssl
|                                                        |
| Thanks for using Apache.       The Apache Group        |
|                                http://www.apache.org/  |
+--------------------------------------------------------+

Congratulations, Apache is now installed. You can now start the HTTP server by issuing the following command (note that ’startssl’ will not work yet!):

# /usr/local/sbin/apachectl start

After the server has started, check Apache’s error log file (/var/log/error_log by default) to see if the server has indeed started. Do not rely on the status ‘apachectl’ reports. The error log should contain something like the following:

[Fri Jan  2 20:14:41 2004] [notice] Apache/1.3.39 (Unix) mod_perl/1.30 configured -- resuming normal operations
[Fri Jan  2 20:14:41 2004] [notice] suEXEC mechanism enabled (wrapper: /usr/local/sbin/suexec)

If one or both are missing, something went wrong with the compilation. Issue a ‘make clean’, check your configure options and compile again.

Now comes the fun part; configuring Apache.

In order for name-based virtual hosts to work, you need to set the following in /etc/httpd.conf:

Listen 10.0.1.1:80

<IfDefine SSL>
Listen 10.0.1.1:443
</IfDefine>

BindAddress *
NameVirtualHost *

MySQL virtualhosts. The choice of uisng SSL is up to you, there isn’t any too sensitive data transmitted on this connection.

O’Reilly’s ‘Writing Apache Modules with Perl and C’ was of huge help in this case. In it, we learn that VirtualHosts can be specified in the following fashion using mod_perl:

$VirtualHost{'192.168.2.5:80'} = {
  ServerName   => 'www.fishfries.org',
  DocumentRoot => '/home/httpd/fishfries/htdocs',
  ErrorLog     => '/home/httpd/fishfries/logs/error.log',
  TransferLog  => '/home/httpd/fishfries/logs/access.log',
  ServerAdmin  => '


',
};

However, the %VirtualHost syntax from the previous section would not work with name-based virtual hosts, since assigning a hash reference for the given IP address will overwrite the original entry. The solution is to use an array reference whose values are hash references, one for each virtual host entry. For example, like this:

$VirtualHost{'192.168.2.5'} = [
  {
     ServerName   => 'one.fish.net',
     ...
     ServerAdmin  => '


',
  },
  {
     ServerName   => 'red.fish.net',
     ...
     ServerAdmin  => '


',
  },
];

Now, pulling the data from MySQL and inserting it into the array/hashes, doesn’t require that much code. In a simple form, this could be something like this:

<Perl>

#!/usr/bin/perl
# use strict;

  use DBI;
  my $i = 0;
  use vars qw(%VirtualHost);

  # Define anonymous array in vhost hash
  $VirtualHost{'*:80'} = [];

  # open database connection
  my $dbh = DBI->connect("DBI:mysql:database=hosting;host=10.0.1.0;port=3306;mysql_compression=1", 'www', 'GoodPassword');

  my $sth = $dbh->prepare("SELECT domainname,aliases,docroot,serveradmin,suid,sgid,xferlog,errlog FROM domains");
  $sth->execute();

  # populate the anonymous array with SQL data
  while (my @row = $sth->fetchrow_array()) {
   $VirtualHost{'*:80'}->[$i]->{'ServerName'} = "@row[0]";
   $VirtualHost{'*:80'}->[$i]->{'ServerAlias'} = "@row[1]";
   $VirtualHost{'*:80'}->[$i]->{'DocumentRoot'} = "@row[2]";
   $VirtualHost{'*:80'}->[$i]->{'ServerAdmin'} = "@row[3]";
   $VirtualHost{'*:80'}->[$i]->{'User'} = "@row[4]";
   $VirtualHost{'*:80'}->[$i]->{'Group'} = "@row[5]";
   $VirtualHost{'*:80'}->[$i]->{'TransferLog'} = "@row[6]";
   $VirtualHost{'*:80'}->[$i]->{'ErrorLog'} = "@row[7]";

   # Enable userdirs only for certain hosts - needs to be rewritten dor sql...
   if ("@row[0]" eq ("www.foobar.org" || "quux.org")) {
    $VirtualHost{'*:80'}->[$i]->{'UserDir'} = "/home/*/home/public_html";
   } else {
    $VirtualHost{'*:80'}->[$i]->{'UserDir'} = "disabled";
   }

   $i++;
  }

  $sth->finish();
  $dbh->disconnect();

__END__
</Perl>

Attention! The User and Group directives are ordinarily ignored inside <VirtualHost> containers, but in a suexec-enabled server they take on new meaning for the virtual host, defining the identity under which CGI scripts requested through that host will be executed. If a virtual host doesn’t have a User directive, it inherits the server-wide value (which defines the username under which the server itself is running) which will probably result in normal, non-suexec-enabled behaviour. The same goes for Group. Suexec does not need any additional configuration directives in httpd.conf.

Tip: You can debug the Perl sections in httpd.conf file by issuing perl -cx httpd.conf, assuming that you have the appropriate shebang and __END__.

Create the SQL database and table:

mysql> create database hosting;
mysql> use hosting;

mysql> create table domains (
 did integer unsigned auto_increment,
 domainname varchar(255) unique not null,
 aliases blob,
 docroot varchar(255) not null,
 serveradmin varchar(255) default '


',
 suid varchar(32) default 'nobody',
 sgid varchar(32) default 'nogroup',
 xferlog varchar(255) default 'default',
 errlog varchar(255) default 'default',
 stats char(3) default 'off',
 backup char(3) default 'off',
 template varchar(255),
 dtadded timestamp(14),
 contract_id integer unsigned,
 primary key (did)
);

By setting the SQL defaults for the suid and sgid columns to a unprivileged user, we can easily avoid a mistake, where no suid or sgid is inserted in the SQL table. If no uid/gid is found in the SQL table, the script is run using the web server’s uid/gid, which is bad!

The ’stats’, ‘template’ and ‘backup’ columns will be used by my awstats_sql_batch and backuping Perl scripts and are thus not required if you aren’t going to use them. Both scripts use the very same SQL table as Apache. The ‘dtadded’ and ‘did’ columns just make administation easier, and they are not required either.

Don’t forget to grant SELECT privilege to the user you chose, as otherwise your virtual domain setup will fail miserably:

mysql> GRANT SELECT ON hosting.domains TO


 IDENTIFIED BY 'GoodPassword';

However, we run into a problem with name-based virtual hosts and SSL. Currently, SSL does not support name-based virtual hosting, but only IP-based hosting. In other words, you’ll need one IP address/SSL enabled host. However, we can accomplish a “half-working” solution using one certificate and mod_rewrite. The following works reasonably well, at least until TLS (the next version of SSL) starts supporting name-based virtual hosting.

<VirtualHost *:443>

 RewriteEngine On
 RewriteMap lowercase int:tolower
 RewriteMap vhost-map prg:/usr/local/etc/apache/vhost.pl

 # allow Alias /icons/ to work - repeat for other aliases
 RewriteCond %{REQUEST_URI}  !^/icons/

 # Don't apply this rule for userdirs, that start with a tilde (~)
 RewriteCond %{REQUEST_URI} !^/~(.*)
 RewriteRule ^/(.*)$ /${vhost-map:${lowercase:%{SERVER_NAME}}}/$1 [L]

 # Emulate userdir behaviour, but only for certain hosts
 RewriteCond %{HTTP_HOST} ^(cherry|quux.org)$
 RewriteRule ^/~([^/]+)/?(.*)           //home/$1/home/public_html/$2 [L]

</VirtualHost>

NOTE: not not under any circumstances set the NS flag to the rewrite rules. If you do, it will cause PHP to break with a “No input files specified.” error!

vhost.pl is a small Perl script, which pulls the virtual host’s documentroot from the SQL database. The double slashes (//) at the beginning of the paths prevent the document_root from being prefixed to the rewritten paths.

The advantage of this approach is that basically all hosts/URLs can be accessed using SSL encyption simply by prefixing https://. The problem with this approach is that you have only one certificate for all hosts, thus resulting in warnings by the browser accessing the host, saying that the hostname doesn’t match the certificate’s ‘common name’ field (except for the one host you inputted as the common name).

If you want to start Apache at boot time (i.e. when the jail is started), you can simply use a slightly modified apache.sh script, which can be found at the ports collection:

# cp /usr/ports/www/apache13/files/apache.sh /var/jail/10.0.1.1/usr/local/etc/rc.d/
# chmod 0700 /var/jail/10.0.1.1/usr/local/etc/rc.d/apache.sh

Edit the file and change “start” to “startssl” if you wish to use SSL. Don’t forget to add the following in the jail’s rc.conf:

apache_enable="YES"

Also note, that Apache needs to be started AFTER nsvsd, otherwise Apache cannot get the usernames!

LoadModule mysql_auth_module libexec/apache/mod_auth_mysql.so
AddModule mod_auth_mysql.c

PHP 5.x Installation

We’ll need some additional software, if we wish to use PHP’s extra functions. I wanted, so I installed the following inside the jailcell. You could use the ports just as well, but I found the packages to be a faster way:

# pkg_add -r libmcrypt png libxml2

On Ubuntu, do:

# apt-get install libc-client-dev libxml2-dev libfreetype6-dev libjpeg-dev libbz2-dev flex bison gcc g++ make libmysqlclient15-dev libpq-dev libpng12-dev libmcrypt-dev file autoconf libcurl3-openssl-dev

Next up, download and decompress the full PHP sources:

$ fetch http://fi.php.net/get/php-5.2.5.tar.bz2/from/this/mirror
$ tar xfvj php-5.2.5.tar.bz2
$ cd php-5.2.5

Compile and install PHP as a CGI:

$ ./configure –enable-cgi –with-pear –with-openssl \
–with-zlib –with-bz2 –enable-ftp –with-gd –with-kerberos \

–with-mysql –disable-posix –with-pgsql –enable-force-cgi-redirect \
–enable-calendar –with-imap –with-imap-ssl –with-curl \
–with-jpeg-dir=/usr/local/lib/ –with-freetype-dir
$ make
$ make test
# make install

‘Make install’ will print out some useful information you might want to note somewhere, similar to this:

Installing PHP SAPI module:       cgi
Installing PHP CGI into: 	  /usr/local/bin/
Installing PEAR environment:      /usr/local/lib/php/
[PEAR] Archive_Tar    - installed: 1.1
[PEAR] Console_Getopt - installed: 1.2
[PEAR] PEAR           - installed: 1.3.3
Wrote PEAR system config file at: /usr/local/etc/pear.conf
You may want to add: /usr/local/lib/php to your php.ini include_path
[PEAR] XML_RPC        - installed: 1.1.0
Installing build environment:     /usr/local/lib/php/build/
Installing header files:          /usr/local/include/php/
Installing helper programs:       /usr/local/bin/
  program: phpize
  program: php-config
  program: phpextdist

You’ll still need to copy over a default configuration file.

# cp php.ini-recommended /usr/local/lib/php.ini

Install Zend Optimizer for some extra speed (free download once you register):

$ tar xfvz ZendOptimizer-3.3.0-linux-glibc23-x86_64.tar.gz
$ cd ZendOptimizer-3.3.0-linux-glibc23-x86_64
# ./install

We will NOT need the shebang line in every PHP file, if we place the PHP executable (interpreter) inside each user’s DocumentRoot, and define the following Action in httpd.conf:

Action php-script /cgi-bin/php.cgi
AddHandler php-script .php .php3
AddType application/x-httpd-php-source .phps

However, be sure to check that there isn’t a ScriptAlias for /cgi-bin/ defined in httpd.conf. Now, all you need to do is copy /usr/local/bin/php to each user’s cgi-bin directory and name it php.cgi. You won’t actually HAVE to rename it to php.cgi, but I like to keep it that way for consistency.

Next, load /usr/local/Zend/etc/php.ini in your favourite text editor and define at least the following:

short_open_tag = Off
safe_mode = On
open_basedir = /home
disable_functions = phpinfo,system,exec,escapeshellarg,escapeshellcmd,
passthru,syslog,openlog,leak,disk_free_space,diskfreespace,
disk_total_space,chroot,posix_mkfifo,link,symlink,popen,proc_open
mysql.allow_persistent = Off

A few very useful modules I always install are:

# apt-get install libmagic-dev libmhash2
# pear install Mail Mail_Mime MDB2 MDB2#mysql

# pecl install pecl_http fileinfo

If you have several virtual hosts all with PHP installed as a CGI, upgrading can be made easy using find:

# find /home/*/www/ -name php.cgi -exec /bin/cp /usr/local/bin/php-cgi ‘{}’ \;

Java Installation

Java SDK (JDK) Installation

There is a shitload of dependencies… it’ll take a small eternity for it to complete. However, you can speed up the process by installing some dependencies using packages instead of ports:

# pkg_add -r linux_base unzip zip rpm perl m4 popt urwfonts xorg-libraries libiconv nspr open-motif

Installing JDK on FreeBSD is a bit tricky due to Sun’s restrictive licensing. First, grab bsd-jdk14-patches-7.tar.gz, j2sdk-1_4_2-bin-scsl.zip, j2sdk-1_4_2-src-scsl.zip and j2sdk-1_4_2_07-linux-i586.bin to /usr/ports/distfiles. After that, it’s trivial to install the JDK:

# cd /usr/ports/java/jdk14; MINIMAL=YES make all install clean

To confirm a proper installation:

$ /usr/local/linux-sun-jdk1.4.2/bin/java -version
# export JAVA_HOME=”/usr/local/jdk1.4.2″

Tomcat Installation

Once Java is installed, we can install Tomcat in the following fashion:

$ cd /usr/local

$ fetch http://archive.apache.org/dist/jakarta/tomcat-5/v5.0.30/bin/jakarta-tomcat-5.0.30.tar.gz
$ tar xfvz jakarta-tomcat-5.0.30.tar.gz
$ ln -s jakarta-tomcat-5.0.30 jakarta-tomcat-5
# /usr/local/tomcat/dist/bin/startup.sh

To start Tomcat automatically when jail is started:

# cd /usr/local/etc/rc.d
# ln -s /usr/local/jakarta-tomcat-5/bin/catalina.sh

Link Apache to Tomcat using mod_jk.

ProFTPd Installation

$ fetch ftp://ftp.proftpd.org/distrib/source/proftpd-1.2.10.tar.bz2
$ tar xfvj proftpd-1.2.10.tar.bz2

Unpack and copy appropriate module sources to proftpd-1.2.10/contrib.

$ cd proftpd-1.2.10
$ ./configure –with-modules=mod_tls:mod_diskuse:mod_md5fs –sysconfdir=/usr/local/etc \
–with-libraries=/usr/lib/ –with-openssl-dir=/usr/lib
$ make
# make install

ProFTPd is now installed. Next, lets create the certificates for TLS/SSL over
FTP.

$ cd /etc/ssl/certs
$ openssl req -new -x509 -days 365 -nodes -out rsa.pem -keyout rsa-key.pem
$ openssl dsaparam -out dsap-tmp 1024
$ openssl req -newkey dsa:dsap-tmp -x509 -days 365 -nodes -out dsa.pem -keyout dsa-key.pem
$ openssl dhparam -out dhparam.pem 1024
$ rm dsap-tmp

Finally, supervise ProFTPd. On the host system, do something like:

# mkdir -p /service/proftpd
# vim /service/proftpd/run

Place something like this in the run file:

#!/bin/sh
exec jail /usr/jail/10.0.1.1/ www 10.0.1.1 /usr/local/sbin/proftpd -n

Chmod the run file +x and supervise should bring up ProFTPd a few seconds later.

Apache Configuration

Cronolog Installation

First, I installed cronolog, which is required by my Perl script later on.

# pkg_add -r cronolog

On Ubuntu:

# apt-get install cronolog

AwStats Installation

By placing AwStats’ icons in /usr/local/www/icons/awstats/, we can use Apache’s /icons/ alias, thus making AwStats’ icons available to all virtualhosts without the need to copy them over to each virtualhost’s documentroot.

# pkg_add -r awstats
# mkdir /etc/awstats
$ cp /usr/local/www/cgi-bin/awstats.model.conf /etc/awstats/
# perl -MCPAN -e ‘install Geo::IPfree’
# perl -MCPAN -e ‘install URI::Escape’

Notice also the .temp extension. This is actually a template used by my awstats_sql_batch Perl script. More on that later on. Finally, copy over the Perl scripts that AwStats consists of.

$ cp awstats-5.4/tools/* /usr/jail/10.0.0.9/etc/awstats/
$ cp awstats-5.4/wwwroot/cgi-bin/awstats.pl /usr/jail/10.0.0.9/etc/awstats/
$ cp -r awstats-5.4/wwwroot/cgi-bin/lang /usr/jail/10.0.0.9/etc/awstats/
$ cp -r awstats-5.4/wwwroot/cgi-bin/lib /usr/jail/10.0.0.9/etc/awstats/
$ cp -r awstats-5.4/wwwroot/cgi-bin/plugins /usr/jail/10.0.0.9/etc/awstats

We still need to modify Apache’s configuration. I added this for my
awstats_batch_sql Perl script in httpd.conf:

LogFormat "%V %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\" mod_gzip:%{mod_gzip_result}n In:%{mod_gzip_input_size}n Out:%{mod_gzip_output_size}n:%{mod_gzip_compression_ratio}npct." vcommon
CustomLog "|/usr/local/sbin/cronolog --period=6hours /var/log/apache/%d-%m-%Y/%H-access.log" vcommon

Note espacially that the CustomLog directive is piped to a shell command, in this case cronolog, which splits the logfiles in to appropriate directories and files. However, cronolog does not rotate the log files. Also note, that you must specify a full path to cronolog (i.e. just logs/%Y/%m/%d/%H-access.log wouldn’t work).

The following is adapted from the Awstats FAQ. I bolded the configuration I use:


HOW TO ROTATE LOGS WITHOUT LOSING DATA:

I want to archive/rotate my logs using my web server system options or a third software (rotatelog, cronolog) but don’t want to loose any visits information during the rotate process.

SOLUTION:
If you use a rotate system (internal web server feature or third software), this means you probably not use AWStats PurgeLogFile nor ArchiveLogRecords parameter. If your config file is setup to process current log file (because you want to use the AllowToUpdateStatsFromBrowser option), if you don’t want to lose any records during the rotate process, you can just run the AWStats update process on the archived log file just after the update process using the -logfile option (This will avoid you to change the config file). If you choose (for security reason, to avoid CPU abuse on large web site or other) to make updates by your scheduler only on archive files, this means you don’t use the AllowToUpdateStatsFromBrowser “real-time” feature of AWStats. In this case all you have to do is to run the update process just after the rotate was done using a config/domain file configured to process the archived log files (using date tags of LogFile for examples). Note: For Apache users, use of cronolog seems to be a better choice than rotatelog (cronolog is available for Unix/Linux and Windows and is more flexible).


Now there’s the issue of rotating the log files. I wrote a small Perl script that gets the users’ Awstats preferences from a MySQL table, generates temporary Awstats config files from templates, and rotates the cronolog splitted log files. In other words, this script batch builds several statistics with only one cron job and one or more template files. The advantage of this approach is that normally you would have to have one cron job and one config file for each statistic you want to build; i.e. this is my solution for mass virtual hosting. Install awstats_sql_batch Perl script as described in it’s README.

Now we still need to edit /etc/awstats/awstats.conf and change the LogFormat to reflect our custom Apache log format:


LogFile="/var/log/apache/.log"
LogFormat="%virtualname %host %other %logname %time1 %methodurl %code %bytesd %refererquot %uaquot %other %gzipin %gzipout"

mod_gzip Configuration

mod_gzip is a module that allows transparent gzip compression of data between the server and client, if the client supports it (most modern clients do, amongst others Internet Explorer and Mozilla Firefox).

$ wget http://puzzle.dl.sourceforge.net/sourceforge/mod-gzip/mod_gzip-1.3.26.1a.tgz
$ tar xfvz mod_gzip-1.3.26.1a.tgz
$ cd mod_gzip-1.3.26.1a

Edit Makefile and change the build line from:

$(APXS) -Wc -Wall,-O3,-fomit-frame-pointer,-pipe -c mod_gzip.c mod_gzip_debug.c mod_gzip_compress.c -o mod_gzip.so

to the following:

$(APXS) -Wc -Wall -WO3 -Wfomit-frame-pointer -Wpipe -c mod_gzip.c mod_gzip_debug.c mod_gzip_compress.c -o mod_gzip.so
$ make APXS=/usr/local/apache/bin/apxs
# make install APXS=/usr/local/apache/bin/apxs

<IfModule mod_gzip.c>
        mod_gzip_on                   Yes
        mod_gzip_dechunk              Yes
        mod_gzip_minimum_file_size    300
        mod_gzip_maximum_inmem_size   100000
        mod_gzip_keep_workfiles       No
        mod_gzip_temp_dir             "/tmp"
        mod_gzip_item_include         file \.html$
        mod_gzip_item_include         file \.shtml$
        mod_gzip_item_include         file \.shtm$
        mod_gzip_item_include         file \.htm$
        mod_gzip_item_include         file \.txt$
        mod_gzip_item_include         file \.jsp$
        mod_gzip_item_include         file \.php$
        mod_gzip_item_include         file \.pl$
        mod_gzip_item_include         file \.cgi$
        mod_gzip_item_exclude         file \.css$
        mod_gzip_item_exclude         file \.js$
        mod_gzip_item_include         mime ^text/.*
        mod_gzip_item_include         mime ^application/x-httpd-php
        mod_gzip_item_include         mime ^httpd/unix-directory$
        mod_gzip_item_include         handler ^perl-script$
        mod_gzip_item_include         handler ^server-status$
        mod_gzip_item_include         handler ^server-info$
</IfModule>

mod_ssl Configuration

Problem: When using TLS/SSL, virtualhosts must be IP-based, but I wanted to
use name-based virtual hosting.

First, however, you need to create the certificates. I have already create my own certificate authority using newCA.pl. Next generate private key and certificate signing request:

$ DIR=`pwd`/openssl

$ openssl genrsa -out $DIR/httpd.key 2048
$ openssl req -new -key $DIR/httpd.key -out $DIR/httpd.csr

Sign the certificate using the custom CA and
mod_ssl’s sign.sh:

$ ./sign.sh $DIR/httpd.csr

After that, you can safely delete server.csr, it’s not needed anymore.
NOTE: The CA and the certificate CANNOT have the same common name!

Problem: CGI scripts run using the webserver’s UID/GID.

Solution: Use Apache’s suexec wrapper.

Problem: PHP scripts run using the webserver’s UID/GID.

Solution 1: Enable PHP’s safe_mode.

Solution 2: Compile PHP as a CGI and use Apache’s suexec wrapper.

I also uncommented the following lines in httpd.conf, so that
support for CGI and SSI are available:

   # Added for CGI
   AddHandler cgi-script .cgi .pl

   # Added for SSI
   AddType text/html .shtml
   AddOutputFilter INCLUDES .shtml
   

References

Tags: , , , , ,

Leave a Reply

Spam protection by WP Captcha-Free