Instalação manual

Instalação totalmente manual: de um VPS acabado de adquirir até um painel operacional, passo a passo. A preparação do servidor, a criação do site, a base de dados, o config.php e o SSL são descritos aqui. Os comandos de cada ferramenta de segurança e do cron estão no guia de FAQ, com ligações ao longo do texto.

Regra de ouro: ao alterar o SSH ou a firewall, não feche a ligação atual enquanto não tiver testado a nova numa janela separada. Se ainda assim perder o acesso — quase todos os alojamentos disponibilizam uma consola de emergência (VNC/Recovery) no painel de controlo.

01. Comprei um VPS com Ubuntu/Debian — por onde começar

Após a compra, o fornecedor de alojamento envia: endereço IP, nome de utilizador (normalmente root) e palavra-passe (ou chave SSH). É o suficiente para iniciar sessão. Sequência de ações (cada passo é uma secção abaixo):

  1. Ligar ao servidor via SSH;
  2. Atualizar o sistema, definir o nome de anfitrião e o fuso horário;
  3. Criar um utilizador normal com permissões sudo (não trabalhar como root);
  4. Configurar o início de sessão por chave SSH e desativar o início de sessão por palavra-passe;
  5. Ativar a firewall e a autoproteção;
  6. (opcional) instalar o painel de controlo HestiaCP — servidor web, base de dados e correio prontos a usar.

02. Primeira ligação via SSH

SSH é um terminal seguro para o servidor. Substitua o seu IP por 203.0.113.10.

203.0.113.10 é um exemplo, um endereço inexistente (reservado para documentação). Não o introduza tal como está — substitua-o pelo IP real do seu servidor indicado no e-mail do fornecedor de alojamento. Caso contrário, não haverá ligação.

Windows 10/11: abra o PowerShell ou o «Terminal» e utilize o ssh integrado (ou os clientes PuTTY / MobaXterm).
macOS / Linux: abra o «Terminal».

# Entrar como root (a palavra-passe foi enviada pelo fornecedor de alojamento): ssh root@203.0.113.10 # Se o fornecedor de alojamento deu um ficheiro-chave em vez da palavra-passe: ssh -i caminho/para/chave root@203.0.113.10
Na primeira ligação, o SSH pergunta sobre a «authenticity of host» — introduza yes. A palavra-passe não é apresentada ao ser digitada (é normal). Se o fornecedor de alojamento deu uma palavra-passe temporária, altere-a com o comando passwd.

03. Atualização do sistema e configuração básica

Antes de tudo — atualize todos os pacotes e defina o nome de anfitrião e o fuso horário.

# Atualizar o sistema: apt update && apt upgrade -y # Utilitários básicos: apt install -y curl wget ufw fail2ban unattended-upgrades # Fuso horário (exemplo) e nome de anfitrião: timedatectl set-timezone Europe/Lisbon hostnamectl set-hostname myserver # Atualizações de segurança automáticas: dpkg-reconfigure -plow unattended-upgrades
Lista de fusos horários — timedatectl list-timezones. Se, no fim da atualização, surgir uma janela azul «Daemons using outdated libraries» — selecione todos os serviços (Espaço) e prima OK, é seguro.

04. Criar um utilizador com sudo

Trabalhar sempre como root não é seguro. Crie um utilizador normal e dê-lhe permissões sudo (executar comandos de administrador quando necessário). Substitua deploy por qualquer nome.

# Criar utilizador (define a palavra-passe e pede dados — pode carregar em Enter): adduser deploy # Adicionar ao grupo sudo: usermod -aG sudo deploy # Verificar (como root): su - deploy sudo whoami # deve mostrar: root exit
A partir daqui, entre no servidor já com este utilizador: ssh deploy@203.0.113.10, e execute os comandos de administrador com o prefixo sudo.

05. Chaves SSH e desativação do início de sessão por palavra-passe

O início de sessão por chave é mais seguro do que por palavra-passe: uma palavra-passe pode ser adivinhada, uma chave praticamente não. Primeiro criamos a chave no seu computador, copiamo-la para o servidor, testamos o início de sessão — e só depois desativamos a palavra-passe.

Passo 1. Criar a chave no seu computador (Windows PowerShell / macOS / Linux):

