Quando o filesystem e o daemon entram em desincronia
Voltar para blog

Quando o filesystem e o daemon entram em desincronia

07/06/2026 · 9 min · Infraestrutura

Recentemente, peguei um caso que começou com uma simples reconfiguração de autenticação DNS e escalou para uma corrupção profunda de estado no subsistema de e-mail. O ticket relatava que mensagens estavam sendo recebidas pelo servidor, mas nunca chegavam à caixa de entrada do cliente.

O que parecia um problema de roteamento básico revelou-se um desvio grave entre a camada de armazenamento (filesystem) e a máquina de estado do serviço IMAP. Abaixo, detalho a autópsia desse incidente.

O problema técnico#

Inicialmente, atuei na regularização da autenticação do domínio (domain.com). O ambiente cPanel estava defasado em suas chamadas de API antigas (/scripts/email_auth), o que me obrigou a invocar diretamente a UAPI e o ZoneEdit via terminal para injetar as chaves DKIM e a política DMARC (v=DMARC1; p=none).

Com o DNS estabilizado, o real problema se manifestou: um e-mail de teste (ID 1vRXMv-00000001G0c-0CLv) foi disparado, o servidor aceitou a conexão SMTP, mas o cliente não enxergava a mensagem via IMAP ou Webmail. O e-mail virou um fantasma.


A estrutura maildir no filesystem#

Para entender como a desincronia ocorre, precisamos visualizar a anatomia padrão de um diretório Maildir. No Dovecot, cada caixa de e-mail do usuário possui a seguinte árvore:

/home/user/mail/domain.com/user/
├── cur/                    # Mensagens já lidas e processadas pelo cliente
├── new/                    # Mensagens novas entregues, aguardando leitura
├── tmp/                    # Arquivos temporários durante a escrita de entrega
├── .Sent/                  # Subpasta: Mensagens Enviadas
│   ├── cur/
│   ├── new/
│   └── tmp/
├── .Drafts/                # Subpasta: Rascunhos
├── .Trash/                 # Subpasta: Lixeira
├── dovecot.index           # Cache binário rápido de cabeçalhos e flags
├── dovecot.index.log       # Log transacional de alterações do índice
├── dovecot-uidlist         # Mapeamento crítico de nomes físicos ↔ UIDs IMAP
└── subscriptions           # Arquivo texto com pastas assinadas pelo usuário

Rastreando o inode#

A primeira etapa foi rastrear o envelope no MTA. O log de entrega do Exim (/var/log/exim_mainlog) mostrou um fluxo impecável e detalhado:

2026-06-07 12:05:01 [email protected] R=virtual_user T=dovecot_virtual_delivery U=user C="250 2.0.0 <[email protected]> sNJzJ6b0MmkR7QMAgRW0Lg Saved" QT=1.234s DT=0.567s

O status Saved provava que o Exim fez o handoff com sucesso para o processo LMTP do Dovecot. O MTA não estava dropando o pacote nem enfrentando loops de roteamento. O problema estava do lado do MDA (Mail Delivery Agent).

Ao inspecionar o Maildir da usuária, um grep pelo ID da mensagem falhou silenciosamente. Fui checar o diretório físico da fila new/ usando um ls -latr. O arquivo estava lá: o inode havia sido alocado às 12:05 sob o nome 1781476345.M965412P23485.servidor.domain.com,S=4035,W=4112.

Por que o grep falhou se o arquivo existia? Ao dar um cat no arquivo, o terminal exibiu lixo binário. O servidor estava rodando o Dovecot com o plugin Zlib ativado. O grep padrão opera em plain text e bateu no muro da compressão. Usando um zcat, consegui ler o buffer descomprimido e ver os cabeçalhos SMTP intactos. O arquivo estava perfeito no disco, mas o daemon IMAP estava cego para ele.


Rastreabilidade de código: a máquina de estado do Dovecot#

O fluxo de falha ocorreu na interface entre o VFS (Virtual File System) e o indexador do Dovecot. Quando o LMTP do Dovecot recebe o stream do Exim, ele invoca a biblioteca Zlib para comprimir o payload antes de acionar a syscall de escrita (write()) no disco.

Após o flush no disco, o Dovecot precisa atualizar o arquivo dovecot-uidlist, que mapeia o nome físico do arquivo para um UID sequencial padrão IMAP, e o arquivo dovecot.index. Uma race condition, uma queda abrupta do daemon ou um I/O lock corrompeu essa estrutura. O arquivo foi gravado, mas a transação de metadados falhou. O daemon se recusava a apresentar qualquer arquivo na pasta new/ que não estivesse rigidamente mapeado no seu uidlist.


Diagnóstico forense no servidor#

