Em incidentes recorrentes em servidores cPanel, o webmail Roundcube na porta :2096 ou /webmail pode parar de responder repentinamente e exibir a mensagem genérica:
Oops... something went wrong!
An internal error has occurred. Your request cannot be processed at this time.
Investigando os logs internos do cPanel e do próprio Roundcube, a causa raiz técnica ficou evidente:
DB Error: [14] unable to open database file
(SQL Query: INSERT INTO "session" ...)
in /usr/local/cpanel/base/3rdparty/roundcube/program/lib/Roundcube/rcube_db.php
O ponto-chave é claro: o Roundcube é inicializado com sucesso, mas quebra no momento de persistir a sessão do usuário. Isso indica um erro de permissão de escrita ou corrupção na camada do banco de dados SQLite, e não um bug interno da aplicação PHP do Webmail.
1. O que o erro [14] significa no contexto do SQLite#
No engine do SQLite, o código de erro [14] mapeia diretamente para SQLITE_CANTOPEN (impossibilidade de abrir o arquivo de dados). No ecossistema cPanel/Roundcube, quando a falha é acionada durante operações de escrita (como INSERT INTO session), as causas geralmente estão divididas em:
- Diretório ou arquivo inacessível: Permissões incorretas impedindo o processo do Roundcube (geralmente executado sob o usuário da conta do cPanel) de ler ou escrever no arquivo do banco de dados.
- Impossibilidade de gerar Lock/Journal: O SQLite necessita de permissão para criar arquivos temporários (como
.db-journal,.db-walou.db-shm) na mesma pasta onde o arquivo.dbreside. Se a pasta pai não tiver permissão de escrita, o SQLite falhará mesmo se o arquivo.dbtiver permissões 777. - Agotamento de Recursos: Ausência de espaço em disco ou esgotamento de inodes livres no filesystem.
- Bloqueio de Segurança: Contextos incorretos de arquivos no SELinux ou restrições de enclausuramento (CageFS/CloudLinux).
2. Identificação de versões#
Antes de aplicar correções, valide as versões do painel e da aplicação para referências de caminhos de configuração:
# Verificar a versão instalada do cPanel
cat /usr/local/cpanel/version
# Verificar a versão do Roundcube integrada ao cPanel
cat /usr/local/cpanel/base/3rdparty/roundcube/program/include/rcube.php | grep -i version
3. Primeira camada: o contexto da conta e integridade do SQLite#
Cada conta de e-mail do cPanel possui um banco de dados SQLite próprio para registrar contatos, preferências da interface e sessões. O caminho padrão segue este padrão:
/home/USER/etc/domain.com/email@domain.com.rcube.db
3.1 rotina de backup preventivo obrigatório#
Antes de fazer qualquer alteração manual ou exclusão de banco de dados, execute uma cópia de segurança completa das configurações de e-mail da conta afetada:
# Criar diretório seguro de backup datado
BACKUP_DIR="/root/roundcube-backup-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$BACKUP_DIR"
# Fazer backup recursivo do diretório etc da conta
cp -r /home/USER/etc/ "$BACKUP_DIR/"
# Fazer cópia dedicada do banco de dados SQLite
cp /home/USER/etc/domain.com/email@domain.com.rcube.db "$BACKUP_DIR/"
echo "Backup salvo com sucesso em: $BACKUP_DIR"
3.2 verificar a integridade do SQLite#
Se o banco de dados estiver corrompido, o SQLite recusará novas conexões de gravação:
# Executar verificação interna de integridade
sqlite3 /home/USER/etc/domain.com/email@domain.com.rcube.db "PRAGMA integrity_check;"
# Saída esperada: "ok"
# Se retornar qualquer outro valor, o arquivo de dados está fisicamente corrompido.
# Verificar o tamanho do arquivo (arquivos com 0 bytes indicam truncamento por falta de disco)
ls -lh /home/USER/etc/domain.com/email@domain.com.rcube.db
# Verificar se existem arquivos de lock ou journal órfãos retendo o banco
ls -la /home/USER/etc/domain.com/email@domain.com.rcube.db*
3.3 corrigir permissões e ownership#
O processo que executa o webmail deve ser o proprietário do arquivo. Aplique o baseline de permissões seguro:
# Ajustar dono e grupo do diretório etc da conta
chown -R USER:mail /home/USER/etc
# Definir permissões restritivas nas pastas
chmod 750 /home/USER/etc
chmod 750 /home/USER/etc/domain.com
# Definir permissões de leitura/escrita para os bancos SQLite
chmod 640 /home/USER/etc/domain.com/*.db
3.4 recriar banco corrompido#
Se o PRAGMA retornou erros ou se o arquivo possui 0 bytes, force a regeneração do arquivo de dados movendo o antigo:
# Renomear o banco para gerar um novo no próximo login
mv /home/USER/etc/domain.com/email@domain.com.rcube.db \
/home/USER/etc/domain.com/email@domain.com.rcube.db.corrupted
Nota: Ao realizar esta ação, as preferências visuais do webmail e a lista de contatos locais do Roundcube serão resetadas. As mensagens de e-mail continuam intactas no Maildir.
4. Segunda camada: extensão PHP SQLite#
O Roundcube precisa das extensões SQLite ativas na versão do PHP utilizada pelo painel do cPanel (através do EasyApache).
Verificar se as extensões estão carregadas:#
php -m | grep -i sqlite
# Deve retornar:
# sqlite3
# pdo_sqlite
Se não retornarem, edite o arquivo php.ini do sistema (/opt/cpanel/ea-phpXX/root/etc/php.ini) correspondente e descomente ou adicione as extensões:
extension=sqlite3
extension=pdo_sqlite
Após alterar, reinicie o serviço PHP-FPM associado:
/scripts/restartsrv ea-php-fpm
5. Terceira camada: escopo global de usuários afetados#
Para diagnosticar se a falha é isolada em um usuário ou afeta todo o servidor, execute uma busca global por bancos vazios ou com permissões incorretas:
# Localizar todos os bancos do Roundcube no servidor
find /home -name "*.rcube.db" -type f 2>/dev/null
# Localizar bancos com tamanho zerado (indício de estouro de quota anterior)
find /home -name "*.rcube.db" -type f -size 0 -exec ls -la {} \;
6. Quarta camada: virtualização CloudLinux & CageFS#
Se o servidor utiliza CloudLinux com CageFS, o sistema de arquivos dos usuários é enclausurado em diretórios virtuais. Um travamento no mapeamento desses diretórios impede o usuário de gravar no banco de dados.
Verificar se o CloudLinux e o CageFS estão ativos:#
# Verificar release do sistema operacional
cat /etc/redhat-release
# Verificar a versão do CageFS
cagefsctl --version
# Listar se o usuário afetado está enclausurado
cagefsctl --list-users | grep USER
Reconstruir e forçar remount do CageFS:#
Se o erro for causado por falha no CageFS, force a atualização dos mounts virtuais:
# Atualizar as permissões e mounts de um usuário específico
cagefsctl --remount USER
# Forçar atualização global de diretórios temporários e paths do CageFS
cagefsctl --update
7. Quinta camada: inodes, disco e quotas em /tmp e /home#
O SQLite necessita criar arquivos de diário e escrita no diretório /tmp do sistema e gravar permanentemente em /home. Falta de inodes livres ou espaço nessas partições gerará o erro [14].
Verificar espaço e inodes globais:#
# Verificar partições /home e /tmp
df -h /home /tmp
df -i /home /tmp
Verificar quotas de inodes e disco por usuário:#
# Verificar se o usuário estourou sua quota do cPanel
repquota -a | grep USER
# Medir o consumo da conta
du -sh /home/USER/
Validar as permissões e o sticky bit do /tmp:#
O diretório /tmp deve possuir a permissão 1777 (Sticky Bit) para que múltiplos usuários do sistema operacional possam ler e escrever seus arquivos temporários sem interferências.
ls -ld /tmp
# Deve retornar: drwxrwxrwt
# Se a permissão estiver incorreta, corrija:
chmod 1777 /tmp
8. Sexta camada: estado do filesystem#
Quedas de storage ou falhas no disco rígido podem fazer com que o kernel remonte as partições como somente leitura (ro) para proteger a integridade dos dados, impedindo gravações.
# Verificar o estado dos mounts ativos
mount | grep -E ' /home | /tmp | / '
Caso encontre a flag ro ligada às partições principais, execute verificações de integridade (fsck) nas partições após desmontá-las.
9. Sétima camada: SELinux e contextos de segurança#
Em distribuições baseadas em RHEL (AlmaLinux, Rocky Linux) com o SELinux no modo Enforcing, políticas de segurança podem bloquear a gravação do Apache/cPanel nos diretórios /home/USER/etc.
Abordagem segura para troubleshooting de SELinux:#
- Validar o status atual:
getenforce
- Desativar temporariamente para teste de diagnóstico rápido (limite de 5 minutos):
setenforce 0
- Realizar o teste de login no Roundcube.
- Reativar o SELinux imediatamente após o teste:
setenforce 1
- Se o login funcionou apenas com o SELinux desativado, restaure os contextos corretos da conta:
restorecon -Rv /home/USER/etc
10. Oitava camada: reparo e reinstalação de pacotes do cPanel#
Se todas as camadas de infraestrutura física estiverem operacionais, corrija possíveis corrupções de binários ou scripts integrados do cPanel.
# 1. Comando do cPanel para validar e corrigir RPMS quebrados
/scripts/check_cpanel_rpms --fix
# 2. Se necessário, reinstale o pacote de dados do Roundcube
dnf reinstall cpanel-roundcube -y
11. Validação pós-fix#
Após aplicar as correções, ateste se o banco voltou a funcionar normalmente:
# 1. Tentar login no Webmail (porta 2096)
# 2. Verificar se o novo arquivo .db foi instanciado no diretório etc
ls -la /home/USER/etc/domain.com/email@domain.com.rcube.db
# 3. Auditar logs de erros locais do usuário em busca de exceções remanescentes
tail -n 20 /home/USER/etc/domain.com/email@domain.com/../roundcube/errors.log
# 4. Validar permissões e tamanho do banco de dados gerado
stat /home/USER/etc/domain.com/email@domain.com.rcube.db
Tabela de correlação: sintomas vs. causas do erro [14]#
| Causa Raiz | Sintoma Adicional | Diagnóstico Rápido | Correção Recomendada |
|---|---|---|---|
| Permissão de Arquivo | Arquivo .db pertence ao root | ls -la /home/USER/etc/ | chown -R USER:mail |
| Estouro de Quota | Banco com tamanho zero (0 bytes) | repquota -a ou df -h | Aumentar quota ou liberar espaço |
| Falta de Inodes | Espaço em disco disponível, mas sem inodes | df -i | Remover arquivos temporários órfãos |
| Bloqueio do CageFS | O erro some com login fora do webmail | cagefsctl --list-users | cagefsctl --remount USER |
| SELinux Context | Bloqueios de escrita acusados no audit.log | getenforce | restorecon -Rv /home/USER/etc |
| Corrupção de SQLite | Erros de leitura com comando sqlite3 | sqlite3 [arquivo] "PRAGMA..." | Mover banco para .corrupted |
Runbook de atendimento e triagem rápida#
Siga estes passos sequenciais ao receber chamados de erro de banco de dados do Roundcube:
- [ ] Fase 1: Coleta de Logs
- Verifique o log
/var/cpanel/roundcube/logs/errors.logou os logs locais do usuário em/home/USER/etc/domain.com/roundcube/errors.log. - [ ] Fase 2: Espaço e Cotas
- Verifique espaço e inodes com
df -hedf -inas partições/homee/tmp. - Valide as permissões da pasta
/tmpexecutandols -ld /tmp(deve ser1777). - [ ] Fase 3: Backup e Permissões
- Crie uma cópia de backup do banco
.dbem/root/roundcube-backup-.... - Corrija permissões e donos com
chown -R USER:mail /home/USER/etc. - [ ] Fase 4: Integridade do SQLite
- Teste a integridade do banco com
sqlite3 [arquivo] "PRAGMA integrity_check;". - Se estiver quebrado, renomeie o arquivo e force a geração de um novo banco limpo.
- [ ] Fase 5: Regras de Enjaulamento e Segurança
- Se usar CloudLinux, execute
cagefsctl --remount USER. - Se usar SELinux, execute temporariamente
setenforce 0, teste o login, restaure comsetenforce 1e normalize comrestorecon.
12. Script de diagnóstico automatizado (diagnose-roundcube-db14.sh)#
Para agilizar o atendimento de suporte nível 2/3, utilize este script de diagnóstico no servidor para mapear automaticamente todas as camadas descritas e apontar as possíveis causas do erro:
#!/bin/bash
# diagnose-roundcube-db14.sh
# Script de diagnóstico para o erro DB Error [14] no Roundcube cPanel.
# Deve ser executado como root.
set -euo pipefail
# Configuração de Cores para output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0;37m' # Sem cor
log_info() {
echo -e "[${GREEN}INFO${NC}] $1"
}
log_warn() {
echo -e "[${YELLOW}WARN${NC}] $1"
}
log_error() {
echo -e "[${RED}ERROR${NC}] $1"
}
# 1. Verificar privilégios de root
if [ "$EUID" -ne 0 ]; then
log_error "Este script precisa ser executado como root."
exit 1
fi
log_info "Iniciando diagnóstico do Roundcube DB Error [14]..."
# Argumentos: Usuário cPanel e/ou Email opcional
CP_USER=""
EMAIL=""
if [ $# -ge 1 ]; then
CP_USER="$1"
fi
if [ $# -ge 2 ]; then
EMAIL="$2"
fi
# 2. Informações do Painel cPanel e Roundcube
log_info "--- Versões do Sistema e Painel ---"
if [ -f /usr/local/cpanel/version ]; then
CP_VER=$(cat /usr/local/cpanel/version)
log_info "cPanel Versão: $CP_VER"
else
log_warn "cPanel não detectado ou versão inacessível."
fi
RCUBE_PATH="/usr/local/cpanel/base/3rdparty/roundcube"
if [ -d "$RCUBE_PATH" ]; then
if [ -f "$RCUBE_PATH/program/include/rcube.php" ]; then
RC_VER=$(grep -i "RCMAIL_VERSION" "$RCUBE_PATH/program/include/rcube.php" || grep -i "version" "$RCUBE_PATH/program/include/rcube.php" | head -n 1)
log_info "Roundcube Versão: $RC_VER"
fi
else
log_warn "Diretório do Roundcube do cPanel não localizado."
fi
# 3. Verificar extensões SQLite no PHP
log_info "--- Extensões PHP SQLite ---"
if command -v php >/dev/null 2>&1; then
PHP_OPTS=$(php -m)
if echo "$PHP_OPTS" | grep -iq "sqlite3" && echo "$PHP_OPTS" | grep -iq "pdo_sqlite"; then
log_info "Extensões sqlite3 e pdo_sqlite carregadas no PHP global."
else
log_warn "Extensões sqlite3 ou pdo_sqlite ausentes no PHP global."
fi
else
log_warn "Binário php não encontrado no PATH global."
fi
# 4. Verificar espaço em disco e inodes nas partições críticas
log_info "--- Espaço em Disco e Inodes ---"
check_partition() {
local part="$1"
if df -P "$part" >/dev/null 2>&1; then
local space_pct=$(df -P "$part" | awk 'NR==2 {print $5}' | sed 's/%//')
local inode_pct=$(df -iP "$part" | awk 'NR==2 {print $5}' | sed 's/%//')
log_info "Partição $part: Uso de espaço: ${space_pct}%, Uso de inodes: ${inode_pct}%"
if [ "$space_pct" -gt 95 ]; then
log_error "Espaço crítico na partição $part (>95%): $space_pct%"
fi
if [ "$inode_pct" -gt 95 ]; then
log_error "Uso crítico de inodes na partição $part (>95%): $inode_pct%"
fi
else
log_warn "Não foi possível verificar a partição para $part"
fi
}
check_partition "/home"
check_partition "/tmp"
# Verificar Sticky Bit de /tmp
TMP_PERM=$(stat -c "%a" /tmp 2>/dev/null || stat -c "%A" /tmp 2>/dev/null)
if [[ "$TMP_PERM" == *"1777"* || "$TMP_PERM" == *"rwxrwxrwt"* || "$TMP_PERM" == *"777"* ]]; then
log_info "/tmp permissões: $TMP_PERM (Sticky Bit correto)"
else
log_warn "/tmp permissões incomuns: $TMP_PERM (deveria ser 1777 / drwxrwxrwt)"
fi
# 5. Estado do Filesystem (ReadOnly check)
log_info "--- Estado do Filesystem ---"
if mount | grep -q "on /home .*\(ro\)"; then
log_error "Partição /home está montada como SOMENTE LEITURA (ro)!"
else
log_info "Partição /home montada como leitura/escrita."
fi
# 6. CloudLinux e CageFS
log_info "--- CloudLinux e CageFS ---"
if [ -f /etc/redhat-release ] && grep -qi "cloudlinux" /etc/redhat-release; then
log_info "SO: CloudLinux detectado."
if command -v cagefsctl >/dev/null 2>&1; then
CAGE_VER=$(cagefsctl --version 2>/dev/null || echo "Desconhecida")
log_info "CageFS ativo: $CAGE_VER"
if [ -n "$CP_USER" ]; then
if cagefsctl --list-users | grep -qw "$CP_USER"; then
log_info "Usuário '$CP_USER' está enjaulado no CageFS."
else
log_warn "Usuário '$CP_USER' NÃO está enjaulado no CageFS."
fi
fi
else
log_warn "cagefsctl não encontrado no sistema."
fi
else
log_info "CloudLinux não detectado."
fi
# 7. Status do SELinux
log_info "--- SELinux ---"
if command -v getenforce >/dev/null 2>&1; then
SEL_STATUS=$(getenforce)
log_info "Status do SELinux: $SEL_STATUS"
if [ "$SEL_STATUS" = "Enforcing" ]; then
log_warn "SELinux está em Enforcing. Certifique-se de que os contextos do diretório etc de e-mail estão corretos."
fi
else
log_info "SELinux não ativo/instalado."
fi
# 8. Mapear e auditar bancos de dados do Roundcube
log_info "--- Auditoria dos Bancos de Dados SQLite do Roundcube ---"
find_dbs() {
local search_path="$1"
find "$search_path" -type f -name "*.rcube.db" 2>/dev/null
}
DB_FILES=""
if [ -n "$CP_USER" ]; then
USER_HOME="/home/$CP_USER"
if [ -d "$USER_HOME" ]; then
log_info "Mapeando bancos para o usuário: $CP_USER em $USER_HOME"
if command -v quota >/dev/null 2>&1; then
quota -vs "$CP_USER" 2>&1 | head -n 5 || true
fi
DB_FILES=$(find_dbs "$USER_HOME")
else
log_error "Diretório home do usuário não encontrado: $USER_HOME"
exit 1
fi
else
log_info "Nenhum usuário especificado. Escaneando todo o diretório /home..."
DB_FILES=$(find_dbs "/home")
fi
if [ -z "$DB_FILES" ]; then
log_warn "Nenhum arquivo .rcube.db localizado no caminho de busca."
else
for db in $DB_FILES; do
if [ -n "$EMAIL" ] && [[ "$db" != *"$EMAIL"* ]]; then
continue
fi
log_info "Analisando banco: $db"
owner=$(stat -c '%U:%G' "$db")
perm=$(stat -c '%a' "$db")
size=$(stat -c '%s' "$db")
log_info " Dono/Grupo: $owner | Permissões: $perm | Tamanho: $size bytes"
user_from_path=$(echo "$db" | cut -d'/' -f3)
if [ "$owner" != "$user_from_path:mail" ] && [ "$owner" != "$user_from_path:nobody" ]; then
log_error " [Permissão] Proprietário incorreto! Esperado '$user_from_path:mail' ou '$user_from_path:nobody', encontrado '$owner'."
fi
if [ "$size" -eq 0 ]; then
log_error " [Integridade] Arquivo de banco de dados está com tamanho ZERADO (0 bytes)."
fi
if command -v sqlite3 >/dev/null 2>&1; then
check_res=$(sqlite3 "$db" "PRAGMA integrity_check;" 2>&1 || echo "Erro de Execução")
if [ "$check_res" = "ok" ]; then
log_info " [Integridade] SQLite PRAGMA integrity_check: OK"
else
log_error " [Integridade] Corrupção física detectada: $check_res"
fi
else
log_warn " sqlite3 CLI não instalada. Pulando pragma integrity_check."
fi
if [ -f "${db}-journal" ]; then
log_warn " [Locks] Arquivo temporário de Journal ativo: ${db}-journal"
fi
if [ -f "${db}-wal" ]; then
log_warn " [Locks] Arquivo temporário Write-Ahead Log ativo: ${db}-wal"
fi
done
fi
log_info "Diagnóstico concluído."
Este artigo foi útil?
Deixe uma reação rápida para apoiar o conteúdo:
Este post está licenciado sob CC BY-NC.



Comentários
Participe da discussão abaixo.
0 comentários