--- name: security-check description: Auditoria de seguranca de infraestrutura — certificados SSL, portas abertas, actualizacoes pendentes, backups recentes, CSF/firewall e email security. Score por servidor. context: fork --- # /security-check v1.0 (infraestrutura) Auditoria de seguranca dos servidores Descomplicar. Verifica SSL, portas, actualizacoes, backups, firewall e email security. > **Auditoria de dependencias de codigo:** usar `/dep-audit` (plugin dev-tools). **Referencia:** PROC-Security-Audit-API-Keys.md | PROC-Backup-Sistema.md | Memory: `infra.md`, `cwp-ssl-autorenew-fix.md` --- ## Servidores Alvo | Alias | Host | IP | SO | Firewall | Gestor SSL | |-------|------|----|----|----------|------------| | server | server.descomplicar.pt | 176.9.3.158 | CentOS (CWP) | CSF v15 | acme.sh | | easy | qibspu.easypanel.host | 178.63.18.51 | Ubuntu 24.04 | — | EasyPanel/Traefik | | gateway | mcp-hub.descomplicar.pt | — | — | — | nginx/acme | --- ## Modos de Execucao | Comando | Descricao | |---------|-----------| | `/security-check` | Auditoria completa (todos os servidores, todos os checks) | | `/security-check server` | Apenas servidor CWP | | `/security-check easy` | Apenas EasyPanel | | `/security-check ssl` | Apenas certificados SSL (todos os servidores) | | `/security-check quick` | Resumo rapido: SSL a expirar + backups + firewall | --- ## Protocolo de Execucao ### Check 1: Certificados SSL (server) ```bash # Executar via mcp__ssh-unified__ssh_execute(server="server") # Listar todos os certs e datas de expiracao for dir in /root/.acme.sh/cwp_certs/*_ecc; do domain=$(basename "$dir" | sed 's/_ecc$//') cert="$dir/${domain}.cer" if [ -f "$cert" ]; then expiry=$(openssl x509 -enddate -noout -in "$cert" 2>/dev/null | cut -d= -f2) days=$(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 )) if [ "$days" -lt 15 ]; then echo "CRITICAL $domain expires in ${days}d ($expiry)" elif [ "$days" -lt 30 ]; then echo "WARN $domain expires in ${days}d ($expiry)" else echo "OK $domain expires in ${days}d" fi fi done | sort ``` **Thresholds:** - **OK:** >30 dias para expirar - **WARN:** 15-30 dias - **CRITICAL:** <15 dias ### Check 2: Portas Abertas (server) ```bash # Portas a escutar no servidor CWP ss -tlnp | awk 'NR>1 {print $4}' | sed 's/.*://' | sort -n | uniq # Portas esperadas (CWP padrao): # 22(SSH) 25(SMTP) 80(HTTP) 110(POP3) 143(IMAP) 443(HTTPS) # 465(SMTPS) 587(Submission) 993(IMAPS) 995(POP3S) # 2031(CWP) 2087(CWP Admin) 3306(MySQL) # 8443(CWP SSL) 9443(SSH alternativo) EXPECTED="22 25 80 110 143 443 465 587 993 995 2031 2087 3306 8443 9443" # Comparar: portas abertas que NAO estao na lista esperada ss -tlnp | awk 'NR>1 {print $4}' | sed 's/.*://' | sort -n | uniq | while read port; do if ! echo "$EXPECTED" | grep -qw "$port"; then proc=$(ss -tlnp | grep ":${port} " | sed 's/.*users:(("//' | cut -d'"' -f1) echo "UNEXPECTED :${port} ($proc)" fi done ``` ### Check 3: Actualizacoes Pendentes ```bash # CWP Server (CentOS/dnf) # mcp__ssh-unified__ssh_execute(server="server") dnf check-update --quiet 2>/dev/null | grep -v "^$" | wc -l # Se >0: listar security updates dnf updateinfo list security 2>/dev/null | tail -10 # EasyPanel (Ubuntu/apt) # mcp__ssh-unified__ssh_execute(server="easy") apt list --upgradable 2>/dev/null | grep -c upgradable # Se >0: listar security updates apt list --upgradable 2>/dev/null | grep -i secur ``` **Thresholds:** - **OK:** 0 security updates pendentes - **WARN:** 1-5 security updates - **CRITICAL:** >5 security updates OU kernel update pendente ### Check 4: Backups Recentes (server) ```bash # mcp__ssh-unified__ssh_execute(server="server") echo "=== MySQL Daily ===" ls -lt /home/backup/mysql/daily/ 2>/dev/null | head -3 latest_mysql=$(find /home/backup/mysql/daily/ -name "*.sql.gz" -mtime -2 2>/dev/null | wc -l) echo "BDs com backup <48h: $latest_mysql" echo "=== Monthly ===" ls -lt /home/backup/monthly/ 2>/dev/null | head -5 echo "=== Rsync Easy->Server ===" # Verificar ultimo rsync (Easy replica para Server as 06:00) ls -lt /gordito/backup-easy/ 2>/dev/null | head -3 || echo "path nao existe" echo "=== Idade ultimo backup ===" newest=$(find /home/backup/ -type f -name "*.gz" -printf '%T@\n' 2>/dev/null | sort -rn | head -1) if [ -n "$newest" ]; then age_hours=$(echo "scale=1; ($(date +%s) - $newest) / 3600" | bc) echo "Backup mais recente: ${age_hours}h atras" fi ``` **Thresholds:** - **OK:** backup MySQL <48h, monthly <35 dias - **WARN:** backup MySQL 48-72h, monthly 35-60 dias - **CRITICAL:** backup MySQL >72h, monthly >60 dias ### Check 5: Firewall CSF (server) ```bash # mcp__ssh-unified__ssh_execute(server="server") echo "=== CSF Status ===" csf -v csf -l | head -5 echo "=== IPs Bloqueados ===" wc -l /etc/csf/csf.deny echo "Ultimos 5 bloqueios:" tail -5 /etc/csf/csf.deny echo "=== LFD Status ===" systemctl is-active lfd echo "=== Ataques Recentes (24h) ===" grep "$(date +%b\ %d)" /var/log/lfd.log 2>/dev/null | grep -c "blocked" ``` **Thresholds:** - **OK:** CSF activo, LFD activo, <50 bloqueios/dia - **WARN:** >50 bloqueios/dia (possivel ataque) - **CRITICAL:** CSF/LFD inactivo ### Check 6: Email Security (server) ```bash # mcp__ssh-unified__ssh_execute(server="server") # Executar script de verificacao (criado 12-03-2026) bash /root/scripts/email-security-check.sh 2>&1 | tail -20 echo "Exit code: $?" # Exit 0 = OK, Exit 1 = erros detectados ``` **Verifica:** Postfix header/body checks, content_filter (amavis), ClamAV, SpamAssassin, DMARC p=reject, Sieve anti-backscatter. --- ## Execucao Pratica Agrupar comandos para minimizar chamadas SSH (3 chamadas total). **Chamada 1 — server (SSL + portas + firewall):** ```bash echo "=== SSL CERTS ===" && for dir in /root/.acme.sh/cwp_certs/*_ecc; do domain=$(basename "$dir" | sed 's/_ecc$//'); cert="$dir/${domain}.cer"; if [ -f "$cert" ]; then expiry=$(openssl x509 -enddate -noout -in "$cert" 2>/dev/null | cut -d= -f2); days=$(( ($(date -d "$expiry" +%s) - $(date +%s)) / 86400 )); if [ "$days" -lt 15 ]; then echo "CRITICAL ${days}d $domain"; elif [ "$days" -lt 30 ]; then echo "WARN ${days}d $domain"; else echo "OK ${days}d $domain"; fi; fi; done | sort && echo "=== PORTAS ===" && ss -tlnp | awk 'NR>1 {print $4}' | sed 's/.*://' | sort -n | uniq -c | sort -rn && echo "=== CSF ===" && csf -v && systemctl is-active lfd && wc -l /etc/csf/csf.deny && echo "Bloqueios hoje:" && grep "$(date +%b\ %d)" /var/log/lfd.log 2>/dev/null | grep -c "blocked" ``` **Chamada 2 — server (backups + updates + email sec):** ```bash echo "=== BACKUPS ===" && echo "MySQL daily:" && find /home/backup/mysql/daily/ -name "*.sql.gz" -mtime -2 2>/dev/null | wc -l && echo "Monthly:" && ls -lt /home/backup/monthly/ 2>/dev/null | head -3 && echo "=== UPDATES ===" && dnf check-update --quiet 2>/dev/null | grep -v "^$" | wc -l && dnf updateinfo list security 2>/dev/null | tail -5 && echo "=== EMAIL SEC ===" && bash /root/scripts/email-security-check.sh 2>&1 | tail -15 && echo "Exit: $?" ``` **Chamada 3 — easy (updates + portas):** ```bash echo "=== UPDATES ===" && apt list --upgradable 2>/dev/null | grep -v "^Listing" | wc -l && apt list --upgradable 2>/dev/null | grep -v "^Listing" | head -10 && echo "=== PORTAS ===" && ss -tlnp | grep LISTEN | wc -l && echo "=== DOCKER ===" && docker ps --format '{{.Names}}: {{.Status}}' 2>/dev/null | grep -i unhealthy && echo "=== DISCO ===" && df -h / /gordito 2>/dev/null ``` --- ## Output ```markdown ## Security Check — [data via mcp-time] ### Resumo | Servidor | SSL | Portas | Updates | Backups | Firewall | Email | Score | |----------|-----|--------|---------|---------|----------|-------|-------| | server | OK/WARN/CRIT | OK/WARN | OK/WARN/CRIT | OK/WARN/CRIT | OK/WARN/CRIT | OK/WARN | X/6 | | easy | n/a | OK/WARN | OK/WARN/CRIT | n/a | n/a | n/a | X/2 | **Score global:** X/8 checks OK ### SSL Certificados ([N] dominios verificados) | Estado | Dominio | Expira em | Data | |--------|---------|-----------|------| | CRITICAL | example.pt | Xd | YYYY-MM-DD | | WARN | example2.pt | Xd | YYYY-MM-DD | [Se todos OK: "Todos os N certificados validos (minimo Xd)"] ### Portas Inesperadas | Porta | Processo | Accao Sugerida | |-------|----------|----------------| | XXXX | processo | Investigar / Fechar | [Se nenhuma: "Todas as portas dentro do esperado"] ### Actualizacoes Pendentes - **server (CentOS):** N packages (M security) - **easy (Ubuntu):** N packages ### Backups - MySQL daily: N BDs com backup <48h [OK/WARN/CRIT] - Monthly: ultimo em DD-MM-YYYY [OK/WARN/CRIT] ### Firewall CSF - CSF: vXX [activo/inactivo] - LFD: [activo/inactivo] - IPs bloqueados: N permanentes - Bloqueios hoje: N ### Email Security - [resultado do script email-security-check.sh] ### Accoes Recomendadas 1. [CRITICAL] Accao imediata 2. [WARN] Accao recomendada 3. [INFO] Melhorias opcionais ``` --- ## Scoring | Score | Significado | |-------|-------------| | 8/8 | Excelente — tudo operacional | | 6-7/8 | Bom — warnings menores | | 4-5/8 | Atencao — accoes necessarias | | <4/8 | Critico — accao imediata | **Calculo:** 1 ponto por cada check OK. WARN conta 0.5. CRITICAL conta 0. --- ## Troubleshooting ``` SSL CRITICAL (<15d): 1. SSH server: /root/.acme.sh/acme.sh --renew -d DOMAIN --ecc 2. Copiar para /etc/pki/tls/: /scripts/autossl_install_certs 3. Reload nginx: /scripts/cwp_api webservers restart 4. Verificar Cloudflare DNS-only (proxied impede Let's Encrypt) Ref: cwp-ssl-autorenew-fix.md CSF/LFD inactivo: 1. csf -e (activar) 2. service lfd start 3. Verificar /etc/csf/csf.conf (TESTING = "0") Backup MySQL >72h: 1. Verificar cron: crontab -l | grep backup 2. Verificar espaco: df -h /home/backup/ 3. Verificar logs: /var/log/cron | grep backup Muitos bloqueios CSF (>100/dia): 1. Analisar origens: awk '{print $2}' /etc/csf/csf.deny | sort | uniq -c | sort -rn | head 2. Se mesmo IP range: bloquear CIDR 3. Se paises especificos: CC_DENY em csf.conf ``` --- ## Integracao - **/today** pode invocar `/security-check quick` como parte do checkup diario - **/infra-check** (cron) ja faz verificacao basica; `/security-check` e mais profundo e interactivo - **/server-health** cobre metricas de performance; `/security-check` foca-se em seguranca - **/cwp-security** foca-se em operacoes CSF; `/security-check` e auditoria abrangente - Resultado pode ser publicado na discussao #31 (Logs) do projecto #65 --- ## Anti-Patterns - **Nunca** desactivar CSF para "resolver" problemas de acesso - **Nunca** ignorar certificado CRITICAL (<15d) — pode expirar antes do proximo check - **Nunca** actualizar kernel sem janela de manutencao planeada - **Sempre** verificar email-security apos alteracoes em Postfix/AMaViS - **Sempre** incluir timestamp via mcp-time no output --- *Skill v1.0.0 | 12-03-2026 | Descomplicar*