Cómo Actualizar pgAdmin 4 a 9.19 en Modo Servidor sin Perder tus Password Exec Commands

Para actualizar pgAdmin 4 a 9.19 en modo servidor sin perder tus Password Exec Commands, define cada comando en SERVER_PASSEXEC_COMMANDS antes del primer arranque. En ese arranque, pgAdmin convierte los comandos que coinciden exactamente con una entrada y borra el resto, sin posibilidad de deshacerlo.

pgAdmin 4 v9.19 salió el 29 de octubre de 2026. Según el propio proyecto, trae 22 funciones nuevas, 10 cambios de mantenimiento y 104 correcciones de errores, 15 de ellas con identificador CVE. Si usas pgAdmin en el escritorio, actualiza como siempre. Si lo usas como aplicación web compartida, en server mode, cambian tres valores por defecto, y uno de ellos borra datos.

¿Qué es pgAdmin 4 y a quién afecta la versión 9.19?

pgAdmin 4 es la herramienta gráfica de código abierto para administrar PostgreSQL, publicada bajo la PostgreSQL Licence. Funciona de dos formas: como aplicación de escritorio para una sola persona (desktop mode) o como aplicación web con usuarios propios (server mode, SERVER_MODE = True). Si al entrar en pgAdmin te pide un correo y una contraseña de pgAdmin, como la imagen oficial dpage/pgadmin4 con PGADMIN_DEFAULT_EMAIL, estás en modo servidor.

Los cambios delicados afectan solo al modo servidor. En el escritorio, los Password Exec Commands siguen siendo texto libre y los marcadores de sustitución siguen funcionando.

En modo servidor, al actualizar cambian tres cosas:

  1. Los Password Exec Commands de texto libre dejan de ejecutarse. ENABLE_SERVER_PASS_EXEC_CMD queda obsoleto y se ignora. Ahora el administrador define comandos con nombre en SERVER_PASSEXEC_COMMANDS, y cada usuario elige un nombre en las propiedades del servidor.
  2. Los servicios de libpq quedan bloqueados por defecto. El nuevo SERVER_MODE_ALLOWED_SERVICES es una lista vacía, y según config.py, un servidor configurado con cualquier otro servicio no se puede guardar ni conectar.
  3. Los túneles SSH ya no usan el agente SSH. Un túnel que dependía del agente necesita un archivo de identidad o una contraseña.

Solo el primero es irreversible. Los otros dos cortan conexiones hasta que corrijas la configuración.

¿Qué pasa en el primer arranque después de actualizar?

En ese arranque, pgAdmin lee SERVER_PASSEXEC_COMMANDS una sola vez y convierte la base de datos de configuración:

  • Cada comando de texto libre guardado se convierte en el nombre de la entrada cuyos argumentos, unidos con un solo espacio, son idénticos a él.
  • Todos los demás comandos guardados se borran, y eso no se puede deshacer.
  • Si el diccionario está vacío, la conversión se pospone: los comandos antiguos quedan en la base de datos sin usarse, se registra una advertencia en cada arranque, y la conversión se ejecuta en el primer arranque que tenga al menos una entrada.

Esa postergación es tu red de seguridad, pero léela con atención: en cuanto añades una entrada, todos los comandos que no coinciden con alguna entrada se borran en ese arranque. Añade todas las entradas que necesitas de una vez.

En los servidores compartidos, si se borra el comando propio de un usuario, ese usuario pasa a heredar el comando del propietario, si el propietario tiene uno.

¿Cómo actualizar pgAdmin 4 en Docker sin perder los comandos?

Haz una copia de la base de datos de configuración, revisa qué comandos y servicios usas, tradúcelos a la nueva configuración y solo entonces actualiza. Los ejemplos usan la imagen oficial; si instalas el wheel de Python o un paquete del sistema, los pasos son los mismos con tus rutas.

Paso 1: copia la base de datos de configuración

Dentro de pgAdmin, el borrado no se puede deshacer. Una copia hecha antes de actualizar es tu forma de volver atrás. En el contenedor oficial, la base de datos de configuración es pgadmin4.db, dentro de /var/lib/pgadmin:

docker cp <contenedor>:/var/lib/pgadmin/pgadmin4.db ./pgadmin4-pre-9.19.db

En una instalación con wheel o paquete, el archivo es el que indique SQLITE_PATH. Si guardas la configuración de pgAdmin en una base de datos PostgreSQL externa, haz la copia de esa base.

Paso 2: lista los comandos y servicios que usas

Ejecuta esta consulta de solo lectura sobre la copia. Funciona con el esquema de 9.18, en el que las tablas server y sharedserver tienen las columnas name, passexec_cmd y service:

SELECT 'server' AS source, name, passexec_cmd, service
FROM server
WHERE passexec_cmd IS NOT NULL OR service IS NOT NULL
UNION ALL
SELECT 'sharedserver', name, passexec_cmd, service
FROM sharedserver
WHERE passexec_cmd IS NOT NULL OR service IS NOT NULL;
sqlite3 ./pgadmin4-pre-9.19.db < inventario.sql

Copia los valores de passexec_cmd tal como salen. La coincidencia es carácter por carácter.

Paso 3: convierte cada comando en una entrada con nombre

Cada entrada asocia un nombre a una lista: primero el ejecutable y después sus argumentos, uno por elemento.

Antes (9.18):

# config_local.py
SERVER_MODE = True
ENABLE_SERVER_PASS_EXEC_CMD = True

y, en las propiedades del servidor, el comando en texto libre:

/usr/local/bin/get-pg-pass --ttl 300

Después (9.19):

# config_local.py
SERVER_MODE = True
SERVER_PASSEXEC_COMMANDS = {
    'vault': ['/usr/local/bin/get-pg-pass', '--ttl', '300']
}

/usr/local/bin/get-pg-pass, --ttl y 300 unidos con un espacio dan exactamente el comando guardado, así que ese servidor se convierte a vault y sigue funcionando.

Si configuras el contenedor con variables de entorno, el valor es una expresión de Python:

# docker-compose.yml
environment:
  PGADMIN_CONFIG_SERVER_PASSEXEC_COMMANDS: "{'vault': ['/usr/local/bin/get-pg-pass', '--ttl', '300']}"

Un /pgadmin4/config_local.py montado en el contenedor tiene prioridad sobre las variables PGADMIN_CONFIG_*. El script tiene que existir dentro del contenedor, que se ejecuta con el usuario pgadmin (UID 5050).

Paso 4: permite los servicios de libpq que usas

Si el inventario mostró algún valor en service, añádelo, o esos servidores dejarán de conectar:

SERVER_MODE_ALLOWED_SERVICES = ['reporting']

Los servicios se buscan en el archivo de servicios de la cuenta con la que se ejecuta pgAdmin (~/.pg_service.conf, PGSERVICEFILE o PGSYSCONFDIR), no en un archivo de cada usuario. config.py es claro sobre el coste: añadir un servicio comparte sus credenciales con todos los usuarios de pgAdmin, que además pueden cambiar el host de la entrada y enviar esa contraseña a un servidor que elijan ellos. Añade solo servicios cuyas credenciales pueda usar cualquiera con cuenta en pgAdmin.

Paso 5: da un archivo de identidad a los túneles que usaban el agente SSH

En modo servidor, los túneles nunca usan el agente SSH de la cuenta con la que se ejecuta pgAdmin. Cualquier servidor cuyo túnel dependía de él necesita un archivo de identidad o una contraseña en la configuración del túnel SSH.

Paso 6: actualiza, arranca una vez y revisa el log

Actualiza la imagen o el paquete y arráncalo. Si dejaste el diccionario vacío, busca en el log la advertencia de conversión pospuesta. Después abre cada servidor que tenía un Password Exec Command y comprueba que el desplegable muestra el nombre esperado y no “None”.

Si instalas con el wheel de Python: en Python 3.10 o superior, pgAdmin 9.19 requiere Flask-Security-Too 5.8.3 o posterior, que corrige una redirección abierta (GHSA-ccgp-pv8r-95wm). Las instalaciones con Python 3.9 se quedan en la serie 5.6, así que no fuerces esa dependencia en 3.9.

Las versiones de base de datos compatibles son PostgreSQL y EDB Advanced Server de la 14 a la 18. Las utilidades incluidas (psql, pg_dump, pg_dumpall y pg_restore) son la 18.6.

¿Qué comandos no se van a convertir?

No se convierte ningún comando que dependa de un shell, que use marcadores de sustitución o que no coincida carácter por carácter con una entrada:

  • Los que usaban un shell. Los comandos ahora se ejecutan directamente, sin shell, así que las tuberías, las redirecciones y las $VARIABLES no funcionan, y una lista no puede expresarlas. Pasa esa lógica a un script y añade el script a la lista.
  • Los que tenían marcadores de host, puerto o usuario. En modo servidor ya no se sustituyen. Los datos de conexión llegan ahora como variables de entorno: PGADMIN_PASSEXEC_HOST, _PORT, _USERNAME, _DATABASE, _PGADMIN_USER y _AUTH_SOURCE.
  • Los que tienen un espacio doble o comillas. No coinciden con la unión por espacios simples de ninguna lista.

