FAQ

Este é o guia de instalação, configuração e manutenção do Arcivéo Monitor. As secções estão agrupadas: visão geral, implementação do painel, ligação de ferramentas de segurança, módulos integrados e diagnóstico. Pode copiar os comandos com o botão à direita.

Por onde começar

01. Instalação do painel — escolha o método

A instalação do painel está em páginas dedicadas, passo a passo. Escolha o método:

Se tiver dúvidas, opte pela automática. Este manual continua a ser a fonte única sobre SSL, ferramentas, cron e diagnóstico — as páginas de instalação remetem para as suas secções, sem duplicar nada.

Visão geral

02. O que é o Arcivéo Monitor

Arcivéo Monitor — o painel de segurança do servidor. Recolhe dados das ferramentas instaladas (Fail2ban, UFW, Lynis, ModSecurity, AIDE, ClamAV, Auditd, CrowdSec, Suricata, Falco, etc.) e apresenta-os numa interface única, com dashboard, mapa de ataques e páginas detalhadas para cada ferramenta.

O Monitor não é um meio de proteção ativo — não bloqueia ataques por si só. A sua função é agregar a informação das ferramentas já em funcionamento e apresentá-la de forma prática.

03. Como o monitor funciona no servidor

O monitor funciona apenas localmente — tem de ser instalado no mesmo servidor que monitoriza. Não existe qualquer SSH nem API remota.

Todos os comandos (fail2ban-client, ufw status, ipset list, etc.) são executados pelo painel em nome do utilizador do servidor web (normalmente www-data, nos painéis de alojamento — a conta do site) com um conjunto restrito de permissões sudo — apenas para utilitários específicos, sem acesso root geral. Os resultados são analisados e apresentados no navegador.

Para vários servidores, instale o monitor em cada um separadamente, com um domínio único.

04. Como é calculada a Pontuação de segurança

A pontuação começa no máximo e diminui por cada problema detetado:

  • UFW inativo — −30
  • Fail2ban não iniciado (sem jail ativas) — −25
  • Sem chaves WebAuthn — −15
  • Lynis hardening index < 60 — −20; 60–79 — −10
  • IPset ipsum não carregado — −10
  • Ameaças detetadas pelo ClamAV — −20
  • Alterações de ficheiros pelo AIDE — −15
  • SSL expirado — −30, expira em <14 dias — −15, <30 dias — −5
  • CrowdSec instalado, mas não iniciado — −5
  • Suricata instalada, mas não iniciada — −5
  • SGBD/cache (MySQL, PostgreSQL, Redis…) acessíveis externamente — −10
  • Login root por SSH permitido (PermitRootLogin yes) — −20
  • Atualizações de segurança por instalar — −5

Resultado: 80+ = Protegido, 60–79 = Atenção, <60 = Em risco.

As deduções por ClamAV, AIDE, CrowdSec e Suricata só se aplicam se a ferramenta estiver instalada. Lynis e AIDE sem base inicializada aparecem como «sem dados» e não reduzem a pontuação. O número de ataques de hoje é apresentado no painel, mas não afeta a Pontuação de segurança.

Definições e licença

05. WebAuthn — autenticação de dois fatores

WebAuthn — norma de autenticação sem palavra-passe através de uma chave física. Suporta YubiKey, Touch ID, Face ID, Windows Hello e Passkey.

Após iniciar sessão com a palavra-passe, o sistema pede a confirmação através da chave registada. Mesmo que a palavra-passe seja divulgada, sem a chave física ou a biometria é impossível iniciar sessão.

Para configurar, abra Chaves WebAuthn no menu lateral e clique em «Registar chave». Registe logo duas chaves: se a única chave se perder ou avariar, o acesso ao painel através dela será impossível.

O WebAuthn só funciona através de HTTPS. Numa ligação HTTP, o registo e o início de sessão por chave não estão disponíveis.

06. Notificações: Telegram e Email

O painel pode enviar o relatório de segurança para o Telegram e por email (com um botão ou de forma agendada). Configura-se na secção «Definições».

Telegram. São necessários o token do bot e o chat id:

  1. No Telegram, escreva a @BotFather/newbot → obtenha o token do tipo 123456:ABC....
  2. Envie qualquer mensagem ao seu novo bot (para que ele lhe possa responder).
  3. Descubra o seu chat id: escreva ao bot @userinfobot ou abra https://api.telegram.org/bot<TOKEN>/getUpdates e procure "chat":{"id":...}.
  4. Insira o token e o chat id em «Definições» → Telegram e clique em «Guardar e enviar teste».

Email. Duas opções em «Definições» → Email:

  • SMTP — anfitrião, porta (465/SSL ou 587/TLS), utilizador e palavra-passe da sua caixa de correio;
  • Resend — API moderna: indique a chave de API (re_...) e um domínio de remetente confirmado.
O botão «Enviar teste» verifica logo o canal. O agendamento do relatório automático faz-se via cron (secção «Todas as tarefas cron»): ele aciona o envio e os canais são obtidos das definições.

Estado do relatório: «ATENÇÃO» ou «OK». O título passa a «ATENÇÃO» apenas perante um problema real ou uma ação pendente: ameaça detetada pelo ClamAV, alterações de ficheiros no AIDE, eventos críticos do Falco (Emergency/Alert/Critical nas últimas 24 h), serviço em falha no Monit, reinício necessário, SSL a expirar (≤14 dias) ou atualizações de segurança pendentes. O ruído de fundo — tentativas de força bruta de bots por SSH, IP banidos pelo fail2ban, alertas do Suricata, avisos do Lynis e pedidos já bloqueados pelo ModSecurity — não eleva o estado, por isso esses números no relatório não significam «ATENÇÃO» por si só.

07. Licença — introdução e ativação

Os módulos de monitorização detalhados (Lynis, UFW, ModSecurity, mapa de ataques, AIDE, ClamAV, etc.) ficam disponíveis com uma licença válida. Sem ela, o dashboard, as definições e a conta funcionam, mas os módulos mostram o cartão «Licença necessária».

Após a compra na área pessoal, tem um código de ativação no formato ARCIVEO-XXXX-XXXX-XXXX-XXXX. É preciso «ativá-lo» para o domínio do seu painel — isto transforma o código num ficheiro de licença assinado (bloco [license]), que insere no painel.

Como ativar (3 passos):

  1. Obtenha o código de ativação. Área pessoal my.arciveo.com → secção «Licenças» / «Ativação de licença» — copie o código ARCIVEO-….
  2. Ative o código para o seu domínio. Ainda na área pessoal, abra «Ativação de licença», introduza: o código de ativação, o seu email e o domínio do painel (o endereço em que o monitor abre, ex.: monitor.example.com). Clique em ativar — o sistema gera um ficheiro de licença associado a esse domínio e mostra-o num campo com o botão «Copiar».
  3. Insira a chave no painel. Copie todo o texto da licença → no painel abra «Definições» → bloco «Licença», cole e clique em «Guardar». Os módulos são desbloqueados de imediato.

O painel verifica a chave criptograficamente: a assinatura, a associação ao domínio e a validade.

Na ativação, o domínio tem de coincidir exatamente com o endereço do painel. Retire-o da constante APP_URL em config.php e introduza apenas o nome do host — sem https:// e sem o prefixo www. A ativação é única: o código transforma-se numa licença para o domínio introduzido e não volta a ser ativado — se errar o domínio, a chave não servirá ao seu painel e o código ficará gasto. Por isso, introduza o domínio com atenção.
Se a validade expirar ou o domínio mudar, surge um aviso no topo do painel. A licença fica associada ao domínio para sempre e não é transferível para outro domínio: para um novo período ou um novo domínio é preciso uma chave nova (comprada na área pessoal e ativada uma única vez).

08. Ficheiro config.php — todas as definições do painel

Todos os parâmetros principais do painel estão definidos num único ficheiro config.php na raiz (junto à pasta public/), através de constantes define() normais. O ficheiro é criado durante a instalação; raramente é necessário editá-lo manualmente — sobretudo ao mudar de domínio, na migração ou ao ligar a outra base de dados. Após qualquer alteração, reinicie o PHP-FPM (caso contrário, devido ao OPcache, as alterações não serão aplicadas).

Introduza os seus valores nos locais realçados; deixe o resto como está:

// --- Base de dados --- define('DB_HOST', 'localhost'); // manter define('DB_NAME', 'db_name'); // o que definiu ao criar a BD define('DB_USER', 'user'); // o que definiu ao criar a BD define('DB_PASS', 'db_password'); // o que definiu ao criar a BD define('DB_CHARSET', 'utf8mb4'); // manter // --- Aplicação --- define('APP_URL', 'https://monitor.example.com'); // endereço do painel, sem barra no fim define('TIMEZONE', 'Europe/Lisbon'); // o seu fuso horário // --- Tempo de sessão --- define('SESSION_LIFETIME', 28800); // inatividade até novo início de sessão, seg (28800 = 8 h)

Base de dados. Dados de ligação ao MySQL/MariaDB:

  • DB_HOST — anfitrião do SGBD, quase sempre localhost;
  • DB_NAME — nome da base de dados do painel;
  • DB_USER — utilizador da BD (acesso apenas à sua própria base);
  • DB_PASS — palavra-passe deste utilizador;
  • DB_CHARSET — codificação da ligação, deixe utf8mb4.

