Cover V10, I05
Article

may2001.tar


Virtual Hosting, FTP, and LDAP

Tristan Greaves

Virtual hosting (serving more than one domain from a single server) is a prime area of development for many ISPs, with Apache generally being the Web server of choice. However, the Web hosting is just one side of the story -- customers must also be granted whatever access is appropriate to maintain their sites. Recently, we performed a move of all the domains that our ISP division (Argonet) hosts to a new architecture. We saw this as an opportunity to re-evaluate our approach to solving the problem.

We had two primary requirements:

1. Customer information had to be stored in a flexible way, for maintenance purposes.

2. We needed a secure, stable ftp server that would "lock down" customers to their home directories, using our specified information store. Only ftp was to use this resource, thus denying customers any other control over the machine (telnet, etc.)

Previously, the account details for customers were being stored in a typical UNIX password fashion, and we wanted to get away from this approach. We decided upon LDAP as a mechanism for storing account details. LDAP is well-used for authentication purposes, and modules exist for many languages to interface with it. The aim was to develop the management tools in Perl.

Getting OpenLDAP Up and Running

LDAP is a client-server protocol for accessing a directory service. It harks back to the days of X.500, but now may be used with other directory services. We required an "all-in-one" LDAP package, that implemented the protocols as well as the database storage mechanisms. Specifically, we chose the latest stable version of OpenLDAP (1.2.11), to be deployed in a Linux 2.2.x environment. The system's sole purpose was to authenticate ftp accounts, and so the database design is deliberately simplistic. Integration into an existing LDAP database would take a little more thought.

The installation process followed the standard documentation, which can be found at http://www.openldap.org/, along with the relevant source downloads. slapd.conf is the core configuration file for slapd (the OpenLDAP project's LDAP server). There are three core directives to be looking at:

suffix          "dc=your-domain, dc=com"
rootdn          "cn=Admin, dc=your-domain, dc=com"
rootpw          your-secret-password
The rootdn and rootpw are the effective user name and passwords that will be used by systems that access the database, which I will cover later. The above is a very simple example, and it is recommended that you employ encrypted passwords for authentication, and again, the documentation will point you in the right direction. Listing 1 shows a complete sample slapd.conf. (All listings for this article are avaiable at: http://www.sysadminmag.com.)

Please be sure to look fully into the security of your set-up. By default, your database will be world readable by any host that can see it on the Internet! So, make good use of the "access" parameter and firewalling elsewhere in your network to lock things down to your preference.

After slapd was up and running, the key issue was to implement the object class to store account information in. The OpenLDAP schema may be extended in this way to customize the database (LDAP uses concepts of objects and classes, instead of items like fields and tables in relational databases).

It was important to implement a format that our ftp daemon can understand. As discussed in detail later, we based it upon the recommended configuration for John Morrissey's mod_ldap ProFTPD module.

As a result, the following was created in local.oc.conf:

# FTP account object class.

objectclass ftpAccount
        requires
                objectclass,
                cn,
                uid,
                homeDirectory
        allows
                userPassword,
                accountStatus,
                loginShell,
                uidNumber,
                gidNumber
Most of these attributes will be familiar to systems administrators, with the possible exception of "cn". This stands for "common name", and for the purposes of this class, we will utilize it to store the domain name in. Because our implementation is domain-centric, any administrative work will most likely use the domain as a search term, for example.

With this schema, every domain that the system knows about has an associated ftp account (the domain name in "cn", the ftp account name in "uid"). There is one account per domain.

Here is a quick summary of the class attributes:

objectclass -- The class of the entry (ftpAccount)

cn -- Domain name (described above)

uid -- ftp account name

userPassword -- Password for ftp authentication (see later)

accountStatus -- For administrative use (open?, payment late?, etc.)

loginShell -- Not applicable (users do not have shell access)

uidNumber -- UID granted to the user

gidNumber -- GID granted to the user

Please note that uidNumber/gidNumber do not apply, as we override these values as part of our ftp server configuration. Details are provided later.

It was necessary to reference this file in the master slapd configuration file, slapd.conf, with the line:

include         /usr/local/etc/openldap/local.oc.conf
This is all that is required to make our new class "go live", apart from a quick HUP to the server. Only one issue remained for us -- the database didn't have any account information in it yet!

Populating the Database

First, the existing account information for the hosted domains was exported to a simple CSV file. A typical line would be:

www.some-domain.com,fred,AaBbCcDdEeFfg,/virtual/www.some-domain.com
where the fields indicate the domain's name, the account to use for ftp uploads, the encrypted password associated with the account, and the home directory (where the Web pages are stored).

A Perl script was then created that would take the data from this file, and create corresponding entries in our LDAP database. This is domains2ldap (see Listing 2). The script will need to be adjusted to suit your needs. Specifically, you'll need to change the details of the user that has permission to alter the LDAP database and where the database actually is. Net::LDAP is required. It is naturally available at CPAN (http://www.cpan.org/) under the name perl-ldap. I used version 0.22.

Manual Manipulation of the Database

This script aside, OpenLDAP comes with several tools to assist in dealing with the database.

To add a new account in this manual way, put something like the following in /tmp/add.tmp:

dn:            uid=ftpfoo, dc=my-domain, dc=com
objectclass:   ftpAccount
uid:           ftpfoo
cn:            www.foobar.com
homeDirectory: /home/vhosts/www.foobar.com/
userPassword:  {crypt}AaBbCcDdEeFfg
This is for the virtual host www.foobar.com, to be accessed with the ftp account "ftpfoo". The encrypted form of their password has been used, but a plain-text one is usable by prefixing it with {clear} instead. (The configuration issues involved with this will be discussed later).

Next, invoke ldapadd as follows:

ldapadd -D "cn=Admin, dc=your-domain, dc=com" -w <Password> -f /tmp/add.tmp
The -D parameter should be the reference for the user with permissions to perform the addition. This will typically be identical to the "rootdn" as described in slapd.conf earlier in this article. Consequently, the -w parameter is the password associated with the "rootdn", which is stored in "rootpw". This will perform the addition, binding as Admin for the permissions required.

Deleting entries is easier. The following will remove the previous entry:

ldapdelete -D "cn=Admin, dc=your-domain, dc=com" -w <Password> "uid=ftpfoo"
Refer to the man pages for more information (and for the command ldapmodify).

Linking in the ftp Server

Our choice of ftp server was ProFTPD (http://www.proftpd.net). It is secure, with excellent configurability, and (as previously mentioned) there is a module available that allows user authentication via LDAP.

We employed 1.2.0rc2, but encountered problems with the version of the mod_ldap module bundled with it -- it simply didn't work. This was solved by utilizing the latest version from the module's author; the home page of the module is: http://horde.net/~jwm/software/proftpd-ldap/.

The new mod_ldap.c was copied into the "contrib" directory of the ProFTPD source tree, and the system was compiled as follows:

./configure --with-modules=mod_ldap
make
make install
Naturally, your system architecture may require a few tweaks, which the documentation at the ProFTPD Web site will aid you with.

The key elements of ProFTPD's configuration file (proftpd.conf) to enable LDAP-based authentication are:

LDAPServer               ldap.your-domain.com
LDAPDNInfo               "cn=Admin,dc=your-domain,dc=com" "your-secret-password"
LDAPDoAuth               on "dc=your-domain,dc=com" "uid=%u"
LDAPDefaultAuthScheme    "crypt"
LDAPDoUIDLookups         off
LDAPDoGIDLookups         off
LDAPNegativeCache        on
LDAPDefaultGID           50
LDAPDefaultUID           50
In our environment, all of the account home directories were owned by a generic user/group combination -- ProFTPD is handling access, after all. Thus, Lookups are disabled, and the default GID and UID configured. Second, encrypted passwords are the default. If you examine domains2ldap (Listing 2) closely, you will see that the imported (encrypted) passwords are prefaced by {crypt}. This isn't strictly necessary, due to the LDAPDefaultAuthScheme line, but it is good practice to assume anything could happen (i.e., some plain-text passwords may one day need to be used).

At this point, everything should magically work. If not, check the logs of both ProFTPD and slapd to see if anything obvious is broken. Unfortunately, in our case of the "bad" mod_ldap module, the problem wasn't obvious -- the LDAP database was being successfully searched, it was just that ProFTPD rejected the user every time. So, persevere!

A Note on ftp Virtual Hosting

As this article shows, access to the virtual host directories is performed on a username/password basis. All users were informed of the host they should use. Additionally, because we host their DNS too, we added an entry for ftpadmin.their-domain.com for easy access.

It would be useful if separate authentication databases could be used on a per-domain basis. In most cases, it will not be feasible to allocate each Web site its own IP address, so a process similiar to Apache's "Name-Based" virtual hosting is appropriate, perhaps to allow users to have their own, separate, anonymous ftp sites for customers to grab content from (but running on one server).

Unfortunately, this feature is not available with the ftp protocol. ftp does not utilize an equivalent of the HTTP protocol's "Host-Header" system, so there is no way for a server to determine which specific domain the user is trying to ftp to, if they share the same IP address.

Tristan Greaves works as a systems engineer for Argogroup, performing customer consultancy duties in the area of wireless Internet interoperability. He previously worked for ICL, assisting with the development of the European network for Sega's Dreamcast games console, after graduating in Computer Science from the University of Southampton, England. He can be reached at: tristan@extricate.org. Thanks to Andy Loukes, the other engineer on this project, for his advice.