Proftpd mit PostgreSQL auf Debian

Heute benötigten wir für einen Kunden FTP-Zugang zu einem unserer Server. Ich setze dazu normalerweise ProFTPd ein, einen flexiblen FTP-Daemon für unixoide Betriebssysteme. Als Alternative bietet sich PureFTPd an, den ich z.B. auf meinem privaten Server zwecks Anbindung an LDAP verwendet habe. Für die gestellten Anforderungen genügt aber ProFTPd vollauf. Wir setzen auf Debian 5.0 Lenny als Betriebssystem, welches für ProFTPd fertige Pakete bereithält, die rasch installiert sind:

ftp-server$> sudo apt-get  update
ftp-server$> sudo apt-get install proftpd

Im Gegensatz zu sonst wollen wir die Benutzer aber nicht in einer lokalen Datei speichern, sondern in einer PostgreSQL-Datenbank. Das von uns dafür zusätzlich benötigte Paket proftpd-mod-pgsql wird von APT automatisch mit installiert und eine Anleitung auf HowtoForge liefert uns wichtige Hinweise. Als erstes müssen wir für ProFTPd die Module mod_sql und mod_sql_postgres aktivieren; dazu entfernt man in der Datei /etc/proftpd/modules.conf einfach die Kommentarzeichen vor den entsprechenden LoadModule xyz.c-Zeilen.

Selbst virtuelle Nutzer brauchen ein Linux-Benutzerkonto, mit dem sie “offiziell” arbeiten. Darum erzeugen wir als nächstes den Benutzer ftpusers, den wir auch gleich der Gruppe www-data hinzufügen, damit z.B. Apache ohne Umschweife auf diese Dateien zugreifen könnte:

ftp-server$> sudo adduser ftpusers
ftp-server$> usermod -a -G www-data ftpusers

Jetzt kommt die eigentliche Konfigurationsarbeit in den Dateien /etc/proftpd/proftpd.conf und /etc/proftpd/sql.conf – zwecks Übersichtlichkeit sind die Dateien auf das Wesentliche reduziert:

/etc/proftpd/proftpd.conf

    Include /etc/proftpd/modules.conf
    UseIPv6                         on
    IdentLookups                    off
    ServerName                      "Produktivserver"
    ServerType                      standalone
    DeferWelcome                    off

    MultilineRFC2228                on
    DefaultServer                   on
    ShowSymlinks                    on

    TimeoutNoTransfer               600
    TimeoutStalled                  600
    TimeoutIdle                     1200

    DisplayLogin                    welcome.msg
    DisplayChdir                    .message true
    ListOptions                     "-l"

    DenyFilter                      *.*/

    # Use this to jail all users in their homes
    DefaultRoot                     ~
    # We're using /bin/false for our virtual users
    RequireValidShell               off
    Port                            21
    MaxInstances                    30
    User                            proftpd
    Group                           nogroup
    # we want new files to be writeable by the group
    Umask                           002  002
    AllowOverwrite                  on
    # We only need authentication agains PostgreSQL
    AuthOrder       mod_sql.c
    TransferLog /var/log/proftpd/xferlog
    SystemLog   /var/log/proftpd/proftpd.log

    <IfModule mod_quotatab.c>
    QuotaEngine off
    </IfModule>
    <IfModule mod_ratio.c>
    Ratios off
    </IfModule>
    <IfModule mod_delay.c>
    DelayEngine on
    </IfModule>
    <IfModule mod_ctrls_admin.c>
    AdminControlsEngine off
    </IfModule>

    # Include our configuration file for PostgreSQL-auth
    Include /etc/proftpd/sql.conf
    # Just a precaution
    RootLogin off
    AuthPAM off

/etc/proftpd/sql.conf

    <IfModule mod_sql.c>
    SQLBackend      postgres
    SQLEngine on
    SQLAuthenticate on
    SQLAuthTypes Plaintext Crypt
    SQLConnectInfo database@db-server.tao.at user password

    # uid of "ftpusers" - pay your /etc/passwd a visit ;)
    SQLDefaultUID 1001

    # gid of "www-data" - you can look it up in your /etc/group
    SQLDefaultGID 33
    SQLDefaultHomedir /home/ftpusers
    # Describes both users/groups tables
    # "ftp" is the schema we use
    SQLUserInfo ftp.users userid passwd uid gid homedir shell

    # We're not using groups but this is needed by proftpd anyway
    SQLGroupInfo ftp.groups groupname gid members
    SQLLogFile /var/log/proftpd/proftpd-sql.log
    </IfModule>