Aplicação.

  • APP_URL — endereço completo do painel (ex.: https://monitor.example.com). Deve coincidir com o domínio no qual a licença foi ativada — caso contrário a chave será rejeitada (ver a secção «Licença»);
  • TIMEZONE — fuso horário do PHP: afeta apenas a forma como o painel mostra datas e horas. No horário de execução das tarefas cron não influi — aí vigora o fuso do sistema (ver «Todas as tarefas cron»).

Tempo de sessão. SESSION_LIFETIME — tempo limite de inatividade da sessão em segundos (deslizante: renova-se com a atividade). Por predefinição, 28800 = 8 horas; após esse tempo de inatividade o painel pedirá para iniciar sessão de novo. Por exemplo, 3600 = 1 hora, 86400 = um dia.

Registo de erros. Os erros nunca são mostrados aos visitantes; são escritos em logs/php_errors.log — podem ser vistos na página «Registos da aplicação». Estas linhas (display_errors=0, log_errors=1, o caminho error_log) normalmente não precisam de ser alteradas — as definições estão declaradas no próprio ficheiro e não dependem do php.ini.

config.php — ficheiro secreto. Contém a palavra-passe da BD. Fica na raiz do painel (junto ao public/), e neste painel a raiz web (DocumentRoot) é precisamente a raiz do painel, não o public/. O próprio ficheiro não «vaza»: no .htaccess da raiz existe uma proibição explícita para ele (Require all denied) — o servidor devolve 403. Mesmo sem essa regra o código-fonte não vazaria: é PHP — o servidor executa-o e não o devolve como texto. Por precaução: não o publique em repositórios públicos nem o envie ao suporte com a palavra-passe real. As permissões do ficheiro são 640.
Ao migrar ou ao recuperar o acesso, este ficheiro é a principal fonte de dados: o nome da BD, o utilizador e a palavra-passe são obtidos precisamente daqui (ver as secções «Atualização e migração do painel» e «Recuperação de acesso»).

Ferramentas de segurança

09. Firewall UFW

UFW (Uncomplicated Firewall) — uma interface simples para nftables/iptables. Fecha todas as portas de entrada, exceto as explicitamente permitidas. A página «Firewall UFW» mostra o estado e as regras.

sudo apt install ufw # Permitir SSH (obrigatório ANTES de ativar!) e web sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # Fechar a BD ao exterior (acesso apenas local) sudo ufw deny 3306 # Ativar e verificar sudo ufw enable sudo ufw status verbose
Antes de ufw enable, permita obrigatoriamente o SSH (ufw allow OpenSSH), caso contrário perderá o acesso ao servidor.
A «exposição externa» no painel tem em conta o UFW: uma porta fechada por uma regra deny não é considerada acessível a partir do exterior.
Skipping adding existing rule — não é um erro. É assim que o UFW indica que já existe exatamente essa regra e não a adiciona novamente. Ao executar novamente a configuração automática (que é idempotente), esta é uma mensagem normal — não é necessário reagir.

10. Instalação do Fail2ban

Bloqueia automaticamente o IP após exceder o número de tentativas de início de sessão falhadas. Analisa os registos do SSH, Nginx, Apache e outros serviços.

sudo apt install fail2ban sudo systemctl enable --now fail2ban # Verificar o estado: sudo fail2ban-client status
A configuração operacional (jail.local com dezenas de jails e banimento automático a partir de ipsum) encontra-se na secção seguinte.

11. Configuração operacional Fail2ban + ipsum

A instalação básica está acima. Aqui fica a configuração operacional que gera dezenas de jails ativas e milhares de bloqueios: definições gerais, jails principais e banimento automático de IP maliciosos da lista ipsum.

Ficheiro /etc/fail2ban/jail.local — definições gerais e as jails mais importantes:

[DEFAULT] bantime = 1w findtime = 900 maxretry = 3 backend = systemd banaction = nftables-multiport ignoreip = 127.0.0.1/8 ::1 <YOUR_IP> <TRUSTED_NETS> # Banimento progressivo: cada repetição dura mais tempo bantime.increment = true bantime.factor = 2 bantime.maxtime = 5w bantime.rndtime = 300 [sshd] enabled = true maxretry = 5 bantime = -1 # banimento permanente por brute-force de SSH findtime = 3600 # Reincidentes: quem apanhou vários banimentos é banido para sempre [recidive] enabled = true logpath = /var/log/fail2ban.log banaction = %(banaction_allports)s bantime = -1 findtime = 86400 maxretry = 2 [http-get-dos] enabled = true maxretry = 100 findtime = 300 bantime = 1w # Serviços web (apache-*, nginx-*, php-url-fopen, phpmyadmin-syslog): [nginx-http-auth] enabled = true port = http,https [apache-badbots] enabled = true port = http,https # … e as restantes jails por serviço (dovecot, exim, postfix-sasl, # mysqld-auth, vsftpd, portscan, pam-generic) — enabled = true
Em ignoreip indique obrigatoriamente o seu IP e as redes de confiança, caso contrário pode banir-se a si próprio. Após as alterações: sudo fail2ban-client reload.

Carregamento automático da lista de bloqueio ipsum — no cron do root (sudo crontab -e): o level 1 (mais de 100 mil IP) é carregado no conjunto ipsum, que é filtrado na firewall (mais detalhes na secção «Lista de bloqueio IPset»):

# 04:00 — atualização do ipset ipsum (level 1, cobertura máxima): 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1
O conjunto tem de se chamar ipsum — é esse que o dashboard lê (cartão «IPset ipsum»). Níveis: levels/1.txt — cobertura máxima, levels/3.txt — mais preciso (3+ fontes).

Porque é que o «Monitor de segurança» está dividido em duas zonas. A proteção funciona em dois níveis, e o dashboard não os mistura:

  • Ataques reais (reativo) — tudo o que o fail2ban apanhou: tentativas de invasão em curso (jails sshd, apache-*, nginx-*, etc.) e reincidentes graves (jail recidive — os que já foram banidos várias vezes). São IP que realmente o atacaram — estão no mapa de ataques e na «Linha temporal».
  • Bloqueio preventivo (proativo) — lista pública de bloqueio de IP maliciosos conhecidos ipset ipsum, filtrada na firewall pela regra DROP. Na sua maioria, estes endereços nem sequer chegaram ao seu servidor — são cortados antecipadamente; o contador «IPset ipsum» mostra quantos foram filtrados preventivamente.

A diferença é simples: reativo — «estes atacaram e foram banidos», preventivo — «estes foram bloqueados antes sequer de tentar». Antes, a level-3 do ipsum era colocada artificialmente na recidive (daí a antiga divisão «recidive por lista»); agora a recidive contém apenas reincidentes verdadeiros, e o preventivo fica inteiramente na firewall.

12. Lista de bloqueio IPset (ipsum)

ipsum — lista pública de IP maliciosos, atualizada diariamente. O Monitor mostra o número de endereços carregados no painel e no mapa de ataques e considera-o na Avaliação de segurança (−10 se o conjunto não estiver carregado).

Variante mínima sem fail2ban — um conjunto ipsum separado com bloqueio através de iptables:

# Criar o conjunto (uma vez): sudo ipset create ipsum hash:ip # Script de atualização /usr/local/bin/update-ipsum.sh: #!/bin/bash ipset flush ipsum for ip in $(curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/3.txt); do ipset add ipsum "$ip" 2>/dev/null done iptables -C INPUT -m set --match-set ipsum src -j DROP 2>/dev/null \ || iptables -I INPUT -m set --match-set ipsum src -j DROP # Cron (sudo crontab -e, diariamente às 4:00): 0 4 * * * /usr/local/bin/update-ipsum.sh
A variante avançada com fail2ban-recidive está na secção «Configuração operacional Fail2ban + ipsum».
O ipset reside na memória e perde-se ao reiniciar. Apenas o cron diário deixaria o conjunto vazio desde o arranque até à execução seguinte (o painel mostrará 0). Carregue o conjunto também no arranque — coloque o carregamento num script e associe-o a @reboot. Ao mesmo tempo, o comando create … -exist define o limite maxelem 300000 (por predefinição 65536 — o level 1 não cabe, dando «Hash is full»):
# /usr/local/bin/load-ipsum.sh #!/bin/bash curl -s https://raw.githubusercontent.com/stamparm/ipsum/master/levels/1.txt \ | grep -v '^#' | sed 's/^/add ipsum /' \ | (echo "create ipsum hash:ip hashsize 131072 maxelem 300000 -exist"; echo "flush ipsum"; cat) \ | ipset restore -exist # sudo crontab -e — diariamente às 04:00 E a cada arranque: 0 4 * * * /usr/local/bin/load-ipsum.sh @reboot sleep 60 && /usr/local/bin/load-ipsum.sh
Como funciona na instalação automática. O script carrega a lista completa level 1 (mais de 100 mil IP) no conjunto ipsum e, se a firewall for gerida pelo instalador (VPS novo — perfis «Completa»/«Ligeira»), liga o conjunto ao UFW com uma regra DROP — o tráfego desses IP é realmente bloqueado. A regra fica depois de ESTABLISHED,RELATED, por isso as ligações atuais (incluindo o seu SSH) não são cortadas — só se bloqueiam novas ligações da lista. O conjunto é restaurado no arranque do serviço ipsum-load.service antes da firewall (caso contrário o UFW não arrancaria) e é atualizado pelo cron às 04:00. Num servidor já configurado (painel, firewall próprio) o instalador não mexe na firewall — aí o ipsum permanece como lista para o painel e o mapa de ataques, e a regra DROP é adicionada manualmente se assim o desejar (a variante mínima com iptables … --match-set ipsum … -j DROP está acima). Na instalação automática não é preciso fazer nada manualmente.

13. Instalação do CrowdSec

Substituto moderno do Fail2ban com threat intelligence coletiva: bloqueios da comunidade mais regras próprias. Requer um bouncer separado para aplicar os bloqueios à firewall.

curl -s https://packagecloud.io/install/repositories/crowdsec/crowdsec/script.deb.sh | sudo bash sudo apt install crowdsec sudo systemctl enable --now crowdsec # Bouncer para iptables/nftables: sudo apt install crowdsec-firewall-bouncer-iptables # Verificar estado: sudo systemctl status crowdsec sudo cscli decisions list sudo cscli bouncers list
Estado «Não iniciado» no painel = o serviço está instalado, mas o serviço não está ativo (o monitor verifica-o através de systemctl is-active crowdsec). Iniciar: sudo systemctl enable --now crowdsec; em caso de falha, consultar sudo journalctl -u crowdsec -n 30. A mesma regra aplica-se a qualquer serviço no estado «Não iniciado» (Suricata, Falco, Monit, MySQL).
«0 cenários» ou «0 bouncers» no dashboard. O CrowdSec vem quase vazio de origem — sem coleções não deteta nada, e sem um bouncer registado os bloqueios não são aplicados à firewall. Instale as coleções básicas e confirme que o bouncer está na lista:
# Coleções básicas (Linux + SSH + servidor web): sudo cscli collections install crowdsecurity/linux crowdsecurity/sshd crowdsecurity/base-http-scenarios sudo systemctl reload crowdsec # O bouncer deve estar na lista e com estado de ligação ativa: sudo cscli bouncers list
No log do bouncer surge stream halted / os bloqueios não são aplicados. Trata-se de uma chave de API órfã: o bouncer foi removido de cscli bouncers list, mas a sua chave antiga permaneceu em /etc/crowdsec/bouncers/*.yaml. Volte a registar o bouncer e defina a nova chave:
sudo cscli bouncers add fw-bouncer # gera a nova api_key # insira esta chave em api_key: em /etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml sudo systemctl restart crowdsec-firewall-bouncer
A instalação automática (perfil «Proteção completa») instala as coleções e regista o firewall-bouncer por si — manualmente só é necessário numa instalação manual ou após uma intervenção manual no CrowdSec.

14. Instalação do AIDE

AIDE (Advanced Intrusion Detection Environment) tira uma imagem do sistema de ficheiros e, em cada verificação, comunica alterações em /etc, /bin, /usr. Após a instalação, é obrigatório inicializar a base de dados (aideinit).

sudo apt install aide # Inicialização da base de dados (5–15 minutos): sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db # Ubuntu 24.04: a diretoria /var/lib/aide é criada em modo 700 (dono _aide), # e o painel (www-data) não vê a base de dados → mostra «Não inicializada». # Abrir a diretoria para acesso (os ficheiros da base mantêm-se 600): sudo chmod 755 /var/lib/aide # Primeira verificação COM ESCRITA no log que o monitor lê. # No Ubuntu/Debian o aide exige --config explícito (senão «missing configuration»; # o binário aide.wrapper não é fornecido nas versões novas): sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
Durante o aideinit o terminal fica 5–15 minutos parado na linha Running aide --init... — é normal (hashing de todo o sistema de ficheiros, carga no disco). Não interrompa com Ctrl+C. Se o processo parecer «pendurado» mas não escrever nada — talvez esteja à espera de resposta a um pedido oculto Overwrite existing aide.db.new [Yn]? (prima Y). Verifique a atividade a partir de outra sessão: pgrep -af aide.
Erro no aideinit: «21_aide_spamassassin … printf: invalid number» (return code 20) — bug conhecido do snippet de configuração do AIDE no Ubuntu 22.04. A base de dados não é criada. Remova o snippet defeituoso e repita:
sudo mv /etc/aide/aide.conf.d/21_aide_spamassassin /etc/aide/21_aide_spamassassin.disabled sudo aideinit sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db
Estados no painel. «Não inicializada» = o painel não vê o ficheiro da base: ou o aideinit nunca foi executado, ou (Ubuntu 24.04) a diretoria /var/lib/aide foi criada em modo 700 e está inacessível ao www-data — resolve-se com sudo chmod 755 /var/lib/aide (ver o bloco acima). «Sem verificações» = a base existe, mas ainda não foi feita nenhuma verificação — não é um erro. O monitor lê os resultados de /var/log/aide/aide.log.
Verificação regular → log para o painel. O /etc/cron.daily/aide padrão nas versões recentes de Ubuntu/Debian pode não escrever /var/log/aide/aide.log no formato esperado (e o aide.wrapper já não existe nelas). É mais fiável adicionar o seu próprio cron com --config explícito — este escreve o log em nome de root no modo 644, e o monitor lê-o sem grupos adicionais:
# sudo crontab -e — verificação diária às 02:00: 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # Executar agora, sem esperar pelo agendamento: sudo bash -c 'aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1'
A instalação automática já faz tudo isto: chmod 755 /var/lib/aide e o cron de verificação às 02:00 — não é preciso fazer nada manualmente.
Faça a primeira inicialização num servidor limpo — antes de instalar as aplicações web. Após alterações legítimas, recrie a base de dados: sudo aideinit && sudo cp /var/lib/aide/aide.db.new /var/lib/aide/aide.db.

15. Instalação do ClamAV

Antivírus para Linux. É especialmente útil para verificar /var/www em busca de shells PHP e código malicioso.

sudo apt install clamav clamav-daemon sudo systemctl enable --now clamav-daemon # Atualizar a base de assinaturas: sudo freshclam # Analisar a pasta manualmente: sudo clamscan -r /var/www --infected
O daemon clamd mostra «Inativo» após enable --now? Três causas típicas:

1. Ficou no ficheiro de configuração a linha Example — o clamd recusa-se a iniciar enquanto ela existir:

sudo sed -i '/^Example/d' /etc/clamav/clamd.conf sudo systemctl restart clamav-daemon

2. A base de assinaturas não foi descarregada — o clamd não inicia sem ela:

sudo systemctl stop clamav-freshclam sudo freshclam sudo systemctl start clamav-freshclam sudo systemctl restart clamav-daemon

3. Está simplesmente a carregar — o clamd carrega ~8 milhões de assinaturas para a memória em 30–60 seg. Aguarde e verifique: systemctl is-active clamav-daemon (estado activating → ainda a carregar).

Diagnóstico: sudo journalctl -u clamav-daemon -n 30 --no-pager.
No painel aparece «Ficheiros analisados: 0» / «Última análise: —»? O daemon clamd apenas mantém as assinaturas na memória, ele próprio não analisa nada por agendamento. O painel mostra os resultados da análise agendada, por isso é necessário um cron que analise e escreva o registo. A instalação automática coloca o invólucro /usr/local/bin/clamav-scan.sh e um cron às 01:30 — após a primeira execução preenchem-se «Ficheiros analisados» e «Última análise». Para executar já, sem esperar pelo agendamento: sudo /usr/local/bin/clamav-scan.sh.

16. Instalação do Linux Malware Detect (maldet)

Linux Malware Detect (LMD) — analisador de software malicioso para ameaças web: shells PHP, backdoors web, downloaders. Usa o motor do ClamAV e complementa-o com as suas próprias assinaturas.

wget https://www.rfxn.com/downloads/maldetect-current.tar.gz tar -xzf maldetect-current.tar.gz dir=$(ls -d maldetect-*/ | head -1) && cd "$dir" && sudo bash install.sh && cd ~ rm -rf maldetect-* maldetect-current.tar.gz # Atualizar assinaturas: sudo maldet -u # Analisar /var/www: sudo maldet -a /var/www
O LMD e o ClamAV funcionam bem em conjunto. Último relatório: maldet --report.
Durante a instalação pode surgir a linha update-rc.d: error: unable to read /etc/init.d/maldet — é inofensiva. O maldet não usa o init.d; a atualização de assinaturas e as análises são executadas via /etc/cron.daily/maldet. Se abaixo vir installation completed, então instalou tudo.
A página do LMD indica «Não instalado» apesar de estar instalado? O maldet não é instalado via apt, mas sim em /usr/local/maldetect, e com o open_basedir ativo a sua presença é verificada via shell — consulte a secção «A página está vazia apesar de haver dados no servidor».

