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.
|