Configuración de ProFTPD+PostFix validados en MySQL


Índice

  1. Introducción
  2. MySQL: Distribucion de las tablas
    1. Tablas para ProFTPD
    2. Tablas para PostFix
  3. ProFTPD:
    1. Configuración básica
    2. Validación
    3. Otras directivas
    4. Ejemplo: proftpd.conf
  4. PostFix:
    1. Configuración básica
    2. Validación
    3. Otras directivas
    4. Ejemplos de configuración
  5. Scripts de Control en PHP
  6. Bibliografía

Introducción:

El objetivo de esta guía es conseguir unos servidores de FTP y SMTP (correo) totalmente funcionales En nuestro caso vamos a utilizar ProFTPD (lógicamente para FTP) y PostFix (como servidor de correo).
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 :)
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.

En principio, no hay problema con las versiones que uséis. Leed todo el documento antes de poneros manos a la obra, pues como veremos hay que tener cuidado con ciertas cosas, sobre todo si las compilais vosotros mismos. Más adelante veremos que además nos hacen falta las librerías SASL y PAM, para que os vayáis haciendo a la idea :)

Antes de nada, decir que casi todo lo que he aprendido sobre Postfix y SQL ha sido mientras realizaba esta guía (con ProFTPD me había pegado antes), y por tanto puede haber algunos errores de conceptos :)

Por último, decir que podría haber estructurado todo en dos documentos, pero me gusta más así :)

Sin más, vamos a ello.


MySQL: Distribución de las tablas:

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

Tablas para ProFTPD:

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)
	

Generamos el usuario:

	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)
	

La parte de ProFTPD ya está. Si sólo estás montado el servidor FTP, puedes saltar a aquí.
Si estás montado todo el tinglado, sigamos con Postfix :)

Tablas para PostFix:

Como vamos a ver ahora mismo, vamos a hacer que PostFix utilice MySQL para todo, no sólo para validar usuarios. Por tanto, vamos a necesitar unas cuantas tablas más que para ProFTPD :)


ProFTPD:

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 el parámetro '--with-modules=mod_sql:mod_sql_mysql' (más todos los que suelas usar).

Configuración básica

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
  • Es el superservidor inetd el que se encarga de escuchar al puerto y de lanzar la conexión al servidor FTP cada vez que recibe una petición.
  • Se pueden utilizar tcpd para controlar el acceso (hosts.deny, hosts.allow).
  • Aconsejado para servidores pequeños.
standalone
  • El servidor corre como demonio independiente.
  • Tenemos posibilidad de utilizar los Virtual Hosts.
  • Recomendado para servidores en producción o con mucha carga.
Yo como pretendo usar Virtual Hosts algun día, lo pongo en 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 las de enjaulados (chroot) que son muy útiles.

Al menos creo que hemos visto como funciona básicamente el ProFTPD, vayamos a otros temas :)

Validación

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) Fue una de las razones por las que argumenté que era mejor separar los datos de cada servicio en bases y tablas diferentes. 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
En cualquier caso, si tenéis mucho interés en que explique cómo se guardarían todos los usuarios puedo hacer un anexo (aunque creo que sabríais hacerlo, solo habría que jugar con campos activo_ftp y activo_smtp).

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.

Otras directivas


Bibliografía

Última revisión: 6-8-2003
Estado de las pruebas: Completo.
Estado del documento: Incompleto.
Contacto: max[en]bandaancha.st