ProFTPD is als FTP server vergelijkbaar met andere populaire implementaties als vsftpd, pure-ftpd of wuftpd, het protocol blijft immers steeds gelijk. Het allergrootste voordeel deze configuratie is dat de gebruikers virtueel zijn: de gebruikers bestaan alleen in MySQL maar hoeven niet als fysieke gebruikers op de server aanwezig te zijn. Daarnaast kun je de gebruikersnaam en het wachtwoord evenals de homedirectory zelf bepalen, wat deze configuratie heel flexibel maakt.

Met onderstaande handleiding kun je in een paar simpele stappen een snelle en betrouwbare FTP-server neerzetten.

Installatie van ProFTPD met Mysql back-end

Er wordt tijdend onderstaand stappenplan vanuit gegaan dat je al een werkende mysql-server hebt geïnstalleerd.

  1. Start allereerst met het installeren van de benodigde packages:

    apt-get install proftpd proftpd-mod-mysql

    Kies tijdens het installeren voor de "standalone" installatie:

    proftpd-standalone-configuratie

  2. Download het bestand met de SQL voor de proFTPd-tabellen en voeg deze toe aan een bestaande of nieuwe database. Dit kan bijvoorbeeld op onderstaande manier:

    # Download de SQL
    wget -O proftpd-structure.sql https://static.perfacilis.com/userfiles/1/proftpd-structure.sql
    # Verbind met mysql
    mysql --defaults-file=/etc/mysql/debian.cnf

    # In de prompt van SQL, maak een nieuwe database aan indien gewenst
    CREATE DATABASE `proftpd` DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
    # Maak een nieuwe gebruiker aan en stel de rechten in.
    # Verander het wachtwoord in onderstaande queries en sla het op een veilige plaats op:
    CREATE USER 'proftpd'@'localhost' IDENTIFIED BY 'wachtwoord';
    GRANT SELECT, INSERT, UPDATE, DELETE ON proftpd.* TO 'proftpd'@'localhost' IDENTIFIED BY 'wachtwoord';
    FLUSH PRIVILEGES;

    # Vervolgens wordt het gedownloadde bestand geïmporteerd
    USE proftpd;
    SOURCE proftpd-structure.sql;
    QUIT;
  3. Pas in het bestand /etc/proftpd/proftpd.conf het volgende regels aan (of uncomment deze):

    RequireValidShell off
    DefaultRoot ~
    PassivePorts 12000 12049

    User ftp-data
    Group ftp-data

    Include /etc/proftpd/modules.conf
    Include /etc/proftpd/sql.conf

    De optie PassivePorts is optioneel, pas de range indien gewenst aan en zorg dat een eventuele firewall TCP connecties op de betreffende poorten samen met poort 21 (de standaard poort voor FTP) toestaat.

    Controleer daarnaast goed of modules.conf al elders in het bestand wordt geladen, gezien de fout "error loading module" kan ontstaan als dit bestand twee keer is toegevoegd.

  4. In bovenstaande stap wordt de gebruiker en de groep ftp-data gedefinieerd, eventueel kun je hier een andere gebruiker voor kiezen - echter dient dit een gebruiker te zijn die fysiek in het systeem bestaat. Voer onderstaande opdrachten uit om de gebruiker en groep aan te maken:
    groupadd -g 2001 ftp-data
    useradd -u 2001 -s /bin/false -d /bin/null -c "proftpd user" -g ftp-data ftp-data
  5. Verplaats of verwijder het bestand /etc/proftpd/sql.conf en maak het opnieuw aan met de volgende inhoud:

    # enable sql
    SQLEngine on
    SQLAuthTypes Plaintext
    SQLAuthenticate users groups
    SQLBackend mysql

    # enable sql log
    SQLLOGFILE /var/log/proftpd/sql.log

    # connect to database
    # databasename@host database_user user_password
    SQLConnectInfo proftpddatabase@localhost proftpduser password

    # set table and column names for users
    SQLUserInfo proftpd_users user_name user_password user_id group_id user_homedir user_shell

    # set table column names for groups
    SQLGroupInfo proftpd_groups group_name group_id group_member

    # min id for uids and gids
    SQLMinID 30

    # sql where for user
    SQLUserWhereClause "user_enabled = 1"

    # sql enabled check for group
    SQLGroupWhereClause "group_enabled = 1"

    # update login count and last login
    SQLLog PASS update_login
    SQLNamedQuery update_login UPDATE "user_login_count = user_login_count + 1, user_last_login = now() WHERE user_name = '%u'" proftpd_users

     Pas in dit bestand de regel beginnend met SQLConnectInfo aan met de juiste gebruikersnaam, wachtwoord en database uit stap 2.

  6. Schakel de benodigde modules in door de volgende regels te uncommenten in /etc/proftpd/modules.conf:

    LoadModule mod_sql.c
    LoadModule mod_sql_mysql.c
  7. Start als laatste de service opnieuw op:

    /etc/init.d/proftpd restart

