Home > Appunti > Gestione utenti FTP con proFTPD e MySQL

Gestione utenti FTP con proFTPD e MySQL

Inserito da amedeo on January 27, 2012

Nel mondo GNU/Linux è molto diffuso il server FTP proFTD, considerato affidabile e con buone prestazioni. Purtroppo la gestione degli utenti, così come viene installato di default, non è molto comoda ed obbliga alla creazione di utenti interativi sul server, cosa che potrebbe non essere desiderata quando il servizio da dare prevede esclusivamente l'accesso FTP e WEB.

Il fatto di avere anche gli utenti interattivi non danneggia in sé le prestazioni del servizio FTP o WEB ma può rappresentare un ulteriore potenziale problema di sicurezza a fronte di una caratteristica non utilizzata.

Fortunatamente proFTP ci viene incontro ed ha la possibilità di configurare la gestione degli utenti tramite diversi backend tra cui alcuni database SQL. 

Tra i differenti tipi di database disponibili, prenderemo in considerazione MySQL, che è il più diffuso, ma in linea di massima potete utilizzarne un altro, quale l'ottimo PostgreSQL, Sqlite o ODBC.

La descrizione di alcune operazioni si riferisce a distribuzioni GNU/Linux derivate da Debian per cui se ne utilizzate un tipo differente dovrete apportare le modifiche del caso ai comandi di installazione dei moduli. In particolare, queste note si riferiscono ad unserver con Ubuntu Server 10.4 LTS.

Quello che otterremo alla fine della procedura descritta sarà quindi un server FTP la cui utenza viene gestita su database MySQL, con la creazione automatica della directory utente contenente la struttura base standard per il sito dello stesso.

Lo scopo di questo articolo è mostrare come configurare il server proFTP, senza entrare nel merito dell'installazione e di come accedere al database, per cui partiamo dal presupposto di avere un server con sia proFTP che MySQL installati e che sappiate come interagire col sistema e con MySQL per creare e modificare i file interessati e per creare e popolare le tabelle necessarie.

Ovviamente dovremo per prima cosa creare un database dove mantenere le informazioni relative ai nostri utenti. Nel nostro caso prendiamo in considerazione un database dedicato ma nulla impedisce che sia condiviso per centralizzare la gestione di altri servizi come, ad esempio, la posta elettronica.

Creiamo quindi un database proftpd ed un utente proftpd con accesso in lettura ed aggiornamento ed inserimento di record. Effettueremo le suddette operazioni utilizzando un account amministratore del database, in quanto l'utente proftpd dovrà avere un accesso al database limitato alle operazioni necessarie a leggere le informazioni degli account, aggiornarne alcuni campi ed inserire i record per il log dei trasferimenti.

Iniziamo con la tabella groups che utilizzeremo per memorizzare le informazioni relative ai gruppi di utenti:

CREATE TABLE IF NOT EXISTS `groups` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `groupname` varchar(64) NOT NULL,
  `gid` bigint(20) unsigned NOT NULL,
  `members` varchar(256) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `groupname` (`groupname`),
  UNIQUE KEY `gid` (`gid`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
 

Come si può notare, la tabella è abbastanza semplice. Contiene i campi per

  • groupname: il nome del gruppo
  • gid: (Group ID), l'identificativo del gruppo
  • members: uno o più nomi utente appartenenti al gruppo.

L'accesso viene configurato nel file sql.conf con la direttiva:

  • SQLGroupInfo groups groupname gid members
    che ne descrive la struttura al server 

Va notato che utilizzando la direttiva SQLAuthenticate users* groups* ,per ogni gruppo possono essere presenti più record, con differenti utenti nella colonna members ed allo stesso tempo in quella colonna possono essere elencati più utenti (separati da virgole).

Potete approfondire l'argomento visitando la pagina sulla direttiva SQLAuthenticate di proFTP.

Proseguiamo con la creazione della tabella users dove manterremo le informazioni relative agli utenti:

CREATE TABLE IF NOT EXISTS `users` (
  `id` bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
  `username` varchar(64) NOT NULL,
  `passwd` varchar(256) NOT NULL,
  `uid` int(10) UNSIGNED NOT NULL,
  `gid` int(10) UNSIGNED NOT NULL,
  `fullname` varchar(256) DEFAULT NULL,
  `homedir` varchar(256) NOT NULL,
  `shell` varchar(256) DEFAULT '/bin/false',
  `lastlogin` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `lastlogout` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `logincount` bigint(20) UNSIGNED NOT NULL DEFAULT '0',
  `disabled` tinyint(1) DEFAULT '0',
  `expiration` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
 

la tabella contiene i campi per:

  • username: nome utente
  • passwd: la password dell'utente
  • uid: User ID, identificativo utente
  • gid: Group ID, identificativo gruppo
  • fullname: nome completo dell'utente
  • homedir: il percorso della cartella home
  • shell: la shell per l'accesso interattivo
  • lastlogin: data e ora dell'ultimo accesso
  • lastlogout: data e ora dell'ultimo logout
  • logincount: contatore degli accessi
  • disabled: indica se l'utente è disabilitato
  • expiration: data di termine validità per l'accesso

Per avere un livello di sicurezza più alto, nel campo passwd utilizzeremo la cifratura, compatibile con il meccanismo di autenticazione utilizzato da proFTP.

Vedremo più avanti come i campi disabled e expiration ci permettano di aggiungere due condizioni per convalidare l'accesso dell'utente; tramite essi potremo disabilitare l'utente senza la necessità di cancellara l'account ed impostare una data di scadenza oltre la quale l'utente non potrà più collegarsi.

L'accesso a questa tabella viene configurato nel file sql.conf le righe:

  • SQLUserInfo users username passwd uid gid homedir shel
    che descrive a proFTP la struttura della tabella.
  • SQLUserWhereClause  "disabled=0 AND (NOW()<=expiration OR expiration='0000-00-00 00:00:00')"
    che aggiunge una clausola WHERE per tutte le query effettuate per richiedere dati dell'utente; in particolareverifica che l'account non sia disabilitato o scaduto.
  • SQLLog PASS counter
    SQLNamedQuery counter UPDATE "lastlogin=now(), logincount=logincount+1 WHERE `username`='%u'" users
    che istruiscono il server ad aggiornare il contatore di accessi e la data di ultimo accesso per l'utente ad ogni login.
  • SQLLog BYE,QUIT,EXIT time_logout
    SQLNamedQuery time_logout UPDATE "lastlogout=now() WHERE username='%u'" users
    che istruiscono il server ad aggiornare la data di ultimo logout per l'utente
  • SQLShowInfo PASS "230" "Last login was: %{login_time}"
    SQLNamedQuery login_time SELECT "lastlogin from users where username='%u'"
    che istruiscono proFTP a visualizzare la dicitura di ultimo login effettuato alla connessione dell'utente

Ora aggiungiamo una tabella accesslog per memorizzare le operazioni di accesso e disconnessione dal nostro server FTP dove troveremo le indicazioni di login, logout ed i tentativi di accesso falliti:

CREATE TABLE IF NOT EXISTS `accesslog` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(64) NOT NULL,
  `host` varchar(256) NOT NULL,
  `ip` varchar(32) NOT NULL,
  `action` varchar(15) NOT NULL,
  `accesstime` datetime NOT NULL,
  `success` tinyint(1) DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
 

In questa tabella troviamo i campi:

  • username: il nome dell'utente che ha effettuato l'operazione
  • host: il nome host del client da cui è stata effettuata la richiesta
  • ip: l'indirizzo del client
  • action: l'operazione effettuata
  • accesstime: l'orario dell'operazione
  • success: riporta se l'operazione sia andata a buon fine o no (1 = buon fine, 0 = errore)

In generale troveremo le operazioni di login e logout andate a buon fine ed i tentativi di accesso (password errata, utente inesistente) falliti.

La tabella viene configurata per l'utilizzo nel file sql.conf, con le righe:

  • SQLLog PASS accesslog1
    SQLLog BYE,QUIT,EXIT accesslog1
    SQLNamedQuery accesslog1 INSERT "NULL, '%u', '%h', '%a', '%m', now(), '1'" accesslog

    che istruiscono proFTP ad inserire una nuova riga per ogni accesso (login) e disconnessione (logout)  effettuati con successo
  • SQLLog ERR_USER,ERR_PASS accesslog2
    SQLNamedQuery accesslog2 INSERT "NULL, '%U', '%h', '%a', '%m', now(), '0'" accesslog
    che istruiscono proFTP ad inserire una riga per ogni tentativo di accesso fallito

Per tenere traccia delle operazioni, creiamo ora la tabella xferlog che ci servirà per memorizzare le operazioni di trasferimento file:

CREATE TABLE IF NOT EXISTS `xferlog` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(64) NOT NULL DEFAULT '',
  `filename` text,
  `size` bigint(20) DEFAULT NULL,
  `host` tinytext,
  `ip` tinytext,
  `action` tinytext,
  `duration` tinytext,
  `localtime` timestamp NULL DEFAULT NULL,
  `success` tinyint(1) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `idx_usersucc` (`username`,`success`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;
 

la tabella contiene i campi:

  • username: il nome dell'utente che ha effettuato il trasferimento
  • filename: il nome del file trasferito
  • size: le dimensioni del file
  • host: il nome host del client che ha effettuato l'operazione
  • ip: l'indirizzo del client che ha effettuato l'operazione
  • action: il comando eseguito, troveremo STOR per gli upload e RETR per i download
  • duration: la durata dell'operazione
  • localtime: l'orario (sul server) dell'operazione
  • success: indica se l'operazione sia andata a buon fine o se sia fallita.

La configurazione per l'utilizzo viene effettuata in sql.conf nelle righe:

  • SQLLog RETR,STOR transfer1
    SQLNamedQuery  transfer1 INSERT "NULL, '%u', '%f', '%b', '%h', '%a', '%m', '%T', now(), '1'" xferlog
    che istruisce proFTP ad inserire una riga per ogni trasferimento di file effettuato correttamente
  • SQLLog ERR_RETR,ERR_STOR transfer2
    SQLNamedQuery  transfer2 INSERT "NULL, '%u', '%f', '%b', '%h', '%a', '%m', '%T', now(), '0'" xferlog
    che istruisce proFTP ad inserire una riga per ogni operazione di trasferimento andata male

Nel file /etc/proftpd/sql.log andremo a configurare quanto necessario per indicare a proFTP quale database utilizzare e come interfacciarsi alle tabelle.

Per l'accesso al database assumiamo di avere:

  • username: proftpd
  • host: 127.0.0.1
  • database: proftpd
  • passwordFTPD-MYSQL-USER-PSSWORD

quindi dovrete apportare le necessarie modifiche per adattare la configurazione alla vostra situazione nella riga che contiene:

  • SQLConnectInfo proftpd@127.0.0.1 proftpd FTPD-MYSQL-USER-PSSWORD

Abbiamo ancora alcune direttive da prendere in esame:

  • SQLBackend mysql
    indica che va utilizzato MySQL come database; in realtà potrebbe essere omesso se nel file /etc/proftpd/modules.conf viene disabilitato il modulo postgres altrimenti deve comparire e specificare mysql o postgres a seconda del database server utilizzato.
  • SQLAuthenticate users* groups* 
    indica la modalità di autenticazione 
  • SQLAuthTypes Crypt
    per forzare l'utilizzo di password crittografate nel db; utilizzando phpMyAdmin per inserire gli utenti nel db, bisognerà quindi utilizzare la funzione ENCRYPT() per il campo passwd
  • CreateHome on skel /usr/local/etc/proftpd/skel dirmode 700
    serve ad indicare che all'atto del login, se non esiste ancora la struttura di directory per l'utente, questa verrà creata automativamente ed alla directory principale verranno assegnati i permessi indicati (in questo caso accesso solo al proprietario).
    La struttura di modello deve essere presente nel percorso indicato, verrà creata nel percorso indicato come homedir nella tabella utenti econ il contenuto del modello (quindi nel nostro caso il contenuto di /usr/local/etc/proftpd/skel).
    Questa opzione è molto comoda per avere una struttura standard per i nuovi siti web, con una serie di directory e file di servizio; un utilizzo tipico è in concomitanza delle direttive Error della configurazione dei web server virtuali di Apache, ad esempio, per:
    • ErrorDocument 404 /error/404.html
      creeremo una cartella error ed in essa il file 404.html con il contenuto personalizzato da visualizzare in caso di pagina inesistente
  1. <IfModule mod_sql.c>
  2. #
  3. # Choose a SQL backend among MySQL or PostgreSQL.
  4. # Both modules are loaded in default configuration, so you have to specify the backend
  5. # or comment out the unused module in /etc/proftpd/modules.conf.
  6. # Use 'mysql' or 'postgres' as possible values.
  7. #
  8. SQLBackend mysql
  9. #
  10. SQLEngine on
  11. SQLAuthenticate users* groups*
  12. #
  13. ## Use a backend-crypted or a crypted password
  14. ## may user MySQL ENCRYPT funtion to encript passwords
  15. #
  16. SQLAuthTypes Crypt
  17. #
  18. ## Set minimum GID and UID for SQL-managed accounts
  19. #
  20. SQLMinUserGID 5001
  21. SQLMinUserUID 5001
  22. #
  23. ## Database Connection:
  24. # SQLConnectionInfo dbname@hostname username password
  25. #
  26. SQLConnectInfo proftpd@127.0.0.1 proftpd FTPD-MYSQL-USER-PSSWORD
  27. #
  28. ##Describes both users/groups tables
  29. #
  30. # SQLGroupInfo <tablename> <groupname fieldname> <gid fieldname> <members fieldname>
  31. # SQLUserInfo <tablename> <username fieldname> <password fieldname> <uid fieldname> <gid fieldname> <home dir fieldname> <shell path fieldname>
  32. #
  33. SQLGroupInfo groups groupname gid members
  34. SQLUserInfo users username passwd uid gid homedir shell
  35. #
  36. ## Create user directory skeleton
  37. ## user directory skeleton must be created (not provided with default installation) and must NOT be world-writeable
  38. #
  39. CreateHome on skel /usr/local/etc/proftpd/skel dirmode 700
  40. #
  41. ## Add custom WHERE clause to every user query:
  42. ## Account must be NOT disabled and NOT expired ('disable' and 'expiration' fields must be added to users table)
  43. ##
  44. SQLUserWhereClause "disabled=0 AND (NOW()<=expiration OR expiration='0000-00-00 00:00:00')"
  45. #
  46. ## Log the user logging in:
  47. ## Increment login counter and update last login date ('lastlogin' and 'logincount' fields must be added to users table)
  48. #
  49. SQLLog PASS counter
  50. SQLNamedQuery counter UPDATE "lastlogin=now(), logincount=logincount+1 WHERE `username`='%u'" users
  51. #
  52. ## insert log record for succesful login and logout
  53. #
  54. SQLLog PASS accesslog1
  55. SQLLog BYE,QUIT,EXIT accesslog1
  56. SQLNamedQuery accesslog1 INSERT "NULL, '%u', '%h', '%a', '%m', now(), '1'" accesslog
  57. #
  58. ## insert log record for login fail
  59. #
  60. SQLLog ERR_USER,ERR_PASS accesslog2
  61. SQLNamedQuery accesslog2 INSERT "NULL, '%U', '%h', '%a', '%m', now(), '0'" accesslog
  62. #
  63. ## logout log
  64. ## update logout date ('lastlogout' field must be added to users table)
  65. #
  66. SQLLog BYE,QUIT,EXIT time_logout
  67. SQLNamedQuery time_logout UPDATE "lastlogout=now() WHERE username='%u'" users
  68. #
  69. #
  70. ## display last login time when PASS command is given
  71. #
  72. SQLNamedQuery login_time SELECT "lastlogin from users where username='%u'"
  73. SQLShowInfo PASS "230" "Last login was: %{login_time}"
  74. #
  75. ## transfers Log in mysql
  76. #
  77. SQLLog RETR,STOR transfer1
  78. SQLNamedQuery transfer1 INSERT "NULL, '%u', '%f', '%b', '%h', '%a', '%m', '%T', now(), '1'" xferlog
  79. #
  80. SQLLog ERR_RETR,ERR_STOR transfer2
  81. SQLNamedQuery transfer2 INSERT "NULL, '%u', '%f', '%b', '%h', '%a', '%m', '%T', now(), '0'" xferlog
  82. #
  83. </IfModule>

Comments:

Inserito da Andrea il
Ciao, ci voleva una quida così dettagliata per configurare ProFTPD con MySQL, ho cercato in rete per mari e monti ma senza risultati per giorni poi ... eccola precisa e perfetta COMPLIMENTI

Solo un piccolo problemino con la creazione di una sola tabella, quella USERS che quando inserisco il codice SQL per la creazione mi restituisce il seguente errore:

#1072 - Key column 'userid' doesn't exist in table

========================================================================

CREATE TABLE IF NOT EXISTS `users` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(64) NOT NULL,
`passwd` varchar(256) NOT NULL,
`uid` int(10) unsigned NOT NULL,
`gid` int(10) unsigned NOT NULL,
`fullname` varchar(256) DEFAULT NULL,
`homedir` varchar(256) NOT NULL,
`shell` varchar(256) DEFAULT '/bin/false',
`lastlogin` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`lastlogout` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
`logincount` bigint(20) unsigned NOT NULL DEFAULT '0',
`disabled` tinyint(1) DEFAULT '0',
`expiration` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
PRIMARY KEY (`id`),
UNIQUE KEY `userid` (`userid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;

========================================================================

Mi puoi aiutare??? Grazie in anticipo ;-)
Inserito da Andrea il
Ho trovato l'errore, nell'attesa ho smanettato un pochino e sono arrivato alla conclusione che se mettiamo nella riga:

`username` varchar(64) NOT NULL,

Dovremmo metterlo anche nella riga:
UNIQUE KEY `userid` (`userid`)

Quindi l'errore sta proprio in:
UNIQUE KEY `userid` (`username`)

Ma per comodità l'ho modificata così:
UNIQUE KEY `username` (`username`)

Una domanda, come inserisco gli utenti con la relativa password ed il percorso dir ???
Inserito da amedeo il
Scusa per il ritardo estremo (ho molto poco tempo da dedicare a queste pagine). Fortunatamente vedo che hai risolto da solo il problema; ho corretto la riga che hai segnalato rimasta errata a causa delle classiche modifiche dell'ultimo momento (sul nome del campo e sfuggita per l'indice).
Per la gestione puoi utilizzare phpMyAdmin ed inserire manualmente i dati necessari. I rete puoi anche trovare uno o due applicativi PHP che probabilmente hanno bisogno di qualche piccolo ritocco (sempre per i nomi dei campi) ma in linea di massima vanno bene. Purtroppo al momento non ricordo di preciso dove reperirli.
Rispondi



(La tua email non sarà mostrata pubblicamente)

Inserire le lettere ed i numeri mostrati nell'immagine.Codice captchaFai clic sull'immagine per vedere un altro captcha.