← Voltar ao Blog
· 30 min leitura ·

Hardening real de servidores Linux: 14 camadas que aplico em produção com código de automação

Guia avançado de hardening Linux baseado em infraestrutura real: SSH hardening + fail2ban + UFW + kernel sysctl + auditd + systemd sandboxing + nginx auto-fix + Redis bind + MongoDB auth + Postfix relay seguro + backup automatizado com Google Drive + restore orchestrado via Go + monitoramento com Conky — tudo com scripts e configs de produção.

#segurança#cibersegurança#linux#hardening#devops#defensivo#nginx#systemd#automação#cyber-security
Compartilhar

Não é checklist genérico do DigitalOcean. Aqui eu documento as 14 camadas de hardening que aplico em toda infraestrutura que monto — desde VPS de $5 até clusters com múltiplos serviços. Cada item vem com o código real que uso, explicado.

Se você tem um servidor exposto à internet, ele já está sendo escaneado. Bots automatizados testam SSH, portas abertas e configs default 24/7. A questão não é se você vai ser atacado — é quando.

Aviso Legal: Todo o conteúdo deste artigo é publicado exclusivamente para fins educacionais e de segurança defensiva. Configurações foram anonimizadas. Use apenas em infraestrutura própria ou com autorização explícita.

1. SSH Hardening — Superfície de ataque #1

O SSH é o vetor mais explorado. A configuração default do OpenSSH é insegura por design — permite root, senhas, forwarding e ciphers fracos.

# /etc/ssh/sshd_config
Port 2222                          # Porta não-padrão reduz 99% dos bots
PermitRootLogin no                 # NUNCA root via SSH
PasswordAuthentication no          # Somente chaves Ed25519
PubkeyAuthentication yes
MaxAuthTries 3
MaxSessions 2
LoginGraceTime 30
ClientAliveInterval 300
ClientAliveCountMax 2
AllowUsers deploy                  # Whitelist explícita

# Ciphers modernos — descarte tudo pré-2020
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group16-sha512
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

# Desabilitar tudo que não é necessário
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no

Gerar chave Ed25519 com 100 rounds KDF

ssh-keygen -t ed25519 -a 100 -C "deploy@prod-$(date +%Y)"
ssh-copy-id -p 2222 deploy@seu-servidor

# NUNCA desconecte a sessão atual antes de testar
ssh -p 2222 deploy@seu-servidor  # nova janela!

Alerta de login em tempo real

Toda conexão SSH gera notificação:

cat << 'SCRIPT' | sudo tee /usr/local/bin/login_alert.sh
#!/bin/bash
BODY="Login: $PAM_USER from $PAM_RHOST on $(hostname) at $(date)"
echo "$BODY" | mail -s "SSH Alert: $(hostname)" admin@yourdomain.com

# Webhook (Discord/Slack/Telegram)
curl -s -X POST "$WEBHOOK_URL" \
  -H "Content-Type: application/json" \
  -d "{\"content\": \"$BODY\"}" >/dev/null 2>&1 &
SCRIPT

sudo chmod +x /usr/local/bin/login_alert.sh
echo "session optional pam_exec.so /usr/local/bin/login_alert.sh" \
    | sudo tee -a /etc/pam.d/sshd

2. Firewall (UFW) — Deny all, allow explicit

# Reset e configuração limpa
sudo ufw --force reset
sudo ufw default deny incoming
sudo ufw default allow outgoing

# Apenas o necessário
sudo ufw allow 2222/tcp comment "SSH"
sudo ufw allow 80/tcp   comment "HTTP"
sudo ufw allow 443/tcp  comment "HTTPS"

# Rate limit no SSH (anti-brute force no kernel)
sudo ufw limit 2222/tcp

# Ativar
sudo ufw --force enable
sudo ufw status verbose

iptables avançado — conntrack + rate limiting

# Limite de 6 conexões novas por minuto por IP no SSH
sudo iptables -A INPUT -p tcp --dport 2222 -m conntrack --ctstate NEW \
    -m recent --set --name SSH
sudo iptables -A INPUT -p tcp --dport 2222 -m conntrack --ctstate NEW \
    -m recent --update --seconds 60 --hitcount 6 --name SSH -j DROP

# Drop de pacotes inválidos
sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

