Acceso Seguro a MySQL en Minutos con Docker y VPS

author

José Rafael Gutierrez

hace 1 mes

Introducción: El Pedido Urgente de un Cliente

Imagina este escenario: estás gestionando un proyecto en un VPS de Digital Ocean, con una aplicación corriendo suavemente en Docker y una base de datos MySQL alojada en su servicio gestionado. Todo va sobre ruedas hasta que, de pronto, llega un mensaje del cliente: "¡Necesito acceso directo a la base de datos para análisis operativos, y lo necesito ya!". Suena simple, pero hay obstáculos. La base de datos de Digital Ocean no viene con una interfaz web tipo DBMS para explorarla. Está protegida por IP, pero el proveedor de internet del cliente rota las direcciones constantemente. Además, Digital Ocean no permite crear usuarios con permisos específicos ni limitar permisos como "solo lectura" desde su interfaz, y el cliente está presionando por una solución rápida. ¿Qué haces? Te cuento cómo lo resolví en menos de 25 minutos, dejando al cliente feliz y con una solución sólida bajo el brazo.

¿Por Qué No Dar Acceso Directo? La Seguridad Primero

Antes de lanzarme a teclear comandos, me detuve a pensar: ¿por qué no simplemente darle las credenciales del administrador de la base de datos y listo? La respuesta es clara: seguridad. Dar acceso directo sería como dejar la puerta de tu casa abierta con un cartel que dice "entren cuando quieran". Digital Ocean, al ser un servicio gestionado, no te permite crear usuarios con permisos específicos ni contraseñas personalizadas desde su interfaz. Proteger el acceso por IP no era viable, porque las IPs del cliente cambiaban todo el tiempo. Si le daba acceso completo, ¿qué pasaría si, en un descuido, ejecuta un DROP TABLE o un DELETE FROM y borra datos críticos? Podría ser un desastre para su operación y un dolor de cabeza para mí. Así que decidí construir una solución controlada y segura, aprovechando herramientas que ya tenía a mano.

El Procedimiento Paso a Paso: Navegando con Docker, nginx-proxy y Let’s Encrypt

Paso 0: La Infraestructura Original en docker-compose.yml

Antes de añadir phpMyAdmin, mi archivo docker-compose.yml ya incluía servicios clave como nginx-proxy y letsencrypt, que gestionaban el enrutamiento y la seguridad de mis aplicaciones web. Aquí está el estado original del archivo:

services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /certs:/etc/nginx/certs:ro
      - /etc/nginx/vhost.d:/etc/nginx/vhost.d:rw
      - /usr/share/nginx/html:/usr/share/nginx/html:rw
      - /var/run/docker.sock:/tmp/docker.sock:ro
    labels:
      - com.github.nginx-proxy.nginx=true
    networks:
      - proxy-network

  letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    container_name: nginx-proxy-letsencrypt
    volumes:
      - /certs:/etc/nginx/certs:rw
      - /etc/nginx/vhost.d:/etc/nginx/vhost.d:rw
      - /etc/acme.sh:/etc/acme.sh:rw
      - /usr/share/nginx/html:/usr/share/nginx/html:rw
      - /var/run/docker.sock:/var/run/docker.sock:ro
    depends_on:
      - nginx-proxy
    networks:
      - proxy-network

  customer-app:
    image: josefo727/customer-app
    container_name: customer-app-container
    environment:
      - VIRTUAL_HOST=customer-app.jose-gutierrez.com
      - LETSENCRYPT_HOST=customer-app.jose-gutierrez.com
      - LETSENCRYPT_EMAIL=josefo727@gmail.com
    volumes:
      - ./../customer-app:/app
      - ./../customer-app/docker/supervisord.conf:/opt/docker/etc/supervisor.d/system.conf
    depends_on:
      - nginx-proxy
      - redis
    networks:
      - proxy-network

networks:
  proxy-network:
    driver: bridge