Damit haben wir auf dem späteren FTP-Server alle notwendigen Änderungen getroffen. Nun geht es daran, womöglich eine Datenbank (CONNECT), ein Schema (USAGE), die Tabellen und einen Nutzer (z.B. proftpd), der auf all das zugreifen darf, zu erstellen. Zur Erstellung der Tabellen kann man folgenden Schnippsel verwenden:

    CREATE TABLE ftp.users (
    pkid      serial      PRIMARY KEY,
    userid    text        NOT NULL UNIQUE,
    passwd    text,
    uid       int DEFAULT 1001,
    gid       int DEFAULT 33,
    homedir   text,
    shell     text
    );
    CREATE TABLE ftp.groups (
    pkid        serial      PRIMARY KEY,
    groupname   text        NOT NULL UNIQUE,
    gid         int,
    members     text
    );
    GRANT SELECT ON ftp.users TO proftpd;
    GRANT SELECT ON ftp.groups TO proftpd;

Da es sich um PostgreSQL handelt, müssen wir jetzt noch einen Eintrag zur /etc/postgresql/8.3/main/pg_hba.conf hinzufügen, damit sich unser frisch gebackener FTP-Server auch zur Datenbank verbinden kann:

db-server$> sudo vim /etc/postgresql/8.3/main/pg_hba.conf
host    proftpd     proftpd     127.0.0.1/32      md5
db-server$> sudo /etc/init.d/postgresql-8.3 restart

Und nicht darauf vergessen, 127.0.0.1 durch die IP des FTP-Servers zu ersetzen. Damit ist eigentlich alles erledigt – außer der Firewall. Wir ändern flux die Konfigurationsdatei, tragen Port 21 ein und starten die Firewall neu, womit die Sache erledigt sein sollte. Zu guter Letzt erstellen wir noch einen Ordner für unseren ersten FTP-Benutzer und passen ihn so an, dass für alle hochgeladenen Dateien automatisch die Gruppe www-data übernommen wird – ohne Rücksicht auf ProFTPd:

ftp-server$> sudo -u ftpusers mkdir /home/ftpusers/user1
ftp-server$> sudo chown :www-data /home/ftpusers/user1
ftp-server$> sudo chmod g+ws /home/ftpusers/user1

Jetzt kann man den Benutzer “user1” endlich in der Datenbank eintragen; userid=”user1“, passwd=”secret“, uid=”1001“, gid=”33“, homedir=”/home/ftpusers/user1“, shell=”/bin/false“. In diesem Fall wird das Passwort leider im Klartext gespeichert, was durch den Einsatz der crypt-Funktion aus dem postgresql-contrib-8.3 – Paket geändert werden könnte; für uns spielt das jedoch vorerst keine Rolle.

Bevor wir testen können, ob alles geklappt hat, müssen wir noch den FTP-Server neu starten, damit die neue Konfiguration übernommen wird:

ftp-server$> sudo /etc/init.d/proftpd restart

Jetzt verwenden wir einen FTP-Client, wie z.B. Filezilla, um uns anzumelden. Das klappt wunderbar, aber sobald wir versuchen, etwas hochzuladen, kommt die Fehlermeldung: Not connected to a server. Was ist passiert? Die Anmeldung klappt und dann – nichts? Ein kurzer Blick auf das Log von Filezilla gibt den entscheidenden Hinweis: der letzte Befehl war PASV. Das der Wechsel in den passiven Modus bei aktiver Firewall in die Hose gehen kann, hätte man sich eigentlich denken können. Da Probleme in bzw. aus dieser Richtung aber nichts ungewöhnliches sind, ist die Lösung schnell gefunden: In der Firewall definieren wir eine Ausnahme für einen gewissen Portbereich, z.B. 21000 bis 22000, was für iptables im Format 21000:22000 anzugeben ist. ProFTPd weiß von sich aus nichts davon, dass es diesen Portbereich nutzen soll, darum fügen wir der /etc/proftpd/proftpd.conf noch folgende Zeile hinzu und starten den Server wieder neu:

PassivePorts 21000 22000

Noch ein Test – und es funktioniert astrein.

Done.™