# Persistir regras
sudo iptables-save | sudo tee /etc/iptables/rules.v4

3. Fail2Ban — Banimento inteligente com integração UFW

sudo apt install fail2ban -y

cat << 'EOF' | sudo tee /etc/fail2ban/jail.local
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3
banaction = ufw                    # Integra com UFW diretamente
ignoreip = 127.0.0.1/8

[sshd]
enabled = true
port = 2222
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 86400                    # 24h no primeiro ban

[nginx-http-auth]
enabled = true
port = http,https
filter = nginx-http-auth
logpath = /var/log/nginx/error.log
maxretry = 5

[nginx-limit-req]
enabled = true
port = http,https
filter = nginx-limit-req
logpath = /var/log/nginx/error.log
maxretry = 10
findtime = 120
bantime = 7200                     # 2h

[nginx-botsearch]
enabled = true
port = http,https
filter = nginx-botsearch
logpath = /var/log/nginx/access.log
maxretry = 4
findtime = 60
bantime = 86400
EOF

sudo systemctl enable --now fail2ban

# Verificar
sudo fail2ban-client status sshd
# Output: Currently banned: 47, Total banned: 1,283  ← em 30 dias

4. Kernel Hardening — sysctl em produção

Cada parâmetro aqui tem um motivo. Não é copy-paste — é medida de defesa contra classes específicas de ataque:

cat << 'EOF' | sudo tee /etc/sysctl.d/99-hardening.conf
# === Anti-Spoofing ===
net.ipv4.conf.all.rp_filter = 1         # Reverse path filtering
net.ipv4.conf.default.rp_filter = 1

# === Source Routing (desabilitar OBRIGATÓRIO) ===
net.ipv4.conf.all.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0

# === ICMP Redirects (vetor de MitM) ===
net.ipv4.conf.all.accept_redirects = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0

# === SYN Flood Protection ===
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_synack_retries = 2

# === IPv6 (desabilitar se não usar) ===
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1

# === Logging de pacotes impossíveis ===
net.ipv4.conf.all.log_martians = 1

# === ASLR máximo ===
kernel.randomize_va_space = 2

# === Restringir dmesg e kernel pointers ===
kernel.dmesg_restrict = 1
kernel.kptr_restrict = 2

# === Desabilitar Magic SysRq ===
kernel.sysrq = 0

# === Hardlinks/Symlinks protection ===
fs.protected_hardlinks = 1
fs.protected_symlinks = 1

# === Performance (bonus para servers web) ===
net.core.somaxconn = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.ip_local_port_range = 1024 65535
EOF

sudo sysctl -p /etc/sysctl.d/99-hardening.conf

5. Systemd Service Hardening — Sandbox de verdade

Todo serviço que rodo em produção usa sandboxing via systemd. Não basta Restart=always — é preciso limitar o que o processo pode fazer:

# /etc/systemd/system/myapp.service
[Unit]
Description=Production API Service
After=network.target mongod.service redis-server.service
Requires=mongod.service redis-server.service

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/opt/myapp
EnvironmentFile=/opt/myapp/.env
ExecStart=/opt/myapp/.venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4
Restart=always
RestartSec=5

# === HARDENING (o que 90% das pessoas ignora) ===
NoNewPrivileges=true               # Processo não pode escalar privilégios
ProtectSystem=strict               # /usr, /boot, /etc são read-only
ProtectHome=true                   # /home inacessível
ReadWritePaths=/opt/myapp/data     # Apenas o diretório de dados
PrivateTmp=true                    # /tmp isolado por processo
PrivateDevices=true                # Sem acesso a /dev
ProtectKernelTunables=true         # Sem alterar sysctl
ProtectControlGroups=true          # Sem alterar cgroups
ProtectKernelModules=true          # Sem carregar módulos

# === Limites ===
LimitNOFILE=65535
LimitNPROC=4096

# === Logging ===
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

[Install]
WantedBy=multi-user.target

Múltiplos serviços orquestrados

Em produção real, não é um serviço isolado — são vários interdependentes:

# API principal
sudo systemctl enable --now myapp-api.service

# Worker assíncrono (Celery)
sudo systemctl enable --now myapp-worker.service

# Scheduler (Celery Beat)
sudo systemctl enable --now myapp-beat.service

# Frontend (SSR)
sudo systemctl enable --now myapp-portal.service