17. Instalação do Suricata

Sistema de deteção de intrusões na rede: analisa o tráfego ao nível dos pacotes e conhece milhares de assinaturas de ataques. Complementa o ModSecurity (este atua ao nível HTTP, o Suricata ao nível TCP/IP).

sudo add-apt-repository ppa:oisf/suricata-stable sudo apt update && sudo apt install suricata # Descarregar as regras atuais: sudo suricata-update sudo systemctl enable --now suricata
O Suricata está «Ativo», mas o painel não mostra alertas / o número de eventos é 0? O Suricata escreve em /var/log/suricata/eve.json como root com permissões 750 na diretoria, e o servidor web (www-data) não a consegue ler. Abra a diretoria para acesso — os ficheiros dentro dela permanecem protegidos:
sudo chmod o+rx /var/log/suricata
A instalação automática faz isto sozinha — não é preciso fazê-lo manualmente.

18. Instalação do Falco

Interceta chamadas de sistema através de eBPF/kernel module e deteta anomalias em tempo real: shell a partir do nginx, leitura de /etc/passwd por um processo web, escrita em /bin, etc.

curl -fsSL https://falco.org/repo/falcosecurity-packages.asc > /tmp/falco.asc gpg --dearmor < /tmp/falco.asc | sudo tee /usr/share/keyrings/falco-archive-keyring.gpg > /dev/null sudo chmod 644 /usr/share/keyrings/falco-archive-keyring.gpg && rm /tmp/falco.asc echo "deb [signed-by=/usr/share/keyrings/falco-archive-keyring.gpg] https://download.falco.org/packages/deb stable main" \ | sudo tee /etc/apt/sources.list.d/falcosecurity.list sudo apt update && sudo apt install falco sudo systemctl enable --now falco
O Monitor lê os eventos do Falco através de journalctl -u falco (sem sudo — pelo grupo systemd-journal). Certifique-se de que o www-data está nesse grupo — consulte «Configuração de sudo» (ponto 2) na página de instalação manual.
«0 eventos em 24 horas» é o normal, não um erro. O Falco é event-driven: mantém-se em silêncio enquanto está tudo bem e regista um evento apenas perante uma anomalia (shell a partir de um processo web, leitura de /etc/passwd, escrita em diretórios do sistema). Zero eventos críticos num dia num servidor calmo é um estado saudável.
Para o painel, a saída para ficheiro é mais fiável. A leitura através de journalctl exige permissões sobre o journal; para que o painel veja os eventos de forma estável, a instalação automática ativa no Falco o file_output/var/log/falco/falco.log e define UMask=0022 no serviço (o log é lido pelo servidor web). Numa nova instalação não é necessário configurar isto manualmente.