Gebruikers toevoegen

  1. Het toevoegen van een gebruiker gaat in zijn geheel via de database:

    mysql --defaults-file=/etc/mysql/debian.cnf
    > use proftpd;
  2. Allereerst dient de groep 'ftp-data' te worden toegevoegd aan de database (mits dat nog niet gebeurd is):

    INSERT INTO proftpd_groups SET group_id = 2001, group_name = 'ftp-data', group_member = '', group_enabled = 1;
  3. Vervolgens dient de gebruiker te worden toegevoegd aan de database (pas in onderstaande queries de vetgedruktde delen aan):

    INSERT INTO proftpd_users SET user_name = 'gebruiker', group_id = 2001, user_password = 'plainpassword', user_homedir = '/ftp/gebruiker', user_id = 2001,
    user_shell = '/bin/false', user_enabled = 1, user_login_count = 0, user_last_login = '';
    UPDATE proftpd_groups SET group_member = CONCAT(group_member, ',gebruiker') WHERE group_id = 2001;

Versleutelde wachtwoorden

In bovenstaande configuratie worden wachtwoorden als PlainText opgeslagen, handig wanneer een wachtwoord verloren raakt, maar absoluut onhandig wanneer een wachtwoord of je hele database op straat komt te liggen. De grootste uitdaging is dat de algoritmes die worden ondersteund door ProFTPD niet worden ondersteund door MySQL en vice-versa, CRYPT uitgezonderd.

  1. Pas de regel beginnend met SQLAuthTypes in /etc/proftpd/sql.conf als volgt aan:

    SQLAuthTypes PlainText Crypt
  2. Vervolgens kun je bestaande gebruikers aanpassen door gebruik te maken van mysql's ENCRYPT-functie:

    UPDATE proftpd_users SET user_password = ENCRYPT(user_password);

Limieten instellen

Met onderstaande stappen zorg je dat elke gebruiker een maximale hoeveelheid schrijfruimte in zijn/haar FTP map heeft, ookwel Quota genoemd. Dit stuk is niet vereist voor een juiste werking van de FTP-server maar vaak wel handig.

  1. Schakel de QuotaEngine in door aan /etc/proftpd/proftpd.conf het volgende toe te voegen:

    QuotaEngine on
    Include /etc/proftpd/sql_quota.conf
  2. Maak vervolgens een nieuw bestand /etc/proftpd/sql_quota.conf aan met de volgende inhoud:

    # Enable quota
    QuotaDirectoryTally on
    #QuotaDisplayUnits Mb
    QuotaShowQuotas on

    # Quota log
    QuotaLog /var/log/proftpd/quota.log

    # sql get-quota-limit
    SQLNamedQuery get-quota-limit SELECT "user_name, ql_quota_type, ql_per_session, ql_limit_type, ql_bytes_in_avail, ql_bytes_out_avail, ql_bytes_xfer_avail, \
    ql_files_in_avail, ql_files_out_avail, ql_files_xfer_avail FROM proftpd_quota_limits WHERE user_name = '%{0}' AND ql_quota_type = '%{1}'"

    # sql get-quota-tally
    SQLNamedQuery get-quota-tally SELECT "user_name, qt_quota_type, qt_bytes_in_used, qt_bytes_out_used, qt_bytes_xfer_used, qt_files_in_used, qt_files_out_used, \
    qt_files_xfer_used FROM proftpd_quota_tallies WHERE user_name = '%{0}' AND qt_quota_type = '%{1}'"

    # sql update-quota-tally
    SQLNamedQuery update-quota-tally UPDATE "qt_bytes_in_used = qt_bytes_in_used + %{0}, qt_bytes_out_used = qt_bytes_out_used + %{1}, \
    qt_bytes_xfer_used = qt_bytes_xfer_used + %{2}, qt_files_in_used = qt_files_in_used + %{3}, qt_files_out_used = qt_files_out_used + %{4}, \
    qt_files_xfer_used = qt_files_xfer_used + %{5} WHERE user_name = '%{6}' AND qt_quota_type = '%{7}'" proftpd_quota_tallies

    # sql insert-quota-tally
    SQLNamedQuery insert-quota-tally INSERT "%{0}, %{1}, %{2}, %{3}, %{4}, %{5}, %{6}, %{7}" proftpd_quota_tallies

    QuotaLimitTable sql:/get-quota-limit
    QuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally

    # list quota informations
    # when listing directories
    SQLNamedQuery gettally SELECT "round((qt_bytes_in_used / (1024 * 1024)),2) FROM proftpd_quota_tallies WHERE user_name = '%u'"
    SQLNamedQuery getlimit SELECT "ROUND((ql_bytes_in_avail / (1024 * 1024)),2) FROM proftpd_quota_limits WHERE user_name = '%u'"
    SQLNamedQuery getfree SELECT "ROUND(((proftpd_quota_limits.ql_bytes_in_avail - proftpd_quota_tallies.qt_bytes_in_used) / (1024 * 1024)),2) \
    FROM proftpd_quota_limits,proftpd_quota_tallies WHERE proftpd_quota_limits.user_name = '%u' AND proftpd_quota_tallies.user_name = '%u'"

    SQLShowInfo LIST "226" "Used %{gettally}MB from %{getlimit}MB. You have %{getfree}MB available space."
  3. Schakel de benodigde modules in door de volgende regels uit te commenten in /etc/proftpd/modules.conf:

    LoadModule mod_quotatab.c
    LoadModule mod_quotatab_sql.c
  4. Start als laatste de service opnieuw op:

    /etc/init.d/proftpd restart
  5. Vervolgens kun je een schijfruimte limiet van 50MiB instellen voor een bepaalde gebruiker:

    INSERT INTO proftpd_quota_limits SET user_name = 'gebruiker', ql_quota_type = 'user', ql_per_session = false, ql_limit_type = 'soft', ql_bytes_in_avail = 50 * POWER(2, 10);