# Verificar tudo de uma vez
for svc in myapp-api myapp-worker myapp-beat myapp-portal nginx mongod redis-server; do
    STATUS=$(systemctl is-active $svc)
    [ "$STATUS" = "active" ] && echo "  ✓ $svc: ATIVO" || echo "  ✗ $svc: $STATUS"
done

6. Redis Hardening — Bind localhost + LRU

Redis nunca deve ser exposto:

REDIS_CONF="/etc/redis/redis.conf"

# Bind somente localhost
sudo sed -i 's/^bind .*/bind 127.0.0.1 ::1/' "$REDIS_CONF"

# Daemonize
sudo sed -i 's/^daemonize no/daemonize yes/' "$REDIS_CONF"

# Eviction policy LRU (evita crash por memória)
echo "maxmemory 256mb" | sudo tee -a "$REDIS_CONF"
echo "maxmemory-policy allkeys-lru" | sudo tee -a "$REDIS_CONF"

# Desabilitar comandos perigosos
echo 'rename-command FLUSHDB ""' | sudo tee -a "$REDIS_CONF"
echo 'rename-command FLUSHALL ""' | sudo tee -a "$REDIS_CONF"
echo 'rename-command CONFIG ""' | sudo tee -a "$REDIS_CONF"
echo 'rename-command DEBUG ""' | sudo tee -a "$REDIS_CONF"

sudo systemctl restart redis-server
redis-cli ping  # PONG

7. MongoDB Hardening — Auth + Bind + Network

# /etc/mongod.conf
cat << 'EOF' | sudo tee /etc/mongod.conf
storage:
  dbPath: /var/lib/mongodb
  journal:
    enabled: true

systemLog:
  destination: file
  logAppend: true
  path: /var/log/mongodb/mongod.log

net:
  port: 27017
  bindIp: 127.0.0.1              # NUNCA 0.0.0.0

security:
  authorization: enabled          # Auth obrigatória
EOF

# Criar admin user
mongosh --eval '
  use admin;
  db.createUser({
    user: "admin",
    pwd: passwordPrompt(),
    roles: ["root"]
  });
'

sudo systemctl restart mongod

# Health check com retry loop
for i in $(seq 1 15); do
    if mongosh --quiet --eval "db.runCommand({ping:1})" 2>/dev/null; then
        echo "MongoDB healthy"
        break
    fi
    echo "Tentativa $i/15..."
    sleep 2
done

8. Auditoria com auditd — Quem fez o quê

sudo apt install auditd -y

cat << 'EOF' | sudo tee /etc/audit/rules.d/hardening.rules
# Identity changes
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/sudoers -p wa -k sudoers

# SSH config changes
-w /etc/ssh/sshd_config -p wa -k sshd_config

# Cron modifications
-w /etc/crontab -p wa -k cron
-w /var/spool/cron/ -p wa -k cron

# Root commands
-a always,exit -F arch=b64 -S execve -F euid=0 -k root_commands

# Kernel modules
-w /sbin/insmod -p x -k kernel_modules
-w /sbin/modprobe -p x -k kernel_modules

# Network config changes
-w /etc/hosts -p wa -k network
-w /etc/nginx/ -p wa -k nginx_config

# Systemd service manipulation
-a always,exit -F arch=b64 -S execve -F path=/usr/bin/systemctl -k systemd
EOF

sudo systemctl restart auditd

# Consultar: quem editou /etc/passwd nas últimas 24h?
sudo ausearch -k identity --start recent

9. Postfix Relay Seguro — SASL + TLS obrigatório

Para servidores que enviam email (alertas, notificações):

# /etc/postfix/main.cf (trecho relevante)
smtpd_banner = $myhostname ESMTP
biff = no
compatibility_level = 3.6

# TLS obrigatório para envio
smtp_tls_security_level = encrypt
smtp_tls_wrappermode = yes
smtp_tls_CApath = /etc/ssl/certs

# SASL auth para relay externo
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_security_options = noanonymous

# Relay via SMTP externo (porta 465/TLS)
relayhost = [smtp.provider.com]:465

