PostgreSQL

Bases PostgreSQL en el VPS con usuario propio de mínimo privilegio, TLS y acceso externo limitado por lista de IP.

  • Categoría: Servidores e infraestructura
  • Estado: Disponible
  • Requisitos: Un VPS activo con la función postgresql disponible; Ser miembro con acceso al VPS para instalar; admin para revelar la credencial, abrir el acceso o borrar datos
  • Herramientas MCP (Solo leen): ver_catalogo_apps, ver_preflight_app, ver_app, ver_acceso_app, ver_operacion_app, listar_motores_db_vps, listar_bases_db_vps
  • Herramientas MCP (Escriben o modifican): instalar_app, revelar_credencial_app, cambiar_lista_ip_app, reintentar_operacion_app
  • Herramientas MCP (Destructivas): revocar_acceso_app, desinstalar_app

Markdown

¿Qué permite hacer?

Cada base de PostgreSQL es una app: tiene su nombre, su usuario propio con contraseña aleatoria y, si se pide, su acceso desde internet con TLS y lista de IP. El motor se instala una sola vez por VPS y lo comparten todas sus bases.

  • Crear varias bases en el mismo motor, cada una aislada de las demás.
  • Ver la credencial (usuario, contraseña y URI de conexión) cuando hace falta.
  • Abrir, cambiar o cerrar el acceso desde internet por IP.
  • Explorar tablas y datos desde el gestor de bases de datos.
  • Desde la pestaña PostgreSQL del VPS: listar y crear bases, usuarios y permisos.

¿Cuándo conviene usarlo?

  • Una aplicación desplegada en el mismo VPS que necesita una base relacional (se conecta por 127.0.0.1:5432).
  • Un servicio externo (BI, otra nube, tu PC) que tiene que leer la base con TLS desde IP conocidas.

¿Cómo funciona en ArduMaker?

  1. Validar: VPS activo y función postgresql del sistema.
  2. Motor: si no está instalado se instala una sola vez (paquetes postgresql y postgresql-contrib de la distribución, contraseña administrativa aleatoria guardada cifrada). Si ya estaba, no se reinstala ni se cambia su versión ni su contraseña.
  3. Credencial: se genera un usuario app<id>_<hex> con contraseña aleatoria, cifrada en la plataforma antes de tocar el VPS.
  4. Rol y base: el rol se crea LOGIN NOSUPERUSER NOCREATEDB NOCREATEROLE NOREPLICATION; la base se crea con ese rol como dueño, se le quita todo a PUBLIC y se agrega una línea hostssl <base> <rol> … scram-sha-256 en pg_hba.conf, con un rechazo debajo para cualquier otra base.
  5. Verificación: el rol no puede ser superusuario ni crear bases o roles, es dueño de su base y PUBLIC no se conecta. Si algo no coincide, falla permisos_no_verificados.
  6. Acceso externo: solo si se pidió con IP. Se activa el TLS del motor y se pide la apertura como operación aparte; si no se puede abrir todavía, la base queda instalada igual y el motivo queda anotado.

El SQL viaja codificado en el entorno y entra por stdin; la contraseña nunca aparece en una línea de comandos ni en un log.

config.external_access decide si la base sale a internet: {"ips": [...]} abre solo a esas IP; {"local": true} (o false) la deja solo adentro del VPS. Si no se indica, desde el panel se toma la IP de quien instala; por MCP la IP del agente no cuenta, así que sin ips la base queda local en vez de fallar.

¿Qué cambia realmente?

QuéDetalle
MotorServicio postgresql habilitado, escuchando en 127.0.0.1:5432
BaseNombre [A-Za-z_][A-Za-z0-9_]{0,62}, con mayúsculas tal cual (se cita)
Rol de la appapp<id>_<hex>
pg_hba.confSu bloque hostssl y un reject para el resto de las bases
TLS (si hay acceso externo)Certificado propio en /etc/ardumaker/tls/, ssl=on, TLS 1.2 como mínimo, recarga sin reinicio

Acceso desde internet y lista de IP

Por defecto el motor escucha solo dentro del VPS (5432 en 127.0.0.1). Para usar la base desde afuera, ArduMaker publica un endpoint propio del motor: db-<id del VPS> dentro de la zona de la plataforma (registro DNS directo, sin proxy), en un puerto reservado entre 61000 y 64999. En el host, un proxy lleva ese puerto al 5432 del VPS y el firewall deja entrar solo las IP de la lista.

El certificado es propio de ese nombre (no un comodín) y se instala en el motor: PostgreSQL activa ssl con TLS 1.2 como mínimo y recarga la configuración sin reiniciar. El usuario de la app solo entra por TCP con TLS (hostssl) y solo a su base.

Regla de la listaDetalle
FormatoIPv4 o IPv6, con o sin prefijo; una IP suelta es /32 o /128. Nada de nombres, loopback, link-local ni multicast
AmplitudPrefijo mínimo /24 en IPv4 y /48 en IPv6 por defecto; 0.0.0.0/0 y ::/0 se rechazan en la lista
TamañoHasta 20 entradas; vacía = cerrado para todos
Nombres de hostHasta 5 (allowed_hosts), con hasta 8 IP públicas cada uno; se vuelven a resolver cada 5 minutos y una falla de DNS conserva lo último conocido
Abierto a todosSolo con allow_any y confirm_open juntos; la respuesta lo dice en open_to_any

