El objetivo de esta guía es conseguir un servidor FTP totalmente funcional.
En nuestro caso vamos a utilizar ProFTPD.
Como parte importante, haremos que la validación se realice sobre MySQL,
de esta forma no tenemos que crear usuarios del sistema, con la ventaja que nos da en su gestión.
¿Mis motivaciones para escribirlo? Las guias que he encontrado en castellano (quitando honrosas excepciones)
eran muy escuetas, sin explicar qué haces o porqué lo haces, incluso con errores por corregir. Espero que todo eso
no me pase :)
Además, he necesitado montar un servidor hace poco y ya me he animado de paso a escribirlo :)
Señalaré las partes de código para la gente que quiera ir al grano.
No voy a tratar el tema de instalación a partir de tarballs, en la bibliografía añadiré algunos enlaces a sitios donde lo traten. Sólo nombraré los paquetes necesarios, al menos para debian (en el resto no variarán mucho), y las opciones que tenéis que tener en cuenta si compilais desde tarballs.
Todas las pruebas las voy a realizar sobre una Debian Sid (unstable) actualizada.
Antes de nada, decir que casi todo lo que he aprendido sobre SQL ha sido mientras realizaba esta guía (con ProFTPD me había pegado antes), y por tanto puede haber algunos errores de conceptos :)
Sin más, vamos a ello.
El propósito de esta sección es explicar primero qué datos necesitamos y dar el código SQL para generar las tablas. Vamos a utilizar dos bases de datos, una para cada servicio. Podeis meter todas las tablas en una misma base de datos si lo preferís de esa manera, a mi me parece más ordenada la otra. También podríamos incluso compartir las tablas de usuarios, pero hay un detalle de ProFTPD (que más adelante comento) por el cual no voy a hacerlo.
Lo paquetes que necesitamos son los típicos de MySQL: mysql-client, mysql-server y mysql-common
Si lo vas a compilar, no nos hace falta ningún parámetro "especial" para montar el lío este que estamos haciendo ;)
Aviso: Después de instalar MySQL, no olvidéis cambiar la contraseña de root: mysqladmin -u root password nueva_password
ProFTPD, de forma normal, valida sobre el /etc/passwd (y /etc/shadow), por tanto necesita una tabla que simule
los datos que contiene el /etc/passwd.
También utiliza el /etc/group, por lo que necesitamos otra tabla del mismo modo:
Tabla de usuarios:
Campo | Descripción | Ejemplo |
username | Nombre de usuario | max |
password | Contraseña (¡cómo me explico! | motdepasse |
uid | User Id, o número que identifica al usuario | 1001 |
gid | Group Id, o número que identifica al grupo del usuario | 100 |
homedir | Directorio donde se logueará el usuario * | /home/max |
shell | Esto realmente está por compatibilidad sino me equivoco. Puedes poner una cualquiera. ** | /bin/false |
activa | Este campo lo usamos para activar y desactivar cuentas sin borrar el usuario. *** | 1 |
* Aviso: A no ser que uses
DefaultRoot. Más detalle más adelante.
** Aviso: Realmente no puedes poner cualquiera. Tiene que ser de las que están en /etc/shells, si quieres
poder utilizar shells no válidas, debéis usar la opción
RequireValidShell off
*** Aviso: Podéis usar los códigos que queráis. Yo voy a usar 1 para activa, 0 para desactiva.
Tabla de grupos:
Campo | Descripción | Ejemplo |
groupname | Nombre de grupo. Igual que en el /etc/group | ftpuser |
gid | Group Id. Igual que antes | 100 |
members | Miembros del grupo | max,pepe |
Como veis es todo muy similar al /etc/passwd y /etc/group
Vamos a por los comandos y el código SQL para crear las tablas y todo el lío. Podéis copiar la lista de códigos que pongo a continuación y ejecutar directamente el script sobre MySQL, pero yo os recomiendo que lo metáis a mano, se aprende más. :P
Recuerdo que este manual no es de SQL ni de MySQL, y por tanto no me extenderé mucho en su explicación. Si buscáis una guía sobre eso, mirad los enlaces.
Lo primero, creamos la base de datos que vamos a utilizar. Nos logueamos como root y adelante:
valkyr@VaLKyRia:~$ mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 4 to server version: 4.0.13-log Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> create database ftpd; Query OK, 1 row affected (0.08 sec)
mysql> GRANT ALL ON ftpd.* TO proftpd@localhost IDENTIFIED BY "motdepasse"; Query OK, 0 rows affected (0.09 sec)
Explico en detalle (más o menos es una traducción:
Da permisos en todos los campos de la base de datos "ftpd" a "proftpd" que conecta desde el localhost y cuya
contraseña es "motdepasse".
Lo que peor se ve (si cabe) es que ftpd.* indica todos los campos de esa base.
Si eres de los que le gusta añadir usuarios modificando las tablas, no olvides hacer FLUSH PRIVILEGES; después de hacerlo.
Ahora podríamos seguir identificados como root, pero como quiero demostraros que hasta ahora ha funcionado, salimos del terminal de MySQL y nos volvemos a loguear:
mysql> exit Bye valkyr@VaLKyRia:~$ mysql -u proftpd -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 6 to server version: 4.0.13-log Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> show databases; +----------+ | Database | +----------+ | ftpd | | test | +----------+ 2 rows in set (0.00 sec) mysql> use ftpd; Database changed mysql> show tables; Empty set (0.00 sec) mysql> exit Bye
Como veis, ahí tenemos la base ftpd, pero sin ninguna tabla. Lo siguiente es el script que vamos a utilizar, lo más fácil es copiarlo en un editor de texto y cargarlo directamente. Si tenéis ganas de currar más, siempre podéis irlo copiando a mano desde el terminal de MySQL. Yo soy más vago y lo hago de la primera forma. :P
Aquí es donde se iba a demostrar que no tengo ni idea de SQL, así que me he basado en el script de uno de los enlaces para no quedar tan mal :)
-- Host: localhost Database: ftpd --------------------------------------------------------- -- Estructura para tabla 'grupos' -- CREATE TABLE grupos ( groupname varchar(20) NOT NULL, gid smallint(6) NOT NULL default '65534', members longtext ) TYPE=MyISAM; -- -- Estructura para tabla 'usuarios' -- CREATE TABLE usuarios ( username char(12) NOT NULL, password char(100) NOT NULL, uid smallint(6) default NULL, gid smallint(6) default NULL, homedir char(50) default NULL, shell char(20) default NULL, activa tinyint(4) default '1' ) TYPE=MyISAM;
Y lo cargamos:
valkyr@VaLKyRia:~$ mysql -u proftpd --database=ftpd -p < mysql_script Enter password:
Vamos a comprobar que ha funcionado:
valkyr@VaLKyRia:~$ mysql -u proftpd -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 15 to server version: 4.0.13-log Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> use ftpd; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> show tables; +----------------+ | Tables_in_ftpd | +----------------+ | grupos | | usuarios | +----------------+ 2 rows in set (0.00 sec) mysql> describe grupos; +-----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-----------+-------------+------+-----+---------+-------+ | groupname | varchar(20) | | | | | | gid | smallint(6) | | | 32767 | | | members | longtext | YES | | NULL | | +-----------+-------------+------+-----+---------+-------+ 3 rows in set (0.00 sec) mysql> describe usuarios; +----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+---------+-------+ | username | char(12) | | | | | | password | char(100) | | | | | | uid | smallint(6) | YES | | NULL | | | gid | smallint(6) | YES | | NULL | | | homedir | char(50) | YES | | NULL | | | shell | char(20) | YES | | NULL | | | activa | tinyint(4) | YES | | 1 | | +----------+-------------+------+-----+---------+-------+ 7 rows in set (0.00 sec)
A partir de aquí entramos en la configuración concreta del ProFTPD. Si no digo lo contrario, las directivas van todas en el /etc/proftd.conf.
La intención es explicar, sin entrar mucho en profundidad que si no os aburro (y me aburro a mi mismo), las que me parecen las directivas más interesantes para el servidor.
Como una de las principales partes de la guía es la validación de usuarios, no voy a explicar como montar un servidor anónimo. Si andabais esperando eso, os toca buscar en otra parte.
Los paquetes que vamos a necesitar son: proftpd proftpd-common proftpd-mysql. Si compiláis desde tarballs, no olvidéis pasarle al configure los parámetros
--with-modules=mod_sql:mod_sql_mysql --with-includes=/opt/mysql/include/mysql/ --with-libraries=/opt/mysql/lib/mysql/más todos los que suelas usar.
Esta es la parte en la que metemos una mínima configuración para que nuestro servidor sea usable. Esto no quiere que en cuanto paséis esta sección arranquéis el demonio y os liéis a dar cuentas :)
Si queréis una guía más exhaustiva, tenéis la oficial que para algo está :)
Antes de avanzar más, tengo que comentar un poco las secciones que hay (o puede haber) en el fichero de configuración:
Sección | Descripción |
Main server | Aquí es donde están las directivas generales del demonio, por ejemplo el ServerName |
<Anonymous> | Hace referencia a todo lo que tenga que ver con el login anónimo |
<Directory> | Con esto especificamos configuraciones para directorios concretos |
<Global> | Las directivas globales del servidor, como el número de clientes máximo |
<Limit> | Restricciones para comandos y grupos |
Ficheros .ftpaccess | Similares a los .htaccess de Apache, para que los usuarios puedan poner sus propias configuraciones. * |
Aviso: Si os gusta usar los .ftpaccess tenéis que tener cuidado con los permisos, para que un usuario no os borre vuestro propio .ftpaccess
Las secciones que van entre < y > se usan en el proftpd.conf al estilo de las etiquetas HTML. Me explico, señalamos el inicio de
sección con <Sección> y la cerramos con </Sección> (sustituye Sección por Directory, Global, Limit...).
La sección "Main Server" no hay que señarlarla, se supone que todo lo que no va entre < > pertenece a esa parte.
Las sección con menor preferencia es la Global. Por ejemplo, si una directiva dentro de Directory contradice lo que dice una en Global, tiene
preferencia la que está en Directory.
Ya vemos más adelante un ejemplo de configuración por si no me he explicado bien.
Lo primero de todo, es decidir si queremos nuestro servidor en modo inetd o standalone.
Modo | Descripción |
inetd |
|
standalone |
|
Aviso: Si a estas alturas no conoces lo que es inetd/xinetd creo que no deberías estar montando servidores :)
Si quieres aprender lo que es, pásate por aquí
El resto de directivas de esta sección son sencillitas, así que prefiero copiar un trozo de la sección principal de mi /etc/proftpd.conf y poner la explicación con comentarios. Está basado en el proftpd.conf que deja el paquete de Debian, así que si estáis usando ese paquete os sonará bastante :)
# Nombre del servidor. ServerName "VaLKyRia" # Tipo, inetd o standalone. ServerType standalone # Muestra el mensaje de bienvenida antes de identificarse. Si estuviera en ON saldría un mensaje genérico. DeferWelcome off # Puerto al que escuchará el servidor. Port 21 # Número máximo de conexiones. MaxInstances 30 # Usuario y grupo con el que el servidor arrancará. User nobody Group nogroup # Activa el modo de respuesta de la RFC2228 para dar mayor compatibilidad con ciertos navegadores y clientes. MultilineRFC2228 on # Indica que esta configuración va a ser la predeterminada (esto es más útil con Virtual Hosts). DefaultServer on # Directivas para todos los directorios <Directory /*> # Permisos reales de los ficheros Umask 022 022 # Permite sobreescritura de ficheros (ojo si tenemos algún .ftpaccess por ahí). AllowOverwrite on # Permite continuar descargas incompletas AllowRetrieveRestart on # Permite continuar uploads incompletos. Está comentada porque es recomendable ponerlo sólo en los directorios de upload #AllowStoreRestart on </Directory> <Global> # Muestra los enlaces simbólicos ShowSymlinks on # Cortan la transferencia cuando se está sin hacer nada. TimeoutNoTransfer 600 TimeoutStalled 600 TimeoutIdle 1200 # Con esto impedimos que un usuario se pueda identificar como root. RootLogin off # Damos 3 intentos para identificarse antes de cortar la conexión. MaxLoginAttempts 3 # Requiere una Shell válida para iniciar sección. Las shell válidas son las que estan en /etc/shells. RequireValidShell off # Muestra este mensaje al conectar al servidor. ServerIdent on "Bienvenido a VaLKyRia" # Fichero que se muestra al hacer login. DisplayLogin welcome.msg # Fichero que se muestra al entrar en un directorio. DisplayFirstChdir .message # Opciones por defecto al hacer ls. ListOptions "-l" # Filtro de comandos. Todos los que encajen son denegados. DenyFilter \*.*/ # Especifica el directorio donde se guardará el log. TransferLog /var/log/xferlog # Permite bloquear usuarios vía /etc/ftpusers. Todo usuario que esté en ese archivo será bloqueado. Esta directiva podremos quitarla # en cuanto veamos la validación en MySQL. UseFtpUsers on # Número máximo de conexiones por Host/IP. MaxClientsPerHost 2 # Número máximo de conexiones por usuario. MaxClientsPerUser 2 # Número máximo de conexiones en el servidor y su mensaje correspondiente. MaxClients 10 "Servidor Completo" # Usamos esto para que el servidor muestre datos falseados (= fake). De esta manera no se sabe cuales son los propietarios ni los permisos reales. # Falsea el grupo del propietario DirFakeGroup on ftp # Falsea el propietario DirFakeUser on ftp # Falsea los permisos DirFakeMode 0000 </Global>
Realmente no hacen falta todas, pero no está de más tenerlas. Os recuerdo que ProFTPD tiene muchas directivas, y es difícil conocerlas todas.
Lo mejor es echarle un vistazo a todas y poner las que nos interesen.
He dejado unas directivas bastante interesantes sin poner para explicarlas luego con más detalle. Son los enjaulados (chroot), muy útiles, y que no pueden faltar en ningún servidor ;).
Al menos creo que hemos visto como funciona básicamente el ProFTPD, vayamos a otros temas :)
Bueno, ya hemos metido mano en la configuración lo justo para que nuestro servidor eche a andar. Vamos a entrar en la parte interesante del documento :P
Hasta ahora, si queríamos utilizar cuentas en el FTP, teníamos que crear usuarios del sistema. Nuestro objetivo va un poco más allá de eso, queremos que nuestro usuarios no sean usuarios del sistema, sino que estén en una base de datos, con la ventaja que nos da en seguridad y gestión.
Las directivas que vamos a usar son las SQL*. Por
desgracia, muchas de ellas están todavía sin documentar (o no están muy bien explicadas), así que os tocará fiaros de mí ;)
Aquí van:
SQLAuthTypes tipo |
En tipos de identificacion tenemos (entre otras) PlainText (texto plano), Crypt (como en el /etc/shadow) o Backend (utiliza el sistema propio de la base de datos). Aquí depende de con qué vayamos a cifrar las contraseñas en la base de datos. Si lo haces con CRYPT() usa Crypt, si usas PASSWORD() usa Backend. |
SQLAuthenticate usuarios_a_identificar |
Con esta directiva decimos quien tiene que identificarse en MySQL. Podemos seleccionar grupos o usuarios, yo me limito a decir que todos: users* |
SQLConnectInfo base_de_datos@host usuario contraseña |
Son los datos de conexión, no creo que necesite más explicación |
SQLDefaultGID | SQLDefaultUID |
Indican, respectivamente, los GID y UID que se usan por defecto en caso de no poner nada en la tabla |
SQLMinUserGID | SQLMinUserUID |
Indican el UID y el GID mínimo que han de tener los usuarios para poder loguear. |
SQLUserInfo tabla campos |
Se le indica por orden, los campos que hay que seleccionar. * |
SQLUserWhereClause claúsula |
Indica una condición a la hora de seleccionar los campos. Nosotros la usamos para controlar si una cuenta está activa o no |
* Si no me equivoco, en versiones anteriores el ProFTPD hacía un SELECT * FROM tabla
(al menos por lo que decía el mysql.log)
Ahora dudo sobre este aspecto, pues, no se si tras la última actualización de sid, las consultas que realiza ProFTPD son de la forma:
Query SELECT username, password, uid, gid, homedir, shell FROM usuarios WHERE (username='max') and ((activa=1)) LIMIT 1
Aquí va la configuración que vamos a usar nosotros, podéis añadirlo al final de /etc/proftpd.conf :
SQLAuthTypes Backend SQLAuthenticate users* SQLConnectInfo ftpd@localhost proftpd motdepasse SQLDefaultGID 65534 SQLDefaultUID 65534 SQLMinUserGID 500 SQLMinUserUID 500 SQLUserInfo usuarios username password uid gid homedir shell activa SQLUserWhereClause "activa=1"
Aviso: Yo uso SQLAuthType Backend porque cifro las contraseñas con PASSWORD().
Además, ponemos el SQLDefaultUID/GID a 65534 porque son los que indican el usuario nouser y el grupo nogroup (echad un ojo
al /etc/passwd y al /etc/group para comprobarlo).
Ahora que tenemos contraseñas en el fichero de configuración no está de más que le quitéis permisos de lectura para el resto, no queremos que nadie este jugando por ahí con nuestras contraseñas.
VaLKyRia:~# ls -al /etc/proftpd.conf -rw-r--r-- 1 root root 4408 2003-08-03 13:40 /etc/proftpd.conf VaLKyRia:~# chmod o-r /etc/proftpd.conf VaLKyRia:~# ls -al /etc/proftpd.conf -rw-r----- 1 root root 4408 2003-08-03 13:40 /etc/proftpd.conf
¿Que queréis probarlo? Vamos a ello. A faltar del script en PHP que tengo pensado hacer (quizás para cuando leais esto tenéis suerte y esta hecho :P) vamos a crear el usuario a golpe de consola:
valkyr@VaLKyRia:~$ mysql -u proftpd -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 17 to server version: 4.0.13-log Type 'help;' or '\h' for help. Type '\c' to clear the buffer. mysql> use ftpd; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> INSERT INTO usuarios VALUES ('max',PASSWORD('motdepasse'),2000,200,'/tmp','/bin/bash',1); Query OK, 1 row affected (0.02 sec) mysql> SELECT * FROM usuarios; +----------+------------------+------+------+---------+-----------+--------+ | username | password | uid | gid | homedir | shell | activa | +----------+------------------+------+------+---------+-----------+--------+ | max | 614e5cb70bb87e92 | 2000 | 200 | /tmp | /bin/bash | 1 | +----------+------------------+------+------+---------+-----------+--------+ 1 row in set (0.00 sec) mysql> exit Bye
Atentos sobretodo a que todo lo que sea un string (o cadenas de caracteres) va entre comillas (dobles o simples, es igual)
Ojo también a que he usado la función PASSWORD() porque en el SQLAuthTypes elegí el modo Backend.
He puesto como homedir el /tmp porque supongo que todos lo tendréis :P
En shell podéis poner /bin/false si queréis y la habéis metido en el /etc/shells (en realidad da igual, porque nunca la va
a usar). Podéis usar la opción
RequireValidShell off como dije por ahí arriba.
Ahora haced login en el ftp con el usuario y la contraseña que hemos introducido en la tabla:
valkyr@VaLKyRia:~$ ftp localhost Connected to localhost. 220 ProFTPD 1.2.8 Server (VaLKyRia) [VaLKyRia] Name (localhost:valkyr): max 331 Password required for max. Password: 230 User max logged in. Remote system type is UNIX. Using binary mode to transfer files. ftp> pwd 257 "/tmp" is current directory.
Como veis, estamos dentro.
Hora de mirar los logs para que veais como funciona.
Para ver el /var/log/mysql.log necesitais privilegios de root.
VaLKyRia:~# tail -n 3 /var/log/mysql.log 030803 15:45:43 19 Connect proftpd@localhost on ftpd 030803 15:45:49 19 Query SELECT username, password, uid, gid, homedir, shell FROM usuarios WHERE (username='max') and ((activa=1)) LIMIT 1 030803 15:47:43 19 Quit
Creo que es lo suficientemente claro. Comparad esas líneas con las de SQLConnectInfo, SQLUserInfo y SQLUserWhereClause que añadimos en el /etc/proftpd.conf, y seguro que veis cómo funciona la consulta :)
Si veis unos avisos de que no se ha podido autentificar por PAM, añadid al /etc/proftpd.conf la siguiente directiva:
AuthPAM off
No estamos usando PAM, así que no queremos que intente identificarse por ese método.
Ya tenemos nuestro ProFTPD validándose en MySQL :)
Ahora vamos a añadir otras directivas interesantes.
En esta parte, además de ver cómo funciona el enjaulado (chroot), voy a poner un ejemplo de montaje de los directorios del servidor.
Última revisión: 23-8-2003
Estado de las pruebas: Completo.
Estado del documento: Incompleto.
Contacto: max[en]bandaancha.st