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.

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.confsem avisar? - Deixou
PermitRootLogin yesporque “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:
- Roda a auditoria completa
- Extrai o score e os warnings
- Compara com a auditoria anterior
- Gera relatório em HTML
- 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.

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 headerX-Frame-Optionspor 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 systeme 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.
