El problema#
Tengo varios hosts en mi infraestructura doméstica corriendo servicios Docker. Cada uno necesita certificados SSL válidos y credenciales compartidas. Renovar certificados manualmente en cada máquina es un dolor de cabeza, y hacer downtime cada vez es impractical.
La solución: Consul como almacén centralizado de secretos con sincronización automática y un sistema de hooks en systemd para recargar servicios sin parar.
Arquitectura#
- Consul: almacena certificados y secretos con replicación automática
- systemd: monitorea cambios y ejecuta scripts de actualización
- inotify: detecta cambios en archivos de certificados
- Docker: reinicia servicios sin downtime usando health checks
Paso 1: Configuración inicial de Consul#
Primero, instalé Consul en mi servidor principal y lo configuré como servidor:
# /etc/consul.d/server.hcl
server = true
ui = true
bootstrap_expect = 1
client_addr = "0.0.0.0"
bind_addr = "192.168.x.x"
data_dir = "/var/lib/consul"
encrypt = "tu_clave_encriptacion_aqui"En los hosts secundarios, configuré Consul como cliente:
# /etc/consul.d/client.hcl
server = false
client_addr = "0.0.0.0"
bind_addr = "192.168.x.x"
retry_join = ["192.168.x.x"]
data_dir = "/var/lib/consul"
encrypt = "tu_clave_encriptacion_aqui"Paso 2: Almacenar secretos en Consul#
Usé curl para cargar los certificados:
#!/bin/bash
# guardar-secretos.sh
CERT=$(cat /etc/ssl/certs/mi-dominio.crt | base64 -w0)
KEY=$(cat /etc/ssl/private/mi-dominio.key | base64 -w0)
consul kv put secrets/ssl/cert "$CERT"
consul kv put secrets/ssl/key "$KEY"
consul kv put secrets/db/password "tu_password_aqui"Paso 3: Sincronización con systemd#
Creé un servicio systemd que vigila y sincroniza cambios:
# /etc/systemd/system/consul-sync-certs.service
[Unit]
Description=Sincronizar certificados desde Consul
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/consul-sync-watcher.sh
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.targetEl script watcher:
#!/bin/bash
# /usr/local/bin/consul-sync-watcher.sh
CERT_PATH="/etc/ssl/certs/mi-dominio.crt"
KEY_PATH="/etc/ssl/private/mi-dominio.key"
CERT_HASH_FILE="/tmp/cert.hash"
while true; do
# Obtener certificado desde Consul
CERT=$(consul kv get secrets/ssl/cert | base64 -d)
KEY=$(consul kv get secrets/ssl/key | base64 -d)
# Calcular hash
CURRENT_HASH=$(echo "$CERT" | sha256sum | awk '{print $1}')
STORED_HASH=$(cat $CERT_HASH_FILE 2>/dev/null || echo "")
# Si cambió, actualizar y recargar servicios
if [ "$CURRENT_HASH" != "$STORED_HASH" ]; then
echo "$CERT" > "$CERT_PATH"
echo "$KEY" > "$KEY_PATH"
chmod 600 "$KEY_PATH"
echo "$CURRENT_HASH" > "$CERT_HASH_FILE"
# Recargar servicios sin downtime
systemctl reload nginx
docker exec mi-app nginx -s reload 2>/dev/null || true
logger -t consul-sync "Certificados actualizados y servicios recargados"
fi
sleep 60
donePaso 4: Rotación sin downtime#
Para Docker, aseguré health checks funcionales:
# docker-compose.yml
services:
web:
image: nginx:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/health"]
interval: 10s
timeout: 5s
retries: 3
volumes:
- /etc/ssl/certs/mi-dominio.crt:/etc/nginx/certs/cert.crt:ro
- /etc/ssl/private/mi-dominio.key:/etc/nginx/certs/cert.key:roCuando el script detecta cambios, ejecuta:
docker exec mi-app nginx -s reloadNginx recarga la configuración sin cerrar conexiones existentes.
Paso 5: Automatizar renovación de Let’s Encrypt#
Integré Certbot con el flujo:
# /etc/letsencrypt/renewal-hooks/deploy/sync-certs.sh
#!/bin/bash
DOMAIN=$RENEWED_LINEAGE
CERT_PATH="${RENEWED_LINEAGE}/fullchain.pem"
KEY_PATH="${RENEWED_LINEAGE}/privkey.pem"
CERT=$(cat $CERT_PATH | base64 -w0)
KEY=$(cat $KEY_PATH | base64 -w0)
consul kv put secrets/ssl/cert "$CERT"
consul kv put secrets/ssl/key "$KEY"Resultado#
Ahora cuando renuevo un certificado con Certbot, Consul se sincroniza automáticamente con todos los hosts en 60 segundos. Los servicios se recargan sin downtime ni intervención manual.
Monitoreo cambios en los logs de systemd:
journalctl -u consul-sync-certs.service -fEste setup me ahorró horas de administración manual. La replicación automática de Consul garantiza consistencia incluso si un host falla temporalmente.