Para comprovar a desincronia e mapear a causa raiz, executei os seguintes passos de auditoria técnica:

1. Verificar mail_location e caminhos físicos#

Primeiro, verifique onde o Dovecot está configurado para ler e gravar as caixas:

doveconf -n | grep mail_location
# Saída esperada: mail_location = maildir:/home/%d/%n/mail:LAYOUT=fs

Em seguida, valide se o diretório existe fisicamente com o dono correto:

ls -la /home/user/mail/domain.com/user/

2. Auditar logs do Dovecot e LMTP#

Examine os logs específicos do subsistema de correio para capturar falhas de leitura ou escrita de índice:

# Monitorar logs gerais de mail
tail -f /var/log/maillog | grep -E -i "dovecot|lmtp|uidlist|index"

# Ou logs direcionados (dependendo da configuração do sistema)
tail -f /var/log/dovecot.log | grep -i error

3. Verificar status do plugin zlib#

Para checar se a compressão em tempo real está ativa:

# Filtrar configurações ativas de plugin
doveconf -n | grep -A5 plugin

# Verificar especificamente o plugin zlib
doveconf -n | grep -i zlib

4. Verificar espaço em disco e quotas#

Se a partição estiver cheia ou a quota do usuário de e-mail/cPanel estourar durante o processo de gravação do LMTP, os índices do Dovecot serão gerados de forma corrompida.

# Verificar espaço em disco
df -h /home

# Verificar quota do usuário no sistema
repquota -a | grep user

# Verificar uso de disco da pasta de mail do usuário
du -sh /home/user/mail/

# Buscar arquivos de 0 bytes (indicador clássico de falha de gravação de índice ou uidlist)
find /home/user/mail/ -size 0 -type f

5. Verificar versão e configuração completa do Dovecot#

Identifique se a versão rodando possui vulnerabilidades de indexação ou se há pacotes de atualização do Dovecot pendentes:

# Versão do Dovecot
doveadm --version

# Verificar se há atualizações do Dovecot disponíveis
yum check-update dovecot  # Sistemas baseados em RHEL/CentOS
apt list --upgradable | grep dovecot  # Sistemas baseados em Debian/Ubuntu

# Verificar dump de configuração não padrão ativa
doveconf -n | head -50

6. Verificar conexões ativas e estado do IMAP IDLE#

Sessões ativas via IMAP IDLE podem prender os arquivos de metadados no disco e inviabilizar a reindexação adequada das pastas.

# Verificar se IDLE está configurado
doveconf -n | grep -i "idle"

# Verificar conexões e usuários ativos em tempo real
doveadm who

# Listar de forma detalhada e em linha única as sessões travadas
doveadm who -1

7. Verificar TLS/SSL no Dovecot#

Problemas de handshake TLS podem mascarar erros de sincronização nas conexões IMAP dos clientes.

# Verificar configuração SSL/TLS ativa
doveconf -n | grep -i "ssl"

# Verificar permissões e presença dos certificados
ls -la /etc/dovecot/ssl/ 2>/dev/null

# Confirmar se SSL está habilitado globalmente
doveconf -n | grep "ssl = yes"

8. Mapear escopo do problema (verificação multi-contas)#

Verifique se a desincronia de índices é um problema isolado ou sistêmico no servidor:

# Verificar se há outras contas com erros em dovecot-uidlist
find /home/*/mail -name "dovecot-uidlist" -exec grep -l "error" {} \; 2>/dev/null

# Buscar índices de e-mail vazios em outras contas do servidor
find /home/*/mail -name "dovecot.index" -size 0 -type f 2>/dev/null

# Consultar recursivamente o status da Inbox de todos os usuários cadastrados
doveadm user "*" | while read user; do
    doveadm mailbox status -u "$user" INBOX 2>&1 | grep -i "error" && echo "Problema na conta: $user"
done

Hipóteses descartadas#

Antes de partir para a manipulação de disco, isolei as variáveis:

  1. Roteamento Local vs Remoto: Verifiquei o /etc/localdomains. O domínio estava corretamente alocado localmente; não havia bypass no Exim.
  2. Ação de MUA / POP3 Agressivo: Vasculhei os logs buscando sessões POP3 que pudessem ter baixado e enviado o comando DELE milissegundos após a entrega. Negativo.
  3. Formato mdbox vs Maildir: Confirmei a estrutura dos diretórios (cur, new, tmp). Estávamos lidando com o formato Maildir clássico, não com blocos binários mdbox.
  4. Corrupção leve de Índice: Tentei renomear o arquivo via mv para invalidar a leitura de tamanho (,S=...) e forçar o daemon a tratá-lo como um novo evento de I/O. Não surtiu efeito.

