I am not responsible if your devices send you back in time, explodes, implodes, bricks or flies into space from the use of any software I put up.


Sunday, February 26, 2012

Configuring Pure-FTPd with TLS on OpenWRT

This is a simple guide to configuring Pure-FTPd on OpenWRT which is available on the TP-Link WR1043ND OpenWRT Image I compiled.
Pure-FTPd is a secure FTP Server by www.pureftpd.org
Configuring OpenWRT is more like configuring a barebones Linux Terminal Server than many other commercial Routers. There is NO LuCI GUI for Pure-FTPd.
The reason why I chose Pure-Pure-FTPd is
  • Offers Optional TLS Encryption on OpenWRT
  • Not a very big FTP Server
First, you have to login via an SSH Client to your OpenWRT Router and get use to it if you are going to leverage on the Power and Flexibility on OpenWRT (If you custom compile OpenWRT without LuCI GUI you get to save even more flash memory!).

You can use any SSH Client, in Windows I recommend PuTTY while on Linux the built-in SSH will do. To learn how to login via CLI and edit files please go through the previous postings.

Pure-FTPd uses a few configuration files to set itself up.
Originally Pure-FTPd is designed to run without config files. Just run the binary with the correct switches it should set itself up but in OpenWRT it is designed to read the config file to set itself up.

To see the full switches on Pure-FTPd on OpenWRT simply cat the initialization scripts.

The initialization script is located in /etc/init.d/pure-ftpd

Run cat /etc/init.d/pure-ftpd  and you should see the following:
#!/bin/sh /etc/rc.common
# Copyright (C) 2006-2011 OpenWrt.org


# TODO: allow multiple instance to run with different pid-files

# XXX: pure-ftpd changes it's name to 'pure-ftpd (SERVER) ...'

