Credenciales y conexiones a BD
Cómo se manejan usuario/password de SQL Server y Postgres en todos los repos que hablan con las bases de Trivasa, y qué pasó el 2026-08-29 que hizo falta escribir esto.
Regla general
Host, puerto y nombre de base NO son secreto (son topología de red, documentada en Servidores y bases) — se quedan hardcodeados en cada script/archivo. Usuario y password SÍ son secreto — nunca hardcodeados, nunca comiteados a git, siempre por variable de entorno o un archivo de secretos gitignored.
Patrón canónico: trivasa-bi-core
Fuente real de las credenciales: dlt/.dlt/secrets.toml (gitignored).
shared/db_credentials.py lo lee y expone dos cuentas nombradas:
from shared import db_credentials
user, password = db_credentials.sa() # .200 / .204 / .205 (no productivas)
user, password = db_credentials.ealcocer() # .207 (produccion)
Reusado por connections/connection_<host>_<db>.py (naming explícito,
sin ambigüedad — el connection.py genérico sin sufijo se borró
2026-08-27 por ser el más confuso del directorio), por dlt/load_*.py y
por observability/checks/*.py. Cada connection_*.py expone engine +
q(sql)/show(sql), mismo shape en los 4 archivos.
Este patrón nació de un hallazgo real: git grep (2026-08-27) encontró el
mismo usuario/password hardcodeado y duplicado en 12 archivos del repo, ya
comiteados a git pese a que este mismo CLAUDE.md lo prohíbe.
Patrón en repos separados (no pueden importar shared/)
varela-bot y streamlit-reportes viven en su propio repo, sin acceso al
paquete shared/ de trivasa-bi-core. Usan .env (gitignored) →
os.environ, con un prefijo por motor de base:
| Motor | Prefijo |
|---|---|
| SQL Server (TRIVASADB) | TRIVASADB_HOST, TRIVASADB_PORT, TRIVASADB_DATABASE, TRIVASADB_USER, TRIVASADB_PASSWORD |
| Postgres | PG_HOST, PG_PORT, PG_DATABASE, PG_USER, PG_PASSWORD |
Cualquier repo nuevo que no pueda importar shared/db_credentials.py
copia este prefijo tal cual — evita que cada repo invente su propio
esquema de nombres (antes streamlit-reportes usaba DB_HOST/DB_USER/
DB_PASS genérico, inconsistente con varela-bot; corregido 2026-08-29).
Infisical no se usa para inyectar estas credenciales en runtime.
Decisión explícita en varela-bot (2026-08-12): una primera versión sí
conectaba vía infisical run y se revirtió por ser más de lo que se pedía.
Infisical queda como respaldo de secretos puntuales (tokens, no
credenciales de BD que ya tienen su propio .env/secrets.toml como
fuente real).
dbt
~/.dbt/profiles.yml, fuera de cualquier repo, nunca se comitea — dbt no
lo autogenera, se arma a mano por máquina. Ya es la práctica estándar de
dbt, no hace falta nada adicional.
Incidente 2026-08-29 — password de sa expuesto en repo público
docs/proyectos/consumo-interno-fifo/scripts/connection_205_trivasadb3.py
en este mismo repo (público en GitHub) tenía el password de sa en
texto plano desde antes de la centralización del 2026-08-27 — era una
copia manual de trivasa-bi-core/connections/connection_205_trivasadb3.py
que nunca se actualizó cuando el original se arregló.
No se pudo rotar el password (sin acceso a la cuenta sa para
cambiarlo). Mitigación aplicada en su lugar:
- Se arregló el archivo para leer
TRIVASADB_USER/TRIVASADB_PASSWORDde variable de entorno, se comiteó y pusheó normal. - Se reescribió toda la historia de git con
git-filter-repo(--replace-text, reemplazando el password literal por un placeholder) — afectaba 18 commits. git push --force --all— verificado después congit grepsobre todos los commits: cero apariciones del password en el historial.- Se borraron 2 archivos más con el mismo password hardcodeado que
vivían fuera de cualquier repo git (
~/por_ordenar/exploracion/ solicitud-material/scripts/,~/proyectos/duckdb-trivasadb3-poc/) — nunca estuvieron públicos, pero sin razón para conservarlos.
Repo sin forks (forkCount: 0) al momento del incidente, así que el
force push no dejó copias huérfanas de la historia vieja en GitHub.
Riesgo residual: cualquier clon local que alguien haya hecho antes de
2026-08-29 conserva la historia vieja con el password — no hay forma de
alcanzar esos clones desde aquí.
Lección — regla para copiar connection_*.py entre repos
trivasa-bi-core/connections/ es la fuente. Cuando un proyecto en otro
repo necesita una copia (patrón ya documentado en el skill
trivasa-streamlit-reportes: "copiado, no symlink"):
- Nunca copiar la versión con
create_engine(url_con_password_inline)— eso fue exactamente lo que pasó aquí. - Si el repo destino no puede importar
shared/db_credentials.py, la copia debe leer usuario/password de variable de entorno (os.environ), no traerlos embebidos. - Antes de comitear una copia nueva,
git grep -n "mssql+pymssql://.*:.*@"sobre el archivo — si aparece un match con un password real en vez de una variable, no se comitea.
Pendiente / no resuelto
- Harlequin (cliente SQL ad-hoc, en otro host, no en ctunlinux): no
auditado desde aquí — no se pudo confirmar si su config (
~/.config/ harlequin/...u otro) tiene el password en texto plano. Revisar en ese host y aplicar la misma regla (variable de entorno o keychain si el adaptador ODBC lo soporta) si lo tiene. ~/.trivasa/secrets.env(creado 2026-08-04, permisos 600): no se encontró ningún cron ni script activo que lo lea — probable leftover de antes deshared/db_credentials.py. Confirmar que nada lo usa y borrarlo.- Password de
sasigue siendo el mismo de antes del incidente — rotarlo en cuanto haya acceso a la cuenta, y actualizardlt/.dlt/secrets.toml(y su respaldo en Infisical si existe) cuando pase.