Opções de recuperação escalonadas (do simples ao nuclear)#

Diante da falha técnica, as ações de mitigação devem seguir uma ordem lógica de severidade, evitando a perda de dados.

Nível 1: Resync e desconexão de sessões ativas#

Antes de intervir fisicamente, remova todas as conexões presas da usuária e force a sincronização lógica da inbox:

# Desconecta sessões IMAP/POP3 ativas do usuário
doveadm kick -u [email protected]

# Sincroniza a Inbox principal
doveadm force-resync -u [email protected] INBOX

Nível 2: Reindexação forçada das pastas#

Se o resync simples falhar, obrigue o Dovecot a invalidar e reconstruir os índices para todas as pastas da conta:

# Força a reconstrução de índices da Inbox
doveadm index -u [email protected] INBOX

# Sincroniza todas as pastas recursivamente
doveadm force-resync -u [email protected] "*"

Nível 3: Recuperação de mailbox nativa#

Invoque a rotina interna do Dovecot de reparação de caixas Maildir/Mdbox:

doveadm mailbox recover -u [email protected]

Nível 4: Reconstrução manual de índices (sem deletar a conta)#

Se as ferramentas automáticas do doveadm falharem em reestabelecer o UID, você pode limpar os arquivos físicos de metadados manualmente.

# 1. Parar temporariamente as conexões do usuário
doveadm kick -u [email protected]

# 2. OBRIGATÓRIO: Backup preventivo dos arquivos de índice específicos antes da remoção
BACKUP_IDX="/root/dovecot-index-backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_IDX"
cp -a /home/user/mail/domain.com/user/dovecot.index* "$BACKUP_IDX/"
cp -a /home/user/mail/domain.com/user/dovecot-uidlist "$BACKUP_IDX/" 2>/dev/null
echo "Backup de índices salvo em: $BACKUP_IDX"

# 3. OBRIGATÓRIO: Verificar a integridade do backup de índices
ls -la "$BACKUP_IDX/"
du -sh "$BACKUP_IDX/"
# Garantir que os arquivos críticos de cache foram copiados com tamanho maior que zero

# 4. Remover arquivos de índice e controle corrompidos (NUNCA os diretórios cur/new/tmp!)
find /home/user/mail/domain.com/user/ -maxdepth 1 -name "dovecot.index*" -delete
rm -f /home/user/mail/domain.com/user/dovecot-uidlist

# 5. Forçar resync para gerar um novo UIDlist e índices limpos
doveadm force-resync -u [email protected] "*"

Nível 5: Opção nuclear controlada (expurgo de estrutura)#

Se a reindexação de baixo nível falhar por corrupções no banco de dados do próprio painel (cPanel), o expurgo lógico da conta se faz necessário:

# 1. ANTES de tudo: Realizar backup completo dos e-mails e logs de auditoria
BACKUP_DIR="/root/mail-backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
cp -a /home/user/mail/domain.com/user/ "$BACKUP_DIR/"
cp /var/log/exim_mainlog "$BACKUP_DIR/"
cp /var/log/maillog "$BACKUP_DIR/"
echo "Backup emergencial criado em: $BACKUP_DIR"

# 2. OBRIGATÓRIO: Verificar a integridade do backup do Maildir gerado
echo "=== Validando Integridade do Backup ==="
ls -la "$BACKUP_DIR/user/"
du -sh "$BACKUP_DIR/user/"
# Verificar se todos os 6 diretórios de e-mail críticos estão presentes
ls -d "$BACKUP_DIR/user"/{cur,new,tmp,.Sent,.Drafts,.Trash} 2>/dev/null | wc -l
# Deve retornar 6. Se retornar menos, o backup está incompleto!

# 3. Mover a pasta antiga para fora do path do daemon
mv /home/user/mail/domain.com/user /home/user/mail/domain.com/user_OLD_BKP

# 4. Deletar a conta logicamente via UAPI para limpar lixo no banco do painel
uapi Email delete_pop user=user domain=domain.com

# 5. Recriar a conta limpa via UAPI
uapi Email add_pop user=user domain=domain.com password='MinhaSenhaSeguraAqui'

# 6. OBRIGATÓRIO: Verificar se a conta foi devidamente recriada e registrada no painel e no Dovecot
echo "=== Verificando Recriação da Conta ==="
ls -la /home/user/mail/domain.com/user/
uapi Email list_pop user=user domain=domain.com
doveadm user [email protected]

# 7. Enxertar cirurgicamente os e-mails (apenas pastas cur/new/tmp e subpastas de sistema)
# Ignoramos arquivos como dovecot.index e dovecot-uidlist para não trazer corrupção de volta
cp -r /home/user/mail/domain.com/user_OLD_BKP/{cur,new,tmp,.Sent,.Drafts,.Trash} /home/user/mail/domain.com/user/

