Auditoria Automatizada de Segurança Linux: Como Lynis + Bash Me Deram Sono Tranquilo

Se você acha que configurar UFW e Fail2ban deixa seu servidor “seguro”, tenho más notícias. Eu pensava assim também — até rodar minha primeira auditoria com Lynis e descobrir que meu VPS tinha 47 vulnerabilidades. Quarenta e sete. Incluindo permissões soltas em /etc/shadow, SSH permitindo login de root, e um kernel com patch desatualizado há 6 meses.

A verdade sobre segurança de servidores Linux é cruel: hardening não é evento, é processo. Você configura, esquece, e em 3 meses metade das proteções já foi enfraquecida por updates, mudanças de config e aquele “quick fix” que você jurou que era temporário.

Neste artigo vou te mostrar como montei um sistema de auditoria automatizada que roda toda semana, gera relatórios comparativos, e manda alerta quando o score de segurança cai. Tudo com Lynis, Bash, e um bocado de teimosia.

Monitoramento de segurança cibernética em servidor Linux com painel de análise de vulnerabilidades

O Problema: Hardening É Efêmero

Vamos ser honestos. Quantas vezes você:

  • Abriu uma porta “só para testar” e esqueceu de fechar?
  • Instalou um pacote que modificou /etc/sysctl.conf sem avisar?
  • Deixou PermitRootLogin yes porque “ia mudar depois”?
  • Atualizou o sistema parcialmente e reiniciou sem verificar se os serviços subiram com a config correta?

Se nunca fez nada disso, parabéns — você é uma lenda urbana. Para nós, meros mortais, a solução é auditoria contínua.

Já mostrei como fazer hardening inicial de servidor Linux e como montar um WAF caseiro com nftables. Agora vamos além: como garantir que tudo isso continua funcionando meses depois.

Conhecendo o Lynis (O Cara Que Vai Te Julgar)

Lynis é uma ferramenta de auditoria de segurança para sistemas Unix/Linux. open-source, leve, sem dependências malucas. Ele verifica centenas de parâmetros — de permissões de arquivo a configuração de kernel — e gera um score de 0 a 100.

O score é brutalmente honesto. Meu primeiro resultado: 62/100. Num servidor que eu jurava que estava “blindado”.

Instalação

# Ubuntu/Debian
sudo apt update && sudo apt install lynis -y

# Ou a versão mais recente direto do GitHub
git clone https://github.com/CISOfy/lynis.git /opt/lynis
sudo ln -s /opt/lynis/lynis /usr/local/bin/lynis

Primeira Auditoria

sudo lynis audit system

A saída é um relatório completo que cobre:

  • Autenticação: PAM, SSH, senhas
  • File systems: Permissões, mounting options
  • Kernel: Hardening, módulos carregados
  • Networking: Firewall, ports, DNS
  • Software: Updates, vulnerabilidades conhecidas

No final, ele te dá sugestões específicas. Cada uma com prioridade e referência.

Automatizando a Auditoria com Bash

Rodar Lynis manualmente não adianta — você vai esquecer. A solução é automatizar. Criei um script que:

  1. Roda a auditoria completa
  2. Extrai o score e os warnings
  3. Compara com a auditoria anterior
  4. Gera relatório em HTML
  5. Envia por email ou Telegram
#!/bin/bash
# audit-security.sh — Auditoria automatizada com Lynis
# Adicione ao cron: 0 3 * * 0 /opt/scripts/audit-security.sh

set -euo pipefail

REPORT_DIR="/var/log/lynis-reports"
DATE=$(date +%Y-%m-%d)
PREV_DATE=$(date -d "7 days ago" +%Y-%m-%d 2>/dev/null || date -v-7d +%Y-%m-%d)
REPORT_FILE="$REPORT_DIR/audit-${DATE}.txt"
PREV_FILE="$REPORT_DIR/audit-${PREV_DATE}.txt"

mkdir -p "$REPORT_DIR"

echo "[$(date)] Iniciando auditoria Lynis..."