En este punto, nginx-proxy ya estaba configurado como un guardia de tráfico, redirigiendo las solicitudes a los contenedores correctos según el dominio especificado en VIRTUAL_HOST. Por su parte, letsencrypt se encargaba de generar certificados SSL automáticamente para cada dominio definido en LETSENCRYPT_HOST. Esta base me permitió añadir nuevos servicios web de forma rápida y segura.

Paso 1: Conectar y Crear un Usuario Seguro

Primero, me conecté a la base de datos MySQL desde la consola usando las credenciales de administrador que Digital Ocean proporciona. El comando fue algo así:

mysql -h db-mysql-customer-do-user-12345678-0.c.db.ondigitalocean.com -P 25060 -u superadmin -p

Una vez dentro, creé un usuario específico para el cliente, restringiendo su acceso a la IP de mi VPS y limitando sus permisos a solo lectura:

CREATE USER 'customer_user'@'vps-ip' IDENTIFIED BY 'secure-password';
GRANT SELECT ON customer_db.* TO 'customer_user'@'vps-ip';

¿Por qué solo SELECT? Para evitar accidentes. Si el cliente, en un momento de prisa, ejecutara un comando destructivo como DELETE FROM, los datos críticos estarían en riesgo. Con permisos de solo lectura, le doy una herramienta para analizar, no para destruir.

Para asegurarme de que todo estuviera listo, también visité la interfaz de DigitalOcean (DO). Seleccioné el motor SQL correspondiente, busqué la lista de usuarios, y reseteé la contraseña del usuario customer_user. Esto garantiza que las credenciales sean válidas y seguras, especialmente porque DigitalOcean genera contraseñas robustas. Este paso rápido en la web de DO me dio tranquilidad antes de continuar.

db mysql

Paso 2: Configurar phpMyAdmin con Docker

El cliente necesitaba una interfaz web, así que elegí phpMyAdmin. Como ya tenía un entorno Docker, integrarlo fue sencillo. Añadí este bloque a mi docker-compose.yml:

phpmyadmin:
  image: phpmyadmin/phpmyadmin
  container_name: customer-db-container
  environment:
    - PMA_HOST=db-mysql-customer-do-user-12345678-0.c.db.ondigitalocean.com
    - PMA_ARBITRARY=0
    - PMA_PORT=25060
    - PMASSL=1
    - VIRTUAL_HOST=customer-db.jose-gutierrez.com
    - LETSENCRYPT_HOST=customer-db.jose-gutierrez.com
    - LETSENCRYPT_EMAIL=josefo727@gmail.com
  restart: always
  depends_on:
    - nginx-proxy
  networks:
    - proxy-network

No incluí usuario ni contraseña en el archivo, dejando que phpMyAdmin solicite las credenciales al iniciar sesión. Esto me permitió usar el usuario customer_user que creé, manteniendo la seguridad y el control.

Paso 3: La Magia de nginx-proxy y Let’s Encrypt

Aquí entra en juego la potencia de mi infraestructura existente. Al añadir el bloque de phpMyAdmin con VIRTUAL_HOST=customer-db.jose-gutierrez.com, nginx-proxy detectó automáticamente el nuevo servicio y comenzó a redirigir el tráfico desde ese subdominio al contenedor de phpMyAdmin. Al mismo tiempo, letsencrypt vio la variable LETSENCRYPT_HOST=customer-db.jose-gutierrez.com y generó un certificado SSL para el dominio, asegurando una conexión cifrada. Este proceso fue automático gracias a la configuración previa en el docker-compose.yml original, sin necesidad de ajustar configuraciones manuales de Nginx o certificados.

Paso 4: El Archivo docker-compose.yml Actualizado

Tras añadir el bloque de phpMyAdmin, mi archivo docker-compose.yml quedó así:

services:
  nginx-proxy:
    image: jwilder/nginx-proxy
    container_name: nginx-proxy
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /certs:/etc/nginx/certs:ro
      - /etc/nginx/vhost.d:/etc/nginx/vhost.d:rw
      - /usr/share/nginx/html:/usr/share/nginx/html:rw
      - /var/run/docker.sock:/tmp/docker.sock:ro
    labels:
      - com.github.nginx-proxy.nginx=true
    networks:
      - proxy-network

  letsencrypt:
    image: jrcs/letsencrypt-nginx-proxy-companion
    container_name: nginx-proxy-letsencrypt
    volumes:
      - /certs:/etc/nginx/certs:rw
      - /etc/nginx/vhost.d:/etc/nginx/vhost.d:rw
      - /etc/acme.sh:/etc/acme.sh:rw
      - /usr/share/nginx/html:/usr/share/nginx/html:rw
      - /var/run/docker.sock:/var/run/docker.sock:ro
    depends_on:
      - nginx-proxy
    networks:
      - proxy-network

  customer-app:
    image: josefo727/customer-app
    container_name: customer-app-container
    environment:
      - VIRTUAL_HOST=customer-app.jose-gutierrez.com
      - LETSENCRYPT_HOST=customer-app.jose-gutierrez.com
      - LETSENCRYPT_EMAIL=josefo727@gmail.com
    volumes:
      - ./../customer-app:/app
      - ./../customer-app/docker/supervisord.conf:/opt/docker/etc/supervisor.d/system.conf
    depends_on:
      - nginx-proxy
      - redis
    networks:
      - proxy-network

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: customer-db-container
    environment:
      - PMA_HOST=db-mysql-customer-do-user-12345678-0.c.db.ondigitalocean.com
      - PMA_ARBITRARY=0
      - PMA_PORT=25060
      - PMASSL=1
      - VIRTUAL_HOST=customer-db.jose-gutierrez.com
      - LETSENCRYPT_HOST=customer-db.jose-gutierrez.com
      - LETSENCRYPT_EMAIL=josefo727@gmail.com
    restart: always
    depends_on:
      - nginx-proxy
    networks:
      - proxy-network

networks:
  proxy-network:
    driver: bridge

El nuevo servicio de phpMyAdmin se integró perfectamente con nginx-proxy y letsencrypt, aprovechando sus capacidades para enrutamiento y seguridad sin requerir ajustes adicionales.

Paso 5: Ejecutar y Probar

Con el archivo actualizado, ejecuté:

docker-compose up -d --remove-orphans --build

En menos de un minuto, el contenedor de phpMyAdmin estaba funcionando. Fui a customer-db.jose-gutierrez.com en mi navegador, inicié sesión con las credenciales de customer_user, y todo estaba listo. Le envié las credenciales al cliente, quien pudo acceder y explorar sus datos de inmediato.

Conclusión: Ganar-Ganar con Docker y docker-compose

Resolver este desafío en menos de 25 minutos fue una victoria doble: el cliente quedó satisfecho con una solución rápida y segura, y yo reafirmé el poder de Docker y docker-compose. Mostrar el docker-compose.yml antes y después deja claro cómo nginx-proxy y letsencrypt simplifican la adición de servicios como phpMyAdmin. Estas herramientas son como un barco veloz que te lleva a puerto sin tormentas: con contenedores, puedo escalar servicios en minutos, mantener todo organizado en un solo archivo, y asegurar conexiones con SSL sin esfuerzo. Si estás empezando, te invito a probar estas tecnologías: son una puerta a la innovación con tiempo de sobra para disfrutar el viaje. ¡Cliente feliz, yo con una solución eficiente, y tú, quizás, con ganas de sumergirte en el mundo de los contenedores!

José Rafael Gutierrez

Soy un desarrollador web con más de 14 años de experiencia, especializado en la creación de sistemas a medida. Apasionado por la tecnología, la ciencia, y la lectura, disfruto resolviendo problemas de...

Suscríbete para Actualizaciones

Proporcione su correo electrónico para recibir notificaciones sobre nuevas publicaciones o actualizaciones.