19. Instalação do ModSecurity (WAF)

ModSecurity — firewall de aplicações web (WAF) para Apache ou Nginx. Bloqueia ataques ao nível da aplicação: injeções SQL, XSS, travessia de diretórios, scanners.

# Apache: sudo apt install libapache2-mod-security2 sudo a2enmod security2 # Conjunto de regras OWASP Core Rule Set: sudo apt install modsecurity-crs # OBRIGATÓRIO: sem este ficheiro o motor de regras está desligado sudo cp /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf sudo sed -i 's/^SecRuleEngine DetectionOnly/SecRuleEngine On/' /etc/modsecurity/modsecurity.conf sudo apache2ctl configtest && sudo systemctl reload apache2 # Verificação: deve devolver 403 curl -s -o /dev/null -w '%{http_code}\n' "https://monitor.example.com/?id=1%20UNION%20SELECT%201,2--"
A instalação do pacote, por si só, não protege nada. O Apache inclui os ficheiros de configuração com a linha IncludeOptional /etc/modsecurity/*.conf, mas o pacote instala apenas modsecurity.conf-recommended — que não corresponde à máscara *.conf. Se não o copiar para modsecurity.conf, o SecRuleEngine mantém-se Off: o módulo está carregado, as regras CRS estão carregadas, mas o tráfego não é verificado e o registo de auditoria não é criado. O modo intermédio DetectionOnly apenas escreve eventos no registo, sem bloquear pedidos — o painel apresenta-o a amarelo.

Acesso do painel ao registo de auditoria. O registo /var/log/apache2/modsec_audit.log pertence ao root (permissões 640), o utilizador web não o consegue ler. O painel obtém os dados através de um wrapper — crie-o:

sudo tee /usr/local/bin/monitor-modsec >/dev/null <<'EOF' #!/bin/sh echo "ENGINE=$(grep -hE '^SecRuleEngine[[:space:]]+' /etc/modsecurity/*.conf 2>/dev/null | tail -1 | awk '{print $2}')" echo "---LOG---" tail -n 3000 /var/log/apache2/modsec_audit.log 2>/dev/null echo "---RULES---" for f in /etc/modsecurity/crs/rules/*.conf /usr/share/modsecurity-crs/rules/*.conf /etc/modsecurity/custom-rules.conf; do [ -f "$f" ] && { echo "===FILE:$(basename "$f")==="; cat "$f"; } done EOF sudo chown root:root /usr/local/bin/monitor-modsec sudo chmod 755 /usr/local/bin/monitor-modsec # em /etc/sudoers.d/monitor (utilizador = aquele com que o PHP-FPM corre): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec
O wrapper obtém a última diretiva SecRuleEngine sem indentação: as linhas indentadas estão dentro de blocos <LocationMatch>/<Directory> (por exemplo, a desativação do WAF para o phpMyAdmin) e não definem o modo global.
O utilizador em sudoers deve coincidir com o utilizador do pool FPM: num Apache/Debian normal é o www-data, no HestiaCP o pool do site corre com o dono do site (por exemplo, admin) — verifique com grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.
Se o site estiver atrás de um proxy Nginx (HestiaCP), o Apache vê o próprio proxy como cliente — o painel obtém o IP real do atacante a partir do cabeçalho X-Forwarded-For. Só entram nas estatísticas as transações com uma regra acionada: a diretiva SecAuditLogRelevantStatus escreve no registo de auditoria quaisquer respostas 4xx/5xx, por isso também lá entram os 403/500 normais — o painel não os conta como eventos do WAF.
O bloco ---RULES--- é necessário para a secção «Todas as regras ativas» — o painel apresenta não só as regras acionadas, mas todas as regras CRS carregadas + as personalizadas. Os três caminhos no ciclo for f in … são os locais típicos para as regras CRS e para as adições locais; se tiver uma disposição diferente (o pacote coloca os ficheiros no seu próprio diretório, ou as regras personalizadas não estão em /etc/modsecurity/custom-rules.conf), encontre os caminhos reais com o comando sudo grep -rl 'IncludeOptional\|^Include ' /etc/apache2/mods-enabled/security2.conf /etc/apache2/conf-enabled/*.conf 2>/dev/null e coloque-os na lista. Se o wrapper for antigo (sem esta secção) — a secção apenas apresenta o aviso «indisponível», o resto da página funciona como antes.

20. Instalação do Auditd

Auditd (Linux Audit Daemon) regista chamadas de sistema ao nível do kernel: inícios e términos de sessão, comandos sudo, tentativas de autenticação falhadas e alterações de ficheiros. O Monitor mostra os inícios de sessão, as tentativas falhadas e os comandos sudo de hoje.

sudo apt install auditd audispd-plugins sudo systemctl enable --now auditd # Verificar o estado e os eventos: sudo systemctl status auditd sudo ausearch -m USER_LOGIN -ts today
O Monitor lê os eventos através do ausearch (/usr/sbin/ausearch) e, se necessário, a partir de /var/log/audit/audit.log com o comando tail. Ambos têm de estar no sudoers.

21. Instalação do Monit

Vigia os serviços (nginx, php-fpm, mysql, etc.) e reinicia-os em caso de falha. Pode enviar alertas por email.

sudo apt install monit sudo systemctl enable --now monit # Configurações: sudo nano /etc/monit/monitrc ls /etc/monit/conf.d/
O Monitor obtém a lista de serviços através de monit status. Em /etc/monit/monitrc a interface HTTP deve estar ativada (bloco set httpd com allow localhost), caso contrário monit status devolve um erro.
«0 serviços sob monitorização» no painel? Duas razões. (1) A interface HTTP está desativada — em monitrc a linha set httpd está comentada (por predefinição surge como # set httpd port 2812 …). Descomente o bloco e permita o localhost. (2) O httpd ativado por si só não monitoriza nada — o Monit considera apenas o que está descrito em blocos check; sem eles a lista fica vazia mesmo com a interface a funcionar. Configuração mínima funcional:
# /etc/monit/conf.d/00-httpd — interface HTTP para localhost: set httpd port 2812 use address localhost allow localhost # exemplos de blocos check (o que monitorizar): check process sshd with pidfile /run/sshd.pid start program = "/usr/bin/systemctl start ssh" stop program = "/usr/bin/systemctl stop ssh" check filesystem rootfs with path / if space usage > 90% then alert
sudo monit -t # verificar a sintaxe (Control file syntax OK) sudo systemctl reload monit sudo monit status
A instalação automática coloca um conf.d pronto com httpd na 2812 e um conjunto de verificações — numa instalação nova não é preciso configurar manualmente.
Serviço no estado «Com erros»? O Monitor apenas mostra o estado e, propositadamente, não reinicia os serviços a partir do painel web (isso seria execução remota de comandos root num painel de segurança). O diagnóstico e o reinício fazem-se por SSH através do Monit:
sudo monit status <service> # causa do erro sudo monit restart <service> # reinício através do Monit # se o Monit não iniciar o serviço — consulte o unit próprio: sudo systemctl status <unit> --no-pager sudo journalctl -u <unit> -n 50 --no-pager

22. Instalação do PSAD (deteção de varrimento de portas)

PSAD analisa o registo do iptables e deteta varrimentos de portas e ataques de rede, atribuindo a cada origem um nível de ameaça (1–5). Complementa o fail2ban e o Suricata.

sudo apt install psad # O PSAD lê o log do iptables — é preciso ativar o registo (o UFW faz isto sozinho). # Para iptables puro, adicione regras LOG às cadeias INPUT/FORWARD. sudo psad --sig-update sudo systemctl enable --now psad
O Monitor lê os dados através de psad --Status (necessário no sudoers). Sem o registo do iptables, a página fica vazia — é normal enquanto não houver varrimentos.

23. AppArmor / SELinux (controlo de acesso)

O Mandatory Access Control limita a que ficheiros e recursos um programa pode aceder, mesmo que este tenha sido comprometido. No Ubuntu/Debian, é usado por predefinição o AppArmor (normalmente já instalado e ativo).

# AppArmor (Ubuntu/Debian): sudo apt install apparmor apparmor-utils sudo systemctl enable --now apparmor sudo aa-status # verificar perfis
O monitor lê o estado através do aa-status (necessário no sudoers). Mostra o número de perfis em modo enforce/complain e os processos sem perfil.

«Perfis carregados» ser mais do que enforce + complain é normal. No AppArmor 4.x (Ubuntu 24.04 e mais recente) surgiu o modo unconfined: o perfil está carregado no kernel, mas não limita nada. O Ubuntu marca assim dezenas de perfis para programas que usam user namespaces (navegadores, clientes torrent e afins). Quando existem esses perfis, o cartão «Perfis carregados» fica âmbar e mostra o seu número — por exemplo unconfined: 90 com 120 carregados e 26 em enforce. Só protegem de facto os perfis em enforce; no Ubuntu 22.04 (AppArmor 3.x) este modo não existe e os números batem sempre certo.

sudo aa-status | grep -E "profiles are" # divisão por modos sudo aa-enforce /etc/apparmor.d/profile-name # passar o perfil para enforce
Passar para enforce os perfis que o Ubuntu deixou intencionalmente em unconfined só deve ser feito de forma consciente: não estão desativados por engano, mas porque de outro modo os próprios programas deixam de funcionar. Os perfis em complain são outra questão: aí as regras já estão escritas e apenas não são aplicadas.

24. Instalação do debsums (integridade dos pacotes)

O debsums verifica se os ficheiros dos pacotes instalados coincidem com as somas de verificação do repositório — deteta binários de sistema adulterados (complementa o AIDE). Uma verificação completa demora 1–2 minutos, por isso é executada por cron, e o painel lê o resultado de data/debsums/debsums.log e classifica-o por categorias (só importam os binários e as bibliotecas).

A tarefa fica no cron do root (sudo crontab -e). O wrapper pronto debsums-scan.sh é colocado em /usr/local/bin/ (chmod +x; ver o resumo das tarefas cron) e ele próprio grava o relatório em data/debsums/ do painel.

sudo apt install debsums # Linha cron (diariamente às 4:30): 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

O wrapper debsums-scan.sh encontra sozinho a pasta data/ do painel — não é preciso indicar o caminho.

Alterações em /etc/ (configurações) e /usr/share/ (recursos) no servidor costumam ser normais — o painel assinala-as com uma cor distinta. Preocupantes são as alterações de binários e bibliotecas (/bin, /sbin, /usr/lib, etc.) — o cartão «Binários / bibliotecas» mostra precisamente essas.

25. Configuração dos relatórios do Lynis

O Lynis é executado manualmente ou por cron. O relatório deve ser guardado na pasta data/lynis/ do projeto — o monitor lê o ficheiro lynis-report.dat.

# Execução única (indique o seu caminho para a raiz do painel): sudo lynis audit system --report-file /path/to/monitor/data/lynis/lynis-report.dat # Auditoria diária — linha do cron (wrapper lynis-scan.sh pronto em /usr/local/bin/, ver resumo): 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1
Após a primeira execução, a página «Auditoria Lynis» mostra logo o hardening index, os avisos e as recomendações.
Botão «Executar auditoria» na página do Lynis. Executa o lynis-scan.sh em segundo plano diretamente a partir do painel (sem esperar pelo cron): mostra «A analisar…» e, ao concluir, atualiza o relatório automaticamente. Para isso, o utilizador web precisa de uma linha no sudoers para executar o script — o instalador adiciona-a automaticamente em /etc/sudoers.d/monitor. Se o painel foi instalado manualmente/anteriormente, acrescente-a com o mesmo utilizador já indicado no ficheiro:
u=$(sudo awk '/NOPASSWD/ && !/lynis-scan/ {print $1; exit}' /etc/sudoers.d/monitor) [ -n "$u" ] && echo "$u ALL=(ALL) NOPASSWD: /usr/local/bin/lynis-scan.sh" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c && sudo chmod 440 /etc/sudoers.d/monitor

26. Configuração de relatórios Logwatch

O Logwatch deve guardar os relatórios diários na pasta data/logwatch/ do projeto em formato .txt. O monitor mostra o último relatório e o arquivo.

# Diariamente (6:00) — linha cron (wrapper pronto logwatch_daily.sh em /usr/local/bin/, ver resumo): 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1

Módulos do painel

27. Monitor de rede (integrado)

O monitor de rede não requer instalação — é uma página integrada no painel. Mostra o estado de rede do servidor a partir de fontes locais:

  • interfaces e tráfego — de /proc/net/dev;
  • estado das ligações (UP/DOWN) e IP — através de ip;
  • ligações e portas em escuta — através de ss;
  • eventos de rede do kernel nas últimas 24 h — através de journalctl -k.

As três primeiras fontes funcionam sem sudo, por isso as interfaces, o tráfego, as ligações e as portas ficam visíveis de imediato. O bloco «Eventos do kernel» utiliza journalctl -k — é lido através do grupo systemd-journal («Configuração do sudo», p.2), não é preciso sudo. Para verificar que tudo está acessível ao utilizador web:

# Verificação em nome de www-data (é sob ele que o PHP corre): sudo -u www-data bash -c 'cat /proc/net/dev' sudo -u www-data bash -c 'ip -o link show' sudo -u www-data bash -c 'ss -s' sudo -u www-data journalctl -k --no-pager -n 5
O bloco «Eventos de rede do kernel» mostra eventos da pilha de rede do kernel (mudança de ligação up/down, erros de portadora, «network unreachable»). Os registos da firewall UFW BLOCK não aparecem aqui — estão nas páginas «Firewall UFW» e «Mapa de ataques». Um bloco vazio com um visto verde = não houve falhas de rede nas últimas 24 horas.

28. Disco e SMART

A página integrada mostra três coisas:

  • Sistemas de ficheiros — ocupação das partições (df); a barra fica vermelha a partir de ≥90%;
  • Unidades de armazenamento — lista de discos (lsblk), apenas os reais (loop/snap ocultos);
  • Saúde (SMART) — estado do disco e atributos (smartctl).

O espaço e a lista de dispositivos funcionam de imediato, sem configuração. Para o SMART é necessário o pacote smartmontools. O processo web não tem acesso direto aos dispositivos de disco, por isso o SMART é recolhido por cron para o ficheiro data/disk/smart.txt, e o painel lê-o.

A tarefa fica no cron do root (sudo crontab -e). O wrapper pronto smart-scan.sh é colocado em /usr/local/bin/ (chmod +x; ver o resumo das tarefas cron) e escreve ele próprio em data/disk/ do painel.

sudo apt install smartmontools # Linha de cron (a cada 30 minutos): */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1