# Roda auditoria e salva output completo
sudo lynis audit system --report-file "$REPORT_FILE" --quick 2>&1 | tee "${REPORT_FILE}.raw"

# Extrai score
SCORE=$(grep "hardening_index" "$REPORT_FILE" | awk '{print $2}')
WARNINGS=$(grep -c "^warning\[" "$REPORT_FILE" 2>/dev/null || echo "0")
SUGGESTIONS=$(grep -c "^suggestion\[" "$REPORT_FILE" 2>/dev/null || echo "0")

echo "Score: $SCORE | Warnings: $WARNINGS | Suggestions: $SUGGESTIONS"

# Compara com semana anterior se existir
if [ -f "$PREV_FILE" ]; then
    PREV_SCORE=$(grep "hardening_index" "$PREV_FILE" | awk '{print $2}')
    DELTA=$((SCORE - PREV_SCORE))

    if [ $DELTA -gt 0 ]; then
        TREND="📈 +${DELTA} (melhorou!)"
    elif [ $DELTA -lt 0 ]; then
        TREND="📉 ${DELTA} (PIOROU!)"
    else
        TREND="➡️ 0 (estável)"
    fi
else
    PREV_SCORE="N/A"
    TREND="🆕 Primeira auditoria"
fi

# Salva resumo JSON para dashboard
cat > "${REPORT_DIR}/latest-summary.json" << EOF
{
    "date": "$DATE",
    "score": $SCORE,
    "prev_score": ${PREV_SCORE:-0},
    "warnings": $WARNINGS,
    "suggestions": $SUGGESTIONS,
    "trend": "$TREND"
}
EOF

echo "Resumo salvo em ${REPORT_DIR}/latest-summary.json"

O script é simples de propósito. Não tenta fazer tudo — faz bem o que faz. Roda toda domingo às 3 da manhã via cron e gera um histórico semanal completo.

Checks Customizados Além do Lynis

O Lynis é ótimo, mas não cobre tudo que eu preciso. Adicionei verificações específicas para o meu cenário:

#!/bin/bash
# custom-checks.sh — Verificações específicas do meu servidor

FAILED_CHECKS=0

# 1. Verifica se UFW está ativo
if ! sudo ufw status | grep -q "Status: active"; then
    echo "❌ UFW DESATIVADO!"
    FAILED_CHECKS=$((FAILED_CHECKS + 1))
else
    echo "✅ UFW ativo"
fi

# 2. Verifica se Fail2ban está rodando
if ! systemctl is-active --quiet fail2ban; then
    echo "❌ Fail2ban PARADO!"
    FAILED_CHECKS=$((FAILED_CHECKS + 1))
else
    # Verifica quantos IPs estão banidos
    BANNED=$(sudo fail2ban-client status sshd | grep "Banned IP" | grep -oP '\d+')
    echo "✅ Fail2ban ativo (${BANNED} IPs banidos)"
fi

# 3. Verifica SSH - sem root login, sem password auth
SSH_CONFIG="/etc/ssh/sshd_config"
for OPT in "PermitRootLogin no" "PasswordAuthentication no" "PubkeyAuthentication yes"; do
    KEY=$(echo "$OPT" | awk '{print $1}')
    VAL=$(echo "$OPT" | awk '{print $2}')
    if ! grep -qE "^${KEY}\s+${VAL}" "$SSH_CONFIG"; then
        echo "❌ SSH: ${KEY} não está como ${VAL}"
        FAILED_CHECKS=$((FAILED_CHECKS + 1))
    else
        echo "✅ SSH: ${KEY}=${VAL}"
    fi
done

# 4. Verifica se ports inesperados estão abertos
EXPECTED_PORTS="22 80 443"
OPEN_PORTS=$(ss -tlnp | grep LISTEN | awk '{print $4}' | grep -oP ':\d+$' | sort -u | tr -d ':')
UNEXPECTED=""

for PORT in $OPEN_PORTS; do
    if ! echo "$EXPECTED_PORTS" | grep -qw "$PORT"; then
        UNEXPECTED="$UNEXPECTED $PORT"
    fi
done

