Servidor FTP · 7 min read · Dec 17, 2025

PostgreSQL y Proftpd

¿Así que quieres configurar un servidor ftp, necesitas funcionalidad avanzada y no sabes a dónde acudir? Prueba proftpd.

Parte 1: Configurando proftpd con un backend de postgresql

Este tutorial es muy específico, solo voy a cubrir proftpd, y solo en la medida de hacerlo operativo con Postgres. Se asume un conocimiento básico de proftpd y Postgres. Este tutorial te permitirá poner en marcha proftpd bastante rápido, y hacer que utilice una base de datos, si esa base de datos tiene triggers, las posibilidades de esta configuración son casi infinitas.

Consiguiendo Proftpd

Primero necesitarás proftpd, estaré usando Dapper de Ubuntu; hay un meta-paquete preparado gracias a Francesco Paolo Lovergine titulado “proftpd-pgsql,” que configurará una copia de proftpd compilada con los siguientes módulos. Como nota al margen, aunque proftpd viene con los mods, el archivo de configuración es el estándar de proftpd. Te sugiero encarecidamente que revises la documentación de mod_sql, que aunque es deficiente, sigue siendo útil – la documentación en el sitio de proftpd es muy antigua, usa estas en su lugar: Link a la documentación. Además, ten cuidado con la versión, proftpd ahora está en 1.3, aunque yo estaré usando 1.2.10 – la versión utilizada en Dapper.

Los mods compilados en esta versión de proftpd son los siguientes

  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_postgres.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.c

Adelante, emite el comando para obtener proftpd-pgsql:

apt-get install proftpd-pgsql

Configurando Permisos.

Para mi propósito, (recuerda, soy el centro del universo) todo lo que necesitaba era una estructura unidimensional, porque los grupos se almacenan en un nivel diferente en lugar de permisos unix. Así que todos los archivos deberían venir con permisos estáticos – solo me importa el propietario y el grupo del propietario. Mi plan era hacer que el usuario predeterminado de proftpd, ftp, perteneciera a su propio grupo apropiadamente nombrado que iba a crear – ‘ftp’. Luego iba a hacer que Postgres fuera miembro de este grupo, consulta la parte 2 para la razón. Necesitaba que ambos fueran miembros del mismo grupo, y necesitaba que la carpeta chroot de proftpd fuera 775, para que todo el grupo pudiera agregar (mover) archivos, también hice que la carpeta fuera propiedad del usuario ‘ftp’, con el grupo ‘ftp’. Logré esto con el siguiente script.

groupadd ftp
usermod -G ftp ftp
usermod -G ftp,postgres postgres
chown ftp:ftp ./ftp_directory/
chmod 775 ./ftp_directory/

Configurando Postgres

Ahora, vamos a preparar nuestras tablas sql. Diseño utilizando esquemas, y para eso es el prefijo ‘ftp.’ en las tablas. He puesto todas mis tablas en un esquema ftp, hay una cosa que vale la pena mencionar, he sido extremadamente restrictivo con los permisos para este proyecto, porque no estoy seguro de cuán seguro es mod_sql. Al hacerlo, he creado un usuario especial, llamado proftp, para proftpd en mi base de datos (que más aplicaciones utilizan que proftpd), este usuario está restringido a selecciones en la auth_table, y a inserciones en la tabla file_log. Te sugiero encarecidamente que adoptes alguna forma de esta política. Al menos esto previene algún grado de devastadores ataques de inyección sql o filtraciones de información en caso de que el código de mod_sql no sea tan seguro como nos gustaría pensar. Una rápida búsqueda en Google revela un problema de seguridad anterior con la base de código de mod_sql. Cada consulta que mod_sql envía a la base de datos se le añade una cláusula “LIMIT 1”, y con mod_delay también configurado por defecto, encuentro esto suficientemente adecuado para este proyecto.

Mi script de inicialización sql es el siguiente.

ALTER USER proftp UNENCRYPTED PASSWORD 'dealermadeftp';
CREATE SCHEMA ftp;
GRANT USAGE ON SCHEMA ftp TO proftp;

CREATE TABLE ftp.users (
        pkid      serial      PRIMARY KEY,
        userid    text        NOT NULL UNIQUE,
        passwd    text,
        uid       int,
        gid       int,
        homedir   text,
        shell     text
);
GRANT SELECT ON ftp.users TO proftp;
INSERT INTO ftp.users ( userid, passwd ) VALUES ( 'ecarroll', 'adm1n' );
INSERT INTO ftp.users ( userid, passwd ) VALUES ( 'jgallagher', 'adm1n' );

CREATE TABLE ftp.file_log (
        pkid               serial      PRIMARY KEY,
        userid             text        REFERENCES ftp.users(userid),
        abs_path           text,
        file               text,
        dns                text,
        time_transaction   text,
        ts_in              timestamp with time zone   NOT NULL DEFAULT CURRENT_TIMESTAMP
);
GRANT INSERT ON ftp.file_log TO proftp;
GRANT UPDATE ON TABLE ftp.file_log_pkid_seq TO proftp;

Conéctate a tu base de datos y ejecuta eso con:

\i script_name

o, puedes usar enviar ‘–file script_name’ a psql como un argumento de línea de comando.

Aquellos en bases de datos menores (usando este tutorial con mysql), podrían querer reemplazar text con varchar; pero, debido al diseño de Postgres no hay ventaja en esto.

Configurando proftpd