O wrapper smart-scan.sh encontra ele próprio a pasta data/ do painel — não é preciso indicar o caminho. Internamente, lsblk -e7,11 exclui loop/cdrom.

Em discos virtuais (QEMU/KVM e semelhantes) normalmente só está disponível o estado geral «saúde: OK», enquanto a temperatura, as horas de funcionamento e os setores realocados podem estar vazios — isto é normal. Num servidor físico são apresentados todos os atributos.

29. Desempenho (CPU/RAM/Rede/Disco)

A página mostra o histórico de carga do servidor nas últimas 24 horas — Load Average, ocupação da CPU e espera de I/O, RAM/Swap, tráfego de rede (receção/envio), I/O de disco (leitura/escrita), ocupação do disco e inodes, descritores de ficheiro abertos e ligações MySQL, além do número atual de ligações TCP e processos.

Os dados são recolhidos pelo cron/collect_metrics.php — a cada 5 minutos regista um instantâneo «em bruto» dos contadores (/proc/loadavg, /proc/meminfo, /proc/stat, /proc/net/dev, /proc/diskstats, df/df -i, /proc/sys/fs/file-nr, SHOW GLOBAL STATUS LIKE 'Threads_connected') na tabela da BD system_metrics; as percentagens e velocidades são calculadas pela própria página com base na diferença entre instantâneos consecutivos (ocupação do disco/inodes/descritores/ligações MySQL — valores instantâneos, sem recálculo). Não é necessário sudo — as fontes são lidas sem privilégios de root. Os pontos com mais de 24 horas são eliminados automaticamente a cada registo.

# Linha do cron (a cada 5 minutos): */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1

O wrapper collect-metrics-all.sh (ver o resumo das tarefas cron) encontra automaticamente todas as instâncias do painel instaladas no servidor e executa o cron/collect_metrics.php de cada uma em nome do proprietário do site.

Enquanto o coletor não tiver corrido pelo menos duas vezes (os primeiros ~10 minutos após a instalação), a página mostra «a recolher dados» — os gráficos precisam de, no mínimo, um par de pontos consecutivos para calcular velocidades e percentagens.

Alertas de carga (secção «Definições» → «Alertas de carga») — ao ultrapassar o limite de CPU/RAM/disco/inodes, o painel envia uma notificação para Telegram/Email (os mesmos canais do relatório diário — não é preciso ativá-los à parte para os alertas), e mais uma quando a métrica regressa ao normal. Não faz spam enquanto o limite se mantém: a notificação seguinte só chega depois do ciclo «recuperou → voltou a ultrapassar».

Os limites são verificados pelo mesmo collect_metrics.php a cada execução (a cada 5 minutos) — não é preciso um cron à parte. O estado «já notificado / ainda não» é guardado em data/alerts_state.json, e os limites nas definições do painel.

30. Mapa de ataques (GeoIP)

A página «Mapa de ataques» determina o país a partir do IP com o comando geoiplookup. Sem o pacote GeoIP os países não são determinados e os pontos não aparecem no mapa:

sudo apt install geoip-bin geoip-database # Verificação: geoiplookup 8.8.8.8
Sudo não é necessário — a base /usr/share/GeoIP/GeoIP.dat é lida por todos, os resultados ficam em cache em tmp/geoip_cache.json. O próprio mapa (Leaflet + tiles do OpenStreetMap) carrega no navegador — é preciso Internet no computador onde o painel está aberto.

31. Exposição externa, atualizações e atualizações automáticas

Dois cartões integrados no dashboard que mostram não o «ligado/desligado» de uma ferramenta, mas a proteção real do servidor. Não requerem instalação e são lidos localmente sem sudo.

Exposição externa — quantos serviços escutam todas as interfaces (0.0.0.0/[::]) e estão acessíveis do exterior. Fica vermelho se uma base de dados ou cache estiver exposta (MySQL, PostgreSQL, Redis, MongoDB, Memcached, Elasticsearch) — é uma falha direta (−10 na Pontuação de segurança). Fonte: ss -tuln.

Se o cartão estiver vermelho — feche a base de dados ao exterior: associe-a a 127.0.0.1 (bind-address na configuração do MySQL/PostgreSQL, bind 127.0.0.1 no Redis) ou feche a porta na UFW.
«Porta aberta» ≠ «acessível do exterior». Um serviço que escuta em 127.0.0.1 (loopback) só é visível para o próprio servidor — não é alcançável de fora, mesmo que a porta esteja «aberta». Por isso o Postfix na porta 25, associado ao loopback, é seguro: a configuração automática define inet_interfaces = loopback-only (mais um smtpd_banner neutro — resolve o aviso Lynis MAIL-8818 sobre a exposição da versão). O cartão «Exposição externa» conta como externo apenas o que escuta em 0.0.0.0/[::]; os serviços em loopback não entram nessa conta.
Lynis MAIL-8818 manualmente (se instalou o correio por conta própria): em /etc/postfix/main.cf defina smtpd_banner = $myhostname ESMTP (sem versão nem SO) e inet_interfaces = loopback-only, depois sudo systemctl restart postfix.