ssh-keygen -t ed25519 -C "my-laptop" # Enter em todas as perguntas (a chave fica em ~/.ssh/id_ed25519)

Passo 2. Copiar a chave pública para o servidor:

# macOS / Linux: ssh-copy-id deploy@203.0.113.10 # Windows (PowerShell): type $env:USERPROFILE\.ssh\id_ed25519.pub | ssh deploy@203.0.113.10 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

Passo 3. Testar o início de sessão por chave numa nova janela — deve permitir o acesso sem palavra-passe:

ssh deploy@203.0.113.10
Não execute a Variante B enquanto o início de sessão por chave não estiver verificado e funcional (Passos 1–3), e não feche a sessão de trabalho. Ela desativa o início de sessão por palavra-passe para todos os utilizadores, incluindo o root. Sem uma chave funcional, perderá totalmente o acesso ao servidor — só o poderá recuperar através da consola do fornecedor de alojamento. Não tem chave — use a Variante A.

Passo 4. Reforçar o acesso por SSH. As definições vão para um ficheiro separado, sem mexer no ficheiro de configuração principal. Escolha a variante conforme a situação:

Variante A — apenas fechar o root, mantendo a palavra-passe. Não é preciso chave e não perde o acesso:

echo 'PermitRootLogin no' | sudo tee /etc/ssh/sshd_config.d/00-hardening.conf >/dev/null sudo systemctl restart ssh

Variante B — hardening completo. Desativar o início de sessão por palavra-passe e deixar o root apenas por chave. Execute depois de confirmar que o início de sessão por chave funciona:

sudo tee /etc/ssh/sshd_config.d/00-hardening.conf >/dev/null <<'EOF' PubkeyAuthentication yes PasswordAuthentication no PermitRootLogin prohibit-password KbdInteractiveAuthentication no EOF sudo systemctl restart ssh
Em ambas as variantes, o root por palavra-passe fica fechado. PermitRootLogin no proíbe totalmente o root, prohibit-password — deixa o início de sessão apenas por chave (para administração, inicie sessão como deploy e use sudo). Se quiser mudar a porta SSH — adicione a linha Port 2222, mas primeiro abra a nova porta na firewall (secção seguinte) e teste o início de sessão, caso contrário ficará sem acesso.

06. Firewall básico e autoproteção

Feche tudo o que é desnecessário com o firewall e ative o fail2ban (bane tentativas de adivinhar palavras-passe por SSH). Primeiro permita o SSH, caso contrário perde o acesso depois de ativar o UFW.

# Permitir SSH (ou a sua porta, se a alterou) e a web: sudo ufw allow OpenSSH sudo ufw allow 80,443/tcp # Ativar o firewall: sudo ufw enable sudo ufw status verbose # fail2ban — proteção do SSH contra tentativas por força bruta (perfil básico ativo de imediato): sudo systemctl enable --now fail2ban sudo fail2ban-client status sshd
Isto é o mínimo. As configurações de trabalho do fail2ban, a lista de bloqueio ipsum, o UFW avançado e as restantes ferramentas estão no grupo «Ferramentas de segurança» do manual. O próprio painel Arcivéo Monitor mostra de forma clara o estado de tudo isto.

07. Instalação do painel HestiaCP (opcional)

HestiaCP — painel de controlo de alojamento gratuito: instala e configura o servidor web (nginx + apache), PHP, base de dados (MariaDB), correio, DNS e certificados SSL, e disponibiliza uma interface web para os sites. É prático se não quiser configurar tudo manualmente e planear alojar sites (incluindo o próprio painel Arcivéo Monitor).

Instale o HestiaCP num servidor limpo (Ubuntu/Debian recente e suportado, no mínimo ~1–2 GB de RAM), antes de instalar outros servidores web e bases de dados — caso contrário haverá conflitos. A instalação demora 10–20 minutos e reinicia o servidor.
# Descarregar o instalador e executar: wget https://raw.githubusercontent.com/hestiacp/hestiacp/release/install/hst-install.sh sudo bash hst-install.sh

O instalador pede o email e o nome do host e, em seguida, instala toda a stack. Após a reinicialização, o painel fica acessível no endereço https://YOUR_IP:8083 (o instalador mostra o login e a palavra-passe no final).