ProFTPD samen met Apache - oplossing voor FTP rechtenprobleem

Doordat bestanden geuploadt door via de FTP server als gebruiker gelijkgesteld worden aan de gebruiker en groep die in de mysql database is ingesteld, kan het voorkomen dat Apache niet bij deze bestanden kan of de bestanden niet kan bewerken (dit kan bijvoorbeeld problemen opleveren bij het updaten van Wordpress). Dit kun je op twee manieren oplossen:

De gebruiker ftp-data toevoegen aan de groep www-data

Deze oplossing heeft mijn persoonlijke voorkeur. De bestanden worden op deze manier namelijk nog steeds als de ftp-gebruiker weggeschreven maar de groep www-data heeft wel de juiste rechten op deze bestanden. Voeg op onderstaande manier de gebruiker ftp-data toe aan de groep www-data:

usermod -a -G www-data ftp-data

De groep www-data toevoegen in ProFTPD

De tweede oplossing is het toevoegen van groep www-data aan ProFTPD. Op deze manier wordt het bestand weggeschreven met als eigenaar de betreffende gebruiker, maar omdat deze gebruiker in de groep www-data zit zijn de groepsrechten van deze groep op dat bestand van kracht. Zoek hervoor als eerste het unieke nummer van de groep op:

cat /etc/group | grep www-data

Voeg vervolgens de groep toe zoals bovenstaand beschreven. Wanneer de groep-id bijvoorbeeld 33 is, dan volgt daar onderstaande query uit:

INSERT INTO proftpd_groups SET group_id = 33, group_name = 'www-data', group_member = 'gebruiker', group_enabled = 'true';

Debuggen

Het debuggen van de configuratie gaat het gemakkelijkste en het snelst door de logs te volgen terwijl je in een apart venster (bijvoorbeeld via FileZilla) een FTP-verbinding maakt. In de logbestanden wordt meestal een duidelijke foutmelding gegeven waarmee de problemen opgelost kunnen worden. De 'algemene' fouten kun je terugvinden in het algemene logbestand voor ProFTPD:

tail -f /var/log/proftpd/proftpd.log

Fouten die specifiek gerelateerd zijn aan de configuratie met mysql vindt je in het sql.log:

tail -f /var/log/proftpd/sql.log

Fouten die specifiek gerelateerd zijn aan de quota configuratie vindt je in het quota.log:

tail -f /var/log/proftpd/quota.log

ProFTPD service stopt vanzelf

In Ubuntu 14.04 (waarschijnlijk ook in andere Linux relases) komt het voor dat ProFTPD elke nacht vanzelf stopt. De Logrotate service ruimt dagelijks de logs op, waarbij de service opnieuw wordt gestart (zie /etc/logrotate.d/proftpd-basic). Doordat bij het herstaren van de service het stoppen van ProFTPD te lang duurt wordt er geen nieuwe instance meer gestart. Om dit op te lossen kun je een "retry" parameter toevoegen aan de cronjob.

Zoek in het bestand /etc/init.d/proftpd de volgende regel op:

start-stop-daemon --stop --signal $SIGNAL --quiet --pidfile "$PIDFILE"

En pas deze als volgt aan:

start-stop-daemon --stop --signal $SIGNAL --retry 1 --quiet --pidfile "$PIDFILE"

Conclusie

In een paar simpele stappen heb je een goed werkende en stabiele FTP server geïnstallerd. Doordat deze FTP server "standalone" is, is deze - behalve de mysql laag - niet afhankelijk van andere services en hoeft deze ook geen andere service te vertragen.

Ondanks dat deze methode een tamelijk veilige manier is om gebruikersgegevens op te slaan, is een FTP-server op zichzelf een beveiligingsriciso; doordat FTP door zo veel servers geaccepteerd wordt is het een veelvuldig misbruikt doelwit voor kwaadwillenden. Omdat het FTP-protocol niet perfect is adviseren we om ProFTPD zo veel mogelijk up to date te houden en indien mogelijk een firewall in te stellen en indien mogelijk Fail2ban toe te voegen.