Hay una advertencia de seguridad en config.py que conviene tomar en serio: solo PGADMIN_PASSEXEC_PGADMIN_USER y PGADMIN_PASSEXEC_AUTH_SOURCE identifican a quien pide la contraseña. El host, el puerto, el usuario y la base de datos los define quien edita el servidor, así que tu script debe decidir qué contraseña devolver según la identidad de pgAdmin y tratar los otros cuatro valores como entrada no confiable.

¿El comando sigue viendo mis variables de entorno?

No. Recibe PATH, HOME, LANG, LANGUAGE, TZ, TMPDIR, TMP, TEMP, USER, LOGNAME, cualquier variable LC_* y las variables PGADMIN_PASSEXEC_*. Si tu script necesita algo más, como la dirección de Vault, decláralo de forma explícita:

SERVER_PASSEXEC_ENV_PASSTHROUGH = ['VAULT_ADDR']

No pases variables con secretos que el comando no necesita.

¿Qué hay de nuevo en pgAdmin 4 9.19?

Además del cambio de seguridad, 9.19 trae mejoras para todo el mundo, también en el escritorio:

  • Exportar resultados del Query Tool a JSON o XML, elegir la codificación del archivo y, si quieres, añadir una marca de orden de bytes (BOM).
  • Editar filas a través de vistas simples actualizables en View/Edit Data.
  • Un nodo Statistics para gestionar estadísticas extendidas.
  • Gestión de cadenas y tareas de pg_timetable.
  • PostgreSQL 18: tablas particionadas UNLOGGED y las nuevas opciones de pg_dump y pg_restore en los diálogos de Backup y Restore.
  • Rejilla de resultados y editor: “copiar con encabezados”, selección de rangos con shift+clic, atajo para duplicar líneas y Escape para cerrar diálogos.
  • OAuth2: los claims de grupo pueden limitar qué servidores compartidos ve cada usuario (OAUTH2_SERVER_GROUP_CLAIM), y OAUTH2_AUTO_LOGIN se salta la página de inicio de sesión cuando OAuth2 es el único proveedor.
  • pgEdge Starfleet en el asistente de despliegue en la nube.

¿Qué vulnerabilidades corrige pgAdmin 9.19?

Las notas de la versión listan 15 CVE por identificador y número de issue, sin puntuación de gravedad, así que esta tabla tampoco la incluye.

CVE Issue Corrección
CVE-2026-116238 #10676 Evasión del límite de intentos de inicio de sesión fallidos
CVE-2026-116239 #10675 LDAP/Kerberos iniciando sesión en la cuenta equivocada
CVE-2026-116240 #10674 File Manager operando sobre rutas sin verificar
CVE-2026-116241 #10663 Inyección de comandos del sistema en Import/Export Data (corrección incompleta de CVE-2026-17566)
CVE-2026-116242 #10662 Identidad SSH de la cuenta de servicio usada en túneles
CVE-2026-116243 #10656 Ruta de binarios ejecutable dentro del almacenamiento compartido
CVE-2026-116244 #10655 Credenciales Kerberos de la cuenta de servicio
CVE-2026-116245 #10654 Servicios de conexión sin restricción
CVE-2026-116246 #10653 Contraseñas que quedaban en el entorno del proceso
CVE-2026-116247 #10652 Archivos de cliente de la cuenta de servicio usados por libpq
CVE-2026-116248 #10651 Usuarios no administradores compartiendo servidores
CVE-2026-116249 #10650 Rutas de archivos de conexión que escapaban del almacenamiento
CVE-2026-116250 #10647 Condiciones de carrera con symlinks en Import/Export Servers y en las subidas del File Manager
CVE-2026-116251 #10646 Sesiones que sobrevivían a un cambio de contraseña
CVE-2026-116252 #10645 Redirección de conexión mediante nombres de base de datos al refrescar vistas materializadas

Dos correcciones relacionadas no tienen CVE: las mismas condiciones de carrera con symlinks en los archivos de Backup, Restore e Import/Export Data (#10648), y el rechazo de hard links y FIFOs en ambas correcciones de symlinks (#10649). Varias de estas vulnerabilidades, como los servicios sin restricción o las credenciales de la cuenta de servicio, explican por qué cambiaron los valores por defecto de arriba: las nuevas restricciones son la corrección.


¿Y tú? ¿Cómo obtienen hoy tus servidores de pgAdmin las contraseñas de PostgreSQL: Password Exec Command, archivo pgpass, servicio de libpq u otra cosa?

¿Cómo ejecutas pgAdmin 4?
  • Escritorio (desktop mode)
  • Modo servidor en Docker
  • Modo servidor con el wheel de Python o un paquete del sistema
  • Uso otra herramienta (cuéntanos cuál)
0 votantes

Comenta abajo o, si este artículo te llegó por correo, responde directamente al email: tu respuesta se publica aquí.

Relacionado: I Replaced Redis with PostgreSQL (And It’s Faster)