Segurança de Servidor Linux: SSH Keys, UFW, Fail2Ban e Hardening
Configuração completa de segurança em VPS Ubuntu: SSH key com Ed25519, desativar root login, UFW com regras granulares, Fail2Ban com jail para SSH e Nginx, Docker e UFW (o problema das iptables), e unattended-upgrades.
Ao provisionar um VPS, ele entra no ar com o IP público visível ao mundo. Em questão de minutos, bots automatizados começam a tentar login SSH com senhas comuns. Logs do /var/log/auth.log em servidores sem hardening mostram centenas de tentativas por hora. Este artigo cobre a sequência completa de hardening para um servidor Ubuntu recém-criado.
O hardening não precisa ser complexo para ser eficaz. Os 5 passos abaixo cobrem a grande maioria dos vetores de ataque em servidores VPS: eliminar a conta root remota, substituir senhas por criptografia de chave pública, restringir portas expostas, banir IPs que fazem brute force, e manter o sistema atualizado automaticamente. Execute-os na ordem apresentada — preferencialmente logo após o provisionamento.
1. Crie um Usuário Não-Root
# Nunca opere como root em produção
# Crie um usuário dedicado para a aplicação
adduser deploy
usermod -aG sudo deploy
# Copie a chave SSH do root para o novo usuário
mkdir -p /home/deploy/.ssh
cp /root/.ssh/authorized_keys /home/deploy/.ssh/
chown -R deploy:deploy /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys2. SSH com Chave Ed25519 (sem senhas)
A chave Ed25519 é preferida sobre RSA 2048: chaves menores, mais rápidas de assinar, resistentes a ataques de canal lateral e amplamente suportadas. Desativar o PasswordAuthentication elimina completamente ataques de força bruta baseados em senha — um atacante sem a chave privada não tem como entrar, independente de quantas tentativas faça.
# Na sua máquina LOCAL: gere uma chave Ed25519 (mais moderna e segura que RSA 2048)
ssh-keygen -t ed25519 -C "deploy@meu-projeto" -f ~/.ssh/meu-projeto_ed25519
# Copie a chave pública para o servidor
ssh-copy-id -i ~/.ssh/meu-projeto_ed25519.pub deploy@IP_DO_SERVIDOR
# No servidor: configure o SSH
sudo nano /etc/ssh/sshd_config
# Altere ou confirme estas linhas:
# PermitRootLogin no # Nunca root via SSH
# PasswordAuthentication no # Apenas chave criptográfica
# PubkeyAuthentication yes
# Port 2222 # Opcional: porta não-padrão (reduz bots)
# AllowUsers deploy # Apenas o usuário deploy pode conectar
sudo systemctl reload ssh
# ATENÇÃO: teste a conexão em um terminal separado ANTES de fechar o atual
# ssh -i ~/.ssh/meu-projeto_ed25519 deploy@IP -p 22223. Firewall com UFW
O UFW (Uncomplicated Firewall) é uma interface de linha de comando para o iptables, muito mais simples de usar. A regra fundamental é deny incoming por padrão: tudo que não for explicitamente permitido é bloqueado. Abra apenas as portas que a aplicação realmente precisa — nenhuma porta de banco de dados, Redis ou serviço interno deve ser exposta externamente.
# UFW: bloqueia tudo por padrão, abre apenas o necessário
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Se mudou a porta SSH, use o número correto!
sudo ufw allow 2222/tcp comment 'SSH'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
# Portas internas (banco, redis): NUNCA abra para 0.0.0.0
# Acesse apenas via SSH tunnel ou rede interna do Docker
sudo ufw enable # Confirme com 'y'
sudo ufw status verboseDocker e UFW não se falam bem. O Docker modifica as iptables diretamente, contornando as regras do UFW. Se você expõe uma porta com ports: ['5432:5432'], o PostgreSQL fica acessível publicamente — ignorando o ufw deny. A solução: bind apenas no loopback ports: ['127.0.0.1:5432:5432'] para serviços que não devem ser acessados externamente. Para serviços entre containers, use a rede interna do Docker sem mapear portas.
4. Fail2Ban: Banimento Automático
O Fail2Ban monitora arquivos de log em tempo real e bane IPs que excedem um número de tentativas falhas. Para SSH, 3 senhas erradas equivalem a um ataque de força bruta — um usuário legítimo não erra a senha 3 vezes seguidas. O ban de 24 horas para SSH torna ataques distribuídos exaustivamente lentos. A configuração para Nginx também protege contra tentativas de enumeração de endpoints via rate limiting no nível do log.
sudo apt-get install -y fail2ban
# Crie uma configuração local (nunca edite jail.conf diretamente)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.local[DEFAULT]
# Tempo de banimento padrão: 1 hora
bantime = 3600
# Janela de observação: 10 minutos
findtime = 600
# Número de falhas permitidas antes do ban
maxretry = 5
# Ignorar IPs locais
ignoreip = 127.0.0.1/8 ::1
[sshd]
enabled = true
port = 2222 # Sua porta SSH personalizada
logpath = %(sshd_log)s
maxretry = 3 # SSH: 3 tentativas — mais rigoroso que o padrão
bantime = 86400 # Ban por 24h em caso de falha no SSH
[nginx-limit-req]
enabled = true
port = http,https
logpath = /var/log/nginx/error.log
maxretry = 10sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Verificar status e IPs banidos
sudo fail2ban-client status sshd
sudo fail2ban-client status nginx-limit-req
# Desbanir um IP (caso você mesmo seja banido)
sudo fail2ban-client set sshd unbanip SEU_IP5. Atualizações Automáticas de Segurança
Vulnerabilidades em pacotes do sistema (OpenSSL, Kernel, SSH daemon) são a causa de muitas violações de segurança em servidores. O unattended-upgrades aplica automaticamente apenas patches de segurança — sem atualizar versões major que poderiam quebrar a aplicação. A janela entre a divulgação de uma CVE e sua exploração em massa é frequentemente de horas. Com atualizações automáticas, seu servidor é patcheado antes de você acordar.
sudo apt-get install -y unattended-upgrades
sudo dpkg-reconfigure --priority=low unattended-upgrades
# Confirme que está ativado
sudo systemctl status unattended-upgrades
# O arquivo de configuração fica em:
# /etc/apt/apt.conf.d/50unattended-upgrades
# Por padrão, aplica apenas atualizações de segurança — não quebra o sistemaVerifique periodicamente o log /var/log/auth.log para entender a atividade no servidor: sudo grep 'Invalid user\|Failed password\|Accepted publickey' /var/log/auth.log | tail -50. Se você ver centenas de tentativas de IPs diferentes, o Fail2Ban está funcionando — eles estão sendo banidos após 3 falhas.
Conclusão
Os 5 passos — usuário não-root, SSH com Ed25519 sem senha, UFW bloqueando tudo exceto o necessário, Fail2Ban banindo brute force, e atualizações automáticas de segurança — cobrem a superfície de ataque mais comum em servidores expostos à internet. O Docker requer atenção especial: sempre use bind no loopback (127.0.0.1) para portas que não devem ser públicas, pois o Docker contorna o UFW via iptables direto.