Servidor FTP · 7 min read · Dec 17, 2025

PostgreSQL e Proftpd

Então você quer configurar um servidor ftp, precisa de funcionalidades avançadas e não sabe a quem recorrer? Experimente o proftpd.

Parte 1: Configurando o proftpd com um backend postgresql

Este tutorial é muito específico, vou cobrir apenas o proftpd, e apenas na medida em que ele se torne operacional com o Postgres. Assume-se um entendimento básico de proftpd e Postgres. Este tutorial fará com que você configure o proftpd rapidamente e o utilize um banco de dados; se esse banco de dados tiver triggers, as possibilidades dessa configuração são quase infinitas.

Obtendo Proftpd

Primeiro, você precisará do proftpd, estarei usando o Dapper do Ubuntu; há um meta-pacote preparado graças a Francesco Paolo Lovergine intitulado “proftpd-pgsql,” que configurará uma cópia do proftpd compilada com os seguintes módulos. Como nota lateral, enquanto o proftpd vem com os mods, o arquivo de configuração é o padrão do proftpd. Eu recomendo fortemente que você consulte a documentação do mod_sql, que embora seja inferior, ainda é útil – a documentação no site do proftpd é muito antiga, use estas em vez disso: Link para a documentação. Além disso, fique atento à versão, o proftpd agora está na 1.3, embora eu esteja usando a 1.2.10 – a versão usada no Dapper.

Os mods compilados nesta versão do proftpd são os seguintes

  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

Vá em frente e emita o comando para obter o proftpd-pgsql:

apt-get install proftpd-pgsql

Configurando Permissões.

Para meu propósito, (lembre-se, eu sou o centro do universo) tudo o que eu precisava era de uma estrutura unidimensional, porque os grupos são armazenados em um nível diferente em vez de permissões unix. Portanto, todos os arquivos devem entrar com permissões estáticas – eu só me importo com o proprietário e o grupo do proprietário. Meu plano era fazer com que o usuário padrão do proftpd, ftp, pertencesse ao seu próprio grupo apropriadamente nomeado que eu iria criar – ‘ftp’. Eu então faria o Postgres um membro desse grupo, veja a parte 2 para a razão. Eu precisava que ambos fossem membros do mesmo grupo, e precisava que a pasta chroot do proftpd fosse 775, para que todo o grupo pudesse adicionar (mover) arquivos, eu também fiz a pasta ser de propriedade do usuário ‘ftp’, com grupo ‘ftp’. Eu consegui isso com o seguinte script.

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

Configurando Postgres

Agora, vamos preparar nossas tabelas sql. Eu projeto utilizando esquemas, e é para isso que serve o prefixo ‘ftp.’ nas tabelas. Coloquei todas as minhas tabelas em um esquema ftp, há uma coisa que vale a pena notar, fui extremamente restritivo nas permissões para este projeto, porque não tenho certeza de quão seguro o mod_sql é. Ao fazer isso, criei um usuário especial, chamado proftp, para o proftpd no meu banco de dados (que mais aplicações utilizam do que o proftpd), esse usuário é restrito a seleções na auth_table e inserções na tabela file_log. Eu altamente sugiro que você adote alguma forma dessa política. No mínimo, isso previne algum grau de devastadores ataques de injeção sql ou vazamentos de informações no caso de o código do mod_sql não ser tão seguro quanto gostaríamos de pensar. Uma rápida pesquisa no google revela um problema de segurança anterior com a base de código do mod_sql. Cada consulta que o mod_sql envia ao db é anexada com uma cláusula “LIMIT 1”, e com o mod_delay também configurado por padrão, considero isso suficiente para este projeto.

Meu script de inicialização sql é o seguinte.

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;

Conecte-se ao seu banco de dados e execute isso com:

\i script_name

ou, você pode usar enviar ‘–file script_name’ para psql como um argumento de linha de comando.

Aqueles em bancos de dados menores (usando este tutorial com mysql), você pode querer substituir text por varchar; mas, devido ao design do Postgres, não há vantagem nisso.

Configurando proftpd

Esta parte é complicada e varia ligeiramente se você estiver usando 1.3 – mais uma vez, este tutorial é para 1.2.10.

Adicionei esta parte ao meu arquivo de configuração padrão do proftpd, localizado em /etc/proftpd.conf:

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

SQLDefaultUID        110   # MUDE PARA O UID DOS SEUS USUÁRIOS FTP ENCONTRADO EM /etc/passwd
SQLDefaultGID        1001  # MUDE PARA O GID DOS SEUS USUÁRIOS FTP, ENCONTRADO EM /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
  • As variáveis do template estão comentadas e são apenas para sua própria referência. Um breve resumo da configuração é o seguinte:
  1. Aqui definimos o AuthOrder, para mod_sql.c, ativando assim o mod_sql.
  2. Eu selecionei usar senhas em texto simples, ou no caso de eu não atribuir uma, permitir senhas vazias. (Mais tarde implementarei um trigger para SHA1 as senhas no banco de dados.)
  3. Em seguida, configurei SQLAuthenticate para usuários, escolhi não usar grupos para este projeto, se você precisar de grupos, mais informações podem ser encontradas nessas tabelas em: Este site para documentação sobre grupos.
  4. SQLConnectInfo é o dsn no formato “banco_de_dados@host usuário senha,”
  5. SQLDefaultUID, SQLDefaultGID, SQLDefaultHomedir, configuram o padrão, deixando a maioria das minhas colunas como NULL, raramente vou substituir isso.
  6. RequireValidShell também está definido como off, se você configurá-lo como on, e fornecer um shell inválido, você falhará na autenticação.
  7. O SQLUserInfo corresponde às tabelas do Postgres, é um mapeamento literal, novamente o “ftp.” especifica o esquema para a tabela de autenticação.
  8. SQLNegativeCache, armazena em cache falhas de autenticação,
  9. SQLLogFile para registro detalhado enquanto configura seu servidor. Eu desativarei quando for para produção.
  10. SQLLog especifica qual ação ftp você gostaria de registrar, porque meus usuários só podem fazer upload, eu só registro STOR, o formato é ‘ftpaction query_to_execute’.
  11. SQLNamedQuery Verifique a sintaxe na documentação para uma melhor explicação, o formato que uso é ‘qryname FREEFORM custom_query_here’, faço isso principalmente porque desejo capturar muitas das variáveis de template do proftpd, há outras sintaxes mais simples.

É isso. Se tudo correr conforme o planejado, você deve estar funcionando. Agora, quando alguém faz upload de um arquivo, este deve ser o 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: Além de ‘funcionando’

… Mas funcionando nunca é o suficiente, certo? Vamos prosseguir para responder à seguinte pergunta postada na faq de configuração do proftpd:
Posso rotacionar arquivos de um diretório de upload após o upload?

E a resposta, você com certeza pode. Eu vou realizar isso através de um procedimento armazenado, assim os arquivos são movidos da maneira certa – em tempo real – sem um daemon.

Você primeiro precisará instalar untrusted plperlu no sistema, isso pode ser feito com

apt-get install postgresql-plperl-8.1

Em seguida, você precisa instalar plperlu no banco de dados

createlang plperlu your_database_here

Em seguida, precisamos criar um rápido script perl trigger, que será acionado em cada inserção na nossa tabela 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 pode ser instalado da mesma forma que o anterior, (aquele usado para criar as tabelas)

Share: X/Twitter LinkedIn

Receba novas postagens na sua caixa de entrada

Sem spam. Cancele a assinatura a qualquer momento.