Certificate management: renovação automática com Let's Encrypt
1. Fundamentos do Let's Encrypt e Certbot
O Let's Encrypt é uma autoridade certificadora gratuita que automatiza a emissão e renovação de certificados TLS/SSL através do protocolo ACME (Automatic Certificate Management Environment). O Certbot é o cliente oficial desenvolvido pela Electronic Frontier Foundation (EFF) que implementa esse protocolo em sistemas Unix-like.
Instalação do Certbot
A instalação moderna recomendada utiliza o snap:
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
Para distribuições baseadas em Debian/Ubuntu via apt:
sudo apt update
sudo apt install certbot python3-certbot-nginx
Para RHEL/CentOS via yum:
sudo yum install epel-release
sudo yum install certbot python3-certbot-nginx
Modos de Validação ACME
O protocolo ACME oferece três métodos principais de validação de domínio:
- HTTP-01: Verifica a criação de um arquivo em
/.well-known/acme-challenge/no servidor web - DNS-01: Requer registro TXT no DNS do domínio (ideal para wildcards)
- TLS-ALPN-01: Utiliza uma conexão TLS na porta 443 com um certificado de desafio
Para scripts de automação, o modo HTTP-01 é o mais simples, desde que o servidor web esteja rodando na porta 80.
2. Estrutura de Diretórios e Arquivos de Configuração
Após a primeira emissão, o Certbot organiza os certificados em:
/etc/letsencrypt/live/seudominio.com/
├── cert.pem # Certificado do domínio
├── chain.pem # Cadeia de certificados intermediários
├── fullchain.pem # cert.pem + chain.pem combinados
└── privkey.pem # Chave privada (protegida)
Os logs são armazenados em:
/var/log/letsencrypt/
└── letsencrypt.log
Para configurações persistentes, crie o arquivo /etc/letsencrypt/cli.ini:
# Configuração global do Certbot
server = https://acme-v02.api.letsencrypt.org/directory
email = admin@seudominio.com
agree-tos = true
non-interactive = true
3. Script de Renovação Automática com Bash
O script central renew_certs.sh gerencia todo o processo de renovação:
#!/bin/bash
# renew_certs.sh - Script de renovação automática de certificados Let's Encrypt
# Configurações
LOGFILE="/var/log/letsencrypt/renew.log"
LOCKFILE="/var/run/certbot-renew.lock"
SERVICES=("nginx" "postfix" "dovecot")
ADMIN_EMAIL="admin@seudominio.com"
# Função para logging estruturado
log() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[${timestamp}] [${level}] ${message}" >> "${LOGFILE}"
}
# Prevenção de execução simultânea
exec 200>"${LOCKFILE}"
flock -n 200 || {
log "ERROR" "Outra instância do script está em execução"
exit 1
}
# Execução da renovação
log "INFO" "Iniciando renovação dos certificados"
RENEW_OUTPUT=$(certbot renew --quiet --non-interactive 2>&1)
RENEW_EXIT_CODE=$?
if [ ${RENEW_EXIT_CODE} -eq 0 ]; then
log "INFO" "Renovação concluída com sucesso"
# Verificar se houve renovação real
if echo "${RENEW_OUTPUT}" | grep -q "No renewals were attempted"; then
log "INFO" "Nenhum certificado precisou ser renovado"
else
log "INFO" "Certificados foram renovados"
# Recarregar serviços
for service in "${SERVICES[@]}"; do
if systemctl is-active --quiet "${service}"; then
systemctl reload "${service}"
log "INFO" "Serviço ${service} recarregado"
fi
done
fi
else
log "ERROR" "Falha na renovação. Código de saída: ${RENEW_EXIT_CODE}"
log "ERROR" "Saída: ${RENEW_OUTPUT}"
exit 1
fi
4. Notificações e Alertas no Script
Adicione funções de notificação para monitoramento proativo:
# Função para enviar e-mail de alerta
send_email_alert() {
local subject="$1"
local body="$2"
echo "${body}" | mail -s "${subject}" "${ADMIN_EMAIL}"
}
# Função para notificação via webhook Slack
send_slack_notification() {
local message="$1"
local webhook_url="https://hooks.slack.com/services/SEU/WEBHOOK/URL"
curl -s -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"${message}\"}" \
"${webhook_url}" > /dev/null 2>&1
}
# Função para notificação via Telegram
send_telegram_notification() {
local message="$1"
local bot_token="SEU_BOT_TOKEN"
local chat_id="SEU_CHAT_ID"
curl -s -X POST \
"https://api.telegram.org/bot${bot_token}/sendMessage" \
-d "chat_id=${chat_id}&text=${message}" > /dev/null 2>&1
}
# Exemplo de uso no script principal
if [ ${RENEW_EXIT_CODE} -ne 0 ]; then
send_email_alert "Falha na renovação SSL" "Detalhes: ${RENEW_OUTPUT}"
send_slack_notification ":x: Falha na renovação SSL em $(hostname)"
fi
5. Pós-renovação: Recarga de Serviços
A validação da integridade do certificado renovado é crucial:
# Função para validar certificado renovado
validate_certificate() {
local domain="$1"
local cert_path="/etc/letsencrypt/live/${domain}/fullchain.pem"
if [ ! -f "${cert_path}" ]; then
log "ERROR" "Certificado não encontrado: ${cert_path}"
return 1
fi
# Verificar data de expiração
local expiry_date=$(openssl x509 -enddate -noout -in "${cert_path}" | cut -d= -f2)
local expiry_epoch=$(date -d "${expiry_date}" +%s)
local current_epoch=$(date +%s)
local days_remaining=$(( (expiry_epoch - current_epoch) / 86400 ))
log "INFO" "Certificado ${domain} expira em ${days_remaining} dias"
if [ ${days_remaining} -lt 30 ]; then
log "WARN" "Certificado ${domain} expirará em menos de 30 dias"
fi
}
# Recarga segura de serviços usando Docker
reload_docker_service() {
local container="$1"
local service="$2"
docker exec "${container}" systemctl reload "${service}" 2>/dev/null || \
docker exec "${container}" service "${service}" reload 2>/dev/null
}
# Exemplo de recarga para múltiplos serviços
for domain in $(ls /etc/letsencrypt/live/); do
validate_certificate "${domain}"
done
systemctl reload nginx
systemctl reload postfix
6. Agendamento com Cron e Systemd Timers
Configuração com Cron
Agende a execução diária do script:
# Editar crontab: crontab -e
# Executar todos os dias às 03:00
0 3 * * * /usr/local/bin/renew_certs.sh
# Alternativa com redirecionamento de saída
0 3 * * * /usr/local/bin/renew_certs.sh >> /var/log/certbot-cron.log 2>&1
Configuração com Systemd Timer
Para maior controle, crie um service e timer do systemd:
# /etc/systemd/system/certbot-renew.service
[Unit]
Description=Certbot Renewal Service
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/renew_certs.sh
User=root
Group=root
StandardOutput=journal
StandardError=journal
# /etc/systemd/system/certbot-renew.timer
[Unit]
Description=Daily Certbot Renewal Timer
Requires=certbot-renew.service
[Timer]
OnCalendar=daily
RandomizedDelaySec=3600
Persistent=true
[Install]
WantedBy=timers.target
Ative e inicie o timer:
sudo systemctl daemon-reload
sudo systemctl enable certbot-renew.timer
sudo systemctl start certbot-renew.timer
7. Casos Especiais e Resolução de Problemas
Renovação Forçada e Testes
# Teste de renovação (dry-run)
certbot renew --dry-run
# Renovação forçada de um domínio específico
certbot renew --force-renewal --cert-name seudominio.com
# Listar todos os certificados gerenciados
certbot certificates
Script de Rollback Automático
#!/bin/bash
# rollback_certs.sh - Script de rollback em caso de falha
BACKUP_DIR="/var/backups/letsencrypt"
DATE=$(date +%Y%m%d_%H%M%S)
# Criar backup antes da renovação
backup_certificates() {
mkdir -p "${BACKUP_DIR}"
tar -czf "${BACKUP_DIR}/certs_${DATE}.tar.gz" /etc/letsencrypt/live/
}
# Restaurar backup
restore_certificates() {
local latest_backup=$(ls -t ${BACKUP_DIR}/certs_*.tar.gz | head -1)
if [ -n "${latest_backup}" ]; then
tar -xzf "${latest_backup}" -C /
log "INFO" "Certificados restaurados de: ${latest_backup}"
fi
}
# Verificar limites de taxa (rate limits)
check_rate_limits() {
local remaining=$(curl -s https://acme-v02.api.letsencrypt.org/directory | \
python3 -c "import sys,json; print(json.load(sys.stdin).get('newOrder',''))" 2>/dev/null)
if [ -z "${remaining}" ]; then
log "WARN" "Não foi possível verificar rate limits"
fi
}
Resolução de Problemas Comuns
- Porta 80 ocupada: Use
certbot renew --standalone --pre-hook "systemctl stop nginx" --post-hook "systemctl start nginx" - DNS pendente: Verifique propagação com
dig +short TXT _acme-challenge.seudominio.com - Renovação de wildcards: Requer plugin DNS (ex:
certbot-dns-cloudflare)
Referências
- Documentação Oficial do Certbot — Guia completo de instalação, configuração e uso do cliente ACME oficial
- Let's Encrypt - Como Funciona — Explicação detalhada do protocolo ACME e processo de validação
- ACME Protocol Specification (RFC 8555) — Especificação técnica do protocolo Automatic Certificate Management Environment
- DigitalOcean - How To Use Certbot Standalone Mode — Tutorial prático sobre renovação automática com modo standalone
- Systemd Timer vs Cron: Which One to Use — Comparativo entre agendamento com systemd timers e cron para tarefas automatizadas
- OpenSSL Certificate Validation Commands — Referência de comandos OpenSSL para validação e inspeção de certificados
- Bash Scripting: Logging and Error Handling — Técnicas avançadas de logging e tratamento de erros em scripts Bash