O HestiaCP gere ele próprio o UFW e o fail2ban — não é preciso configurá-los à parte, ele trata disso. Ainda assim, configure as chaves SSH e a desativação da palavra-passe (secção anterior).

08. Requisitos de sistema e ionCube

O painel é uma aplicação PHP numa pilha LAMP/LEMP típica:

  • SO: Linux (recomendam-se Ubuntu/Debian);
  • Servidor web: nginx ou Apache com PHP-FPM;
  • PHP 8.0+ com as extensões: pdo_mysql, openssl, curl, json, mbstring;
  • ionCube Loader — extensão do PHP necessária para o funcionamento do painel;
  • BD: MySQL 5.7+ ou MariaDB 10.3+;
  • HTTPS — obrigatório (o início de sessão e o WebAuthn só funcionam por https);
  • sudo para o utilizador do servidor web (conjunto restrito — passo 13).
# Verificar a versão do PHP e as extensões: php -v php -m | grep -iE 'pdo_mysql|openssl|curl|mbstring|ioncube'

Instalação do ionCube Loader (se ainda não estiver instalado). Num alojamento com painel (HestiaCP, cPanel), o ionCube ativa-se com uma marca nas definições do PHP. Manualmente em Ubuntu/Debian:

# Descobrir a versão do PHP e o diretório de extensões: php -v EXTDIR=$(php -r 'echo ini_get("extension_dir");'); PHPVER=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;') # Descarregar e extrair os loaders (64-bit): cd /tmp wget -q https://downloads.ioncube.com/loader_downloads/ioncube_loaders_lin_x86-64.tar.gz tar xzf ioncube_loaders_lin_x86-64.tar.gz # Copiar o loader da sua versão do PHP para o diretório de extensões: sudo cp ioncube/ioncube_loader_lin_${PHPVER}.so "$EXTDIR"/ # Ativar (CLI + PHP-FPM) e reiniciar: echo "zend_extension=ioncube_loader_lin_${PHPVER}.so" | sudo tee /etc/php/${PHPVER}/mods-available/ioncube.ini sudo phpenmod ioncube sudo systemctl restart php${PHPVER}-fpm # Verificação — no resultado aparecerá a linha "with the ionCube PHP Loader": php -v
A versão do loader tem de coincidir com a versão do PHP (por exemplo ioncube_loader_lin_8.1.so para PHP 8.1). Se usar várias versões do PHP — ative o loader para cada uma.

09. Domínio e DNS

Para abrir o painel num endereço como monitor.example.com e obter SSL gratuito, precisa de um domínio que aponte para o seu servidor. No painel de gestão de DNS, crie um registo A:

Tipo: A Nome: monitor (subdomínio → monitor.example.com) ou @ (raiz do domínio → example.com) Valor: 203.0.113.10 ← IP do seu servidor TTL: 3600

Após alguns minutos, verifique se o domínio aponta para o servidor:

dig +short monitor.example.com # deve devolver o seu IP # ou, se não tiver o dig: getent hosts monitor.example.com
O certificado SSL da Let's Encrypt só é emitido para um domínio — o DNS tem de apontar para o servidor antes da emissão do certificado.

10. Criar o site e enviar os ficheiros do painel

Apache: DocumentRoot — na raiz do painel, NÃO em public/. Os estilos (CSS/JS), o sw.js e o manifest.json ficam em assets/ junto de public/ e são pedidos a partir da raiz do site. O .htaccess raiz é o controlador frontal. Se, com o Apache, definir o DocumentRoot para public/, o painel abre sem estilos. Com nginx puro é o contrário: a raiz passa a ser public/ e assets/ é servido por uma regra separada (ver o bloco nginx abaixo).
Os ficheiros do painel (o arquivo da distribuição) são descarregados após a compra na área pessoal em my.arciveo.com«Transferências». Extraia o arquivo antes de o enviar.

1) Crie a diretoria do painel e envie para ela o conteúdo da distribuição (para que dentro fiquem public/, assets/, config.php, etc.):

sudo mkdir -p /var/www/monitor # depois enviar os ficheiros da distribuição para /var/www/monitor (FileZilla / WinSCP / scp)