# Restringir relay
smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_unauth_destination
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
# Configurar credenciais
echo "[smtp.provider.com]:465 user@domain.com:password" | sudo tee /etc/postfix/sasl_passwd
sudo postmap /etc/postfix/sasl_passwd
sudo chmod 600 /etc/postfix/sasl_passwd /etc/postfix/sasl_passwd.db
sudo systemctl restart postfix

# Testar
echo "Test" | mail -s "Hardening Alert Test" admin@yourdomain.com

10. Automação de Setup — Script one-shot

Automatizo toda a instalação com um setup script que provê o servidor inteiro:

#!/bin/bash
set -euo pipefail

echo "=== Setup Production Server ==="

# 1. System dependencies
apt-get update && apt-get install -y \
    curl wget git build-essential certbot python3-certbot-nginx \
    logrotate fail2ban auditd ufw

# 2. Node.js 22 LTS
curl -fsSL https://deb.nodesource.com/setup_22.x | bash -
apt-get install -y nodejs

# 3. MongoDB 8.0
curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | \
    gpg --dearmor -o /usr/share/keyrings/mongodb-server-8.0.gpg
echo "deb [signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg] \
    https://repo.mongodb.org/apt/ubuntu $(lsb_release -cs)/mongodb-org/8.0 multiverse" \
    | tee /etc/apt/sources.list.d/mongodb-org-8.0.list
apt-get update && apt-get install -y mongodb-org

# 4. Redis
apt-get install -y redis-server

# 5. Nginx
apt-get install -y nginx

# 6. Python venv + secrets gerados
python3 -m venv /opt/myapp/.venv
SECRET_KEY=$(openssl rand -hex 32)
JWT_SECRET=$(openssl rand -hex 32)

cat > /opt/myapp/.env << ENV
SECRET_KEY=$SECRET_KEY
JWT_SECRET_KEY=$JWT_SECRET
JWT_ALGORITHM=HS256
JWT_ACCESS_TOKEN_EXPIRE_MINUTES=30
MONGODB_URL=mongodb://localhost:27017
REDIS_URL=redis://localhost:6379/0
ENV

# 7. Deploy systemd services
# (instala cada um dos .service files)
systemctl daemon-reload
for svc in myapp-api myapp-worker myapp-beat; do
    systemctl enable --now "$svc"
done

echo "=== Setup completo ==="

11. Backup automatizado com Google Drive

Backup diário às 3h com upload para Google Drive e retenção de 30 dias:

# Cron
echo "0 3 * * * /opt/myapp/backup.sh --quiet >> /var/log/backup.log 2>&1" \
    | sudo crontab -

# O que o script faz:
# 1. mongodump → /tmp/backup/mongodb/
# 2. redis-cli BGSAVE → copia RDB
# 3. Copia configs: .env, nginx.conf, systemd units
# 4. rsync do source code (--exclude __pycache__, node_modules, .git)
# 5. Gera metadata JSON com versões e timestamps
# 6. tar.gz com timestamp
# 7. Upload para Google Drive via API
# 8. Remove backups locais > 30 dias

Restore com opções seletivas

# Restore completo
./restore.sh --from /backups/backup-2026-03-08.tar.gz

# Restore seletivo
./restore.sh --skip-code --skip-redis   # só banco e configs
./restore.sh --skip-mongo --skip-config  # só código

# Sempre faz backup do .env atual antes de sobrescrever

12. Logrotate — Manter disco limpo

cat << 'EOF' | sudo tee /etc/logrotate.d/myapp
/var/log/myapp*.log {
    daily
    missingok
    rotate 14
    compress
    delaycompress
    notifempty
    create 0640 root root
    postrotate
        systemctl reload myapp-api 2>/dev/null || true
    endscript
}
EOF

13. Monitoramento ativo

Verificação de saúde automatizada

#!/bin/bash
# /usr/local/bin/health_check.sh — roda via cron a cada 5min

FAILED=""

# Verificar serviços
for svc in nginx mongod redis-server myapp-api; do
    if ! systemctl is-active --quiet "$svc"; then
        FAILED+="$svc "
        systemctl restart "$svc"  # auto-restart
    fi
done

# Verificar portas
for PORT in 80 443 8000 27017 6379; do
    if ! ss -tlnp | grep -q ":$PORT "; then
        FAILED+="port:$PORT "
    fi
done

