panels-origin/db/provision/05-redis-acl.sh
Cursor Agent b87b0205a8
db: migrar infraestructura de BD a Postgres (fase 0-1)
- Fase 0: scripts de aprovisionamiento (db/provision/) para roles, dos
  bases de datos separadas (panels_platform / panels_product con
  esquemas iam+core) y ACLs de Redis por modulo, con verificacion
  automatizada de aislamiento (verify-isolation.sh) y setup local
  reproducible (dev-local.sh).
- Fase 1: changelogs de Liquibase reescritos para Postgres
  (db/platform, db/iam, db/core reemplazan db/app + los changesets
  SQLite de platform). Baseline como estado final (no replay literal),
  tipos traducidos (IDENTITY, TIMESTAMPTZ/DATE, NUMERIC, BOOLEAN,
  CITEXT), contexts dev vs. schema/catalogos, RLS por tenant_id como
  defensa en profundidad, uploaded_by/created_by como snapshot
  desnormalizado (sin FK hacia iam).
- Migraciones ya no corren en el arranque de la API: paso explicito de
  deploy via db/update.sh con credenciales _owner.

Verificado end-to-end contra Postgres 16 + Redis local.

Co-authored-by: alberto.martinez <alberto.martinez@mrdev.mx>
2026-09-02 20:00:49 +00:00

64 lines
2.8 KiB
Bash
Executable file

#!/usr/bin/env bash
# PANELS · Redis · Fase 0 (usuarios ACL por módulo)
#
# Crea dos usuarios ACL (Redis 6+) con acceso restringido por patrón de
# llave: panels_iam_redis (~iam:*) y panels_core_redis (~core:*). Un
# prefijo de llave por sí solo NO aísla nada — cualquier cliente con la
# misma contraseña puede escanear todo el keyspace; el aislamiento real
# lo da el ACL server-side, no la convención de nombres.
#
# Uso:
# REDIS_ADMIN_URL="redis://:admin-password@host:6379" \
# IAM_REDIS_PASSWORD="$(openssl rand -base64 32)" \
# CORE_REDIS_PASSWORD="$(openssl rand -base64 32)" \
# ./05-redis-acl.sh
set -euo pipefail
: "${REDIS_ADMIN_URL:?Define REDIS_ADMIN_URL (redis://[:password@]host:port)}"
: "${IAM_REDIS_PASSWORD:?Define IAM_REDIS_PASSWORD}"
: "${CORE_REDIS_PASSWORD:?Define CORE_REDIS_PASSWORD}"
# Categorías necesarias para sesiones (hash/string + TTL) y cache
# (string/hash/set): keyspace cubre DEL/EXPIRE/EXISTS/TTL/SCAN (el patrón de
# llave sigue acotando el alcance real). Sin @scripting (no se usa Lua/EVAL,
# menor superficie de ataque) ni @admin/@dangerous.
ACL_COMMANDS="-@all +@read +@write +@keyspace +@string +@hash +@set +@sortedset +@connection"
# Importante: "reset" debe ir PRIMERO (deja al usuario en blanco); si va
# después de "on >password" borra la contraseña que se acaba de fijar.
echo "Creando usuario panels_iam_redis (~iam:*)..."
redis-cli -u "$REDIS_ADMIN_URL" ACL SETUSER panels_iam_redis \
reset \
on ">${IAM_REDIS_PASSWORD}" \
'~iam:*' '&iam:*' \
$ACL_COMMANDS
echo "Creando usuario panels_core_redis (~core:*)..."
redis-cli -u "$REDIS_ADMIN_URL" ACL SETUSER panels_core_redis \
reset \
on ">${CORE_REDIS_PASSWORD}" \
'~core:*' '&core:*' \
$ACL_COMMANDS
redis-cli -u "$REDIS_ADMIN_URL" ACL SAVE >/dev/null 2>&1 || {
echo "Aviso: ACL SAVE falló (normal si no hay aclfile configurado; revisar persistencia de ACLs por separado)."
}
HOST_PORT=$(echo "$REDIS_ADMIN_URL" | sed -E 's#^redis://##; s#^[^@]*@##')
echo "Verificando aislamiento (se espera NOPERM al cruzar de modulo)..."
IAM_PROBE=$(redis-cli -u "redis://panels_iam_redis:${IAM_REDIS_PASSWORD}@${HOST_PORT}" GET core:probe 2>&1 || true)
if echo "$IAM_PROBE" | grep -qi "NOPERM"; then
echo "OK: panels_iam_redis no puede leer llaves core:*"
else
echo "ADVERTENCIA: no se confirmó NOPERM al probar panels_iam_redis -> core:*. Salida: $IAM_PROBE"
fi
CORE_PROBE=$(redis-cli -u "redis://panels_core_redis:${CORE_REDIS_PASSWORD}@${HOST_PORT}" GET iam:probe 2>&1 || true)
if echo "$CORE_PROBE" | grep -qi "NOPERM"; then
echo "OK: panels_core_redis no puede leer llaves iam:*"
else
echo "ADVERTENCIA: no se confirmó NOPERM al probar panels_core_redis -> iam:*. Salida: $CORE_PROBE"
fi
echo "Listo. Guarda IAM_REDIS_PASSWORD/CORE_REDIS_PASSWORD como secrets (REDIS_URL_IAM/REDIS_URL_CORE)."