2) Configure o servidor web. Apache: DocumentRoot — na raiz do painel (NÃO em /public); o AllowOverride All é obrigatório. O caminho para o socket do PHP-FPM é detetado automaticamente. O bloco é colado no terminal por inteiro:

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # deteção automática do socket PHP-FPM sudo tee /etc/apache2/sites-available/monitor.conf > /dev/null <<'EOF' <VirtualHost *:80> ServerName monitor.example.com DocumentRoot /var/www/monitor <Directory /var/www/monitor> AllowOverride All Require all granted </Directory> <FilesMatch \.php$> SetHandler "proxy:unix:__PHPSOCK__|fcgi://localhost" </FilesMatch> </VirtualHost> EOF sudo sed -i "s#__PHPSOCK__#${PHPSOCK}#" /etc/apache2/sites-available/monitor.conf sudo a2dissite 000-default.conf sudo a2ensite monitor.conf sudo apache2ctl configtest sudo systemctl reload apache2

nginx: o nginx não tem .htaccess, por isso a raiz passa a ser public/, e assets/, sw.js, manifest.json (um nível acima) são servidos por uma regra separada:

PHPSOCK=$(ls -1 /run/php/php*-fpm.sock 2>/dev/null | head -1) # deteção automática do socket PHP-FPM sudo tee /etc/nginx/sites-available/monitor.conf > /dev/null <<'EOF' server { listen 80; server_name monitor.example.com; root /var/www/monitor/public; index index.php; # assets, service worker e manifest ficam um nível acima de public/ location ~ ^/(assets/|sw\.js|manifest\.json) { root /var/www/monitor; } location / { try_files $uri $uri/ /index.php?$query_string; } location ~ \.php$ { include snippets/fastcgi-php.conf; fastcgi_pass unix:__PHPSOCK__; } } EOF sudo sed -i "s#__PHPSOCK__#${PHPSOCK}#" /etc/nginx/sites-available/monitor.conf sudo ln -s /etc/nginx/sites-available/monitor.conf /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx

Envio dos ficheiros — SFTP/SCP (FileZilla, WinSCP) ou scp:

# Exemplo via scp a partir do computador local: scp -r ./monitor/* deploy@203.0.113.10:/var/www/monitor/
Defina as permissões dos ficheiros — este passo é obrigatório. Se enviou como root ou por SFTP, os ficheiros pertencem a root e o servidor web (www-data) não os consegue ler — o painel abre vazio ou com erro 403 (no log: .htaccess unreadable / directory not executable). O comando abaixo corrige isto:
# Normalizamos as permissões de todo o webroot: uma diretoria criada por root fica inacessível # ao servidor web (www-data) — sem isto o painel devolve uma página vazia ou 403. # O Apache corre como www-data; se tiver outro utilizador web, substitua-o. cd /var/www/monitor # Criamos as pastas de trabalho ANTES do chown — senão as novas diretorias ficam root:root # e, com chmod 750, o servidor web (www-data) não consegue escrever nelas. sudo mkdir -p data/lynis data/logwatch tmp logs sudo chown -R www-data:www-data /var/www/monitor sudo find /var/www/monitor -type d -exec chmod 755 {} \; sudo find /var/www/monitor -type f -exec chmod 644 {} \; sudo chmod 640 /var/www/monitor/config.php sudo chmod 750 data tmp logs

3) Abra para si próprio o envio de ficheiros por SFTP. Depois do comando acima todos os ficheiros pertencem ao www-data, enquanto o FileZilla / WinSCP se ligam com o seu próprio utilizador — o envio falhará com SSH_FX_PERMISSION_DENIED (Permission denied). Entrar como root para enviar não é opção — o acesso root foi desativado no passo 05. Escolha uma das duas variantes.

Variante A — uma ACL apenas para o seu utilizador (recomendado). A permissão de escrita fica só consigo; o servidor web continua sem poder substituir o código do painel:

sudo apt install -y acl # Permissão de escrita para o seu utilizador em todo o diretório do painel: sudo setfacl -R -m u:deploy:rwX /var/www/monitor # A mesma regra por omissão — para ficheiros e pastas criados mais tarde: sudo setfacl -R -d -m u:deploy:rwX /var/www/monitor

Variante B — através do grupo www-data. Mais simples, mas a permissão de escrita nos ficheiros do painel passa também para o servidor web: com uma vulnerabilidade no PHP o código poderia ser substituído. A ordem dos comandos importa — config.php e as pastas de trabalho são fechadas por último:

sudo usermod -aG www-data deploy # Escrita para o grupo + setgid (o bit 2): os ficheiros enviados por SFTP # permanecem no grupo www-data — caso contrário o painel não os consegue substituir. sudo find /var/www/monitor -type d -exec chmod 2775 {} \; sudo find /var/www/monitor -type f -exec chmod 664 {} \; sudo chmod 640 /var/www/monitor/config.php sudo chmod 2750 /var/www/monitor/data /var/www/monitor/tmp /var/www/monitor/logs
Depois da variante B volte a ligar-se no FileZilla (Servidor → Desligar e entrar de novo) — o novo grupo só passa a valer numa nova sessão, até lá continua sem permissões. Verificação: id deploy — na lista de grupos deve aparecer www-data; ls -ld /var/www/monitor — permissões drwxrwsr-x, a letra s em vez de x significa que o setgid está ativo.

11. Base de dados

Crie a base de dados e o utilizador, depois importe o esquema. O bloco é colado no terminal na íntegra. monitor_db e monitor_user são nomes de exemplo, pode definir os seus; memorize o nome da base, do utilizador e a palavra-passe — irá inseri-los em config.php no passo seguinte:

# 1. Base de dados. A palavra-passe define-se UMA vez em DBPASS e é usada em todas as linhas. # O bloco é colado no terminal NA ÍNTEGRA; sudo mysql entra como root via socket unix # (não precisa da palavra-passe de root). NÃO use o interativo `sudo mysql -u root -p` # com copiar/colar — ao colar, as linhas SQL vão para o pedido de palavra-passe e perdem-se. DBPASS='CHOOSE_A_PASSWORD' # ← altere apenas esta linha sudo mysql <<SQL CREATE DATABASE IF NOT EXISTS monitor_db CHARACTER SET utf8mb4; CREATE USER IF NOT EXISTS 'monitor_user'@'localhost' IDENTIFIED BY '$DBPASS'; GRANT ALL ON monitor_db.* TO 'monitor_user'@'localhost'; FLUSH PRIVILEGES; SQL # Verificação (deve mostrar monitor_db): mysql -u monitor_user -p"$DBPASS" -e "SHOW DATABASES;" # Insira esta mesma palavra-passe em config.php → DB_PASS.
Não é preciso importar o esquema — o painel cria as tabelas e a conta admin no primeiro acesso pelo navegador (a partir de database/db.sql), se a base de dados estiver vazia. A importação manual do esquema só é necessária se a inicialização automática não funcionar.
Se usou o instalador no navegador public/start_db.phpelimine-o logo após a instalação: ele permite recriar a base de dados sem autenticação. Enquanto o ficheiro estiver na raiz do painel ou em public/, o painel mostra um aviso vermelho.

12. Configuração do config.php

config.php na raiz do painel (/var/www/monitor/config.php) é o único ficheiro que precisa de editar manualmente. Todas as definições do painel são especificadas nele através de constantes define(). Abra-o num editor:

sudo nano /var/www/monitor/config.php

Introduza os seus valores nos locais destacados; deixe o resto como está:

// --- Base de dados (do passo 11) --- define('DB_HOST', 'localhost'); // manter define('DB_NAME', 'db_name'); // o que criou no passo 11 define('DB_USER', 'user'); // o que criou no passo 11 define('DB_PASS', 'db_password'); // o que definiu no passo 11 define('DB_CHARSET', 'utf8mb4'); // manter // --- Aplicação --- define('APP_URL', 'https://monitor.example.com'); // endereço do painel, sem barra no final 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)

O que alterar:

  • DB_NAME, DB_USER, DB_PASS — exatamente o mesmo nome de base, utilizador e palavra-passe que definiu ao criar a BD no passo 11 (se deixou os exemplos — monitor_db / monitor_user). Não mexa em DB_HOST e DB_CHARSET.
  • APP_URL — endereço completo do painel com https://, sem barra no final e sem www. Tem de coincidir com o domínio no qual ativa a licença (passo 16), caso contrário a chave será rejeitada.
  • TIMEZONE — o seu fuso horário (lista — timedatectl list-timezones). Afeta apenas a forma como o painel mostra as datas; não afeta a hora de execução das tarefas cron (aí aplica-se o fuso do sistema).
  • SESSION_LIFETIME — ao fim de quantos segundos de inatividade o painel pede novo início de sessão (por predefinição 8 horas). Ex. 3600 = 1 hora, 86400 = um dia.
  • Bloco de registo de erros (display_errors, log_errors, error_log) — deixe por predefinição.