# HTTP smoke test
for ENDPOINT in "/api/health" "/"; do
    CODE=$(curl -s -o /dev/null -w '%{http_code}' "http://127.0.0.1:8000$ENDPOINT")
    [ "$CODE" != "200" ] && FAILED+="http:$ENDPOINT($CODE) "
done

# Redis ping
redis-cli ping | grep -q PONG || FAILED+="redis-ping "

# Alertar se algo falhou
if [ -n "$FAILED" ]; then
    echo "FALHA: $FAILED" | mail -s "ALERT: $(hostname)" admin@yourdomain.com
fi

Disk e memory watch

# Alerta se disco > 85%
DISK_USAGE=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
[ "$DISK_USAGE" -gt 85 ] && echo "Disk at ${DISK_USAGE}%!" | mail -s "DISK ALERT" admin@yourdomain.com

# Alerta se RAM > 90%
MEM_USAGE=$(free | awk '/Mem/{printf("%.0f"), $3/$2*100}')
[ "$MEM_USAGE" -gt 90 ] && echo "RAM at ${MEM_USAGE}%!" | mail -s "MEM ALERT" admin@yourdomain.com

14. Cleanup seguro antes de reprovisionar

Quando preciso reprovisionar um servidor, faço cleanup completo:

#!/bin/bash
set -euo pipefail

# 1. Parar serviços
for svc in myapp-api myapp-worker myapp-beat mongod redis-server nginx; do
    systemctl stop "$svc" 2>/dev/null || true
    systemctl disable "$svc" 2>/dev/null || true
done

# 2. Matar processos órfãos
pkill -f 'uvicorn' || true
for PID in $(pgrep -x nginx); do kill -9 "$PID"; done

# 3. PRESERVAR certs Let's Encrypt (evitar rate limit)
mkdir -p /tmp/letsencrypt-saved
cp -a /etc/letsencrypt/live /tmp/letsencrypt-saved/ 2>/dev/null || true
cp -a /etc/letsencrypt/archive /tmp/letsencrypt-saved/ 2>/dev/null || true

# 4. Purge de pacotes
apt-get purge -y nginx* mongodb* redis* || true
apt-get autoremove -y

# 5. Remover dados
rm -rf /var/lib/mongodb /var/log/mongodb
rm -rf /var/lib/redis /var/log/redis

# 6. Limpar systemd
rm -f /etc/systemd/system/myapp-*.service
systemctl daemon-reload

echo "=== Cleanup completo — servidor pronto para reprovisionar ==="

Checklist final — 14 camadas

#CamadaStatus
1SSH: porta custom, root off, somente Ed25519, ciphers modernos
2UFW: deny all, allow explícito, limit SSH
3Fail2Ban: SSH 24h ban, nginx filter, UFW integration
4Kernel: sysctl anti-spoof, SYN flood, ASLR, kptr_restrict
5Systemd: sandbox com NoNewPrivileges, ProtectSystem, PrivateTmp
6Redis: bind 127.0.0.1, LRU, comandos perigosos desabilitados
7MongoDB: auth enabled, bind localhost
8Auditd: identity, SSH config, root commands, kernel modules
9Postfix: SASL + TLS obrigatório, relay restrito
10Setup automatizado: one-shot script, secrets gerados
11Backup: mongodump, Redis RDB, configs, Google Drive, retenção 30d
12Logrotate: rotação diária, compressão, 14 dias
13Monitoramento: health check, disk/mem alerts, auto-restart
14Cleanup: preserva certs, purge completo, reprovisioning

Conclusão

Segurança é infraestrutura como código. Cada camada aqui é um script que pode ser versionado, testado e replicado. O objetivo não é perfeição — é reduzir a superfície de ataque a um mínimo aceitável e ter visibilidade sobre o que acontece no seu servidor.

A diferença entre um servidor “configurado” e um servidor hardened está nos detalhes: systemd sandboxing, Redis com comandos renomeados, auditd monitorando cada execve como root, backups que de fato funcionam quando você precisa.

Este artigo documenta práticas de segurança defensiva aplicadas em infraestrutura real. Todas as configurações foram anonimizadas. Use estas técnicas apenas em servidores próprios ou sob autorização.


Rafael Cavalcanti da Silva — Fullstack Developer & Security Specialist rafaelroot.com

Rafael Cavalcanti da Silva — Fullstack Developer & Security Specialist rafaelroot.com