Atualizações de segurança — quantos patches de segurança aguardam instalação e se é necessário reiniciar após a atualização do kernel (−5 na Pontuação de segurança se houver patches). Fonte: /usr/lib/update-notifier/apt-check, ficheiro /var/run/reboot-required. Lista detalhada — na página «Atualizações de segurança».

# Instalar atualizações: sudo apt update && sudo apt upgrade # Verificar o que escuta para o exterior: ss -tuln | grep -E '0\.0\.0\.0|\[::\]'
O cartão de atualizações funciona no Ubuntu/Debian (update-notifier-common). Se o apt-check não existir — o monitor calcula os patches através de apt-get -s upgrade.

Atualizações de segurança automáticas (unattended-upgrades) — na página «Atualizações de segurança» um cartão à parte mostra se a instalação automática de patches de segurança está ativa e quando foi executada pela última vez. Não é preciso sudo — o estado é lido através de apt-config dump.

sudo apt install unattended-upgrades sudo dpkg-reconfigure -plow unattended-upgrades # ativar # Verificar o que está ativo: apt-config dump | grep Unattended-Upgrade

Manutenção

32. Cópias de segurança

A cópia de segurança é o seguro principal: a perda de dados é pior do que qualquer intrusão. São precisas duas coisas — cópia do servidor/sites e, à parte, cópia da BD do painel (contém utilizadores, chaves WebAuthn, definições, licença).

Opção A — HestiaCP: separador Backup do utilizador → botão de criação de cópia (ou por agendamento nas definições do servidor). A cópia inclui os sites e as respetivas BD.

Opção B — manual (cron): dump da BD + arquivo do diretório data/ do painel:

# cron do root (sudo crontab -e) — cópia diária às 2:30 (indique os seus nomes/caminhos): 30 2 * * * mysqldump -u root MY_DB | gzip > /var/backups/monitor-db-$(date +\%F).sql.gz 40 2 * * * tar czf /var/backups/monitor-data-$(date +\%F).tar.gz -C /path/to/monitor data # Eliminar arquivos com mais de 14 dias: 0 3 * * * find /var/backups -name 'monitor-*' -mtime +14 -delete
Uma cópia no mesmo servidor protege de erros, mas não da perda do servidor. Copie os arquivos para um armazenamento externo (outro servidor, S3, rclone para a nuvem). Verifique se o restauro funciona mesmo.

33. Atualização e migração do painel

Atualização para uma nova versão. Comece por fazer uma cópia de segurança. Depois, volte a carregar os ficheiros de código, mantendo os seus dados:

  • substituir (código): public/, includes/, assets/, cron/, database/, bem como os .htaccess de raiz (o controlador frontal — o encaminhamento não pode ficar da versão antiga), manifest.json, sw.js;
  • não mexer: config.php (dados da BD), data/ (relatórios), logs/, tmp/ (sessões e cache).
# Após o carregamento — limpar a cache do PHP (se o opcache estiver ativo): sudo systemctl reload php*-fpm
O FileZilla indica SSH_FX_PERMISSION_DENIEDPermission denied. Os ficheiros do painel pertencem ao www-data (foi assim que ficaram definidos na instalação), enquanto o cliente SFTP se liga com o seu próprio utilizador, que não tem permissão de escrita. Entregar todo o painel ao www-data «para funcionar» é precisamente o que causa este erro; abaixo há três formas, qualquer uma resolve o problema.
# Variante A (recomendada) — separar os proprietários: o código é seu, as pastas de trabalho do servidor web. # O servidor web não recebe de todo permissão de escrita no CÓDIGO do painel: sudo chown -R deploy:www-data /path/to/monitor sudo chown -R www-data:www-data /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo find /path/to/monitor -type d -exec chmod 755 {} \; sudo find /path/to/monitor -type f -exec chmod 644 {} \; sudo chmod 750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs sudo chmod 640 /path/to/monitor/config.php # Variante B — ACL por cima dos proprietários atuais (não movemos nada): sudo apt install -y acl sudo setfacl -R -m u:deploy:rwX /path/to/monitor sudo setfacl -R -d -m u:deploy:rwX /path/to/monitor # Variante C — através do grupo www-data. Mais simples, mas a escrita nos ficheiros # do painel passa também para o servidor web (com falha no PHP o código é substituível): sudo usermod -aG www-data deploy sudo find /path/to/monitor -type d -exec chmod 2775 {} \; sudo find /path/to/monitor -type f -exec chmod 664 {} \; sudo chmod 640 /path/to/monitor/config.php sudo chmod 2750 /path/to/monitor/data /path/to/monitor/tmp /path/to/monitor/logs
Porque a variante A é segura. O painel escreve apenas em três diretórios — data/ (relatórios), tmp/ (sessões e cache), logs/; que continuam a pertencer ao www-data. O restante é código, e o servidor web só precisa dele para leitura, garantida pelo grupo www-data com permissões 644. Vantagem adicional: em caso de falha no PHP, os ficheiros do painel já não podem ser reescritos. Em painéis de alojamento (HestiaCP e semelhantes) a variante A não é necessária: aí os ficheiros do site já pertencem à conta com que se liga por SFTP, e o servidor web lê-os pelo grupo.
Armadilha da variante B: qualquer chmod posterior sobre os ficheiros repõe a máscara da ACL e o acesso desaparece silenciosamente. Se, depois de «arrumar as permissões», o carregamento voltar a esbarrar em Permission denied, repita ambos os comandos setfacl.
O bit 2 na variante C é o setgid: os ficheiros enviados por SFTP permanecem no grupo www-data, caso contrário o painel não os consegue substituir. Depois da variante C volte a ligar-se no FileZilla — o novo grupo só passa a valer numa nova sessão. Verificação: id deploy (deve aparecer o grupo www-data) e ls -ld /path/to/monitor (drwxrwsr-x — a letra s significa que o setgid está ativo).

Migração para outro servidor:

  1. No novo servidor, coloque o site em funcionamento + HTTPS (ver a página de instalação manual).
  2. Copie todos os ficheiros do painel juntamente com config.php, data/.
  3. Migre a BD: mysqldump no antigo → importar no novo; corrija os dados da BD em config.php.
  4. Repita no novo servidor: sudoers, pertença ao grupo adm, tarefas cron.
  5. A licença está associada ao domínio — se o domínio for o mesmo, a chave continua a funcionar.

34. Recuperação de acesso (chave, palavra-passe ou IP bloqueado)

Se não conseguir iniciar sessão, tudo se resolve diretamente na BD a partir do servidor. Abra a BD (o nome está em config.php):

sudo mysql MY_DB

Chave WebAuthn perdida (o segundo fator não passa) — desative o 2FA, inicie sessão com a palavra-passe e registe uma nova chave:

UPDATE users SET webauthn_enabled = 0;

Palavra-passe esquecida — defina um novo hash (gere-o no servidor e insira-o):

# Gerar o hash da nova palavra-passe: php -r "echo password_hash('NEW_PASSWORD', PASSWORD_BCRYPT), \"\n\";" # Na BD (insira o hash obtido): # UPDATE users SET password = '$2y$10$...' WHERE username = 'admin';

Bloqueou-se a si próprio com o filtro de IP — desative a restrição:

UPDATE settings SET value = '0' WHERE name = 'ip_restriction_enabled';
O acesso à BD está sempre disponível: sudo mysql no servidor, ou o phpMyAdmin / secção da BD no painel do alojamento. Após a recuperação, volte a ativar o WebAuthn e o filtro de IP.

35. Todas as tarefas cron num só lugar

O resumo das tarefas fica no cron root do servidor (adicionadas via sudo crontab -e). Deixe apenas as linhas das ferramentas que usa; ajuste os caminhos ao seu servidor.