Guarde o ficheiro (Ctrl+O, Enter, depois Ctrl+X) e reinicie o PHP-FPM — caso contrário, devido ao OPcache, as alterações não serão aplicadas:

sudo systemctl restart php*-fpm
config.php — ficheiro secreto (contém a palavra-passe da BD). Fica na raiz do painel, que é a própria raiz web, mas está protegido: permissões 640 (definidas no passo 10) e proibição explícita no .htaccess da raiz. Não o coloque em repositórios públicos nem o envie para o suporte com a palavra-passe real.
Análise detalhada de todos os parâmetros — nas FAQ: «Ficheiro config.php — todas as definições do painel».

13. Configurar o sudo para o servidor web

O PHP é executado com o utilizador do servidor web, que não tem permissões sobre comandos de sistema. O acesso é concedido de forma restrita: sudo pontual para utilitários específicos e leitura de registos através de grupos (sem sudo). Uma invasão da camada web não dá root.

Nos exemplos, www-data é o utilizador padrão do Apache. Se o seu for outro (nalguns painéis o PHP corre com um utilizador próprio), substitua em todo o lado. Para saber: ps -o user= -C php-fpm | sort -u.

1. Crie o /etc/sudoers.d/monitor com sudo visudo -f /etc/sudoers.d/monitor e cole (remova as linhas dos módulos que não usa):

# UFW — estado e regras (página «Firewall») www-data ALL=(ALL) NOPASSWD: /usr/sbin/ufw status, /usr/sbin/ufw status verbose, /usr/sbin/ufw status numbered www-data ALL=(ALL) NOPASSWD: /usr/sbin/ufw allow [0-9]*, /usr/sbin/ufw deny [0-9]*, /usr/sbin/ufw --force delete [0-9]* # Fail2ban — estado, ban e unban (banned devolve os bans de todos os jails num só comando; # ban/unban são precisos para os botões do painel) www-data ALL=(ALL) NOPASSWD: /usr/bin/fail2ban-client status, /usr/bin/fail2ban-client status *, /usr/bin/fail2ban-client banned, /usr/bin/fail2ban-client set * banip *, /usr/bin/fail2ban-client set * unbanip * # Atualizações de segurança (cartão «Atualizações»). Apenas leitura, mas mesmo como root: # a cache do apt (~70 MB) só é acessível ao root; um não-root reconstrói-a a cada chamada # (4,2 s de CPU contra 0,01 s). Sem wildcard — exatamente este único comando, não instala nada. www-data ALL=(ALL) NOPASSWD: /usr/bin/apt list --upgradable # IPset (mapa de ataques, dashboard) www-data ALL=(ALL) NOPASSWD: /usr/sbin/ipset list -t ipsum # CrowdSec www-data ALL=(ALL) NOPASSWD: /usr/bin/cscli decisions list *, /usr/bin/cscli alerts list *, /usr/bin/cscli bouncers list *, /usr/bin/cscli scenarios list * # Auditd — pesquisa de eventos + leitura das últimas linhas do registo (caminho exato) www-data ALL=(ALL) NOPASSWD: /usr/sbin/ausearch -m * www-data ALL=(ALL) NOPASSWD: /usr/bin/tail -n 300 /var/log/audit/audit.log # Monit / ModSecurity / AppArmor / PSAD www-data ALL=(ALL) NOPASSWD: /usr/bin/monit status www-data ALL=(ALL) NOPASSWD: /usr/sbin/apache2ctl -M www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-modsec www-data ALL=(ALL) NOPASSWD: /usr/sbin/aa-status www-data ALL=(ALL) NOPASSWD: /usr/sbin/psad --Status # Portas abertas (os registos do kernel/SSH/Falco leem-se SEM sudo — através do grupo # systemd-journal, ver ponto 2; NÃO é preciso dar sudo ao journalctl e é inseguro) www-data ALL=(ALL) NOPASSWD: /usr/bin/ss -tuln, /usr/sbin/ss -tuln, /bin/ss -tuln # PostgreSQL (só se o usar) — script read-only fixo, # crie-o pelo FAQ «PostgreSQL não aparece»; sem ele, elimine a linha www-data ALL=(ALL) NOPASSWD: /usr/local/bin/monitor-pgstat
sudo chmod 440 /etc/sudoers.d/monitor sudo visudo -c # deve indicar "parsed OK"

