Proftpd + MySQL + Quotas HOWTO
Generalmente cuando tratemos de poner a funcionar un servicio cliente/servidor, debemos hacer mayor hincapié en la parte del servidor y configurarla de forma adecuada al uso que se le pueda dar en un futuro. Al principio es probable que el número de usuarios sea pequeño y no merezca la pena configurar tasas de transferencia o quotas en disco, pero a medida que se ofrece un servicio, las exigencias van cambiando y se hacen cada vez más necesarias.
Usaremos Proftpd como servidor con dos particularidades. La primera de ellas es el uso de MySQL para autentificación de usuarios virtuales, esto quiere decir que no habrá cuentas del sistema (con o sin shell) para el acceso ftp, con la consiguiente limpieza de /etc/passwd ;). La segunda particularidad es el uso de cuotas de disco para limitar el espacio que otorgamos a cada usuario.
Antes de empezar decir que la instalación y configuración del servidor se ha realizado en una Debian 3.1, siendo extrapolable a cualquier otra distribución con suma facilidad. Ahora si, comencemos.
El primer paso es la instalación del software necesario, una vez instalado miramos si Proftpd tiene los módulos necesarios para seguir con la configuración. Si tiramos de código fuente hemos de tener en cuenta los parámetros de configure (./configure --with-modules=mod_sql:mod_sql_mysql:mod_quotatab:mod_quotatab_sql --with-includes=/usr/include/mysql):
gprs:~# apt-get install proftpd-mysql mysql-server ... gprs:~# proftpd -l Compiled-in modules: mod_core.c mod_xfer.c mod_auth_unix.c mod_auth_file.c mod_auth.c mod_ls.c mod_log.c mod_site.c mod_auth_pam.c mod_quotatab.c mod_sql.c mod_sql_mysql.c mod_quotatab_sql.c mod_ratio.c mod_tls.c mod_rewrite.c mod_radius.c mod_wrap.c mod_quotatab_file.c mod_delay.c mod_readme.c mod_ifsession.c mod_cap.cUna vez instalado en el sistema tendremos que crear un grupo/usuario que será el dueño en cuanto a permisos de los archivos que se transfieran a cada directorio. Lo explico mejor, cuando un usuario se conecte al servidor ftp tendrá su login, su contraseña, su directorio raíz... como si fuera un usuario real del sistema; sin embargo no tiene uid, por lo que sus archivos quedarían sin propietario. Pues para eso sirve este usuario que creamos a continuación. Os estareis preguntando, según lo que explicas... ¿no podría un usuario machacar los archivos de otro?. Podría, pero como cada uno está enjaulado en su directorio raíz (DefaultRoot ~/), no es capaz de entrar en el directorio de otro usuario. Espero que se haya entendido:
gprs:~# groupadd -g 5500 ftpgroup gprs:~# adduser -u 5500 -s /bin/false -d /bin/null -c "proftpd user" -g ftpgroup ftpuserEl siguiente paso es crear la base de datos y las tablas donde guardaremos usuarios virtuales, límites de cuotas, logs... en MySQL. También vamos a crear un usuario MySQL que será el único que tenga acceso a esta estructura:
gprs:~# mysql -uroot -ppassword
grant usage on *.* to 'proftpd'@'localhost' identified by 'password'
create database ftpdb;
grant select, insert, update on ftpdb.* to proftpd@localhost identified by 'password';
use ftpdb;
#
# Table structure for table `ftpgroup`
#
CREATE TABLE `ftpgroup` (
`groupname` varchar(16) NOT NULL default '',
`gid` smallint(6) NOT NULL default '5500',
`members` varchar(16) NOT NULL default '',
KEY `groupname` (`groupname`)
) TYPE=MyISAM COMMENT='ProFTP group table';
INSERT INTO `ftpgroup` VALUES ('ftpgroup', 5500, 'ftpuser');
#
# Table structure for table `ftpquotalimits`
#
CREATE TABLE `ftpquotalimits` (
`name` varchar(30) default NULL,
`quota_type` enum('user','group','class','all') NOT NULL default 'user',
`per_session` enum('false','true') NOT NULL default 'false',
`limit_type` enum('soft','hard') NOT NULL default 'soft',
`bytes_in_avail` float NOT NULL default '0',
`bytes_out_avail` float NOT NULL default '0',
`bytes_xfer_avail` float NOT NULL default '0',
`files_in_avail` int(10) unsigned NOT NULL default '0',
`files_out_avail` int(10) unsigned NOT NULL default '0',
`files_xfer_avail` int(10) unsigned NOT NULL default '0'
) TYPE=MyISAM;
#
# Table structure for table `ftpquotatallies`
#
CREATE TABLE `ftpquotatallies` (
`name` varchar(30) NOT NULL default '',
`quota_type` enum('user','group','class','all') NOT NULL default 'user',
`bytes_in_used` float NOT NULL default '0',
`bytes_out_used` float NOT NULL default '0',
`bytes_xfer_used` float NOT NULL default '0',
`files_in_used` int(10) unsigned NOT NULL default '0',
`files_out_used` int(10) unsigned NOT NULL default '0',
`files_xfer_used` int(10) unsigned NOT NULL default '0'
) TYPE=MyISAM;
#
# Table structure for table `ftpuser`
#
CREATE TABLE `ftpuser` (
`id` int(10) unsigned NOT NULL auto_increment,
`userid` varchar(32) NOT NULL default '',
`passwd` varchar(32) NOT NULL default '',
`uid` smallint(6) NOT NULL default '5500',
`gid` smallint(6) NOT NULL default '5500',
`homedir` varchar(255) NOT NULL default '',
`shell` varchar(16) NOT NULL default '/sbin/nologin',
`count` int(11) NOT NULL default '0',
`accessed` datetime NOT NULL default '0000-00-00 00:00:00',
`modified` datetime NOT NULL default '0000-00-00 00:00:00',
PRIMARY KEY (`id`)
) TYPE=MyISAM COMMENT='ProFTP user table'
INSERT INTO `ftpuser` VALUES (1, 'testaccount', 'ftppasswd', 5500, 5500, '/home/testdomain.com', '/sbin/nologin',0,'','');
Algunas aclaraciones al respecto. La tabla ftpgroup lista los usuarios de cada grupo, como solo habrá un grupo (inicialmente) no necesita más registros para comenzar a usar el servicio. La tabla ftuser guarda los usuarios, estadísticas y es la usada por Proftpd para comparar los datos de autentificación. Usando la directiva 'asdfasf' haremos que Proftpd cree el directorio raíz del usuario si este no existe. La tabla ftpquotalimits define los límites de cuota en disco y ftpquotatalities va guardando los datos de cada usuario para totalizar y comprobar que el límite no sobrepasa lo acordado en ftpquotalimits. Si el valor de algún campo de límite es '0' automáticamente se definirá como ilimitado.Hemos creado un usuario testaccount para probar el servicio. Agreguemos ahora una cuota para ese usuario de 15Mb:
INSERT INTO ftpquotalimits VALUES("testaccount","user","true","hard","15728640","0","0","0","0","0");Antes de probar que todo funciona debemos decirle a Proftpd que debe tener en cuenta todos estos datos, el archivo principal de configuración se encuentra (en Debian) en /etc/proftpd.conf y quedará de la siguiente forma:
# Configuraciones generales
ServerName "Nuestro MegaFTP Server"
ServerType Standalone
ServerAdmin root@localhost
# Ocultamos todo lo posible a usuarios externos
ServerIdent on "Bienvenido a MegaFTP Server..."
DeferWelcome on
DefaultServer on
# Permitimos resumes, configuramos puerto y demás
AllowStoreRestart on
Port 21
Umask 022
MaxInstances 30
User nobody
Group nogroup
# Enjaulamos a nuestros usuarios (chroot)
DefaultRoot ~
AllowOverwrite on
# Para MySQL
SQLAuthTypes Plaintext
SQLConnectInfo ftpdb@localhost proftpd password
SQLUserInfo ftpuser userid passwd uid gid homedir shell
SQLGroupInfo ftpgroup groupname gid members
SQLMinID 500
SQLHomedirOnDemand on
SQLLog PASS updatecount
SQLNamedQuery updatecount UPDATE "count=count+1, accessed=now() WHERE userid='%u'" ftpuser
SQLLog STOR,DELE modified
SQLNamedQuery modified UPDATE "modified=now() WHERE userid='%u'" ftpuser
# Para las cuotas en disco
QuotaEngine on
QuotaDirectoryTally on
QuotaDisplayUnits Mb
QuotaShowQuotas on
SQLNamedQuery get-quota-limit SELECT "name, quota_type, per_session, limit_type, bytes_in_avail, bytes_out_avail, bytes_xfer_avail, files_in_avail, files_out_avail, files_xfer_avail FROM ftpquotalimits WHERE name = '%{0}' AND quota_type = '%{1}'"
SQLNamedQuery get-quota-tally SELECT "name, quota_type, bytes_in_used, bytes_out_used, bytes_xfer_used, files_in_used, files_out_used, files_xfer_used FROM ftpquotatallies WHERE name = '%{0}' AND quota_type = '%{1}'"
SQLNamedQuery update-quota-tally UPDATE "bytes_in_used = bytes_in_used + %{0}, bytes_out_used = bytes_out_used + %{1}, bytes_xfer_used = bytes_xfer_used + %{2}, files_in_used = files_in_used + %{3}, files_out_used = files_out_used + %{4}, files_xfer_used = files_xfer_used + %{5} WHERE name = '%{6}' AND quota_type = '%{7}'" ftpquotatallies
SQLNamedQuery insert-quota-tally INSERT "%{0}, %{1}, %{2}, %{3}, %{4}, %{5}, %{6}, %{7}" ftpquotatallies
QuotaLimitTable sql:/get-quota-limit
QuotaTallyTable sql:/get-quota-tally/update-quota-tally/insert-quota-tally
# No permitimos login a root y no hace falta tener shell
RootLogin off
RequireValidShell off
Arrancamos Proftpd, o lo reiniciamos en caso de tenerlo ya funcionando y ya estamos listos para probar la nueva configuración. Debemos tener a la vista siempre los logs por si hubiera algún error sintáctico a la hora de escribir la configuración (a veces pasa):
gprs:~# /etc/init.d/proftpd restart
Restarting ProFTPD ftp daemon.proftpd.
..proftpd.
done.
gprs:~# tail -f /var/log/syslog
Feb 16 13:45:25 gprs proftpd[1184]: gprs - ProFTPD killed (signal 15)
Feb 16 13:45:25 gprs proftpd[1184]: gprs - ProFTPD 1.2.10 standalone mode SHUTDOWN
Feb 16 13:45:27 gprs proftpd[2678]: gprs - ProFTPD 1.2.10 (stable) (built do mrt 22 18:28:32 CET 2001) standalone mode STARTUP
gprs:~#
//Entramos con algún cliente de ftp a nuestro servidor
gprs:~# tail -f /var/log/mysql/mysql.log
050216 14:19:02 39 Connect proftpd@localhost on ftpdb
39 Query SELECT userid, passwd, uid, gid, homedir, shell FROM ftpuser WHERE (userid='testaccount') LIMIT 1
39 Query SELECT groupname FROM ftpgroup WHERE (gid = 5500) LIMIT 1
39 Query SELECT groupname, gid, members FROM ftpgroup WHERE (groupname = 'ftpgroup')
39 Query SELECT groupname, gid, members FROM ftpgroup WHERE (members = 'testaccount' OR members LIKE 'testaccount,%' OR members LIKE '%,testaccount' OR members LIKE '%,testaccount,%')
gprs:~#
Funciona, vemos como conecta con MySQL mirando los campos que le hemos indicado en la configuración. Si probamos a subir un archivo de más de 15Mb veremos como justo después de hacer la transferencia lo borra porque excede la cuota.Ya tenemos todo listo para dar cuentas a nuestros amigos y dejar que usen el servicio sin preocuparnos demasiado por el espacio en disco. Eso sí, recomendaría redactar una política de uso en el motd y mirar de vez en cuando las estadísticas de cada usuario.
Comentarios
Espero que algun dia pongas todas tus anotaciones en ficheros en pdf como hacen en Bulma, podrias utilizar wp2pdf.
La mayoría de ellos están escritos en Docbook y SGML, por lo que presumiblemente no habrá problema alguno en colgarlos con distintos formatos (ps, pdf, div, html...). Dame un poco de tiempo, habrá sorpresillas ;).
¿Que clase de cuota permite subir un fichero de 50 gigas cuando hay un limite de 15 megas?
P.D.: Felicitaciones por el blog ;) esta currado
adduser -u 5500 -s /bin/false -d /bin/null -c "proftpd user" -g ftpgroup ftpuser
Me devuelve
adduser: No más de dos nombres.
En el man adduser no he encontrado nada que me ayude a dilucidar la cuestion.
Que puede ser....?
Saludos
Muchas gracias...
EXCELENTE!!!!!!!
Despues de los elogios, los problemas.
-No me actualiza la tabla de totalidades, por lo que me deja seguir metiendo datos. Los bytes en uso se me quedan en 0.
-Podrias explicar que es cada campo de para restringir las cuotas, asi se podria personalizar mejor.
te hago una pregunta como lo haces trabajar con mysql y radius, mi intencion es que no se conecte el mismo usuario dos veces o mas veces al mismo tiempo
gracias
Borro
en lo del chroot lo malo es que afecta a todos los usuarios, ya sea local o virtuales...
lo que yo hice para sacar de la jaula a los usuarios locales fue lo siguiente...
en DefaultRoot, le añadi el grupo que queria que estuviese enjaulado (o sea el virtual)...
lo deje de esta manera.
DefautlRoot ~
despues
/etc/init.d/proftpd restart
y el resultado fue el siguiente
Connected to localhost.localdomain.
220 ftpd
Name (localhost:j0ker): felipe
331 Password required for felipe.
Password:
230 User felipe logged in.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> pwd
257 "/" is current directory.
ftp>
ftp> quit
221 Goodbye.
###############################
Arcangel:~# ftp localhost
Connected to localhost.localdomain.
220 ftpd
Name (localhost:j0ker): j0ker
331 Password required for j0ker.
Password:
230 User j0ker logged in.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> pwd
257 "/home/j0ker" is current directory.
ftp>
no se que puede ser...
Si alguien nos puede comentar, yo seguire probando.
Gracias
Magnifica guia
tendras alguna otra solución??
gracias
En la linea:
# adduser -u 5500 -s /bin/false -d /bin/null -c "proftpd user" -g ftpgroup ftpuser
como metemos exactamente eso en consola?
# adduser -u 5500 -s /bin/false -d /bin/null -c ";proftpd user"; -g ftpgroup ftpuser
pudiendo cambiar ";proftpd user"; por otro usuario?