# 8. Corrigir permissões e ownership (Essencial para o Dovecot poder ler)
chown -R user:user /home/user/mail/domain.com/user/
find /home/user/mail/domain.com/user/ -type d -exec chmod 751 {} \;
find /home/user/mail/domain.com/user/ -type f -exec chmod 640 {} \;

# 9. OBRIGATÓRIO: Verificar se as permissões e o ownership foram aplicados corretamente
echo "=== Verificando Permissões Pós-Chown ==="
# Confirmar owner e group (deve ser user:user)
ls -la /home/user/mail/domain.com/user/
# Validar se diretórios têm permissão 751
find /home/user/mail/domain.com/user/ -type d -exec stat -c "%a %n" {} \; | head -n 10
# Validar se arquivos têm permissão 640
find /home/user/mail/domain.com/user/ -type f -exec stat -c "%a %n" {} \; | head -n 10

# 10. Forçar sincronização lógica para mapear os novos arquivos aos índices estéreis
doveadm force-resync -u [email protected] "*"
Significado das permissões recomendadas: Diretórios (751 - rwxr-x--x): Permite leitura, escrita e execução ao dono do sistema (cPanel user); leitura e execução ao grupo (correio/dovecot); e apenas execução a terceiros para acessar os subdiretórios. Arquivos (640 - rw-r-----): Permite leitura e escrita ao dono, apenas leitura ao grupo de correio, e nega completamente o acesso a terceiros.

Validação pós-correção (pós-fix)#

Após aplicar a solução, verifique se a sincronização foi bem-sucedida e se novos e-mails entram normalmente no fluxo:

# 1. Pesquisar e-mails via CLI na Inbox e confirmar se o 'fantasma' aparece na lista
doveadm search -u [email protected] INBOX ALL

# 2. Verificar status atual da caixa
doveadm mailbox status -u [email protected] INBOX

# 3. Disparar e-mail de teste e validar logs em tempo real
tail -n 20 /var/log/maillog | grep -i "error\|fail"

Prevenção e monitoramento proativo#

Para mitigar ocorrências similares no futuro, implemente as seguintes boas práticas de sysadmin:

  1. Monitoramento de Erros de UID: Crie um cron job diário para rodar checagens e alertar sobre erros de metadados:
    doveadm mailbox status -u ALL USERS "uidlist" 2>&1 | grep -i error
  1. Desligamento Graceful e No-Break (UPS): Quedas abruptas de energia causam escritas de índice corrompidas. Certifique-se de que a UPS envie sinais de powerfail para fazer o shutdown limpo do Dovecot.
  2. Controle de Alocação de Disco: E-mails fantasmas acontecem quando a partição ou a cota (quota) atinge 100% durante transações de escrita do LMTP. Implemente alertas ativos de quota a 90%.

Fluxo de decisão: recuperação de email#

flowchart TD A[E-mail não aparece no IMAP] --> B{Verificar Filesystem} B -->|Arquivo existe| C{Verificar Zlib/Compressão} B -->|Arquivo não existe| D[Verificar Logs de Entrega Exim] C -->|Zlib ativo| E[Verificar Índices do Dovecot] C -->|Zlib inativo| F[Verificar Permissões do Maildir] E --> G{Índices Íntegros?} G -->|Sim| H[Verificar UIDlist] G -->|Não| I[Nível 4: Reindexação Manual] H --> J{UIDlist Íntegro?} J -->|Sim| K[Verificar Quota/Espaço em Disco] J -->|Não| L[Nível 5: Reconstrução Nuclear] D --> M[Analisar Logs Exim /var/log/exim_mainlog] M --> N{Entrega com Sucesso?} N -->|Sim| O[Investigar Log/Processo do Dovecot] N -->|Não| P[Corrigir Erro do MTA / Roteamento] I --> Q[Criar Backup IDX + Remover Índices + Force-Resync] L --> R[Backup Completo + Recriar Conta via UAPI + Restaurar E-mails] style I fill:#f59e0b,stroke:#d97706,color:#fff style L fill:#7f1d1d,stroke:#ef4444,color:#fff

Checklist de recuperação#

Checklist: email fantasma - recuperação Dovecot#

1. Diagnóstico e triagem inicial#

2. Nível 1 ao 3 (sincronização & reparação lógica)#

3. Nível 4 (reindexação manual)#

4. Nível 5 (reconstrução nuclear)#

Este artigo foi útil?

Deixe uma reação rápida para apoiar o conteúdo:

CC BY-NC

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.

0 comentários