2. Acesso aos registos e ao journal do systemd. Os módulos leem /var/log/fail2ban.log, auth.log, ufw.log, apache2/*, aide diretamente (no Debian/Ubuntu estes registos estão no grupo adm). Os eventos do kernel, SSH e Falco são obtidos do journald com o comando journalctl sem sudo, através do grupo systemd-journal. Adicione o utilizador web a ambos os grupos e reinicie o PHP-FPM:

sudo usermod -aG adm,systemd-journal www-data sudo systemctl restart php*-fpm # obrigatório, senão os grupos não são aplicados

3. Se o ClamAV ou o Suricata escreverem os registos fora do grupo adm (por vezes fica root:root), conceda acesso através de ACL:

sudo apt install acl sudo setfacl -R -m u:www-data:rX /var/log/clamav /var/log/suricata 2>/dev/null sudo setfacl -d -m u:www-data:rX /var/log/clamav /var/log/suricata 2>/dev/null

4. Wrapper do ModSecurity. O registo de auditoria do WAF (/var/log/apache2/modsec_audit.log) pertence ao root com permissões 640, e o utilizador web não o consegue ler diretamente. A página do ModSecurity obtém o modo do motor, os eventos e a lista de regras ativas através de um script read-only fixo — é esse que está autorizado no sudoers na linha acima:

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
Sem o ficheiro /etc/modsecurity/modsecurity.conf o próprio WAF não funciona: o pacote coloca apenas o modsecurity.conf-recommended e o motor de regras fica desativado — para saber como ativá-lo, ver FAQ → «Instalação do ModSecurity».
O utilizador em todas as linhas do sudoers deve coincidir com o utilizador do pool FPM: num Apache/Debian normal é www-data; no HestiaCP o pool do site corre com o dono do site (por exemplo, admin) — verifique grep -E '^user' /etc/php/*/fpm/pool.d/monitor.example.com.conf.

5. Se houver Nginx à frente do Apache (HestiaCP, ISPmanager e outros painéis — nesse caso o Nginx faz proxy do PHP para o Apache e serve ele próprio os ficheiros estáticos). As diretorias de serviço estão protegidas por ficheiros .htaccess, mas o Nginx não os lê: qualquer ficheiro estático (.json, .txt, .log, .dat) ele serve-o diretamente, sem passar pelo Apache. Para fora vazam as caches do painel e os dados — por exemplo tmp/modsec_cache.json com os eventos do WAF e os IP dos atacantes. Adicione a proibição à configuração do site no Nginx:

location ^~ /data/ { deny all; } location ^~ /tmp/ { deny all; } location ^~ /logs/ { deny all; } location ^~ /includes/ { deny all; } location ^~ /cron/ { deny all; } location ^~ /database/ { deny all; } location = /config.php { deny all; }
O prefixo ^~ é obrigatório: é selecionado antes da regra por expressão regular para estáticos dentro de location /, caso contrário a proibição não funciona.
No HestiaCP, coloque isto num ficheiro separado /home/<user>/conf/web/<domain>/nginx.ssl.conf_deny (e nginx.conf_deny para HTTP) — a configuração do site inclui nginx.ssl.conf_* e, ao reconstruir, não apaga esses ficheiros. Para aplicar: sudo nginx -t && sudo systemctl reload nginx.
Verificação: curl -s -o /dev/null -w '%{http_code}\n' https://monitor.example.com/tmp/modsec_cache.json — deve dar 403. Se o Apache funcionar sem Nginx (escuta ele próprio o 80/443), não é preciso adicionar nada — o .htaccess é suficiente.
Verifique os caminhos dos binários com which (por exemplo which ufw cscli ausearch ss). Edite o sudoers apenas através de visudo. A lista de todas as bases de dados MySQL é ativada por um GRANT separado (FAQ → «Só aparece uma BD»).

