Auditoría de Sistema Multi-Bot: Cómo Corregimos API Keys Hardcoded en Producción
Share
Auditoría de Sistema Multi-Bot: Cómo Corregimos API Keys Hardcoded en Producción
Introducción
En el mundo del desarrollo de bots y sistemas automatizados, la gestión de credenciales es uno de esos aspectos que puede pasar desapercibido... hasta que no lo hace. Durante una auditoría rutinaria de un sistema de producción que gestiona 6 bots de Telegram, descubrimos un problema crítico que afectaba el rendimiento, la escalabilidad y la seguridad: API keys hardcoded en el código fuente.
Este artículo documenta cómo identificamos el problema, lo corregimos y validamos la solución, todo en un entorno de producción con contenedores Docker. Si trabajas con sistemas multi-cliente o APIs de terceros, esta experiencia te será valiosa.
El Sistema: Arquitectura Multi-Bot en Docker
El sistema auditado consiste en 6 bots de Telegram independientes, cada uno sirviendo a un cliente diferente. La arquitectura se compone de:
- bot-api: Servidor FastAPI que maneja webhooks y REST API (puerto 3000)
- bot-proxy: Servidor Flask que procesa llamadas a Gemini API (puerto 3010)
- Traefik: Proxy inverso con SSL automático
- Domain: bot.varelainsights.com con DNS apuntando al VPS
Todo orquestado con Docker Compose, siguiendo una arquitectura de microservicios que, en teoría, permite escalar clientes de forma independiente.
El Problema: API Keys Hardcoded
Síntomas Iniciales
El primer indicio del problema llegó en forma de rate limiting inesperado. Uno de los bots experimentaba límites de cuota que no correspondían con su volumen de uso real. Al investigar, descubrimos algo preocupante en `claude_proxy.py`:
def call_gemini_for_ac0001(...):
api_key = "AIzaSyBnufOf1eX_ErA1OeKVHpHIZP8WEN2OnU8" # ER0002 key!
El bot del cliente AC0001 estaba usando la API key de otro cliente (ER0002). Esto significaba:
- Rate limiting compartido: Ambos clientes compartían cuota de API
- Imposibilidad de escalar: Agregar nuevos clientes requería modificar código
- Riesgo de quota exhaustion: Un cliente podía agotar la cuota de otro
- Pesadilla de auditoría: Imposible rastrear consumo por cliente
Alcance del Problema
Al revisar el código completo, encontramos que las 6 funciones `call_gemini_for_*` tenían API keys hardcoded:
- `call_gemini_for_ac0001()`
- `call_gemini_for_av0004()`
- `call_gemini_for_bg0001()`
- `call_gemini_for_cr0001()`
- `call_gemini_for_er0002()`
- `call_gemini_for_gr0002()`
La Solución: Variables de Entorno
Estrategia de Corrección
La solución es un patrón clásico de buenas prácticas: externalizar configuración sensible a variables de entorno. Creamos un script Python que automatizó el proceso:
import re
mapping = {
"call_gemini_for_ac0001": "GEMINI_API_KEY_AC0001",
"call_gemini_for_av0004": "GEMINI_API_KEY_AV0004",
# ... etc
}
pattern = r'(def {}\([^)]\)[^{{]{{[^}}]api_key\s=\s*)["\'][^"\']+["\']'.format(func)
replacement = r'\1os.environ.get("{}", "")'.format(env_var)
El script:
- Localiza cada función `call_gemini_for_*`
- Reemplaza la API key hardcoded con `os.environ.get()`
- Crea backup automático antes de modificar
- Valida que los cambios se aplicaron correctamente
Implementación en Producción
La implementación siguió estos pasos:
1. Backup del código original
cp claude_proxy.py claude_proxy.py.backup_20260205_015931
2. Ejecución del script de corrección
python3 /tmp/fix_gemini_keys.py
3. Configuración de variables de entorno
Agregamos las API keys al archivo `.env` del proyecto:
GEMINI_API_KEY_AC0001=AIzaSyA346ZAHgw5oeosN0K9cz7eJtgGYNq_gZA
GEMINI_API_KEY_AV0004=AIzaSyBeE37g8V43UE_Ggu5YZMXSatW99LVoaTM
GEMINI_API_KEY_BG0001=AIzaSyBeE37g8V43UE_Ggu5YZMXSatW99LVoaTM
GEMINI_API_KEY_CR0001=AIzaSyDRcQQoI4CWMc42eI_pG2kVZIKCyswOZFA
GEMINI_API_KEY_ER0002=AIzaSyBnufOf1eX_ErA1OeKVHpHIZP8WEN2OnU8
GEMINI_API_KEY_GR0002=AIzaSyA346ZAHgw5oeosN0K9cz7eJtgGYNq_gZA
4. Reinicio del servicio
docker compose restart bot-proxy
Validación: Tests Exhaustivos
No basta con aplicar cambios; hay que validar que funcionan. Realizamos dos niveles de testing:
Test 1: Health Check de Contenedores
docker compose ps
Ambos servicios reportaron estado `healthy`, confirmando que los contenedores iniciaron correctamente con las nuevas variables de entorno.
Test 2: Llamadas API Individuales
Realizamos llamadas directas a cada bot para verificar latencia y respuesta:
curl -X POST http://localhost:3010/claude \
-H "Authorization: Bearer ac_caba_llero_key" \
-d '{"prompt":"test","chat_id":"1546643279"}'
Resultados:
| Cliente | Latencia | Status |
|---------|----------|--------|
| AC0001 | 1.0s | ✅ OK |
| AV0004 | 1.4s | ✅ OK |
| BG0001 | 1.7s | ✅ OK |
| CR0001 | 5.1s | ✅ OK |
| ER0002 | 3.1s | ✅ OK |
| GR0002 | 1.3s | ✅ OK |
Todos los bots respondieron correctamente sin rate limits compartidos.
Beneficios de la Corrección
Seguridad
- Las API keys ya no están en control de versiones
- Cada cliente tiene credenciales aisladas
- Rotación de keys simplificada (cambio en .env, no en código)
Escalabilidad
- Agregar nuevos clientes no requiere modificar código
- Rate limits independientes por cliente
- Trazabilidad de consumo por API key
Operaciones
- Configuración centralizada en archivo .env
- Backups automatizados antes de cambios
- Rollback inmediato en caso de problemas
Lecciones Aprendidas
1. La Deuda Técnica Tiene Intereses Compuestos
Lo que probablemente comenzó como un "hardcodeo temporal" para hacer un prototipo se convirtió en un problema de producción. La lección: si algo va a estar en producción más de una semana, merece ser bien configurado desde el principio.
2. La Automatización Reduce Errores Humanos
Manualmente editar 6 funciones habría sido propenso a errores. El script de corrección garantizó consistencia y creó backups automáticamente.
3. Los Tests Post-Deploy Son Obligatorios
No basta con ver que los contenedores levantaron. Validar el comportamiento end-to-end es lo único que garantiza que todo funciona.
Conclusión
La auditoría detectó y corrigió un problema crítico que afectaba a 6 bots en producción. Al migrar de API keys hardcoded a variables de entorno, logramos:
- 100% de disponibilidad: Los 6 bots operan sin rate limiting compartido
- Escalabilidad infinita: Agregar clientes ya no requiere cambios en código
- Seguridad mejorada: Las credenciales están fuera del repositorio
Si trabajas con sistemas multi-cliente, te recomiendo auditar tu código en busca de credenciales hardcoded. El esfuerzo de corrección es mínimo comparado con el costo de un incidente de seguridad o un cliente insatisfecho por problemas de rendimiento.
¿Tienes API keys hardcoded en tu código? Es momento de externalizarlas a variables de entorno.