fix(security): Correct secrets architecture for autonomous instances

ARCHITECT is the constructor, DECK/CORP are autonomous instances.
Each instance must operate independently at runtime.

Architecture:
- Infisical (ARCHITECT): Central management, source of truth
- Vaultwarden (DECK :8085): Local secrets for autonomous operation
- Vaultwarden (CORP :8081): Local secrets for autonomous operation
- Sync: Infisical → Vaultwarden propagation

Key principle: Instances never depend on ARCHITECT at runtime.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
ARCHITECT
2025-12-25 09:42:57 +00:00
parent 6d15abcb1a
commit 582e425832

View File

@@ -5,205 +5,266 @@
---
## Estado Actual: Triple Gestión
## Arquitectura de Secretos
**PROBLEMA CRÍTICO:** Actualmente existen tres fuentes de secretos sin sincronización.
### Principio Fundamental
> **DECK y CORP son instancias autónomas. Deben operar independientemente de ARCHITECT.**
ARCHITECT es el constructor/coordinador. DECK y CORP son instancias diseñadas para trabajar solas.
### Modelo de Gestión
```
┌─────────────────────────────────────────────────────────────────┐
FUENTES DE SECRETOS
ARCHITECT
│ (Constructor) │
│ │
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
Infisical creds_* .env │
│ (central) (PostgreSQL)│ │ (archivos) │
│ └─────────────┘ └─────────────┘ └─────────────┘
│ ▼ ▼ ▼ │
┌─────────────────────────────────────────────────────────┐
❌ SIN SINCRONIZACIÓN - Riesgo de inconsistencia │ │
└─────────────────────────────────────────────────────────┘
─────────────────────────────────────────────────────────────────┘
┌──────────────┐
│ Infisical │ ← Gestión centralizada
:8082 │ Single source of truth
└─────────────┘
│ │
└───────────────────────────┼─────────────────────────────────────┘
sync/push (en deploy o periódico)
────────────────────────────────
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ DECK │ │ CORP │
│ (Instancia) │ │ (Instancia) │
│ │ │ │
│ ┌──────────────┐ │ │ ┌──────────────┐ │
│ │ Vaultwarden │ │ │ │ Vaultwarden │ │
│ │ :8085 │ │ │ │ :8081 │ │
│ └──────┬───────┘ │ │ └──────┬───────┘ │
│ │ │ │ │ │
│ ▼ │ │ ▼ │
│ CLARA, ALFRED │ │ MARGARET, JARED │
│ leen de aquí │ │ MASON, FELDMAN │
│ │ │ leen de aquí │
└──────────────────────┘ └──────────────────────┘
```
### Gestores por Servidor
| Servidor | Gestor | Puerto | Rol |
|----------|--------|--------|-----|
| ARCHITECT | Infisical | 8082 | Gestión central, source of truth |
| DECK | Vaultwarden | 8085 | Operación autónoma DECK |
| CORP | Vaultwarden | 8081 | Operación autónoma CORP |
---
## Flujo de Secretos
### Administración (en ARCHITECT)
```
Administrador
┌─────────────┐
│ Infisical │ ← Crear/modificar/rotar secretos aquí
│ (central) │
└─────────────┘
```
### Sincronización (ARCHITECT → Instancias)
```
Infisical (ARCHITECT)
│ Script de sync / deploy
├────────────────┬────────────────┐
▼ ▼ ▼
Vaultwarden Vaultwarden (otras instancias
DECK CORP futuras)
```
### Operación (en cada instancia)
```
┌──────────────────────────────────────┐
│ DECK │
│ │
│ CLARA ──────► Vaultwarden ◄─────── ALFRED
│ :8085 │
│ │
│ ✅ No depende de ARCHITECT │
│ ✅ Opera si ARCHITECT está caído │
└──────────────────────────────────────┘
```
---
## Fuente 1: Infisical
## Estado Actual: Problema
**URL:** http://localhost:8082 (ARCHITECT)
**Estado:** Operativo
**Triple gestión sin sincronización:**
### Configuración
| Fuente | Ubicación | Estado |
|--------|-----------|--------|
| Infisical | ARCHITECT :8082 | Parcialmente usado |
| creds_* | PostgreSQL ARCHITECT | Activo |
| .env | /opt/*/.env en cada servidor | Activo, **permisos 644** |
```bash
# Acceso a Infisical
infisical login
infisical secrets --env=prod
```
### Ventajas
- Centralizado
- Versionado
- Auditable
- SDK para múltiples lenguajes
### Uso Actual
- Parcial - no todos los servicios lo usan
**Problemas:**
- Secretos duplicados en múltiples lugares
- Sin sincronización entre fuentes
- .env legibles por todos (644)
- Vaultwarden de DECK/CORP no integrados
---
## Fuente 2: Tablas creds_* (PostgreSQL)
**Base de datos:** architect
**Estado:** Activo
### Tablas
| Tabla | Contenido |
|-------|-----------|
| creds_ia | APIs de IA (OpenAI, Anthropic, etc.) |
| creds_runpod | API keys de RunPod |
| creds_r2 | Credenciales Cloudflare R2 |
| creds_gitea | Tokens Gitea |
| creds_mail | Credenciales SMTP |
| creds_apis | APIs externas varias |
### Schema Típico
```sql
CREATE TABLE creds_ia (
id SERIAL PRIMARY KEY,
servicio VARCHAR(50) NOT NULL,
api_key TEXT NOT NULL,
descripcion TEXT,
activo BOOLEAN DEFAULT true,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
```
### Acceso
```sql
-- Consultar credenciales
sudo -u postgres psql -d architect -c "SELECT servicio, descripcion FROM creds_ia;"
```
---
## Fuente 3: Archivos .env
**Ubicaciones:**
- /opt/clara/.env
- /opt/alfred/.env
- /opt/margaret/.env
- /opt/mason/.env
- /opt/feldman/.env
### ALERTA CRÍTICA
```
Permisos actuales: 644 (legibles por todos)
Permisos correctos: 600 (solo propietario)
```
### Contenido Típico
```bash
# Ejemplo .env
DB_HOST=localhost
DB_PORT=5432
DB_NAME=corp
DB_USER=margaret
DB_PASSWORD=secreto_aqui # ⚠️ Expuesto si 644
R2_ACCESS_KEY=access_key_aqui
R2_SECRET_KEY=secret_key_aqui # ⚠️ Expuesto si 644
```
---
## Decisión D-001: Migración a Infisical
## Decisión D-001: Arquitectura de Secretos
### Objetivo
Infisical como **única fuente de verdad** para todos los secretos.
### Plan de Migración
1. **Infisical** (ARCHITECT): Única fuente de verdad para administración
2. **Vaultwarden** (DECK/CORP): Gestores locales para operación autónoma
3. **Sync**: Mecanismo de propagación Infisical → Vaultwarden
### División de Responsabilidades
| Tipo de Secreto | Gestión en | Operación en |
|-----------------|------------|--------------|
| API keys externas (OpenAI, etc.) | Infisical | Vaultwarden local |
| R2 credentials | Infisical | Vaultwarden local |
| Gitea tokens | Infisical | Vaultwarden local |
| DB password DECK | Infisical | Vaultwarden DECK |
| DB password CORP | Infisical | Vaultwarden CORP |
| Tokens internos DECK | Infisical | Vaultwarden DECK |
| Tokens internos CORP | Infisical | Vaultwarden CORP |
### Flujo de Trabajo
```
Fase 1: Inventario
├── Catalogar todos los secretos en creds_*
├── Catalogar todos los secretos en .env
└── Identificar duplicados/inconsistencias
Fase 2: Centralización
├── Migrar todos los secretos a Infisical
├── Organizar por proyecto/ambiente
└── Establecer políticas de acceso
Fase 3: Actualización de Servicios
├── CLARA: Usar Infisical SDK
├── MARGARET: Usar Infisical SDK
├── MASON: Usar Infisical SDK
├── FELDMAN: Usar Infisical SDK
└── ALFRED/JARED: Usar Infisical SDK
Fase 4: Deprecación
├── Eliminar archivos .env
├── Marcar creds_* como "solo referencia"
└── Documentar proceso de rotación
```
### Implementación con SDK
```python
from infisical_client import InfisicalClient
client = InfisicalClient(
host="http://localhost:8082",
token=INFISICAL_TOKEN
)
# Obtener secreto
db_password = client.get_secret(
secret_name="DB_PASSWORD",
project_id="tzzr",
environment="production"
)
1. Admin modifica secreto en Infisical (ARCHITECT)
2. Script de sync detecta cambio
3. Propaga a Vaultwarden de DECK y/o CORP
4. Servicios locales usan Vaultwarden local
5. ARCHITECT puede caer → DECK/CORP siguen operando
```
---
## Rotación de Secretos
## Plan de Migración
### Política Propuesta
| Tipo | Frecuencia | Responsable |
|------|------------|-------------|
| API Keys externas | 90 días | Orchestrator |
| Contraseñas DB | 180 días | DBA |
| SSH Keys | 365 días | SysAdmin |
| Tokens Gitea | 180 días | DevOps |
### Proceso de Rotación
### Fase 1: Inventario
```
1. Generar nuevo secreto
2. Actualizar en Infisical
3. Desplegar servicios afectados
4. Verificar funcionamiento
5. Revocar secreto anterior
6. Documentar en changelog
├── Catalogar todos los secretos en creds_*
├── Catalogar todos los secretos en .env
├── Identificar duplicados/inconsistencias
└── Mapear qué secretos necesita cada instancia
```
### Fase 2: Centralización en Infisical
```
├── Migrar todos los secretos a Infisical
├── Organizar por instancia (DECK, CORP)
├── Establecer políticas de acceso
└── Documentar cada secreto
```
### Fase 3: Configurar Vaultwarden en Instancias
```
├── DECK: Configurar Vaultwarden :8085
│ ├── Crear organización "DECK"
│ ├── Importar secretos de DECK desde Infisical
│ └── Configurar acceso para servicios
└── CORP: Configurar Vaultwarden :8081
├── Crear organización "CORP"
├── Importar secretos de CORP desde Infisical
└── Configurar acceso para servicios
```
### Fase 4: Implementar Sync
```
├── Crear script de sincronización
│ └── infisical export → vaultwarden import
├── Configurar trigger (manual, cron, o webhook)
└── Probar sync en ambas direcciones
```
### Fase 5: Actualizar Servicios
```
├── CLARA: Leer de Vaultwarden DECK
├── ALFRED: Leer de Vaultwarden DECK
├── MARGARET: Leer de Vaultwarden CORP
├── JARED: Leer de Vaultwarden CORP
├── MASON: Leer de Vaultwarden CORP
└── FELDMAN: Leer de Vaultwarden CORP
```
### Fase 6: Deprecación
```
├── Eliminar archivos .env
├── Marcar creds_* como "solo referencia histórica"
└── Documentar proceso completo
```
---
## Implementación
### Lectura desde Vaultwarden (Python)
```python
import requests
VAULTWARDEN_URL = "http://localhost:8085" # DECK
# VAULTWARDEN_URL = "http://localhost:8081" # CORP
def get_secret(name: str) -> str:
"""Obtiene secreto de Vaultwarden local."""
# Implementar según API de Vaultwarden/Bitwarden
pass
```
### Script de Sync (Infisical → Vaultwarden)
```bash
#!/bin/bash
# /opt/scripts/sync_secrets.sh
# Exportar desde Infisical
infisical export --env=deck --format=json > /tmp/deck_secrets.json
infisical export --env=corp --format=json > /tmp/corp_secrets.json
# Importar a Vaultwarden DECK
ssh deck 'bw import --format json /tmp/deck_secrets.json'
# Importar a Vaultwarden CORP
ssh corp 'bw import --format json /tmp/corp_secrets.json'
# Cleanup
rm /tmp/*_secrets.json
```
---
## Mitigación Inmediata
### Paso 1: Permisos .env
### Paso 1: Permisos .env (URGENTE)
```bash
# Ejecutar en todos los servidores
# DECK
ssh -i ~/.ssh/tzzr root@72.62.1.113 'chmod 600 /opt/clara/.env /opt/alfred/.env'
@@ -211,82 +272,73 @@ ssh -i ~/.ssh/tzzr root@72.62.1.113 'chmod 600 /opt/clara/.env /opt/alfred/.env'
ssh -i ~/.ssh/tzzr root@92.112.181.188 'chmod 600 /opt/margaret/.env /opt/mason/.env /opt/feldman/.env'
```
### Paso 2: Auditoría
### Paso 2: Verificar Vaultwarden
```bash
# Verificar permisos
ls -la /opt/*/.env
# DECK
curl -s http://72.62.1.113:8085/api/health
# Buscar secretos expuestos
grep -r "password\|secret\|key" /opt/*/ 2>/dev/null
# CORP
curl -s http://92.112.181.188:8081/api/health
```
---
## Inventario de Secretos
## Rotación de Secretos
### APIs de IA
### Política
| Servicio | Ubicación Actual | Notas |
|----------|------------------|-------|
| OpenAI | creds_ia | GPT-4 |
| Anthropic | creds_ia | Claude |
| Replicate | creds_ia | Modelos varios |
| RunPod | creds_runpod | GRACE, PENNY, FACTORY |
| Tipo | Frecuencia | Responsable |
|------|------------|-------------|
| API Keys externas | 90 días | Admin vía Infisical |
| Contraseñas DB | 180 días | Admin vía Infisical |
| SSH Keys | 365 días | Admin vía Infisical |
| Tokens Gitea | 180 días | Admin vía Infisical |
### Infraestructura
### Proceso
| Servicio | Ubicación Actual | Notas |
|----------|------------------|-------|
| PostgreSQL | .env + creds_* | Múltiples usuarios |
| R2/Cloudflare | .env + creds_r2 | Access + Secret key |
| Gitea | .env + creds_gitea | Read + Write tokens |
| SMTP | creds_mail | Mailcow |
### Servicios Externos
| Servicio | Ubicación Actual | Notas |
|----------|------------------|-------|
| Stripe | creds_apis | (si aplica) |
| Twilio | creds_apis | (si aplica) |
| AWS | creds_apis | (si aplica) |
---
## Verificación de Seguridad
### Checklist Diario
- [ ] Permisos .env son 600
- [ ] No hay secretos en logs
- [ ] No hay secretos en commits git
- [ ] Infisical accesible
### Comandos de Verificación
```bash
# Verificar permisos
find /opt -name ".env" -exec ls -la {} \;
# Buscar secretos en git history
git log -p | grep -i "password\|secret\|key\|token"
# Verificar Infisical
curl -s http://localhost:8082/api/v1/health
```
1. Rotar en Infisical (ARCHITECT)
2. Ejecutar sync a Vaultwarden (DECK/CORP)
3. Verificar servicios operando
4. Documentar rotación
```
---
## Buenas Prácticas
## Verificación
### DO (Hacer)
- Usar Infisical para todos los secretos nuevos
- Rotar secretos según política
- Auditar accesos regularmente
- Cifrar secretos en reposo
### Checklist
### DON'T (No Hacer)
- Hardcodear secretos en código
- Commitear .env a repositorios
- Compartir secretos por chat/email
- Usar el mismo secreto en múltiples servicios
- [ ] Infisical operativo en ARCHITECT
- [ ] Vaultwarden operativo en DECK (:8085)
- [ ] Vaultwarden operativo en CORP (:8081)
- [ ] Sync configurado y probado
- [ ] Servicios leen de Vaultwarden local
- [ ] .env eliminados o con permisos 600
- [ ] DECK opera si ARCHITECT cae
- [ ] CORP opera si ARCHITECT cae
### Test de Autonomía
```bash
# 1. Detener ARCHITECT (simulado)
# 2. Verificar DECK sigue operando
curl http://72.62.1.113:5051/health # CLARA
# 3. Verificar CORP sigue operando
curl http://92.112.181.188:5051/health # MARGARET
```
---
## Resumen
| Componente | Rol |
|------------|-----|
| **Infisical (ARCHITECT)** | Gestión centralizada, source of truth |
| **Vaultwarden (DECK)** | Operación autónoma instancia personal |
| **Vaultwarden (CORP)** | Operación autónoma instancia empresarial |
| **Sync** | Propagación de cambios |
**Principio clave:** Las instancias (DECK, CORP) nunca dependen de ARCHITECT en runtime.