FTP passivo: firewall bloqueia a listagem de diretórios
O login funciona, mas o comando `ls` trava por timeout. Veja como definir o intervalo de portas passivas, liberar a regra no firewall e ajustar o NAT.
Por que o FTP aceita o login, mas a listagem do diretório fica bloqueada
O modo passivo do FTP falha num firewall porque o FTP usa duas conexões TCP, não uma. A conexão com a porta 21 transporta o login e os comandos. Por isso, o nome de utilizador e a palavra-passe são aceites, e o firewall parece estar configurado corretamente. A primeira ls precisa então de uma segunda conexão numa porta diferente. O firewall não permite essa conexão. Por isso, o cliente fica à espera até ocorrer um timeout.
A correção consiste em definir um intervalo fixo de portas para essas conexões de dados e criar uma regra no firewall que permita o mesmo intervalo. Um servidor atrás de NAT (network address translation) precisa de uma configuração adicional para anunciar o endereço correto. Os connection tracking helpers costumavam fazer este trabalho automaticamente. Atualmente, já não o fazem. É importante saber o motivo antes de copiar um guia antigo.
O canal de controlo e o canal de dados
O FTP (protocolo de transferência de ficheiros) está especificado na RFC 959 e é anterior tanto ao NAT como à firewall com estado. Uma sessão abre uma ligação de controlo para a porta TCP 21 e mantém-na aberta durante toda a sessão. Os comandos são enviados em texto simples. As respostas regressam com um código de três dígitos e uma linha de texto. Essa ligação nunca transporta o conteúdo dos ficheiros.
Cada elemento de dados usa a sua própria ligação TCP: uma para uma listagem de diretório (LIST), uma para cada transferência de ficheiro (RETR), e uma para cada carregamento (STOR). A ligação é aberta, usada uma vez e fechada. A autenticação ocorre inteiramente no canal de controlo, pelo que uma falha no caminho de dados apresenta sempre o mesmo sintoma: início de sessão bem-sucedido, seguido de uma espera indefinida. Se o cliente apresentar uma resposta 230 e depois parar durante a listagem, o problema está no canal de dados, não nas credenciais.
Modo ativo: o servidor liga-se de volta ao cliente
No modo ativo, o cliente escolhe uma porta, fica à escuta nessa porta e informa o servidor sobre onde se deve ligar:
PORT 192,168,1,50,195,80Os primeiros quatro números são o endereço IP do cliente. Os dois últimos são a porta, codificada como dois bytes: 195 * 256 + 80 = 50000. Em seguida, o servidor abre a ligação de dados a partir da sua própria porta 20 para a porta 50000 no cliente.
Do ponto de vista do cliente, essa ligação é de entrada e não solicitada, pelo que a firewall do cliente a bloqueia. Se o cliente estiver atrás de um router doméstico, o endereço no comando PORT será privado e o servidor não conseguirá alcançá-lo. Foi no modo ativo que o FTP ganhou a reputação de não funcionar.
Modo passivo: o cliente abre ambas as ligações
O modo passivo inverte a ligação de dados. O cliente envia PASV e o servidor responde com um endereço e uma porta próprios:
227 Entering Passive Mode (203,0,113,10,195,80)A codificação é a mesma, por isso o cliente liga-se a 203.0.113.10 na porta 50000. O cliente passa a abrir ambas as ligações. É por isso que o modo passivo funciona através de NAT no lado do cliente e que todos os clientes atuais o tentam primeiro.
O problema mudou de lugar, mas não desapareceu. A ligação de entrada não solicitada chega agora ao seu servidor, numa porta alta que muda a cada transferência. Essa firewall é sua, por isso o problema também é seu.
EPSV (modo passivo estendido, RFC 2428) segue a mesma ideia, mas usa uma resposta mais simples:
229 Entering Extended Passive Mode (|||50000|)A resposta não contém um endereço. O cliente reutiliza o endereço que já tem para a ligação de controlo. É isso que permite funcionar através de IPv6 e elimina toda uma classe de erros de NAT. O manual do curl indica que o curl normalmente tenta EPSV antes de PASV. A porta continua a ser escolhida em tempo de execução, por isso EPSV não altera as regras da firewall.
Por que uma regra normal de firewall não consegue permitir o canal de dados
Isso acontece porque o número da porta ainda não existe quando a regra é escrita. O servidor escolhe a porta para cada transferência. Com os valores predefinidos, o vsftpd documenta pasv_min_port e pasv_max_port como 0, ou seja, "usar qualquer porta". Assim, a ligação de dados pode chegar em qualquer porta acima de 1023. sudo ufw allow 21/tcp permite o canal de controlo e nada mais. Essa é exatamente a configuração que produz um login funcional e uma listagem que não funciona. Se o conceito de um serviço escutar numa porta fixa ainda não estiver claro, como funcionam as portas e os sockets de escuta no Linux explica os fundamentos.
Um firewall com estado acompanha as ligações, e o kernel pode aceitar uma nova ligação como RELATED de uma ligação existente. No FTP, isso exige que algum componente leia o fluxo de controlo e extraia a porta de uma linha 227 ou PORT. Por predefinição, nada faz isso.
Por que o helper de rastreamento de ligações FTP já não é a solução
O módulo do kernel nf_conntrack_ftp é o módulo indicado pelos guias antigos. Ele lê o canal de controlo em texto simples, encontra a porta anunciada e regista uma expectativa. Assim, a ligação de dados é aceite sem nenhuma regra que especifique a porta. Quatro aspetos mudaram desde que esses guias foram escritos.
A atribuição automática de helpers está desativada e, nos kernels atuais, já não pode ser reativada. O kernel desativou-a por predefinição em 2016. Em seguida, o commit de Pablo Neira Ayuso "netfilter: remove nf_conntrack_helper sysctl and modparam toggles" (agosto de 2022) removeu completamente essa opção. O commit afirma que "users now have to rely on explicit conntrack helper configuration via ruleset." O sysctl nf_conntrack_helper está presente no Linux 5.19 e desaparece a partir do Linux 6.0. A documentação dos sysctls de conntrack do kernel também já não o lista. Carregar o módulo, por si só, não produz qualquer efeito.
Pode verificar qual é o tipo de kernel em uso. Execute sysctl net.netfilter.nf_conntrack_helper. Uma resposta sysctl: cannot stat /proc/sys/net/netfilter/nf_conntrack_helper: No such file or directory significa que o kernel já não tem atribuição automática de helpers para ativar. Se for devolvido um número, está a usar um kernel anterior ao 6.0. Nesse caso, a opção ainda existe e o valor predefinido é 0.
As interfaces de configuração de firewall também o marcaram como obsoleto. O man ufw-framework no Ubuntu 24.04 indica, sobre a linha IPT_MODULES em /etc/default/ufw: "Unconditional loading of connection tracking modules (nf_conntrack_*) in this manner is deprecated". Também acrescenta que as regras de helpers "must be managed in via the RULES FILES". O firewalld documenta AutomaticHelpers em firewalld.conf como "Deprecated. This option is ignored and no longer used." Associar agora um helper significa escrever manualmente uma regra explícita com o destino CT. Isso dá mais trabalho do que a correção abaixo e deixa de funcionar assim que ativar TLS. iptables e nftables no Ubuntu explica onde essas regras ficam efetivamente.
O TLS elimina a possibilidade de inspeção. Um helper funciona lendo o canal de controlo como texto. Quando esse canal é encriptado, o helper vê texto cifrado e não consegue encontrar a porta. Não existe uma correção para isso, nem deveria existir: um middlebox capaz de ler o seu canal de controlo também é capaz de ler a sua palavra-passe.
Declare uma faixa de portas passivas no servidor
Todos os servidores FTP podem ser configurados para escolher as portas passivas a partir de uma faixa definida por si. Os nomes das opções variam, por isso consulte a documentação do servidor que está efetivamente a executar.
vsftpd, em /etc/vsftpd.conf:
pasv_enable=YES
pasv_min_port=30000
pasv_max_port=30099pasv_enable já tem YES como valor predefinido. As duas opções de portas têm 0 como valor predefinido, o comportamento de "usar qualquer porta" descrito acima. Aplique a alteração com sudo systemctl restart vsftpd e confirme que o serviço voltou a iniciar com systemctl status vsftpd. O vsftpd recusa iniciar quando encontra uma linha de configuração que não consegue analisar, em vez de a ignorar. Se o reinício falhar, consulte journalctl -u vsftpd -n 20 em busca da linha 500 OOPS:, que identifica a opção introduzida.
ProFTPD, em proftpd.conf:
PassivePorts 30000 30099A documentação do ProFTPD não define um valor predefinido neste caso: sem a diretiva, o kernel escolhe a porta. A documentação do ProFTPD também indica que, quando nenhuma porta da sua faixa está livre, o servidor recorre a uma porta atribuída pelo kernel e regista uma mensagem. Uma faixa demasiado pequena falha ocasionalmente em vez de falhar de forma clara, o que é muito mais difícil de diagnosticar. Use portas não privilegiadas, 1024 e superiores.
O Pure-FTPd usa a opção -p first:last, documentada em man pure-ftpd como "Usar primeiro apenas portas dentro da faixa entre a primeira e a última, inclusive, para transferências em modo passivo", o que "torna o pure-ftpd mais compatível com filtros de pacotes". As versões empacotadas normalmente colocam essa opção num ficheiro de configuração. Consulte a documentação da sua distribuição para saber o nome do ficheiro, em vez de o adivinhar.
De quantas portas precisa? Uma por cada ligação de dados ativa. Uma porta TCP fechada permanece em TIME_WAIT durante alguns minutos antes de poder ser reutilizada, por isso reserve várias vezes o pico esperado. Cem portas são suficientes para alguns utilizadores, mas um servidor público com muito tráfego precisa de muitas mais. O vsftpd é menos tolerante do que o ProFTPD neste aspeto: o código tenta algumas portas aleatórias dentro da faixa e, se todas estiverem ocupadas, termina a sessão com 500 OOPS: vsf_sysutil_bind em vez de usar uma porta fora da faixa. Em qualquer dos servidores, uma faixa demasiado pequena interrompe as transferências no momento de maior atividade.
Onde deve ficar a faixa? Execute primeiro sysctl net.ipv4.ip_local_port_range. Numa instalação Ubuntu sem alterações, o resultado é 32768 60999, que corresponde às portas atribuídas pelo kernel para ligações de saída. Uma faixa passiva dentro dessa janela pode colidir com uma ligação de saída que já esteja a utilizar a porta, por isso mantenha a faixa abaixo dela. 30000 a 30099 ficam livres numa instalação predefinida. Verifique o seu próprio servidor em vez de confiar nesse valor.
Abrir o mesmo intervalo no firewall
O ufw escreve um intervalo com dois-pontos, e o manual indica que também pode ser usada uma lista ou um intervalo "para especificar várias portas; nesse caso, o protocolo é obrigatório":
sudo ufw allow 21/tcp
sudo ufw allow 30000:30099/tcp
sudo ufw status verboseufw status verbose deve agora listar as duas entradas. O mesmo comando sem /tcp é rejeitado com um erro que pede a indicação de tcp ou udp, porque o ufw não faz suposições. sintaxe das regras do ufw numa VPS explica o restante.
O firewalld escreve o intervalo com um hífen e precisa de um reload:
sudo firewall-cmd --permanent --add-service=ftp
sudo firewall-cmd --permanent --add-port=30000-30099/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-all--add-service=ftp abre 21/tcp e solicita o helper ftp, que é o nome definido pela definição de serviço fornecida. Não abre o seu intervalo passivo. Por si só, deixa a situação exatamente como estava. zonas e serviços do firewalld numa VPS apresenta o contexto completo.
Com nftables diretamente, dentro da sua cadeia input:
tcp dport { 21, 30000-30099 } acceptHá mais um firewall a considerar: a maioria dos fornecedores executa um firewall de rede no painel de controlo, fora do sistema operativo. Se as regras no servidor parecerem corretas e os pacotes continuarem sem chegar, abra também o mesmo intervalo nesse firewall.
Informe o servidor do seu endereço público quando estiver atrás de NAT
Execute ip -4 addr show no servidor. Se o endereço da interface for aquele ao qual os clientes se ligam, ignore esta secção. Se a interface tiver um endereço privado (10.x, 172.16 a 172.31.x, 192.168.x) e a plataforma mapear um endereço público para esse endereço, o servidor não conhece o próprio endereço público. A documentação do vsftpd define o valor predefinido de pasv_address como "o endereço é obtido do socket ligado de entrada", portanto a resposta 227 contém o endereço privado e o cliente é enviado para um endereço que não consegue alcançar.
O FileZilla identifica este caso exatamente assim:
Server sent passive reply with unroutable address. Using server address instead.O FileZilla corrige o problema e prossegue. Muitos outros clientes não o fazem. Tentam ligar-se a 10.0.0.5 e ficam bloqueados.
O curl também oculta o problema, o que é relevante se o curl for a sua ferramenta de teste. O manual indica que --ftp-skip-pasv-ip "está ativado por predefinição (adicionado na versão 7.74.0)", portanto o curl ignora o endereço na resposta 227 e reutiliza o endereço da ligação de controlo. Uma transferência que funciona com o curl pode continuar a falhar num cliente gráfico por este único motivo.
Defina o endereço explicitamente. O vsftpd aceita pasv_address=203.0.113.10, além de pasv_addr_resolve=YES (predefinição NO), se preferir escrever um nome de anfitrião. O ProFTPD aceita MasqueradeAddress, que pode receber um endereço, um nome DNS ou um nome de interface. O Pure-FTPd aceita -P, documentado para o caso em que "o servidor está atrás de um dispositivo de mascaramento (NAT)". EPSV evita todo o problema porque a respetiva resposta não tem um campo de endereço, mas não pode depender disso, porque é o cliente que decide qual comando enviar.
O que muda com TLS
FTPS é FTP sobre TLS (segurança da camada de transporte). O cliente liga-se à porta 21 como habitualmente, envia AUTH TLS para proteger o canal de controlo e, em seguida, envia PROT P para encriptar também o canal de dados. O FTP simples envia a palavra-passe pela rede como texto legível. Por isso, se tiver de manter o FTP, use FTPS. O vsftpd é distribuído com ssl_enable definido como NO por predefinição.
Daqui resultam duas consequências. Nenhum auxiliar de controlo de ligações pode funcionar. Este é o ponto anterior visto pelo lado oposto. Além disso, sudo tcpdump -nAi any 'tcp port 21' já não mostra a resposta 227. Quando precisar de saber que endereço e porta o servidor anunciou, consulte o log do próprio servidor em vez do tráfego da rede.
Testar a alteração a partir do exterior
Execute estes comandos noutra máquina. Testar a partir do próprio servidor ignora a firewall que está a tentar corrigir.
sudo ss -ltnp | grep :21
curl -v --disable-epsv --user ftpuser:secret ftp://example.com/
nc -vz example.com 30000ss deve mostrar o daemon FTP a escutar na porta 21. Nenhum processo escuta no intervalo passivo enquanto o servidor está inativo, porque esses sockets são criados para uma transferência e fechados depois dela.
--disable-epsv força o curl a usar o caminho PASV, que é o que expõe o problema do endereço. O trace mostra a resposta do servidor e, em seguida, o endereço e a porta para os quais o curl estabelece a ligação:
< 227 Entering Passive Mode (203,0,113,10,117,52)117 * 256 + 52 = 30004, dentro do intervalo declarado. Um endereço privado nessa linha significa que pasv_address não está definido. Uma porta fora do seu intervalo significa que o servidor nunca leu a alteração de configuração. Verifique se editou o ficheiro utilizado pelo serviço em execução.
nc responde por si só à questão da firewall. Um Connection refused imediato significa que o pacote chegou ao servidor e não encontrou nada a escutar, que é o resultado correto para uma porta passiva inativa: a regra funciona. Uma espera até nc desistir significa que algo descartou o pacote silenciosamente. Trata-se de uma firewall, seja a do servidor, seja a do painel do seu fornecedor. Essa distinção é a mesma descrita em recusada versus tempo esgotado no SSH e aplica-se a todas as portas.
Ainda deve continuar a executar FTP?
Para trabalho novo, não. O SFTP (protocolo de transferência de ficheiros sobre SSH) funciona dentro de uma única ligação SSH na porta 22. Não existe um segundo canal, um intervalo passivo, uma configuração de NAT nem um daemon adicional para proteger, porque o OpenSSH já fornece essa funcionalidade. sftp user@example.com funciona num servidor onde nunca configurou um serviço de transferência de ficheiros. Para disponibilizar ficheiros a alguém e nada mais, sshd_config usa ForceCommand internal-sftp juntamente com ChrootDirectory. Esse diretório tem de pertencer a root e não pode ser gravável pelo utilizador. Caso contrário, o sshd recusa a sessão e regista uma linha bad ownership or modes for chroot directory.
O FTP continua a ser útil quando o outro lado não pode mudar. Scanners e impressoras multifunções são fornecidos com firmware que comunica apenas por FTP. Equipamento laboratorial e industrial costuma executar uma imagem fixa que ninguém irá recertificar. Parceiros comerciais publicam um ponto de entrega FTPS e não irão adicionar outro protocolo para um único fornecedor. Em cada um desses casos, o intervalo passivo e a regra de firewall correspondente são todo o trabalho necessário. Deve executar FTPS em vez de FTP simples. O desenho com dois canais é uma decisão de 1980. Já estava definido na RFC 765, cinco anos antes de a RFC 959 o ter mantido. Atualmente, funciona num ambiente que nunca antecipou esse desenho. Esta história é descrita em o historial dos protocolos de transferência de ficheiros.
FAQ
Porque é que o FTP inicia sessão, mas a listagem do diretório fica bloqueada?
O início de sessão usa apenas a ligação de controlo na porta 21, que a firewall permite. A listagem do diretório precisa de uma segunda ligação TCP numa porta diferente, e essa ligação está bloqueada. Defina um intervalo de portas passivas no servidor FTP e abra o mesmo intervalo na firewall. A listagem será concluída. Uma listagem bloqueada é um problema do canal de dados, nunca da palavra-passe.
Que portas preciso de abrir para o modo passivo do FTP?
A porta 21 para o canal de controlo e o intervalo que configurou para as ligações de dados passivas. Não existe um intervalo padrão, porque é você que o escolhe. Um intervalo como 30000 a 30099 funciona: dimensione-o para o número máximo de transferências simultâneas e mantenha-o separado do intervalo de portas de saída do kernel, que pode consultar com sysctl net.ipv4.ip_local_port_range. Se o fornecedor disponibilizar uma firewall de rede no painel de controlo, abra também o mesmo intervalo nesse local.
Ainda preciso de nf_conntrack_ftp?
Não. Num kernel atual, também não pode depender desse módulo. A atribuição automática de auxiliares está desativada por predefinição e, desde o Linux 6.0, a opção net.netfilter.nf_conntrack_helper foi removida. Por isso, sysctl indica que o ficheiro não existe. O manual do ufw considera obsoleto o carregamento incondicional desses módulos, e o firewalld ignora completamente AutomaticHelpers. Um auxiliar também precisa de ler o canal de controlo como texto simples. Por isso, deixa de funcionar assim que ativa FTPS. Defina um intervalo de portas passivas.
Porque é que o meu cliente FTP indica que a resposta passiva contém um endereço não encaminhável?
O servidor respondeu a PASV com o endereço que vê na própria interface, e esse endereço é privado. Isto acontece quando a plataforma associa um endereço público a um endereço privado. Defina explicitamente o endereço público: pasv_address no vsftpd, MasqueradeAddress no ProFTPD ou -P no Pure-FTPd. O FileZilla contorna o problema reutilizando o endereço ao qual já se ligou e regista "Using server address instead". É por isso que alguns clientes continuam a funcionar com a configuração incorreta, enquanto outros ficam bloqueados.
Devo usar FTPS ou SFTP?
Use SFTP para tudo o que controlar nas duas extremidades: existe uma ligação através de SSH na porta 22, não é necessário abrir um canal de dados e o serviço já está disponível. FTPS é FTP através de TLS. Por isso, mantém o modelo com dois canais e todos os problemas de firewall associados. Escolha-o quando a outra extremidade não aceitar outro protocolo. Não use FTP simples através da Internet, porque a palavra-passe atravessa a rede como texto legível.