La lista es del motor, no de cada base: cambiarla desde cualquier base de PostgreSQL cambia el acceso de todas las bases de ese motor en ese VPS (la respuesta lo indica con shared_with_engine y affected_apps).

  1. Se agregan las reglas de firewall que faltan, una por CIDR (ufw allow from <cidr> to any port <puerto> proto tcp).
  2. Después se quitan las que sobran y se cortan sus conexiones abiertas.
  3. Recién entonces se crea el listener y se verifica: reglas iguales a la lista, ninguna regla abierta a cualquiera, listener presente y handshake TLS con el nombre correcto.

Desinstalar, persistencia y copias

  • Sin delete_data (lo normal): se cierra el acceso externo propio y la app deja de figurar como instalada, pero la base, su usuario y sus datos quedan en el VPS. La siguiente reconciliación la muestra como detectada. No se pierde nada.
  • Con delete_data: true (confirmado dos veces, solo admin): DROP DATABASE y, si el usuario lo creó Apps (app<id>_<hex>), también el rol y su contraseña guardada. Nunca un usuario del cliente ni el administrador del motor.
  • El motor nunca se desinstala desde Apps: queda instalado aunque no tenga bases.
  • Si el VPS o el motor no responden, desinstalar_app con forzar=true intenta cada paso una vez, da de baja la app y lista en not_cleaned lo que quedó en el servidor.
  • Los datos viven en el disco del VPS y viajan en su copia semanal, que se toma con el VPS andando. Para una copia consistente de una base concreta conviene además un volcado propio desde el gestor de bases de datos (exportar) o con las herramientas del motor.

Esquema

  1. App en el mismo VPS: 127.0.0.1:5432
  2. Cliente externo: db-<id>:<puerto> con sslmode=verify-full
  3. Firewall del host: solo las IP de la lista
  4. Proxy del host: puerto 61000–64999 → 5432 del VPS
  5. PostgreSQL: hostssl + scram-sha-256, solo su base
Conexiones a una base PostgreSQL

Resultado esperado

La base aparece en Apps colgando de su motor. ver_acceso_app muestra estado, host, puerto, TLS y la lista aplicada; revelar_credencial_app da usuario, contraseña y la URI. La URI solo existe cuando el acceso externo está listo:

postgresql://app<id>_<hex>@db-<vps>.<zona>:<puerto>/<base>?sslmode=verify-full

Errores y resolución

ErrorCausa¿Reintentar?
base_ya_existeYa hay una base con ese nombre de otro dueño; no se pisa ni se adoptaNo
motor_detenido / motor_instalandose / motor_no_instaladoEl motor está parado, instalándose o no se pudo instalarSí
base_no_creada / permisos_no_verificadosFalló el SQL o los permisos del rol no verificaronSí
lista_ip_invalida / lista_ip_demasiado_ampliaEntrada mal formada o rango más ancho que el mínimo; data.invalid dice cuálesCorrigiendo la lista
credencial_legacy / cuentas_del_motor_sin_verificar / tls_no_disponibleEl motor todavía no cumple las condiciones para exponerse (claves antiguas, cuentas propias del cliente sin verificar o TLS pendiente)Tras resolverlo
acceso_externo_no_habilitadoEl acceso externo no está habilitado para ese tipo en la empresaNo
cierre_no_verificadoAl cerrar quedó una regla o el listener; se informa lo que realmente haySí

Estado y pasos de cada operación: ver_operacion_app (incluye si es reintentable). Una operación fallida reintentable se retoma desde el paso que falló con reintentar_operacion_app, sin reinstalar el motor. Pedir la lista que ya está aplicada no encola nada (idempotent: true).

Relación con MCP

Todo el ciclo de la base se opera por MCP con los permisos de la persona que autorizó al agente. Referencia en /mcp/docs y /mcp/bases-de-datos.

  • Lectura: ver_catalogo_apps, ver_preflight_app, listar_apps_empresa, ver_app, ver_acceso_app (host, puerto, TLS y lista de IP, sin secretos), ver_operacion_app.
  • Modifican: instalar_app con app_type: postgres (exige idempotency_key), cambiar_lista_ip_app (solo si su dueño es admin; las IP se nombran explícitas: la IP del agente no es la de la persona), reintentar_operacion_app, refrescar_apps.
  • Sensible: revelar_credencial_app (solo admin, no acepta idempotency_key; la respuesta viene marcada sensible: true y la auditoría registra que se reveló, nunca el valor).
  • Destructivas: revocar_acceso_app (cierra el acceso externo de todo el motor) y desinstalar_app (con delete_data borra los datos; requiere confirm_delete_data).

Para leer y escribir datos dentro de la base, el agente usa las herramientas del gestor de bases de datos. Un agente autorizado solo lectura no ve las herramientas que modifican ni puede revelar credenciales.

Funciones relacionadas