MySQL

Bases MySQL 8 o MariaDB en el VPS con cuenta propia que exige TLS, permisos solo sobre su base y acceso externo por IP.

  • Categoría: Servidores e infraestructura
  • Estado: Disponible
  • Requisitos: Un VPS activo con la función mysql disponible; Si el motor ya existe: MySQL 8.x o MariaDB 10.4 o superior; Ser miembro con acceso al VPS para instalar; admin para revelar, abrir 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 MySQL es una app con su cuenta propia. El motor (MySQL o MariaDB) es uno por VPS y lo comparten sus bases; si no existe, se instala MySQL desde los paquetes de la distribución.

  • Crear varias bases utf8mb4, cada una con su cuenta y sus permisos.
  • Revelar usuario, contraseña y URI de conexión.
  • Abrir el motor a internet solo para ciertas IP, con TLS.
  • Desde la pestaña MySQL del VPS: bases, usuarios, permisos y tablas.

¿Cuándo conviene usarlo?

  • Aplicaciones que esperan MySQL (PHP, Laravel, muchas herramientas de terceros).
  • Un WordPress usa este mismo motor, con una base y una cuenta propias por sitio.

¿Cómo funciona en ArduMaker?

  1. Motor: si el agente dice que no está instalado, se instala una vez (mysql-server y mysql-client); si ya existe, nunca se reinstala ni cambia de versión.
  2. Variante: se lee mysql --version. Solo MySQL 8.x o MariaDB 10.4+; otra cosa falla motor_no_soportado sin ejecutar SQL.
  3. Credencial app<id>_<hex> con contraseña aleatoria, guardada cifrada antes de tocar el VPS.
  4. Se entra como administrador por socket, con la contraseña en un archivo de opciones efímero, nunca en la línea de comandos.
  5. Cuenta 'app<id>_<hex>'@'127.0.0.1' con REQUIRE SSL, y GRANT ALL PRIVILEGES solo sobre <base>.*, sin GRANT OPTION, antes de crear la base.
  6. CREATE DATABASE <base> CHARACTER SET utf8mb4 solo si no existía.
  7. Verificación: TLS exigido, sin privilegios globales y permisos sobre una sola base, la suya.

La cuenta es @'127.0.0.1' a propósito: no % (abriría la cuenta a cualquier origen) ni localhost (socket, sin TLS). Cuando el servidor no tiene partial_revokes (o es MariaDB), el _ del nombre se escapa en el GRANT, porque ahí es un comodín.

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
MotorMySQL o MariaDB escuchando en 127.0.0.1:3306
Baseutf8mb4; un nombre igual con otras mayúsculas cuenta como el mismo
Cuenta'app<id>_<hex>'@'127.0.0.1' REQUIRE SSL
TLS en MySQL 8SET PERSIST + ALTER INSTANCE RELOAD TLS, sin reinicio
TLS en MariaDBArchivo 90-ardumaker-tls.cnf; la primera vez requiere reiniciar el motor

Acceso desde internet y lista de IP

Por defecto el motor escucha solo dentro del VPS (3306 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 3306 del VPS y el firewall deja entrar solo las IP de la lista.

El certificado es propio del nombre db-<id> y lo termina mysqld: el proxy del host solo pasa TCP. La cuenta de la app exige SSL venga por donde venga.

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 MySQL 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): se borra la base y, si la cuenta la creó Apps, también la cuenta y su contraseña guardada. Nunca una cuenta del cliente ni root.
  • 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:3306
  2. Cliente externo: db-<id>:<puerto> con verificación de identidad TLS
  3. Firewall y proxy del host: solo IP de la lista → 3306 del VPS
  4. mysqld: termina TLS; la cuenta exige SSL y ve solo su base
Conexiones a una base MySQL

Resultado esperado

La URI (sin contraseña) aparece cuando el acceso externo está listo; la completa se obtiene con revelar_credencial_app.

mysql://app<id>_<hex>@db-<vps>.<zona>:<puerto>/<base>?ssl-mode=VERIFY_IDENTITY
mysql://app<id>_<hex>@db-<vps>.<zona>:<puerto>/<base>?sslMode=verify-full   (MariaDB)

Errores y resolución

ErrorCausa¿Reintentar?
motor_no_soportadoNo es MySQL 8.x ni MariaDB 10.4+; no se toca nadaNo
base_ya_existeHay una base con ese nombre (sin distinguir mayúsculas) sobre la que la cuenta no tiene permisosNo
motor_detenido / base_no_creada / permisos_no_verificadosMotor parado, SQL fallido o permisos que no verificaronSí
requiere_reinicioMariaDB necesita reiniciar para el primer TLS; el acceso no se abre hasta resolverloTras reiniciar
Acceso externo no pedidoEl motor tiene cuentas anónimas o sin contraseña que el listener expondríaTras limpiar esas cuentas

Las demás causas (lista de IP, TLS, credenciales antiguas, cierre no verificado) son las mismas que en PostgreSQL. El seguimiento es con ver_operacion_app y reintentar_operacion_app.

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: mysql (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