append_bool() {
        local section="$1"
        local option="$2"
        local value="$3"
        local _val
        config_get_bool _val "$section" "$option" '0'
        [ "$_val" -gt 0 ] && append args "$3"

append_string() {
        local section="$1"
        local option="$2"
        local value="$3"
        local _val
        config_get _val "$section" "$option"
        [ -n "$_val" ] && append args "$3 $_val"

start_instance() {
        local section="$1"

        config_get_bool enabled "$section" 'enabled' '1'
        [ $enabled -gt 0 ] || return 1

        append_string "$section" trustedgid "-a"
        append_string "$section" syslogfacility "-f"
        append_string "$section" fortunesfile "-F"
        append_string "$section" maxidletime "-I"
        append_string "$section" maxdiskusagepct "-k"
        append_string "$section" limitrecursion "-L"
        append_string "$section" anonymouscancreate "-M"
        append_string "$section" maxload "-m"
        append_string "$section" quota "-n"
        append_string "$section" altlog "-O"
        append_string "$section" passiveportrange "-p"
        append_string "$section" forcepassiveip "-P"
        append_string "$section" anonymousratio "-q"
        append_string "$section" userratio "-Q"
        append_string "$section" anonymousbandwidth "-t"
        append_string "$section" userbandwidth "-T"
        append_string "$section" minuid "-u"
        append_string "$section" trustedip "-V"
        append_string "$section" tls "-Y"
        append_string "$section" tlsciphersuite "-J"

        append_bool "$section" uploadscript "-o"
        append_bool "$section" natmode "-N"
        append_bool "$section" autorename "-r"
        append_bool "$section" nochmod "-R"
        append_bool "$section" antiwarez "-s"
        append_bool "$section" allowuserfxp "-w"
        append_bool "$section" allowanonymousfxp "-W"
        append_bool "$section" prohibitdotfileswrite "-x"
        append_bool "$section" prohibitdotfilesread "-X"
        append_bool "$section" allowdotfiles "-z"
        append_bool "$section" customerproof "-Z"
        append_bool "$section" anonymouscantupload "-i"
        append_bool "$section" createhomedir "-j"
        append_bool "$section" keepallfiles "-K"
        append_bool "$section" norename "-G"
        append_bool "$section" dontresolve "-H"
        append_bool "$section" verboselog "-d"
        append_bool "$section" displaydotfiles "-D"
        append_bool "$section" anonymousonly "-e"
        append_bool "$section" brokenclientscompatibility "-b"
        append_bool "$section" notruncate "-0"
        append_bool "$section" logpid "-1"
        append_bool "$section" ipv4only "-4"
        append_bool "$section" ipv6only "-6"

        append_string "$section" bind "-S"
        append_string "$section" login "-l"

        append_bool "$section" noanonymous "-E"
        append_bool "$section" chrooteveryone "-A"
        append_string "$section" maxclientsperip "-c"
        append_string "$section" maxclientsnumber "-C"
        append_string "$section" peruserlimits "-y"
        append_string "$section" umask "-U"

        append_string "$section" port "-S"
        append_string "$section" authentication "-l"

        service_start /usr/sbin/pure-ftpd -B $args

start() {
        config_load "pure-ftpd"
        config_foreach start_instance "pure-ftpd"

stop() {
        service_stop /usr/sbin/pure-ftpd
From what you can read you can tell the initialization scripts translates switches like "-k" into human readable config options and all the available switches are shown above.

To control Pure-FTPd the commands are as follow
Syntax: /etc/init.d/pure-ftpd [command]

Available commands:
        start   Start the service
        stop    Stop the service
        restart Restart the service
        reload  Reload configuration files (or restart if that fails)
        enable  Enable service autostart
        disable Disable service autostart
Eg: To start Pure-FTPd run
/etc/init.d/pure-ftpd start

It is that simple so CLI isn't really as difficult as most imagine.

Pure-FTPd also uses 2 database file as access-control lists they are located at
(This is like your UNIX passwd file except it is for Pure-FTPd it contains user accounts, shell etc)
(It is Pure-FTPd database file I like to think of it as your UNIX shadow file)

First you add a system user and system group as a default account on UNIX that Pure-FTPd will create virtual user from.
addgroup pure_ftpd_grp #or any othername as you want

adduser -H -G pure_ftpd_grp pure_ftpd_user #adds user to previously created group - change groupname accordingly, afterwards you will be asked for password for user (-H indicates that I don't want to assign home directory - if you want to you need to change -H to -h /homedirectory)
For me I assign the home directory to the directory I want to be logging from FTP.
FTP will fail if the directory change permission is not correct so you need to set it to the right directory.

Next you add the virtual user to Pure-FTPd Access Control

pure-pw useradd FTP_LOGIN -u pure_ftpd_user -d /ftp_directory #change FTP_LOGIN, pure_ftpd_user and /ftp_directory as you wish (pure_ftpd_user is same as you created in previous step). I set the same directory as above.

To see the Pure-FTPd accounts use the commands
pure-pw useradd <login> [-f <passwd file>] -u <uid> [-g <gid>]
                -D/-d <home directory> [-c <gecos>]
                [-t <download bandwidth>] [-T <upload bandwidth>]
                [-n <max number of files>] [-N <max Mbytes>]
                [-q <upload ratio>] [-Q <download ratio>]
                [-r <allow client ip>/<mask>] [-R <deny client ip>/<mask>]
                [-i <allow local ip>/<mask>] [-I <deny local ip>/<mask>]
                [-y <max number of concurrent sessions>]
                [-z <hhmm>-<hhmm>] [-m]

pure-pw usermod <login> -f <passwd file> -u <uid> [-g <gid>]
                -D/-d <home directory> -[c <gecos>]
                [-t <download bandwidth>] [-T <upload bandwidth>]
                [-n <max number of files>] [-N <max Mbytes>]
                [-q <upload ratio>] [-Q <download ratio>]
                [-r <allow client ip>/<mask>] [-R <deny client ip>/<mask>]
                [-i <allow local ip>/<mask>] [-I <deny local ip>/<mask>]
                [-y <max number of concurrent sessions>]
                [-z <hhmm>-<hhmm>] [-m]

pure-pw userdel <login> [-f <passwd file>] [-m]

pure-pw passwd  <login> [-f <passwd file>] [-m]

pure-pw show    <login> [-f <passwd file>]

pure-pw mkdb    [<puredb database file> [-f <passwd file>]]
                [-F <puredb file>]

pure-pw list    [-f <passwd file>]

-d <home directory> : chroot user (recommended)
-D <home directory> : don't chroot user
-<option> '' : set this option to unlimited
-m : also update the /etc/pureftpd.pdb database
For a 1:10 ratio, use -q 1 -Q 10
To allow access only between 9 am and 6 pm, use -z 0900-1800
Every time you modify the FTP Accounts you need to run pure-pw mkdb to apply the changes.

Now we edit the Pure-FTPd config file at /etc/config/pure-ftpd

vi /etc/config/pure-ftpd

I will provide you a sample so you don't have to read about all the options and what are they used for.

config pure-ftpd
        option port             '21'
        option noanonymous      '1'
        option chrooteveryone   '1'
        option maxclientsperip  '20'
        option maxclientsnumber '40'
        option umask            '133:022'
        option authentication   'unix'
        option enabled          '1'
        option passiveportrange '1985:2000'
        option tls              '1'
        option daemonize        '1'
        option authentication   'puredb:/etc/pureftpd.pdb'
        option prohibitdotfileswrite '1'
        option prohibitdotfilesread '1'
        option userbandwidth '50:50'
#       option natmode '1'
#       option brokenclientscompatibility' '1'
Basically this runs pure-ftpd on Port 21 with passive ports from 1985-2000 with anonymous login disabled, tls set on optional (if client request it will provide if not just unencrypted communication).
The default directory is chrooted which means user cannot change from their default directory to /.
The # comments out lines, it should only be enabled if there are incompatibilities with the firewalls and clients. After you get good at FTP you might want to tweak the bandwidth and number of connections and other advance settings for more performance.

Next we will do TLS encryption

You have to generate a self-signed certification using OpenSSL, you should do it on the PC and copy the certificate to the router unless you are a masochist to want to do it on a 400MHZ MIPS Processor.

To generate the certificate run on your PC (with OpenSSL installed) run 
openssl req -x509 -days 365 -nodes -newkey rsa:1024 -keyout [Destination Directory]/pure-ftpd.pem -out [Destination Directory]/pure-ftpd.pem

You can use days to specify how long before the self-signed certificate expire.

This will create a certificate file called pure-ftpd.pem for TLS in the destination directory.
Note the destination directory has to be the SAME.

Pure-FTPd reads the TLS certificate in /etc/ssl/private so we make a directory there as is it is not created by default

mkdir -p /etc/ssl/private

Then we Upload the file to the router to the directory we created.You should have /etc/ssl/private/pure-ftpd.pem This is location where Pure-FTPd will try to find a certificate to use for TLS communication if any.

Then we change the file permission

chmod 600 /etc/ssl/private/pure-ftpd.pem
Finally we have to open the ports on the firewall to allow FTP communication.
Note there are 2 types of ports we need to open FTP Active Ports and Passive Ports.
We do it via LuCI GUI, go to Network -> Firewall -> Traffic Rules and Add the following Ruleset

Save and Apply.

Finally we restart Pure-FTPd so it will relaunch with the correct settings.
/etc/init.d/pureftpd restart

So now you need an FTP Client. On Web Browsers like Firefox there is a client built in by default.
To utilise TLS encryption you have to use better clients, I recommend FileZilla.
You specify TLS in the Connection Options.
You have to get a Dynamic DNS address to reverse lookup your Router's external IP Address.
There are free and paid options for home users free is usually good enough.
AFRAID @ http://freedns.afraid.org/ is a good Free Dynamic DNS Address Provider(Funny name though =) ).
It allows numerous domains for you to choose from.

Again DDNS Updating Scripts is built into my 3.25 Kernel Trunk Router's Image by default but I will not be writing about setting up Dynamic DNS records (I modified the DDNS scripts to support HTTPS (by skipping checks) default OpenWRT DDNS does not have that).

You should be able to login to your FTP Server from everywhere on Earth from the Internet in both Active and Passive Modes provided your workplace does not block FTP ports if so simply change port 21 to one of the unblock ports.

Hopefully my guide is simple enough for you to understand how to setup an FTP server on OpenWRT.