if [ -n "$UNEXPECTED" ]; then
    echo "⚠️  Portas inesperadas abertas:$UNEXPECTED"
    FAILED_CHECKS=$((FAILED_CHECKS + 1))
else
    echo "✅ Apenas portas esperadas abertas"
fi

# 5. Verifica integridade de arquivos críticos
# (requer configuração prévia com AIDE)
if command -v aide &>/dev/null; then
    echo "Verificando integridade de arquivos..."
    sudo aide --check | tail -5
fi

# 6. Verifica updates de segurança pendentes
SEC_UPDATES=$(apt list --upgradable 2>/dev/null | grep -ci secur || echo "0")
if [ "$SEC_UPDATES" -gt 0 ]; then
    echo "⚠️  ${SEC_UPDATES} updates de segurança pendentes!"
    FAILED_CHECKS=$((FAILED_CHECKS + 1))
else
    echo "✅ Sem updates de segurança pendentes"
fi

echo ""
echo "========================================="
echo "Checks falharam: $FAILED_CHECKS"
echo "========================================="

exit $FAILED_CHECKS

Esses checks complementam o Lynis porque são específicos do meu servidor. O Lynis verifica boas práticas gerais; os checks customizados verificam se as minhas configurações específicas continuam intactas.

Código de auditoria executando no terminal Linux com verificações de segurança automatizadas

Dashboard de Score com Histórico

Ver números isolados não ajuda. O que importa é a tendência. Criei um script que gera um gráfico ASCII do histórico de scores:

#!/bin/bash
# score-history.sh — Visualiza evolução do score

REPORT_DIR="/var/log/lynis-reports"
echo "📊 HISTÓRICO DE SCORE — ÚLTIMAS 8 SEMANAS"
echo "============================================"
echo ""

for file in $(ls -t "$REPORT_DIR"/audit-2*.txt 2>/dev/null | head -8); do
    DATE=$(basename "$file" | sed 's/audit-//;s/\.txt//')
    SCORE=$(grep "hardening_index" "$file" | awk '{print $2}')

    if [ -n "$SCORE" ]; then
        BARS=$(printf '█%.0s' $(seq 1 $((SCORE / 5))))
        SPACES=$(printf '░%.0s' $(seq 1 $((20 - SCORE / 5))))
        echo "$DATE [$BARS$SPACES] $SCORE"
    fi
done

echo ""
echo "Target: 85+ | Acceptable: 75+ | Critical: <60"

A saída fica assim:

📊 HISTÓRICO DE SCORE — ÚLTIMAS 8 SEMANAS
============================================

2026-05-04 [████████████████░░░░] 82
2026-04-27 [███████████████░░░░░] 78
2026-04-20 [███████████████░░░░░] 78
2026-04-13 [█████████████░░░░░░░] 72
2026-04-06 [████████████░░░░░░░░] 65
2026-03-30 [█████████████░░░░░░░] 71

Target: 85+ | Acceptable: 75+ | Critical: <60

Ver a evolução de 62 para 82 em 6 semanas foi satisfatório. Ver que estagnou em 78 por duas semanas me motivou a investigar o que faltava.

Alertas Que Funcionam (Não Spam)

O maior inimigo de alertas é o alarm fatigue. Se toda semana você recebe "tudo ok", você para de ler. Se recebe 50 alertas, também para de ler. A solução: alertar apenas quando algo muda.

# No final do audit-security.sh, adicione:

send_alert() {
    local message="$1"

    # Via Telegram (substitua pelo seu bot token e chat_id)
    curl -s -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \
        -d chat_id="${TG_CHAT_ID}" \
        -d parse_mode="Markdown" \
        -d text="$message" &>/dev/null

    # Ou via email
    # echo "$message" | mail -s "[Security Audit] ${DATE}" admin@example.com
}

# Só envia alerta se:
# - Score caiu
# - Novos warnings apareceram
# - Checks customizados falharam

CUSTOM_FAILED=$(bash /opt/scripts/custom-checks.sh 2>&1 | tail -1 | grep -oP '\d+')

