the djb way


anonymous file transfer with ftpd

ftpd is the second server in the publicfile suite. It provides an anonymous FTP service. We say anonymous because ftpd treats all requests as coming from user anonymous. Although it recognizes the USER and PASS keywords in the FTP protocol, there are no login or authentication facilities provided.

Also, ftpd is designed strictly for serving files. Clients can use "get" commands to download files, but no "put" for upload.

The requirements, configuration and usage of ftpd closely follow those described for httpd. You will need daemontools and ucspi-tcp, as usual. And again we will set up the service to run with a non-root account, this one named pubftp.

In the setup described here, the ftp archive is segregated from the http archive in the directory /var/public/ftp:

# umask 0022
# mkdir -p /var/public/ftp/0

There's that 0 (zero) directory again, just like we saw with the httpd service. Only this time, it is the *only* directory that ftpd will serve documents from. The FTP protocol doesn't provide a means for name-based virtual hosting, as is possible with the HTTP protocol and httpd service.

(Note: Bernstein uses the same 0 directory naming convention as the default archive directory for both services, so that httpd and ftpd may be configured to serve from the same document archive. We describe a shared archive configuration in a later section.)

Make directories for defining the publicfile ftpd service:

# mkdir -p /var/svc.d/publicfile-ftpd/log

Copy this run script into /var/svc.d/publicfile-ftpd/run:

# publicfile-ftpd/run
# daemontools run script for publicfile ftpd service

exec 2>&1
echo "*** Starting publicfile-ftpd on ${FTP_ARCHIVE}:"
exec envuidgid pubftp softlimit -o25 -d250000 \
  tcpserver -vDRH -l0 -b50 \
  -c ${CONLIMIT} \
  -B '220 Features: a p .
  0 21 \
    /usr/local/publicfile/bin/ftpd ${FTP_ARCHIVE}

Make it executable, chmod 755. Notice the use of envuidgid again to run the server. The ftpd executable itself will drop priveleges to the $UID and $GID setup by envuidgid, so we don't need the -U option for tcpserver. The tcpserver -B option is also used to print a banner whenever a client connects. The service listens on the usual ftp port 21.

Note also that the tcpserver invocation in this run script doesn't use any tcprules to restrict connections. The idea with publicfile is that you intend for your files to be public. Otherwise, Bernstein might have named the package privatefile. But if you do want to set up a server with access restricted to certain clients by IP address, use the -x switch to tcpserver, specifying rules in /etc/tcprules as usual.

Next copy this run script into /var/svc.d/publicfile-ftpd/log/run for the logging service:

# publicfile-ftpd/log/run
# daemontools run script for publicfile ftpd logger
exec setuidgid multilog multilog t /var/multilog/ftpd 

Set executable with chmod 755. Create the usual directory for multilog:

# mkdir -p /var/multilog/ftpd
# chown multilog /var/multilog/ftpd

Activate the ftpd service now, linking into /service:

# ln -s /var/svc.d/publicfile-ftpd /service/ftpd

Put some test file in /var/public/ftp/0/testme.txt and point your ftp client at it. If the server doesn't respond, go through the usual trouble-shooting procedures, make sure the run scripts are executable, that multilog owns the log directory, and that the softlimit parameters aren't too tight.

When the server is serving, you can then populate the directory /var/public/ftp/0 with the documents for your site.

The rules for file permissions are the same as those we described for the publicfile httpd server. The files you intend to serve must be readable by everyone, usually mode 644. Also, any directories within the archive must at least have permissions of 555 (usually 755). That is, all directories must have execute bits (also known as the "search" bits) set for everyone.

publicfile ignores files beginning with a dot. Also, publicfile will not serve files with the execute bit set for "other", but not set for "user", This feature is intended to let users mark files as hidden from publicfile, while still letting the file be readable by other users on the host.

publicfile ftpd directory listings will not report filenames containing spaces, tildes, and/or control characters. But these files are still accessible for anyone who knows the name. For example:

# cd /var/public/ftp/0
# echo "hello world" > no\~tice.txt

The file no~tice.txt will not show up in a directory listing for ftp users browsing the archive. But the file can still be retrieved:

$ ftp\~tice.txt

Ahhh, the perfect way to obscure your highly classified military documents...


What about your users? At many sites, Paula may want to put documents in her own directory in ~/public_ftp that are accessible over the Internet via:

To handle this, create a subdirectory for Paula within /var/public/ftp/0/users/paula, with a symbolic link back to her home directory:

# mkdir /var/public/ftp/0/users/paula
# chown paula:group /var/public/ftp/0/users/paula
# ln -s /var/public/ftp/0/users/paula /home/paula/public_ftp

(In the above example, we assume Paula's ~/public_ftp is created for the first time. If it already exists, move it out of the way, make the link, then copy the contents back into the new ~/public_ftp link.)

The symbolic link we made for Paula may seem to be in the opposite direction you might have first expected. Note that all files served by publicfile must physically reside within its archive. This is because publicfile chroots to the directory specified in the argument to the invocation of ftpd. It can't see any files outside of this path.

Since the FTP protocol doesn't support virtual hosts, all ftp requests that resolve to this servers's IP address will be served from the same file archive. This means that, if you are serving virtual hosts from httpd, you will have to decide how to organize directories within your ftpd archive to segregate the material for each site. For example:

# cd /var/public/ftp/0
# mkdir
# mkdir
# mkdir

The owners of each site should understand, of course, that all material from any directory in the archive will be accessible by all users reaching this site, whatever hostname they used to reach it.

The alternative, as mentioned with httpd, is to set up IP-based virtual hosts. This means having the IP address space to start with, a different public IP address allocated for each site. Then alias the network interface with each IP address, and run separate instances of ftpd binding to each IP address. Each ftpd instance would then be set up to serve from its own separate archive.

Copyright © 2002, 2003, 2004, Wayne Marshall.
All rights reserved.

Last edit 2004.03.08, wcm.