# Cron do servidor do monitor (root) — inscreva via: sudo crontab -e # 01:30 — análise ClamAV por caminhos perigosos (web, home, temp) → cartões «Ficheiros verificados» e «Última análise» 30 1 * * * /usr/local/bin/clamav-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 02:00 — verificação de integridade de ficheiros AIDE (é preciso o --config explícito) 0 2 * * * /usr/bin/aide --config /etc/aide/aide.conf --check > /var/log/aide/aide.log 2>&1 # no arranque — restaurar permissões de /var/lib/aide (o ficheiro tmpfiles do pacote # aide-common.conf repõe-nas em 0700, e o painel deixa de ver a base) @reboot chmod 755 /var/lib/aide # 03:00 — auditoria de segurança Lynis 0 3 * * * /usr/local/bin/lynis-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:00 — atualização da lista de bloqueio ipsum (level 1) 0 4 * * * /usr/local/bin/load-ipsum.sh >> /path/to/monitor/logs/cron.log 2>&1 # o conjunto ipsum no arranque é carregado pelo serviço ipsum-load.service (ANTES da firewall, senão # o UFW não vê o conjunto em before.rules) — não é cron. Aqui só o refresh diário acima. # 06:00 — relatório Logwatch 0 6 * * * /usr/local/bin/logwatch_daily.sh >> /path/to/monitor/logs/cron.log 2>&1 # a cada 30 min — verificação de discos SMART */30 * * * * /usr/local/bin/smart-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 04:30 — integridade de pacotes debsums 30 4 * * * /usr/local/bin/debsums-scan.sh >> /path/to/monitor/logs/cron.log 2>&1 # 08:00 — relatório agendado para Email e Telegram 0 8 * * * /usr/local/bin/daily-report-all.sh >> /path/to/monitor/logs/cron.log 2>&1 # a cada hora — atualização das listas de pacotes (para o cartão «Atualizações de segurança») 0 * * * * /usr/bin/apt-get update -qq >/dev/null 2>&1 # a cada 5 min — instantâneo de recursos (CPU/RAM/rede/disco) para a página «Desempenho» */5 * * * * /usr/local/bin/collect-metrics-all.sh >> /path/to/monitor/logs/cron.log 2>&1
Os detalhes de cada uma estão nas secções correspondentes. As tarefas de backup (secção anterior) são adicionadas a este mesmo cron. Após as alterações, verifique: sudo crontab -l e que o serviço cron está ativo.
A hora do cron = fuso horário do servidor, e não a TIMEZONE do config.php. A constante TIMEZONE afeta apenas o PHP (como o painel mostra as datas), mas o daemon cron executa as tarefas pela hora do sistema operativo. Se o fuso do servidor não coincidir com o seu, o relatório «08:00» chegará à hora errada. Exemplo: o servidor está noutro fuso (UTC, UTC+0) e o utilizador está em Lisboa (UTC+1) → o relatório «08:00» chega às 09:00 pelo seu fuso. Verifique e, se necessário, alinhe o fuso do sistema com o seu:
# Verificar o fuso atual do servidor: timedatectl # Definir o seu fuso (exemplo) e reiniciar o cron: sudo timedatectl set-timezone Europe/Lisbon sudo systemctl restart cron
Depois disto, a linha 0 8 * * * será executada às 08:00 pela hora local. Caso contrário, teria de deslocar o próprio cron, mas na mudança para a hora de inverno/verão o desfasamento voltaria a divergir — por isso é mais correto configurar o fuso do sistema.
Scripts wrapper prontos. As suas cópias de trabalho e um modelo de crontab (crontab.txt) estão na pasta system/ junto ao projeto, fora de public_html. Isto não faz parte do site — não é preciso carregá-los para a raiz web; coloque-os no servidor nos caminhos de sistema (como no crontab acima):
  • lynis-scan.sh/usr/local/bin/ (chmod +x) — executa lynis audit system, durante a análise coloca a flag /tmp/lynis-running e copia lynis-report.dat para data/lynis/ do painel;
  • logwatch_daily.sh/usr/local/bin/ (chmod +x) — gera o relatório diário Logwatch (sshd, fail2ban, sudo, postfix) em data/logwatch/;
  • smart-scan.sh/usr/local/bin/ (chmod +x) — recolhe o estado dos discos (smartctl) em data/disk/;
  • debsums-scan.sh/usr/local/bin/ (chmod +x) — verifica a integridade dos pacotes (debsums) em data/debsums/;
  • clamav-scan.sh/usr/local/bin/ (chmod +x) — análise antivírus ClamAV por caminhos perigosos (web, home, temp); escreve o resumo em /var/log/clamav/scan.log, de onde a página ClamAV o lê (linha 01:30 no crontab acima);
  • load-ipsum.sh/usr/local/bin/ (chmod +x) — atualiza o conjunto ipset ipsum (level 1) no lugar, sem quebrar as regras ativas da firewall (linha 04:00 no crontab acima);
  • daily-report-all.sh/usr/local/bin/ (chmod +x) — executa o relatório cron/daily_report.php do painel (linha 08:00 no crontab acima);
  • daily_report.php — já faz parte do painel (cron/daily_report.php), é executado via daily-report-all.sh, não é preciso instalá-lo à parte;
  • collect-metrics-all.sh/usr/local/bin/ (chmod +x) — executa cron/collect_metrics.php do painel (página «Desempenho», linha */5 no crontab acima); o collect_metrics.php já faz parte do painel, não é preciso instalá-lo à parte;
  • crontab.txt (system/cron/) — modelo de tarefas; inscreva as linhas necessárias via sudo crontab -e.