if [ "$DELTA" -lt 0 ] || [ "$CUSTOM_FAILED" -gt 0 ]; then
    ALERT="🚨 *AUDITORIA DE SEGURANÇA — ${DATE}*\n\n"
    ALERT+="Score: *${SCORE}/100* (${TREND})\n"
    ALERT+="Warnings: ${WARNINGS}\n"
    ALERT+="Checks customizados falharam: ${CUSTOM_FAILED}\n\n"

    if [ "$DELTA" -lt 0 ]; then
        ALERT+="⚠️ *SCORE CAIU ${DELTA} PONTOS!*\n"
        ALERT+="Verifique mudanças recentes no servidor.\n\n"
    fi

    ALERT+="Relatório completo: ${REPORT_FILE}"

    send_alert "$ALERT"
else
    # Log silencioso — sem spam
    echo "[$(date)] Score estável em ${SCORE}. Nenhum alerta enviado."
fi

A regra é simples: silêncio é bom sinal, alerta é ação necessária. Em 3 meses usando esse sistema, recebi alertas reais 4 vezes — e em todas, era algo que realmente precisava de atenção.

Resolvendo os Warnings Mais Comuns

Depois de rodar Lynis em dezenas de servidores, esses são os warnings que aparecem em praticamente todo mundo:

1. Kernel Hardening Desativado

# /etc/sysctl.d/99-hardening.conf

# Proteção contra buffer overflow
kernel.randomize_va_space = 2

# Desabilita IP forwarding (se não é router)
net.ipv4.ip_forward = 0

# Proteção contra SYN flood
net.ipv4.tcp_syncookies = 1

# Não responde a pings
net.ipv4.icmp_echo_ignore_all = 1

# Proteção contra IP spoofing
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Não aceita source routing
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0

# Aplica
sudo sysctl --system

2. Permissões em Arquivos Sensíveis

# Permissões restritivas em arquivos críticos
sudo chmod 600 /etc/shadow
sudo chmod 600 /etc/gshadow
sudo chmod 644 /etc/passwd
sudo chmod 644 /etc/group

# Remove SUID de binários que não precisam
sudo chmod u-s /usr/bin/chsh /usr/bin/chfn /usr/bin/passwd

# Verifica o que ainda tem SUID
find / -perm -4000 -type f 2>/dev/null

3. Automação de Updates de Segurança

# Instala unattended-upgrades
sudo apt install unattended-upgrades apt-listchanges -y

# Configura para instalar apenas updates de segurança
sudo dpkg-reconfigure -plow unattended-upgrades

# Verifica se está ativo
sudo unattended-upgrade --dry-run

🔥 Box Perrengue: O Dia Que o Audit Salvou Minha Vida

Numa sexta-feira às 23h, recebi um alerta do audit: "Score caiu de 82 para 71". O que mudou? Um colega tinha atualizado o Nginx às 18h e a atualização reverteu a config do server_tokens off, expôs a versão do servidor, e desabilitou o header X-Frame-Options por causa de um conflito de config. Nada crítico por si só — mas num servidor que já tinha honeypot e era alvo constante de scans, era porta aberta. O audit pegou em 3 horas o que teria ficado invisível por semanas.

Desafio: Quando foi a última vez que você verificou se suas configurações de segurança continuam como você deixou? Faça o teste. Roda sudo lynis audit system e veja a realidade.

Integração com a Pipeline de Segurança

Se você já seguiu os posts anteriores sobre Zero Trust com WireGuard e WAF com nftables, pode integrar tudo numa pipeline coesa:

#!/bin/bash
# security-pipeline.sh — Pipeline completa semanal

echo "🔐 PIPELINE DE SEGURANÇA — $(date)"
echo "========================================"

# 1. Auditoria Lynis
echo "[1/4] Executando auditoria Lynis..."
bash /opt/scripts/audit-security.sh

# 2. Checks customizados
echo "[2/4] Verificando configurações..."
bash /opt/scripts/custom-checks.sh

# 3. Verifica regras do firewall
echo "[3/4] Auditando firewall..."
sudo nft list ruleset > /var/log/nftables-rules-${DATE}.txt
UFW_STATUS=$(sudo ufw status verbose)
echo "$UFW_STATUS" >> /var/log/ufw-status-${DATE}.log