Esta parte es complicada y varía ligeramente si estás usando 1.3 – una vez más, este tutorial es para 1.2.10.

He añadido esta parte a mi archivo de configuración predeterminado de proftpd, ubicado en /etc/proftpd.conf:

AuthOrder            mod_sql.c
SQLAuthTypes         Plaintext Empty
SQLAuthenticate      users
SQLConnectInfo       proftpd@localhost proftp dealermadeftp

SQLDefaultUID        110   # CAMBIA POR EL UID DE TUS USUARIOS FTP EN /etc/passwd
SQLDefaultGID        1001  # CAMBIA POR EL GID DE TUS USUARIOS FTP, EN /etc/groups
SQLDefaultHomedir    /home/ftp
RequireValidShell    off

SQLUserInfo          ftp.users userid passwd uid gid homedir shell

SQLNegativeCache     off
SQLLogFile           /var/log/proftpd-sql
SQLLog               STOR newfile
SQLNamedQuery        newfile FREEFORM "INSERT INTO ftp.file_log(userid,abs_path,file,dns,time_transaction) VALUES ('%U','%f','%J','%V','%T')"

# %U => userid
# %D => --Nada,
# %f => abs_path
# %J => file
# %h => dns_remote, %V => dns_local
# %a => remote_ip, %L => local_ip
# %t => localtime
# %T => transfer_time
  • Las variables de plantilla están comentadas y son solo para tu propia referencia. Un breve resumen de la conf es el siguiente:
  1. Aquí establecemos el AuthOrder, a mod_sql.c, habilitando así mod_sql.
  2. He seleccionado usar contraseñas en texto plano, o en el caso de que no asigne una, permitir contraseñas vacías. (Más tarde implementaré un trigger para SHA1 las contraseñas en la base de datos.)
  3. Luego establezco SQLAuthenticate a users, elegí no usar grupos para este proyecto, si necesitas grupos más información se puede encontrar en esas tablas en: Este sitio web para documentación sobre grupos.
  4. SQLConnectInfo es el dsn en el formato de “base_de_datos@host usuario contraseña,”
  5. SQLDefaultUID, SQLDefaultGID, SQLDefaultHomedir, configuran el predeterminado, dejando la mayoría de mis columnas en NULL, rara vez sobrescribiré esto.
  6. RequireValidShell también está configurado en off, si lo configuras en on, y alimentas un shell inválido fallarás en la autenticación.
  7. El SQLUserInfo corresponde a las tablas de Postgres, es un mapeo literal, nuevamente el “ftp.” especifica el esquema para la tabla de autenticación.
  8. SQLNegativeCache, almacena en caché los fallos de autenticación,
  9. SQLLogFile para un registro detallado mientras configuras tu servidor. Lo deshabilitaré cuando pase a producción.
  10. SQLLog especifica qué acción ftp te gustaría registrar, porque mis usuarios solo pueden subir, solo registro STOR, el formato es ‘ftpaction query_to_execute’.
  11. SQLNamedQuery Revisa la sintaxis en la documentación para una mejor explicación, el formato que uso es ‘qryname FREEFORM custom_query_here’, hago esto en gran medida porque deseo capturar muchas de las variables de plantilla de proftpd, hay otras sintaxis más simples.

Eso es todo. Si todo sale según lo planeado deberías estar funcionando. Ahora, cuando alguien suba un archivo, este debería ser el resultado:

proftpd=# select * from ftp.file_log;
 pkid |  userid  |        abs_path        |     file      |  dns  | time_transaction |             ts_in 
------+----------+------------------------+---------------+-------+------------------+-------------------------------
    1 | ecarroll | /home/ftp/foo/testfile | /foo/testfile | AMD64 | 0.000            | 2006-06-11 20:49:12.623375-05
(1 row)

Parte II: Más allá de ‘funcionando’

… Pero funcionando nunca es suficiente, ¿verdad? Procedamos a responder la siguiente pregunta publicada en la faq de configuración para proftpd:
¿Puedo rotar archivos fuera de un directorio de carga después de la carga?

Y la respuesta, claro que puedes. Lograré esto a través de un procedimiento almacenado, de esa manera los archivos se moverán de la manera correcta – sobre la marcha – sin un daemon.

Primero necesitarás instalar untrusted plperlu en el sistema, esto se puede lograr con

apt-get install postgresql-plperl-8.1

Luego necesitas instalar plperlu en la base de datos

createlang plperlu your_database_here

A continuación, necesitamos crear un trigger de script perl rápido, que se activará en cada inserción en nuestra tabla ftp_log

CREATE OR REPLACE FUNCTION ftp_file() RETURNS TRIGGER AS $$
        use warnings;
        use Cwd;
        use File::Basename;
        use File::Spec;
        use File::Copy;
        move (
                $_TD->{new}{abs_path},
                File::Spec->catfile( '/home/ftp/', basename($_TD->{new}{file}) )
        );
        return;
$$ LANGUAGE 'plperlu' VOLATILE;

CREATE TRIGGER ftp_file
        BEFORE INSERT
        ON ftp.file_log
        FOR EACH ROW
        EXECUTE PROCEDURE ftp_file()
;

Este script sql se puede instalar de la misma manera que el anterior, (el que se utilizó para crear las tablas)

Share: X/Twitter LinkedIn

Recibe nuevas publicaciones en tu bandeja de entrada.

No spam. Cancela la suscripción en cualquier momento.