14. Restrição de acesso por IP

Restrinja o acesso ao monitor por endereço IP — mesmo que o URL se torne conhecido, a página de início de sessão não abrirá. Pode fazê-lo ao nível do servidor web (exemplo para nginx abaixo) ou no próprio painel («Definições» → «Restrição de acesso por IP»). Se usar Apache, utilize a restrição no painel.

Se o site nginx já estiver configurado conforme o passo 10, não adicione um segundo location / — insira as linhas allow/deny no bloco já existente. Dois location / iguais num mesmo server { } são um erro de configuração e o nginx não reiniciará.
# Na configuração do nginx (dentro de server { }): # Mantemos o caminho ACME do Let's Encrypt aberto, contornando a restrição de IP — # para que a emissão e a renovação automática do SSL (passo 15) não dependam do filtro de IP. location ^~ /.well-known/acme-challenge/ { allow all; } location / { allow 203.0.113.10; # ← insira o seu IP allow 10.0.0.0/8; # rede local (se necessário) deny all; try_files $uri $uri/ /index.php?$query_string; } # Recarregar o nginx: sudo nginx -t && sudo systemctl reload nginx

15. Emitir SSL (HTTPS)

O painel funciona apenas por HTTPS. A sessão de início de sessão usa uma cookie segura, e o WebAuthn (2FA) só funciona em HTTPS. Não é possível entrar por http://.

O certificado é gratuito (Let's Encrypt). O DNS do domínio já deve apontar para o servidor. O comando depende do servidor web:

# Apache: sudo certbot --apache -d monitor.example.com # nginx — APENAS se usar mesmo nginx. Em Apache NÃO execute: # apt puxaria o nginx e ocuparia a porta 80, conflito com o Apache. # sudo apt install python3-certbot-nginx # sudo certbot --nginx -d monitor.example.com # o certbot escreve o HTTPS na config e configura a renovação automática
O que o certbot pergunta: e-mail → aceitação dos Terms (Y) → partilha do e-mail com a EFF (opcional). Depois emite o certificado, escreve <VirtualHost *:443>, configura o redirecionamento http→https e a renovação automática.
O DNS deve apontar para o servidor ANTES de executar o certbot (validação de propriedade pela porta 80). Verificação: dig +short monitor.example.com → IP do servidor. Portas 80/443 abertas: sudo ufw allow 80,443/tcp.

Após a emissão: https://monitor.example.com abre com o cadeado, http:// redireciona para https:// (o APP_URL em config.php já foi definido no passo 12).

16. Início de sessão e configuração inicial

Abra https://monitor.example.com, inicie sessão com admin / useradmin e siga a lista de verificação:

  1. Alterar a palavra-passe de admin — secção «Utilizadores» no menu.
  2. Ativar WebAuthn (2FA) — «Chaves WebAuthn» → registar chave/passkey (requer HTTPS). Registe logo duas: se perder a única chave, o início de sessão com ela torna-se impossível. Saber mais.
  3. Restringir o acesso por IP — «Definições» → «Restrição de acesso por IP» (introduza o seu IP antes de ativar, caso contrário bloqueia o seu próprio acesso).
  4. Introduzir a licença — ative o código ARCIVEO-… da sua área pessoal para o seu domínio e cole a chave em «Definições» → «Licença». Saber mais.
  5. Configurar as notificações — Telegram e/ou Email em «Definições». Saber mais.
  6. Eliminar o instalador public/start_db.php, caso ainda exista (passo 11).

17. Ferramentas de segurança (opcional)

O painel já está a funcionar. As ferramentas instalam-se conforme necessário — instale só o que precisa e o painel mostra logo o estado. Os comandos de instalação de cada uma estão no guia (secções separadas por ferramenta):

18. Cron e manutenção

Configura-se uma vez, também opcional, mas recomendado. Comandos detalhados — no manual:

  1. Tarefas cron (relatórios, atualização de listas, verificações);
  2. Cópias de segurança;
  3. Atualização e migração do painel;
  4. Recuperação de acesso — em caso de perda da chave/palavra-passe.
Algo não funciona ou mostra «sem dados»? Consulte o grupo «Diagnóstico» do manual.
Arcivéo - Security Monitor © 2026