# 4. Verifica certificados SSL
echo "[4/4] Verificando certificados..."
DOMAINS="automente.com.br jarvis.alissonsouza.com.br"
for DOMAIN in $DOMAINS; do
    EXPIRY=$(echo | openssl s_client -servername "$DOMAIN" -connect "$DOMAIN":443 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
    if [ -n "$EXPIRY" ]; then
        EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s 2>/dev/null)
        NOW_EPOCH=$(date +%s)
        DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))

        if [ $DAYS_LEFT -lt 14 ]; then
            echo "🚨 $DOMAIN: certificado expira em ${DAYS_LEFT} dias!"
        else
            echo "✅ $DOMAIN: ${DAYS_LEFT} dias restantes"
        fi
    fi
done

echo ""
echo "Pipeline completa. Verifique os relatórios em /var/log/lynis-reports/"

Coloque tudo no cron e tenha uma fotografia semanal completa da saúde de segurança do seu servidor. Sem depender de memória, sem depender de "achar que está ok".

Cron Setup — Colocando Tudo em Piloto Automático

# /etc/cron.d/security-audit
# Auditoria completa toda domingo às 03:00
0 3 * * 0 root /opt/scripts/security-pipeline.sh >> /var/log/security-pipeline.log 2>&1

# Quick check diário — só verifica o essencial
0 6 * * * root /opt/scripts/custom-checks.sh >> /var/log/daily-check.log 2>&1

# Verifica certificados todo dia 1 do mês
0 8 1 * * root /opt/scripts/check-certs.sh >> /var/log/cert-check.log 2>&1

O check diário roda em menos de 10 segundos e verifica se nada crítico mudou. A auditoria completa de domingo leva uns 3 minutos e gera o relatório detalhado.

Checklist de Hardening Revisitado

Baseado em tudo que aprendi com auditorias recorrentes, aqui vai o checklist definitivo que sigo após formatar qualquer servidor:

  • ✅ SSH: key-only auth, sem root, porta não-padrão
  • ✅ Firewall: UFW ou nftables com deny-incoming default
  • ✅ Fail2ban: configurado para SSH, Nginx e Postfix
  • ✅ Updates automáticos de segurança (unattended-upgrades)
  • ✅ Kernel hardening via sysctl (repositório no Ansible que automatizo tudo)
  • ✅ Permissões restritivas em /etc/shadow, /etc/gshadow
  • ✅ Logs centralizados com rotação automática
  • ✅ Lynis rodando semanalmente com alertas condicionais
  • ✅ Monitoramento de integridade com AIDE ou Tripwire
  • ✅ Certificados SSL com renovação automática (Certbot)

Se quiser ver como automatizei grande parte desse checklist com Ansible, confira o post sobre dotfiles com GNU Stow e Ansible.

Quanto Custa Tudo Isso?

A beleza dessa stack é o preço:

  • Lynis: Gratuito (open-source)
  • Bash scripts: Seu tempo (umas 2-3 horas pra montar tudo)
  • AIDE: Gratuito (open-source)
  • Cron: Já vem no Linux
  • Telegram Bot: Gratuito

Total: R$ 0,00 + investimento em conhecimento. O mesmo conhecimento que te custa R$ 500+/mês em um serviço gerenciado de segurança.

Conclusão: Pare de Achar, Comece a Verificar

A mentalidade de "configurei uma vez, está seguro" é a falha mais comum em segurança de servidores. A realidade é que segurança é um processo contínuo, e sem auditorias regulares você está voando cego.

Com Lynis + scripts customizados + alertas condicionais, você transforma uma tarefa chata e esquecível em um processo automático que te avisa quando algo precisa de atenção. É como ter um funcionário que trabalha de graça, nunca tira folga, e só te incomoda quando é importante.

E você, qual parte da segurança do seu servidor nunca auditou? Me conta nos comentários — ou melhor, roda sudo lynis audit system agora e volta pra contar o score. Aposto que vai se surpreender.

Posts Similares