O caminho do script no crontab deve coincidir com o local onde o colocou.
Como colocar o script em /usr/local/bin/. Não é possível gravar diretamente para lá a partir do FileZilla — o diretório pertence ao root, e o cliente SFTP receberá SSH_FX_PERMISSION_DENIED. O procedimento é este: primeiro carregar o ficheiro para /tmp (onde todos podem escrever) e depois movê-lo para o local com um só comando:
# no FileZilla: no campo «Site remoto» introduzir /tmp e carregar aí o script, # depois por SSH (o install define logo o dono e as permissões, chown/chmod não são precisos): sudo install -o root -g root -m 755 /tmp/lynis-scan.sh /usr/local/bin/lynis-scan.sh rm -f /tmp/lynis-scan.sh # verificação: ficheiro no lugar, permissões rwxr-xr-x, sintaxe intacta bash -n /usr/local/bin/lynis-scan.sh && ls -l /usr/local/bin/lynis-scan.sh
Não confunda os diretórios: precisa de /tmp na raiz do servidor — não /var/tmp nem tmp/ dentro do próprio painel (este último pertence ao www-data e está fechado ao seu utilizador). Na árvore do FileZilla, /tmp é um ramo de topo, ao lado de var, e não dentro dele.
Instalou o servidor com a configuração automática? Estes wrappers e as suas tarefas cron já estão instalados pelo script (em /usr/local/bin/, o log é /var/log/arciveo-cron.log) — não é preciso fazer nada manualmente.
Onde os scripts procuram o painel. Os wrappers são neutros quanto ao domínio: encontram as instalações do painel percorrendo /home/*/web/*/public_html e /var/www/*, e colocam os relatórios nos respetivos data/. Se o painel estiver noutro caminho — adicione-o à linha for app in … dentro dos scripts, senão os relatórios do Lynis/SMART/debsums/Logwatch não chegam ao painel.
cron.log e permissões de acesso. O ficheiro logs/cron.log é criado primeiro pelo cron root — pertencerá ao root, e o separador «Registo do cron» no painel não conseguirá lê-lo nem limpá-lo. Crie o ficheiro previamente como utilizador web (dono do diretório do site; no HestiaCP é a conta, p. ex. admin) — assim o cron root apenas acrescenta, sem mudar o dono:
# criar previamente como utilizador web (antes de adicionar as linhas cron): sudo -u OWNER touch /path/to/monitor/logs/cron.log # se o cron.log já foi criado pelo cron root — atribuir ao utilizador web: sudo chown OWNER:OWNER /path/to/monitor/logs/cron.log sudo chmod 644 /path/to/monitor/logs/cron.log
Descobrir o dono do diretório: stat -c %U /path/to/monitor.
Gestão a partir do painel. Na secção «Sistema» existe a página «Crontab» — permite ver e adicionar tarefas sem SSH. O painel edita apenas as tarefas adicionadas por ele próprio (um bloco separado no crontab root, marcado com comentários de serviço); tudo o que já está no crontab (a lista acima) é apresentado em modo de leitura na lista «Outras tarefas do servidor» com o botão «Copiar para o editor» — este apenas transfere o agendamento/comando para o formulário de adição, sem tocar na linha original. Para «passar» uma tarefa existente para a gestão do painel — copie-a para o editor, guarde e depois elimine a linha antiga manualmente (sudo crontab -e), senão será executada duas vezes.
Configuração única no servidor. A página precisa de um script wrapper com privilégios — não do sudo crontab puro (seria uma escalada direta para root por qualquer pessoa que obtivesse acesso à sessão do painel), mas de um script restrito com dois comandos (list/set), que só toca no seu bloco entre os comentários de serviço. Instale uma vez:
sudo install -m 0755 -o root -g root /dev/stdin /usr/local/sbin/arciveo-cron <<'ARCIVEO_CRON_EOF' #!/bin/bash set -euo pipefail BEGIN='# >>> ARCIVEO-CRON-MANAGED (edited from the panel; do not edit by hand) >>>' END='# <<< ARCIVEO-CRON-MANAGED <<<' cmd=${1:-} case "$cmd" in list) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} list" >&2; exit 2; } crontab -l -u root 2>/dev/null || true ;; set) [ "$#" -eq 1 ] || { echo "usage: ${0##*/} set (body on stdin)" >&2; exit 2; } body=$(cat) if grep -qF "$BEGIN" <<<"$body" || grep -qF "$END" <<<"$body"; then echo "invalid body: markers not allowed" >&2; exit 2 fi current=$(crontab -l -u root 2>/dev/null || true) tmp=$(mktemp); trap 'rm -f "$tmp"' EXIT { if grep -qF "$BEGIN" <<<"$current"; then awk -v b="$BEGIN" '{print} $0==b{exit}' <<<"$current" else [ -n "$current" ] && printf '%s\n' "$current" echo "$BEGIN" fi printf '%s\n' "$body" echo "$END" if grep -qF "$END" <<<"$current"; then awk -v e="$END" 'f{print} $0==e{f=1}' <<<"$current" fi } > "$tmp" crontab -u root "$tmp" ;; *) echo "usage: ${0##*/} <list|set>" >&2; exit 2 ;; esac ARCIVEO_CRON_EOF echo "www-data ALL=(ALL) NOPASSWD: /usr/local/sbin/arciveo-cron" | sudo tee -a /etc/sudoers.d/monitor sudo visudo -c
O utilizador web pode ser diferente de www-data — verifique com que utilizador corre o pool PHP-FPM do site (ps -o user= -C php-fpm) e substitua-o na linha do sudoers.
O novo ficheiro foi enviado com o dono errado — a página responde «Access denied.». Se o ficheiro public/crontab_monitor.php foi carregado por FTP/SFTP com um utilizador de sistema diferente (por exemplo, root) do dos restantes ficheiros do site, o servidor web não conseguirá lê-lo. Compare o dono e as permissões com um ficheiro vizinho e alinhe-os:
ls -la public/crontab_monitor.php public/ssl_monitor.php sudo chown OWNER:OWNER public/crontab_monitor.php sudo chmod 644 public/crontab_monitor.php

Diagnóstico

36. A ferramenta está instalada, mas aparece «Não instalada»

O monitor deteta a presença de ferramentas através do dpkg-query — a base de pacotes APT. Se a ferramenta não tiver sido instalada através do apt (manualmente, via snap ou a partir do código-fonte), o dpkg não a vê.

# Verificar através do dpkg: dpkg -l fail2ban | grep '^ii' dpkg -l auditd | grep '^ii' # Encontrar o caminho do binário: which ufw fail2ban-client auditctl # Teste de sudo a partir de www-data: sudo -u www-data sudo fail2ban-client status sudo -u www-data sudo ufw status verbose

37. Resolução de problemas (500, sem dados)

Erro 500 — verifique os registos do PHP, do nginx e do próprio monitor:

tail -50 /var/log/nginx/error.log tail -50 /var/log/php*-fpm.log # Registos do monitor: tail -50 logs/monitor_$(date +%Y-%m-%d).log # Permissões das pastas: ls -la data/ tmp/ logs/
Painel numa consola de alojamento (HestiaCP, ISPmanager, cPanel)? Aí o PHP não corre sob www-data, mas sob a conta do utilizador (por exemplo admin — proprietário do diretório do site). Todas as regras sudo e a pertença a grupos (adm, systemd-journal) têm de ser definidas para esse utilizador, caso contrário os módulos mostrarão «Inativo / 0» com os serviços a funcionar. Para saber o utilizador real do PHP: ps -o user= -C php-fpm | sort -u ou o proprietário do diretório do site stat -c '%U' /path/to/monitor. A seguir, em todos os comandos abaixo, use-o em vez de www-data. A instalação automática deteta o utilizador web por si própria e configura o sudoers para ele.

Os dados não são apresentados — quase sempre por permissões sudo não definidas. Verifique o comando concreto em nome do utilizador web (substitua www-data pelo seu). A flag -n = sem palavra-passe, como no PHP — se pedir palavra-passe, é porque não há regra no sudoers:

sudo -u www-data sudo -n fail2ban-client status sudo -u www-data sudo -n ufw status verbose sudo -u www-data sudo -n ipset list -t ipsum sudo -u www-data sudo -n /usr/sbin/ausearch -m USER_LOGIN -ts today sudo -u www-data sudo -n /usr/sbin/aa-status sudo -u www-data sudo -n /usr/sbin/psad --Status sudo -u www-data journalctl -u falco --no-pager -n 5
O módulo indica «Inativo» / «0», apesar de a ferramenta funcionar (por exemplo sudo aa-status no terminal mostra os perfis, mas a página «AppArmor» diz «Inativo»). Motivo: o utilizador web não tem permissão sudo para o comando desse módulo. Verifique-o na lista acima: se pedir palavra-passe — acrescente a linha em falta em /etc/sudoers.d/monitor («Configuração do sudo»). Comandos «novos» frequentes: /usr/sbin/aa-status (MAC), /usr/sbin/psad --Status (PSAD).
Se uma página específica (Falco, ModSecurity, Auditd, portas abertas do UFW) estiver vazia — confira a lista na secção sobre sudo: provavelmente não estão autorizados apache2ctl, ausearch, aa-status ou ss, ou o utilizador web não está nos grupos adm/systemd-journal (é de lá que se leem os registos do fail2ban/auth/modsec e o journalctl — Falco e eventos do núcleo).

38. A página está vazia, apesar de existirem dados no servidor

Sintoma: no servidor há dados (visíveis via shell), mas a página mostra «sem dados» ou um estado incorreto — por exemplo, o AIDE indica «Não inicializada», embora a base tenha sido criada.

A causa é o open_basedir: muitos painéis e alojamentos restringem o pool do PHP-FPM ao diretório do domínio, por isso as funções PHP file_exists(), file_get_contents(), filemtime() ficam bloqueadas para caminhos de sistema (/var/lib/aide, /var/log, /proc…). O Monitor contorna isto lendo esses caminhos com comandos de sistema padrão (cat, test, stat).

# O ficheiro é visível via shell (é assim que o monitor lê): sudo -u www-data bash -lc 'test -e /var/lib/aide/aide.db && echo VISIBLE || echo NO' # Valor atual de open_basedir para o pool do domínio: grep -ri open_basedir /etc/php/*/fpm/pool.d/ 2>/dev/null
Se o shell «vê» o ficheiro (VISIBLE) mas a página não — é o open_basedir. A solução correta é a leitura por comandos de sistema (já feito para o AIDE e o Monitor de rede). Alargar o open_basedir a /var, /proc não é necessário e é menos seguro.

39. A página SSL não funciona

O monitor verifica os certificados ligando-se diretamente aos domínios pela porta 443. Se o domínio estiver inacessível a partir do próprio servidor ou a porta estiver fechada pela firewall, a verificação falha.

# Verificar o certificado manualmente: echo | openssl s_client -connect monitor.example.com:443 2>/dev/null \ | openssl x509 -noout -dates # Verificar a disponibilidade: curl -I https://monitor.example.com
O monitor obtém os domínios automaticamente das configurações do Nginx (/etc/nginx/sites-enabled/, /etc/nginx/conf.d/) e do Apache (/etc/apache2/sites-enabled/), mais o host atual de HTTP_HOST.
Deteção automática de subdomínios. Os subdomínios são detetados automaticamente a partir dos registos públicos de Certificate Transparency e verificados pela rede — mesmo que estejam alojados noutros servidores. Não é preciso adicionar nada manualmente.

40. Apenas uma BD é visível de várias

O monitor liga-se ao MySQL com o utilizador de config.php, que só tem acesso à sua própria base de dados. O MySQL mostra em information_schema apenas as bases com privilégios — por isso as restantes não aparecem.

Para que o monitor veja todas as BD, conceda a este utilizador permissão apenas de leitura (uma vez como root; substitua o nome do utilizador de config.php):

sudo mysql -u root GRANT SELECT, PROCESS, SHOW DATABASES ON *.* TO 'DB_USER'@'localhost'; FLUSH PRIVILEGES; EXIT;
SELECT ON *.* concede apenas permissão de leitura — não é possível alterar, eliminar ou criar nada, é seguro para monitorização.
Sem este GRANT, o painel só vê a sua própria base — não é um erro, mas uma limitação de permissões. O painel não usa qualquer sudo mysql: a lista de bases é obtida através da sua própria ligação PDO.

41. O PostgreSQL não aparece na página «Base de dados»

O PostgreSQL exige acesso ao nível do utilizador postgres, que o utilizador web do painel não tem. Expor um sudo psql amplo a partir do PHP é inseguro — em vez disso, o painel invoca uma camada restrita sem parâmetros, que apenas imprime a versão, o número de ligações e a lista de bases com os respetivos tamanhos. Crie-a:

sudo tee /usr/local/bin/monitor-pgstat >/dev/null <<'EOF' #!/bin/sh # Arciveo Monitor - read-only PostgreSQL version, connections and per-database size sudo -u postgres psql -tAc "SELECT version();" | grep -oE 'PostgreSQL [0-9.]+' echo "---" sudo -u postgres psql -tAc "SELECT count(*) FROM pg_stat_activity;" echo "---" sudo -u postgres psql -tAc "SELECT datname || '|' || pg_size_pretty(pg_database_size(datname)) FROM pg_database WHERE datistemplate = false;" EOF sudo chown root:root /usr/local/bin/monitor-pgstat sudo chmod 755 /usr/local/bin/monitor-pgstat # em /etc/sudoers.d/monitor (utilizador = aquele sob o qual corre o PHP-FPM): # www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
Se não utilizar o PostgreSQL — remova a linha monitor-pgstat do sudoers (passo 13 da instalação manual) e não crie o script: o cartão do PostgreSQL fica apenas inativo.

42. Um alerta foi disparado — o que fazer

O painel mostra o que está a acontecer; abaixo, o que fazer em situações típicas. Princípio geral: não entrar em pânico, comparar com a atividade legítima (as suas ações, atualizações, backups) e reagir consoante a gravidade.

  • Mapa de ataques / muitos bans do fail2ban — é normal para qualquer servidor na internet (os bots tentam constantemente SSH/web). O importante é que os bans funcionem. Certifique-se de que o acesso por SSH é feito apenas por chave (palavra-passe desativada) e de que o seu IP está em ignoreip.
  • O ModSecurity bloqueou pedidos — o WAF repele ataques ao site, é o trabalho dele. Se estiver a bloquear o seu tráfego legítimo (falso positivo), encontre o rule id nos detalhes e adicione uma exceção na config do CRS.
  • AIDE: ficheiros alterados — compare a lista com aquilo que fez (atualização de pacotes, edição de configs é normal). Alterações a binários do sistema em que não mexeu são motivo de alerta. Após alterações legítimas, atualize a base do AIDE.
  • debsums: binários/bibliotecas alterados (fora de /etc, fora de /usr/share) — possível substituição. Verifique o pacote: debsums PACKAGE_NAME e, em caso de dúvida, reinstale-o (apt install --reinstall).
  • ClamAV / maldet: ameaça encontrada — verifique o ficheiro em quarentena, não o abra. Se for uma web shell no diretório do site, isole o servidor e procure o ponto de entrada (plugin vulnerável, fuga de credenciais).
  • Falco: eventos críticos (execução de shell num contentor, acesso a ficheiros sensíveis) — analise o evento: de quem é o processo, o que o lançou. Muitas vezes é atividade legítima de administração.
  • Exposição externa: BD/cache a vermelho — feche imediatamente: associe o serviço a 127.0.0.1 ou feche a porta na UFW. É uma falha real.
  • SSL a expirar / expirado — renove o certificado (o Let's Encrypt renova-se sozinho; se não, verifique o certbot renew ou as definições no painel).
  • Atualizações de segurança pendentes — instale: sudo apt update && sudo apt upgrade; após atualizar o kernel, reinicie o servidor.
Sinais de uma invasão real (processos/utilizadores desconhecidos, binários alterados, spam de saída, tarefas cron desconhecidas): desligue o servidor do acesso externo, faça um backup para análise e, se os dados forem críticos, prepare um servidor limpo a partir de um backup fiável — remover um rootkit de forma segura é difícil.
Arcivéo - Security Monitor © 2026