Autor: ciberkaos

  • Tor como herramienta de privacidad (no como escondite)

    Tor como herramienta de privacidad (no como escondite)

    Para la realizacion de esta práctica es importante usar:

    Maquina virtual Aislada (NAT)

    VPN

    1. ¿Qué es la deep web?

    Explicación clara y sin mitos:

    • Web superficial: lo indexado por buscadores (Google, Bing).
    • Deep web: contenido no indexado (intranets, foros privados, paneles, correos, Tor).
    • Dark web: pequeña parte de la deep web accesible con Tor.

    Importante: deep web ≠ ilegal.
    La mayoría es aburrida… y eso es buena señal.


    2. ¿Qué es Tor y cómo funciona?

    Tor = The Onion Router

    Conceptos clave:

    • tráfico cifrado en capas (como una cebolla 🧅)
    • múltiples nodos (entrada → intermedio → salida)
    • ningún nodo conoce todo el camino

    Resultado:

    • se oculta el origen del tráfico
    • no se garantiza anonimato absoluto

    Aquí deja clara esta idea:

    Tor reduce rastreo, no elimina la responsabilidad.


    3. Riesgos reales de la deep web (esto es clave)

    Esto es teoría obligatoria, no opcional:

    • Falsa sensación de anonimato
      • errores humanos = exposición
    • Malware camuflado
      • PDFs, scripts, descargas “inocentes”
    • Estafas y desinformación
      • foros que parecen técnicos y no lo son
    • Contenido ilegal accidental
      • enlaces encadenados sin control
    • Fingerprinting
      • configuración incorrecta del navegador

    Conclusión clara para el alumno:

    Tor no te protege de malas decisiones.

    Instalación de Tor Browser

    Requisitos

    • Máquina virtual (VirtualBox / VMware)
    • Sistema: Linux (Ubuntu o Debian)
    • NO usar sistema anfitrión

    Instalación de Tor Browser en Ubuntu (método recomendado)

    ⚠️ Importante
    NO vamos a instalar Tor desde apt install torbrowser-launcher
    Para docencia y seguridad, se usa siempre el Tor Browser oficial.


    Requisitos previos

    • Ubuntu Desktop
    • Usuario sin privilegios raros
    • Conexión a Internet
    • VM recomendada (muy recomendable en clase)

    Paso 1 — Descargar Tor Browser (fuente oficial)

    1. Abre el navegador normal (Firefox).
    2. Entra en la web oficial:
    https://www.torproject.org/download/
    
    1. Descarga Tor Browser para Linux (64-bit)
      → se descarga un archivo tipo:
    tor-browser-linux-x86_64-XX.X.tar.xz
    

    Paso 2 — Extraer el archivo

    1. Abre una terminal.
    2. Ve a la carpeta de descargas:
    cd ~/Descargas
    
    1. Extrae el archivo:
    tar -xf tor-browser-linux-x86_64-*.tar.xz
    

    Se creará una carpeta llamada algo como:

    tor-browser
    

    Paso 3 — Ejecutar Tor Browser

    1. Entra en la carpeta:
    cd tor-browser
    
    1. Lanza el navegador:
    ./start-tor-browser.desktop
    

    👉 La primera vez:

    • pulsa Conectar
    • espera a que establezca circuito Tor

    Si ves el mensaje:

    “Congratulations. This browser is configured to use Tor.”

    Todo va bien.


    Paso 4 — Verificar que funciona

    Dentro de Tor Browser:

    1. Abre:
    https://check.torproject.org
    
    1. Debe aparecer:

    ✔ You are using Tor

    1. Observa:
    • IP diferente
    • país distinto al real (normalmente)


    Primeros enlaces civilizados

    • Usa Tor Browser, nada de Chrome con capa ninja.
    • No te registres con correos personales.
    • Lee más de lo que escribes al principio. La deep web premia al observador paciente.

    Foros y comunidades técnicas

    El “Reddit” de Tor, pero con moderación decente en los subforos técnicos.
    Hay secciones de:

    • programación
    • seguridad informática
    • privacidad
    • Linux
      No todo es oro, pero hay debates serios.

    https://dreadytofatroptsdj6io7l3xptbet6onoyno2yv7jicoxknyazubrad.onion


    Los 31 mejores sitios web oscuros y enlaces Onion en Tor (2026)

    Tu guía para los mejores enlaces de la dark web. Encuentra acceso seguro y anónimo a correo electrónico seguro, wikis ocultas, herramientas de Bitcoin y mercados ocultos de internet.

    https://tornews-com.translate.goog/deep-web/lists/dark-web-sites/?_x_tr_sl=auto&_x_tr_tl=es&_x_tr_hl=es


    Tor News

    btenga noticias de ciberseguridad, guías detalladas y conocimientos de expertos sobre Tor, herramientas de privacidad y seguridad digital de profesionales de la industria.

    https://tornews.com


    Tor.link es, dicho sin dramatismos, un directorio y monitor de servicios .onion.
    No es una web “de la deep web” en sí, sino un catálogo que observa la deep web desde fuera.

    Tor.link es un sitio web (accesible desde la web normal y desde Tor) que:

    • cataloga direcciones .onion
    • comprueba si están vivas o muertas
    • muestra metadatos básicos del servicio
    • NO aloja el contenido del .onion

    Es como una ficha técnica o una ITV de servicios Tor.


    ✅ Lo que SÍ hace

    • Te dice si un .onion responde o no
    • Muestra:
      • última vez visto
      • tiempo de respuesta
      • fecha de creación
    • A veces incluye:
      • descripciones
      • firmas PGP
      • capturas antiguas

    ❌ Lo que NO hace

    • No es un buscador “en tiempo real”
    • No garantiza que un enlace sea ético o legal
    • No mantiene vivos los servicios
    • No es Tor ni forma parte del Proyecto Tor

    https://tor.link


    Buscadores

    Ahmia

    http://juhanurmihxlp77nkq76byazcldy2hlmovfu2epvl5ankdibsot4csyd.onion/

    http://zb2jtkhnbvhkya3d46twv3g7lkobi4s62tjffqmafjibixk6pmq75did.onion/gdark/search.php

    Listas de Enlaces

    The Hidden Wiki (versión moderada)

    Más directorio que buscador.
    Útil como mapa inicial.

    http://zqktlwiuavvvqqt4ybvgvi7tyo4hjl5xgfuvpdf6otjiycgwqbym2qad.onion

    https://thehiddenwiki.org/

    OnionLinks

    http://s4k4ceiapwwgcm3mkb6e4diqecpo7kvdnfr5gg7sph7jjppqkvwwqtyd.onion/

    ONION.live

    https://onion.live

    dark.fail

    http://darkfailenbsdla5mal2mxn2uz66od5vtzd5qozslagrfzachha3f3id.onion/

    Enlaces .onion civiles y verificados (2026)

    Pégalos tal cual en Tor Browser:

    DuckDuckGo (onion oficial):
    https://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion
    
    Ahmia (buscador de servicios onion, con aviso anti-clones):
    http://juhanurmihxlp77nkq76byazcldy2hlmovfu2epvl5ankdibsot4csyd.onion
    
    Dark.Fail (verificador/directorio, útil contra phishing):
    http://darkfailenbsdla5mal2mxn2uz66od5vtzd5qozslagrfzachha3f3id.onion
    

    Y algunos “sitios-serios” (para la práctica, sin morbo):

    Debian (mirror onion):
    http://5ekxbftvqg26oir5wle3p27ax3wksbxcecnm6oemju7bjra2pn26s3qd.onion
    
    Qubes OS (onion):
    http://qubesosfasa4zl44o4tws22di6kepyzfeqv3tg4e3ztknltfxqrymdad.onion
    

    Más enlaces, ojo donde te metes

    ONION.live

    Un completo motor de búsqueda de servicios ocultos tipo cebolla en la red oscura.

    https://onion.live


    Sci-Hub (sí, polémico, pero útil)

    Para papers científicos y técnicos que están detrás de muros de pago.
    Especialmente útil si investigas IA, criptografía o redes.

    https://sci-hub.st


    Software libre y proyectos alternativos

    Riseup

    Servicios éticos: correo, listas, documentación técnica, activismo digital.
    Muy usado por desarrolladores y admins paranoicos (en el buen sentido).

    🔗 https://riseup.net
    🔗 Tor: http://vww6ybal4bd7szmgncyruucpgfkqahzddi37ktceo3ah7ngmcopnpyyd.onion/index.es.html



    Filosofía que subyace (esto es importante)

    La deep web útil no va de esconderse, va de control:

    • control de identidad
    • control de datos
    • control de ruido

    Muchos desarrolladores veteranos están ahí porque no quieren algoritmos decidiendo qué leer ni anuncios persiguiéndolos como NPCs rotos.


    Foros y comunidades generales (multi-tema, tono adulto)



    Lectura, ensayo y cultura digital

    The Hidden Wiki (la versión buena)

    Ojo: hay muchas copias horribles.
    Esta es curada y bastante limpia, más directorio que wiki salvaje.

    http://zqktlwiuavvvqqt4ybvgvi7tyo4hjl5xgfuvpdf6otjiycgwqbym2qad.onion/wiki/index.php/Main_Page

    Útil como mapa, no como destino final.


    Biblioteca anónima / textos largos

    Textos sobre:

    • tecnología
    • ética digital
    • privacidad
    • historia de Internet

    No es espectacular, pero es honesta.

    🔗 http://libraryofbabel.info
    (no es Tor-only, pero muy usada desde Tor)


    Tecnología sin ruido comercial

    Linux / Unix discussions (Tor mirrors)

    Foros pequeños, técnicos, sin SEO, sin tutoriales “10 cosas que no sabías”.

    🔗 http://linuxbbq.org
    🔗 mirrors accesibles desde Tor


    DuckDuckGo (versión Tor)

    El más limpio y didáctico para empezar.
    Busca tanto web normal como algunos servicios .onion.

    🔗 https://duckduckgo.com
    🔗 Tor: http://duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad.onion

    ✔ Ideal para:

    • primeras comparativas
    • ver diferencias de resultados
    • no meter ruido

    Ahmia

    Buscador especializado en servicios .onion.
    Filtra bastante contenido ilegal.

    🔗 https://ahmia.fi
    🔗 Tor: http://juhanurmihxlp77nkq76byazcldy2hlmovfu2epvlv6k3cdoviyd.onion

    ✔ Muy recomendable para docencia
    ✔ Transparente sobre lo que indexa y lo que no


    Torch

    Uno de los más antiguos.
    Más “crudo”, pero sigue siendo útil si se usa con criterio.

    🔗 http://torchg2lt7z4k4u7.onion

    ⚠️ Advertencia docente:

    • no usar búsquedas libres
    • solo términos indicados por el profesor

    no todos los enlaces son igual de fiables


    Dark.Fail

    No es buscador de contenido, sino verificador de servicios Tor.
    Sirve para comprobar si un .onion es legítimo o falso.

    🔗 https://dark.fail
    🔗 Tor: http://darkfailenbsd5c.onion

    OnionShare

    OnionShare es una pequeña criatura elegante del ecosistema de la privacidad. Un programa libre que permite compartir archivos, chatear o incluso alojar una web temporal de forma anónima usando la red Tor. Nada de servidores centrales, nada de intermediarios curiosos mirando por la cerradura digital.

    La idea esencial es esta: cuando usas OnionShare, tu propio ordenador se convierte en un mini-servidor oculto dentro de Tor (lo que se llama servicio onion). Genera una dirección .onion única y solo quien tenga ese enlace puede acceder. El archivo no se sube a “la nube”, viaja directamente de tu máquina a la del receptor, cifrado y envuelto en capas como una cebolla criptográfica —de ahí lo de Onion.

    Funciones principales, en lenguaje humano:

    • Compartir archivos anónimamente
      Envías un enlace .onion, el receptor descarga directamente desde tu equipo.
    • Recibir archivos
      Puedes crear un buzón seguro para que otros te envíen cosas sin revelar identidades.
    • Chat privado
      Conversaciones cifradas sin servidores centrales.
    • Modo sitio web
      Hospedar temporalmente una página accesible solo por Tor.

    Detalles curiosos del universo técnico:
    OnionShare usa los onion services de Tor, que evitan exponer tu IP real porque el tráfico nunca sale directamente a Internet “normal”. Todo va dentro de la red Tor. Además, puedes hacer que el enlace sea de un solo uso, que se cierre al descargar, o mantenerlo abierto como un pequeño faro oculto en el océano oscuro.

    Ahora, realidad práctica (la ciencia ama la honestidad):
    No es magia. Si el equipo está comprometido, si compartes datos sensibles sin cuidado, o si el receptor guarda el archivo y lo difunde… la privacidad desaparece. Tor protege el transporte, no las decisiones humanas. La criptografía no arregla imprudencias.

    En el ecosistema de ciberseguridad educativa —que tú sueles cultivar con tus alumnos— OnionShare es perfecto para enseñar:

    • anonimato vs privacidad
    • transferencia directa vs nube
    • modelo de amenaza (threat model)
    • OPSEC básica (seguridad operacional)

    Es una herramienta simple, pero conceptualmente profunda. Una cebolla pequeña que abre puertas enormes hacia el pensamiento crítico sobre identidad, confianza y control en la red.

    🧅 OnionShare


    ✅ Actividad 1 — Comparativa (Normal vs Tor)

    Preparación (obligatoria)

    1. Abre la VM (Ubuntu/Debian).
    2. Asegúrate de tener:
      • un navegador normal (Firefox/Chromium)
      • Tor Browser instalado
    3. Cierra sesiones personales (Google, redes, etc.). No logins.

    1.1 Elegir el sitio (mismo sitio en ambos)

    1. El profesor indica 1 URL permitida (lista cerrada).
    2. Los alumnos copian esa URL en un bloc de notas dentro de la VM (para no equivocarse).

    Regla: no se usa buscador; se pega la URL tal cual.


    1.2 Comparar IP (Normal vs Tor)

    A) Navegador normal

    1. Abre Firefox/Chromium.
    2. Entra en un verificador de IP (elige 1, por ejemplo):
      • https://check.torproject.org/ (sirve también para Tor)
      • https://icanhazip.com/ (muestra IP en texto)
      • https://ifconfig.me/ (similar)
    3. Apunta en el informe:
      • IP que aparece
      • país aproximado (si lo muestra)

    B) Tor Browser

    1. Abre Tor Browser y espera a que conecte.
    2. Entra en el mismo verificador de IP de antes.
    3. Apunta:
      • IP que aparece
      • país aproximado

    Resultado esperado: las IPs deben ser distintas.


    1.3 Comparar idioma detectado

    1. En navegador normal, visita:
      • https://www.wikipedia.org/
      • o https://www.google.com/ (sin iniciar sesión)
    2. Observa si el sitio:
      • se pone en español automáticamente
      • detecta región/idioma sin preguntar
    3. Repite lo mismo en Tor Browser.

    Anota:

    • ¿sale en español o en otro idioma?
    • ¿te pide elegir idioma/región?

    Resultado esperado: Tor suele “adivinar” menos tu idioma/región (aunque no siempre).


    1.4 Fingerprint básico (huella del navegador)

    Usaremos una página de prueba legal y educativa.

    A) Navegador normal

    1. Entra en:
      • https://coveryourtracks.eff.org/ (EFF)
    2. Ejecuta la prueba (sin instalar nada).
    3. Anota observaciones simples:
      • ¿te considera “único” o “poco común”?
      • ¿detecta bloqueadores?
      • ¿hay muchos datos recogidos?

    B) Tor Browser

    1. Repite lo mismo en Tor Browser.
    2. Anota diferencias.

    Resultado esperado: Tor suele parecer “menos único” (más homogéneo) porque muchos usuarios comparten configuración similar.


    ✅ Actividad 2 — Análisis de cabeceras HTTP (Headers)

    Aquí se ve qué “dice” tu navegador cuando pide una página: User-Agent, idioma, etc.

    2.1 Ver headers desde una web (fácil y limpio)

    A) Navegador normal

    1. Abre:
      • https://httpbin.org/headers
    2. Verás un JSON con headers recibidos.
    3. Busca y anota:
      • User-Agent
      • Accept-Language
      • cualquier cosa tipo DNT, Referer (si aparece)

    B) Tor Browser

    1. Abre el mismo enlace en Tor:
      • https://httpbin.org/headers
    2. Anota los mismos campos.

    Qué comparar

    • User-Agent: ¿es diferente?
    • Accept-Language: ¿manda “es-ES” o algo más neutro?
    • ¿Tor envía menos info o más estandarizada?

    2.2 Bonus opcional (para alumnos más finos)

    Usar otra página equivalente:

    • https://www.whatismybrowser.com/detect/what-http-headers-is-my-browser-sending

    Mismo procedimiento: normal vs Tor, comparar.


    Reglas de seguridad (se imprimen arriba del guion)

    • no interactuar (solo lectura)
    • VM obligatoria
    • sin logins personales
    • no descargar archivos
    • no buscar enlaces nuevos
  • Actividad – Json Invertido

    Actividad – Json Invertido

    Tu tarea es analizar el siguiente código JavaScript y crear el archivo JSON correcto para que funcione sin errores.

    EJERCICIO 1

     Código JavaScript (index.html + script.js)

    Guarda estos archivos y ábrelos en un navegador. (Es  necesario que utilices modo servidor como un contendor o XAMPP)

    https://drive.google.com/file/d/1tK86N1EGZlHKjtxSIHV0TJZ4IcgYyWMd/view?usp=sharingLinks to an external site.

    Si el JSON está bien hecho, al abrir index.html en el navegador debería mostrarse:

    Lista de Productos

    • Tarta de Chocolate – $15 – Stock: 10
    • Cheesecake – $12 – Stock: 5
    • Brownie – $8 – Stock: 7

    EJERCICIO 2

    Tu tarea es analizar el código JavaScript y crear el archivo JSON correcto para que funcione sin errores.

    Código JavaScript (index.html + script.js)

    Guarda estos archivos y ábrelos en un navegador.

    https://drive.google.com/file/d/1spFSlotW1K6Cnm-N5LejSGQhn79phaAd/view?usp=sharingLinks to an external site.

  • Instalación y Primeros Pasos con Docker en Windows

    Instalación y Primeros Pasos con Docker en Windows

    Hay conceptos que, una vez se ven, el cerebro hace clic y ya no vuelve atrás. La diferencia entre máquinas virtuales y contenedores es uno de esos momentos fundacionales en la vida de cualquier persona que empieza a pensar como ingeniero de sistemas.

    La metáfora clásica —y sorprendentemente precisa— es esta:

    👉 Las máquinas virtuales son casas completas.
    👉 Los contenedores son apartamentos dentro del mismo edificio.

    La diferencia que cambia la forma de diseñar sistemas

    Esto es lo verdaderamente importante —y lo que merece que tus alumnos lo entiendan bien:

    Con VMs piensas en:

    «Voy a crear servidores.»

    Con contenedores empiezas a pensar:

    «Voy a desplegar servicios.»

    Instalación de Docker Desktop y Portainer en Windows

    En los entornos tecnológicos actuales, la virtualización ligera mediante contenedores se ha convertido en un estándar para el desarrollo, despliegue y administración de aplicaciones.

    Docker es una plataforma que permite empaquetar aplicaciones junto con todas sus dependencias dentro de contenedores, garantizando que funcionen de forma idéntica en cualquier sistema.

    En este tema aprenderemos a preparar un equipo Windows para trabajar con Docker Desktop y a instalar Portainer, una herramienta gráfica que facilita la gestión de contenedores.

    Esta instalación será la base de todos los proyectos que realizaremos posteriormente.


    1. ¿Por qué Docker necesita Linux en Windows?

    Docker utiliza tecnologías propias del kernel de Linux como:

    • namespaces → aislamiento de procesos
    • cgroups → control de recursos
    • union filesystems → capas de almacenamiento

    Windows no dispone de estas tecnologías de forma nativa.

    La solución es WSL2.


    ¿Qué es WSL?

    WSL (Windows Subsystem for Linux) es una característica de Windows que permite ejecutar un kernel real de Linux dentro del sistema operativo sin necesidad de usar una máquina virtual tradicional.

    • No es un emulador.
    • No es una traducción de comandos.
    • Es Linux funcionando dentro de Windows.

    Ventajas de WSL2 frente a una VM clásica:

    • Arranque casi instantáneo
    • Menor consumo de memoria
    • Mejor rendimiento de disco
    • Integración directa con el sistema

    Docker Desktop utiliza WSL2 como motor de virtualización.

    👉 Sin WSL2, Docker simplemente no puede funcionar correctamente en Windows moderno.



    3. Activar la virtualización

    La virtualización debe estar habilitada en la BIOS/UEFI. Si has estado trabajando con maquinas virtuales con Virtual Box o VMWare seguro que ya lo tienes.

    Cómo comprobarlo
    1. Abrir el Administrador de tareas
    2. Ir a la pestaña Rendimiento → CPU

    Debe aparecer:

    Virtualización: Habilitada
    

    Si está deshabilitada:

    • Reiniciar el equipo
    • Entrar en BIOS/UEFI
    • Activar alguna de estas opciones:
    Intel VT-x
    Intel Virtualization Technology
    AMD-V
    SVM Mode
    

    Guardar cambios y reiniciar.


    4. Instalación de WSL2

    Abrir PowerShell como administrador.

    Ejecutar:

    wsl --install
    

    Este comando realiza automáticamente:

    • Instalación del kernel Linux
    • Activación de componentes necesarios
    • Instalación de Ubuntu
    • Configuración de WSL2 como versión por defecto

    Reiniciar el equipo cuando lo solicite.


    Comprobar que WSL2 está funcionando

    wsl -l -v
    

    Resultado esperado:

    NAME      STATE    VERSION
    Ubuntu    Running  2
    

    Si aparece versión 1:

    wsl --set-version Ubuntu 2
    

    5. Instalación de Docker Desktop

    Descargar desde la web oficial: https://www.docker.com/products/docker-desktop/

    Evitar instaladores de terceros.


    Instalación

    Ejecutar el instalador.

    Durante el proceso es MUY importante verificar que esté marcada la opción:

    Use WSL 2 instead of Hyper-V

    Esto permitirá que Docker funcione con el backend moderno.

    Finalizar la instalación y reiniciar si es necesario.


    6. Primer arranque de Docker

    Abrir Docker Desktop desde el menú inicio.

    El primer arranque puede tardar unos minutos.

    Cuando Docker esté listo aparecerá el mensaje:

    Docker is running
    

    Verificación desde terminal

    Abrir PowerShell o CMD:

    docker version
    

    Si todo está correcto, se mostrará información del cliente y del servidor Docker.

    Probar el primer contenedor:

    docker run hello-world
    

    Si aparece un mensaje de bienvenida, la instalación es correcta.

    Este pequeño contenedor confirma que:

    • Docker descarga imágenes
    • Puede crear contenedores
    • El motor funciona

    7. Configuración recomendada de Docker Desktop

    Ir a:

    Settings → Resources
    

    Asignación recomendada:

    • RAM: 4–6 GB
    • CPU: 2 o más
    • Swap: 1–2 GB

    Si los equipos son potentes, aumentar estos valores mejora la experiencia.


    8. ¿Qué es Portainer?

    Portainer es una interfaz web que permite administrar Docker sin depender únicamente de la terminal.

    Con Portainer es posible:

    • Crear contenedores
    • Gestionar imágenes
    • Administrar volúmenes
    • Supervisar recursos
    • Eliminar servicios

    Es una herramienta muy utilizada en entornos profesionales y facilita enormemente el aprendizaje inicial.


    9. Instalación de Portainer

    Docker desktop permite instalar Portainter como un plugin mas.

    Si deseas hacerlo desde terminal tambien puedes:

    Crear un volumen para almacenar los datos de Portainer.

    docker volume create portainer_data
    

    Ahora ejecutamos el contenedor:

    docker run -d -p 9443:9443 \
    --name portainer \
    --restart=always \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v portainer_data:/data \
    portainer/portainer-ce
    

    ¿Qué estamos haciendo?

    • -d → ejecuta el contenedor en segundo plano
    • -p 9443:9443 → expone la interfaz web
    • docker.sock → permite a Portainer comunicarse con Docker
    • volumen /data → guarda la configuración

    10. Primer acceso a Portainer

    Abrir el navegador:

    https://localhost:9443
    

    Es normal que el navegador advierta sobre el certificado.

    Continuar igualmente.




    Problemas habituales

    Docker no arranca

    Generalmente ocurre porque:

    • WSL no está activo
    • Virtualización deshabilitada
    • Conflictos con Hyper-V

    Solución típica:

    wsl --update
    wsl --shutdown
    

    Reiniciar Docker.


    Error de memoria

    Asignar más RAM desde:

    Settings → Resources
    

    WSL extremadamente lento

    Ejecutar:

    wsl --shutdown
    

    Y reiniciar Docker.


    A partir de aquí comenzaremos a trabajar con:

    • imágenes
    • contenedores
    • redes
    • volúmenes
    • orquestación

  • Docker desde Terminal

    Docker desde Terminal

    Antes de comenzar la misión, tu equipo debe estar preparado.

    Docker será el motor que dará vida a toda la infraestructura.

    Instalar Docker correctamente desde la terminal en Ubuntu utilizando el repositorio oficial.

    Evita instalar versiones antiguas desde repositorios genéricos.

    Queremos herramientas profesionales.


    Actualizar el sistema

    sudo apt update
    sudo apt upgrade -y
    

    Esto reduce la probabilidad de conflictos de dependencias.

    Los ingenieros prudentes actualizan primero.


    Instalar paquetes necesarios

    sudo apt install apt-transport-https ca-certificates curl software-properties-common -y
    

    Estos paquetes permiten a Ubuntu comunicarse de forma segura con repositorios externos.


    Añadir la clave oficial de Docker

    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker.gpg
    

    Una clave GPG verifica que el software proviene de la fuente legítima.

    Sin esto, cualquiera podría hacerse pasar por Docker.

    Y eso sería… una idea terrible.


    Añadir el repositorio oficial

    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker.gpg] \
      https://download.docker.com/linux/ubuntu \
      $(lsb_release -cs) stable" | \
      sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    

    Ahora Ubuntu sabrá dónde buscar Docker.


    Instalar Docker Engine

    sudo apt update
    sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
    

    Aquí ocurre la magia.


    Verificar la instalación

    sudo systemctl status docker
    

    Debe aparecer como:

    👉 active (running)

    Ahora prueba:

    sudo docker run hello-world
    

    Si ves el mensaje de bienvenida…

    Docker está vivo.


    MUY recomendado — Usar Docker sin sudo

    Trabajar con sudo constantemente es incómodo.

    Haz esto:

    sudo usermod -aG docker $USER
    

    Después CIERRA SESIÓN o reinicia.

    No es opcional — el cambio de grupo necesita recargar permisos.

    Comprueba:

    docker run hello-world
    

    Video de la lección

    Crear y ejecutar contenedores

    Contenedor interactivo básico

    docker run -it ubuntu
    

    Qué ocurre aquí:

    • run → crea el contenedor si no existe
    • -i → mantiene STDIN abierto
    • -t → asigna terminal
    • ubuntu → imagen base

    Si no está descargada, Docker la bajará automáticamente.

    Para salir sin detener el contenedor:

    Ctrl + P + Q
    

    Para salir deteniéndolo:

    exit
    

    Nombrar un contenedor

    docker run -it --name mi-ubuntu ubuntu
    

    Nombrar contenedores evita trabajar con IDs largos e ilegibles.

    Tu yo del futuro te lo agradecerá.


    Persistencia usando carpeta del host

    docker run --name mi-ubuntu -it -v /datos-persistentes:/datos ubuntu
    

    Qué significa esto:

    HOST → CONTENEDOR
    /datos-persistentes → /datos
    

    Todo lo que guardes en /datos sobrevivirá aunque borres el contenedor.

    Si la carpeta no existe, Docker la crea.


    Persistencia usando volumen Docker (RECOMENDADO)

    Primero crea el volumen:

    docker volume create mi-volumen
    

    Ahora úsalo:

    docker run --name mi-ubuntu -it -v mi-volumen:/datos ubuntu
    

    👉 Docker gestionará la ubicación real del almacenamiento.

    Ventajas:

    • Más seguro
    • Mejor rendimiento
    • Independiente del sistema de carpetas

    ⚠️ Nota para Windows

    docker run -it -v C:\ruta\local:/ruta/en/contenedor ubuntu
    

    En Ubuntu Desktop NO se usa esta sintaxis.

    La equivalente sería:

    docker run -it -v /home/usuario/datos:/datos ubuntu
    

    Consultar información del sistema Docker

    Ver contenedores en ejecución

    docker ps
    

    Ver TODOS los contenedores

    docker ps -a
    

    Aquí aparecerán también los detenidos.

    Muy útil para debugging.


    Ver imágenes descargadas

    docker images
    

    O su versión moderna:

    docker image ls
    

    Crear imágenes personalizadas

    Imagina que has instalado paquetes dentro del contenedor y no quieres repetir el proceso.

    Puedes congelar ese estado.

    docker commit mi-ubuntu mi-imagen-personalizada
    

    Ahora tienes una nueva imagen lista para usar:

    docker run -it mi-imagen-personalizada
    

    📌 Importante para mentalidad profesional:

    docker commit es útil para aprender…

    Pero en entornos reales se prefiere usar Dockerfile porque permite reproducibilidad.


    Operaciones con contenedores

    Arrancar un contenedor detenido

    docker start mi-ubuntu
    

    Arrancar y conectarte directamente

    docker start -ai mi-ubuntu
    

    Parámetros:

    • -a → attach (conectar terminal)
    • -i → modo interactivo

    Ejecutar comandos dentro de un contenedor activo

    docker exec -it mi-ubuntu bash
    

    Esto abre una nueva shell.

    Extremadamente usado por administradores.


    Eliminar un contenedor

    ⚠️ Debe estar detenido.

    docker rm mi-ubuntu
    

    Para forzar:

    docker rm -f mi-ubuntu
    

    Limpiar contenedores detenidos automáticamente

    docker container prune
    

    Docker pedirá confirmación.

    Este comando es famoso por liberar gigas de disco en laboratorios.

  • Del Contenedor a la Base de Datos: MySQL Docker + Workbench

    Del Contenedor a la Base de Datos: MySQL Docker + Workbench

    Vamos a creae una base de datos naciendo en un contenedor, y desde fuera un cliente elegante (Workbench) hablando con ella a través del vacío digital. Eso es red, puertos y aislamiento en acción.

    1. Crear contenedor MySQL.
    2. Publicar puerto al host.
    3. Conectarse desde MySQL Workbench.
    4. Ver que el contenedor es “otro sistema”, pero accesible.

    Arquitectura:

    Workbench (Host)  →  localhost:3306  →  Contenedor MySQL

    Paso 1 — Crear contenedor MySQL

    En terminal:

    docker run -d \
      --name mysql-demo \
      -e MYSQL_ROOT_PASSWORD=1234 \
      -e MYSQL_DATABASE=demo \
      -p 3306:3306 \
      mysql:8

    Qué significa cada parte (esto es lo que debes explicar)

    • -d → modo daemon (segundo plano)
    • --name mysql-demo → nombre del contenedor
    • MYSQL_ROOT_PASSWORD → password del root (obligatorio)
    • MYSQL_DATABASE → crea BD inicial
    • -p 3306:3306 → publica puerto MySQL al host
    • mysql:8 → imagen oficial

    Paso 2 — Verificar que el contenedor está vivo

    docker ps

    Debe aparecer algo como:

    mysql-demo   mysql:8   Up ...   0.0.0.0:3306->3306/tcp

    Si no arranca:

    docker logs mysql-demo

    MySQL tarda unos segundos en inicializar. Paciencia científica.


    Paso 3 — Conectar desde MySQL Workbench

    En Workbench → New Connection

    Parámetros:

    • Host: 127.0.0.1
    • Port: 3306
    • User: root
    • Password: 1234

    Test Connection → Connect.

    Si todo va bien, verás la BD demo.


    Paso 4 — Prueba real

    En Workbench ejecuta:

    CREATE TABLE alumnos (
      id INT AUTO_INCREMENT PRIMARY KEY,
      nombre VARCHAR(50)
    );
    
    INSERT INTO alumnos (nombre) VALUES ('Neo'), ('Trinity');
    
    SELECT * FROM alumnos;

    Luego demuestra algo mágico:

    Entra al contenedor

    docker exec -it mysql-demo mysql -uroot-p1234

    Dentro:

    USE demo;
    SELECT * FROM alumnos;

    Los datos están ahí → Workbench y contenedor hablan con la misma base.

    Eso hace que los alumnos comprendan el concepto de servicio expuesto por red.

    Problemas típicos (para que no te sabotee el universo)

    Puerto ocupado

    Si tienes MySQL local:

    lsof -i :3306

    Cambia puerto:

    -p3307:3306

    Y en Workbench → puerto 3307.


    No conecta

    Comprobar:

    docker ps
    docker logs mysql-demo

    Esperar inicialización (MySQL tarda).


    Workbench no conecta pero CLI sí

    A veces el plugin de auth:

    docker exec -it mysql-demo mysql -uroot-p1234

    En SQL

    ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '1234';
    FLUSH PRIVILEGES;
  • Del Código al Contenedor: Dominando Dockerfile Paso a Paso

    Del Código al Contenedor: Dominando Dockerfile Paso a Paso

    Un Dockerfile es una receta: describes pasos para construir una imagen.
    Luego ejecutas esa imagen y eso crea un contenedor.

    • Dockerfile: receta
    • Imagen: “plato ya cocinado” (plantilla)
    • Contenedor: “plato servido” (instancia en ejecución)

    Primer Dockerfile “Hola Mundo” (Python)

    Estructura de proyecto:

    mkdir dockerfile-101 && cd dockerfile-101
    mkdir app

    Crea app/app.py:

    cat > app/app.py << 'EOF'
    print("Hola desde un contenedor 😼")
    EOF

    Crea Dockerfile:

    FROM python:3.12-slim
    
    WORKDIR /app
    COPY app/ /app/
    
    CMD ["python", "app.py"]
    EOF

    Construir la imagen:

    docker build -t hola-dockerfile:1.0 .

    Ejecutar:

    docker run --rm hola-dockerfile:1.0

    Puntos clave :

    • Qué hace FROM, WORKDIR, COPY, CMD.
    • Por qué CMD va en formato lista (["..."]) y no como string.

    FROM

    Define la imagen base sobre la que se construirá la nueva imagen.

    Ejemplo:

    FROM python:3.12-slim

    Qué ocurre:

    • Docker descarga la imagen si no existe localmente.
    • Marca el punto de partida del sistema de archivos del contenedor.
    • Solo puede existir una FROM por etapa (en multi-stage puede haber varias).

    Buenas prácticas:

    • Usar imágenes ligeras (alpine, slim).
    • Evitar latest en producción (usar versiones concretas).

    WORKDIR

    Establece el directorio de trabajo dentro del contenedor.
    Equivale a ejecutar cd permanentemente para las siguientes instrucciones.

    WORKDIR /app

    Qué ocurre:

    • Si el directorio no existe, Docker lo crea automáticamente.
    • Todas las instrucciones posteriores (COPY, RUN, CMD) se ejecutan desde esa ruta.

    Ventaja:
    Evita rutas largas y mejora la legibilidad.


    COPY

    Copia archivos desde el sistema host (tu máquina) al contenedor.

    COPY app/ /app/

    Qué ocurre:

    • Se copian los archivos del contexto de build (directorio actual).
    • Se crea una nueva capa en la imagen.
    • Si cambia cualquier archivo copiado, Docker invalida la caché desde ese punto.

    Diferencia con ADD:

    • COPY solo copia archivos.
    • ADD puede descomprimir tar y descargar URLs (menos predecible, se recomienda COPY).

    Buenas prácticas:
    Copiar primero dependencias y luego código para aprovechar la caché:

    COPY requirements.txt .RUN pip install -r requirements.txtCOPY . .

    CMD

    Define el comando por defecto que ejecutará el contenedor al iniciarse.

    CMD ["python", "app.py"]

    Si el usuario pasa otro comando en docker run, este sobrescribe CMD.

    Ejemplo:

    docker run mi-imagen python otro.py

    ¿Por qué CMD debe ir en formato lista?

    Existen dos formas:

    Forma shell (string)

    CMD python app.py

    Docker lo ejecuta así:

    /bin/sh -c "python app.py"

    Problemas:

    • Se crea un proceso intermedio (sh).
    • Señales Unix (SIGTERM, SIGINT) no llegan correctamente al proceso principal.
    • Problemas con contenedores que deben parar limpiamente (servidores, apps, etc.).

    Forma exec (lista) — RECOMENDADA

    CMD ["python", "app.py"]

    Ventajas:

    • No usa shell intermedia.
    • El proceso es PID 1 dentro del contenedor.
    • Recibe correctamente señales del sistema.
    • Comportamiento más predecible.
    • Es el estándar en producción.

    Regla mental:
    Si el comando no necesita shell (|, &&, variables, etc.) → usar formato lista.

    EXPOSE

    Documenta qué puerto usa la aplicación dentro del contenedor.

    EXPOSE80

    Importante:

    • No abre puertos.
    • Solo sirve como metadato informativo.
    • Para publicar puerto se usa -p en docker run.

    Ejemplo:

    docker run -p8080:80 mi-imagen

    ENV

    Define variables de entorno dentro del contenedor.

    ENV APP_ENV=production

    Uso típico:

    • Configuración de aplicaciones
    • Rutas
    • Flags de ejecución

    Acceso en Python:

    importosprint(os.getenv("APP_ENV"))

    RUN

    Ejecuta comandos durante la construcción de la imagen.

    RUN apt update && apt install -y curl

    Diferencia con CMD:

    • RUN ocurre en build
    • CMD ocurre en runtime

    Cada RUN crea una capa nueva → conviene agrupar comandos.


    Dockerfile con dependencias (requests) + capas

    Cambia app/app.py:

    
    
    
    
    
    import requests
    print("IP pública (si hay salida a Internet):", requests.get("https://api.ipify.org").text)
    

    Crea app/requirements.txt:

    cat > app/requirements.txt << 'EOF'
    requests==2.32.3
    EOF

    Actualiza Dockerfile (truco de cache y capas):

    FROM python:3.12-slim
    
    WORKDIR /app
    
    # 1) Copiamos requirements primero (mejor cache)
    COPY app/requirements.txt /app/requirements.txt
    RUN pip install --no-cache-dir -r requirements.txt
    
    # 2) Copiamos el código después
    COPY app/ /app/
    
    CMD ["python", "app.py"]
    

    Rebuild y run:

    docker build -t ip-check:1.0 .
    docker run --rm ip-check:1.0

    Lección nerd útil: si solo cambia el código, Docker reutiliza la capa de pip install y construye más rápido.


    .dockerignore (para no meter “basura” en la imagen)

    Crea .dockerignore:

    .git
    __pycache__/
    *.pyc
    *.log
    .DS_Store
    

    Crear un archivo “gordo” y comprobar que no entra:

    dd if=/dev/zero of=archivo_gordo.bin bs=1M count=50
    docker build -t ignore-test:1.0 .


    Dockerfile para una web (Nginx estático) en 30 segundos

    Estructura:

    mkdir -p web
    cat > web/index.html << 'EOF'
    <h1>Hola desde Nginx en Docker</h1>
    <p>Si ves esto, EXPOSE era postureo, pero ayuda a documentar 😄</p>
    EOF

    Dockerfile:

    cat > Dockerfile << 'EOF'
    FROM nginx:alpine
    COPY web/ /usr/share/nginx/html/
    EXPOSE 80
    EOF

    Build + run:

    docker build -t web-nginx:1.0 .
    docker run --rm -p 8080:80 web-nginx:1.0

    Abrir: http://localhost:8080


    Ejemplos

    Ejemplo: Dockerfile para una app en Python

    FROM python:3.11-slim
    WORKDIR /app
    COPY requirements.txt .
    RUN pip install -r requirements.txt
    COPY . .
    EXPOSE 5000
    
    CMD ["python", "app.py"]

    Este caso muestra cómo:

    • Se usa una imagen base de Python.
    • Se instala lo necesario con pip.
    • Se copia el código de la aplicación.
    • Se expone el puerto del servicio (por ejemplo, Flask).
    • Se ejecuta la app con CMD.

    Ejemplo: Dockerfile para PHP + Apache

    FROM php:8.2-apache
    COPY ./htdocs /var/www/html/
    EXPOSE 80

    Así pueden tener un servidor web con PHP.
    Luego ejecutan:

    docker build -t miwebphp . docker run -d -p 8080:80 miwebphp

    Buenas prácticas

    • Siempre usa imágenes base oficiales.
    • Agrupa las instalaciones en una sola instrucción RUN para reducir el tamaño de la imagen.
    • Usa .dockerignore para no copiar archivos innecesarios.
    • Define variables con ENV si hay configuraciones reutilizables.
    • Usa ENTRYPOINT cuando quieras ejecutar siempre el mismo comando, incluso si el usuario añade argumentos extra.

    Instrucción WORKDIR

    WORKDIR establece el directorio de trabajo dentro del contenedor.
    Equivale a hacer un cd /app dentro del entorno del contenedor antes de ejecutar los siguientes comandos.

    Por ejemplo:

    FROM python:3.11-slim
    WORKDIR /app
    COPY . .
    RUN pip install -r requirements.txt
    
    CMD ["python", "app.py"]

    Qué hace realmente:

    1. Crea el directorio si no existe (/app en este caso).
    2. Cambia el contexto: a partir de esa línea, todos los comandos (COPYRUNCMD, etc.) se ejecutan dentro de /app.

    Así, COPY . . copia el contenido del proyecto local dentro del contenedor en /app, y python app.py se ejecutará desde ahí.


    Sin WORKDIR

    Si no la usas, Docker ejecuta todo desde / (la raíz del contenedor).
    Ejemplo:

    FROM python:3.11-slim
    
    COPY . .
    
    CMD ["python", "app.py"]

    Esto también funcionará, pero el código quedará desperdigado en / — algo poco limpio y poco profesional.


    ¿Qué son las variables de entorno en Docker?

    Las variables de entorno (ENV) son valores que se inyectan en el entorno del contenedor y que las aplicaciones pueden usar para configurarse.

    Por ejemplo:

    • Contraseña del root
    • Nombre de la base de datos
    • Usuario y contraseña de aplicación

    Docker las pasa al contenedor con la instrucción ENV en el Dockerfile o con la opción -e en el docker run.


    Ejemplo básico de Dockerfile con MySQL

    # Imagen base oficial de MySQL
    
    FROM mysql:8.0
    
    # Variables de entorno
    
    ENV MYSQL_ROOT_PASSWORD=supersegura
    
    ENV MYSQL_DATABASE=tienda
    
    ENV MYSQL_USER=usuario
    
    ENV MYSQL_PASSWORD=1234
    
    # Exponer el puerto de MySQL
    
    EXPOSE 3306

    Explicación:

    • MYSQL_ROOT_PASSWORD: establece la contraseña del usuario root.
    • MYSQL_DATABASE: crea automáticamente una base de datos llamada tienda.
    • MYSQL_USER y MYSQL_PASSWORD: crean un usuario adicional con permisos sobre esa base.
    • EXPOSE 3306: indica el puerto por defecto del servicio.

    Construcción y ejecución

    Construir la imagen:

    docker build -t mysql-demo .

    Ejecutar el contenedor:

    docker run -d -p 3306:3306 --name contmysql mysql-demo

    Con eso, MySQL arrancará con la configuración establecida en las variables del Dockerfile.


    Alternativa más flexible (sin tocar el Dockerfile)

    También se pueden pasar las variables en tiempo de ejecución, lo que se recomienda para producción (para no dejar contraseñas dentro del Dockerfile):

    docker run -d -p 3306:3306 \
    
    --name mysql-demo \
    
    -e MYSQL_ROOT_PASSWORD=supersegura \
    
    -e MYSQL_DATABASE=tienda \
    
    -e MYSQL_USER=usuario \
    
    -e MYSQL_PASSWORD=1234 \
    
    mysql:8.0

    Así puedes usar directamente la imagen oficial de MySQL sin construir una nueva.


    Comprobación dentro del contenedor

    Para entrar al contenedor y verificar:

    docker exec -it mysql-demo mysql -u root -p

    Introduce la contraseña (supersegura) y luego:

    SHOW DATABASES;

    Verás que existe la base tienda.


    Usando un .env externo

    Otra práctica útil es usar un archivo .env:

    Archivo .env

    MYSQL_ROOT_PASSWORD=supersegura
    
    MYSQL_DATABASE=tienda
    
    MYSQL_USER=usuario
    
    MYSQL_PASSWORD=1234

    Y luego ejecutarlo con:

    docker run -d --env-file .env -p 3306:3306 --name mysql-demo mysql:8.0

    LABORATORIO – TAREA

    Montar una infraestructura de laboratorio Dockerizada donde cada contenedor representa una parte de una red vulnerable, simulando un pequeño entorno que deben administrar, securizar o incluso “atacar” de forma controlada.


    Cada alumno desplegará una red interna en su máquina virtual Ubuntu Server, con varios contenedores Docker que simulen los componentes de una empresa ficticia hackeable llamada:

    «CyberCorp Ltd.»

    El propósito es:

    • Divertirse con un entorno “ethical hacking friendly”.
    • Aprender a crear y gestionar contenedores.
    • Comprender la comunicación entre ellos
    • Aplicar conceptos de seguridad: contraseñas, puertos, logs, aislamiento, backups.

    Infraestructura propuesta

    ServicioImagen baseRol / DescripciónPuertos
    nginx-webnginx:latestPágina corporativa vulnerable (HTML + PHP)8080
    db-servermysql:8.0Base de datos de empleados con credenciales débiles3306
    ftp-serverdelfer/alpine-ftp-serverServidor FTP para intercambio de archivos21
    mail-servermaildev/maildevServidor SMTP simulado (visualización de correos en web)1080
    monitorgrafana/grafanaPanel de monitorización interno3000
    attackerkalilinux/kali-rollingContenedor con herramientas ofensivasshell interactiva
    loggerfluentdCentralización de l

    1) Preparación en la VM (comandos iniciales)

    # Actualizar y asegurarnos de tener Docker
    sudo apt update && sudo apt install -y docker.io
    # Iniciar y activar Docker
    sudo systemctl enable --now docker
    # Añadir tu usuario al grupo docker (opcional, cerrar sesión/reiniciar)
    sudo usermod -aG docker $USER
    # Crear red y volumenes
    docker network create cybernet
    docker volume create mysql_data
    docker volume create web_html
    

    2) Estructura de carpetas (ejemplo) /home/usuario/hack-containers/

    ├─ web/
    │  ├─ Dockerfile
    │  └─ html/
    │     ├─ index.php
    │     └─ secret/flag.txt
    ├─ mysql/
    │  ├─ Dockerfile
    │  └─ init-db/
    │     └─ init.sql
    ├─ ftp/
    │  └─ Dockerfile
    ├─ mail/
    │  └─ Dockerfile
    ├─ attacker/
    │  └─ Dockerfile
    └─ logger/
       └─ Dockerfile

    3) Dockerfile + archivos — ejemplos

    A) MySQL (mysql/Dockerfile)

    Usamos la imagen oficial de MySQL como base, y añadimos un init SQL.

    FROM mysql:8.0
    
    # Copiamos scripts de inicialización (se ejecutan al primer arranque)
    
    COPY init-db/ /docker-entrypoint-initdb.d/ VOLUME ["/var/lib/mysql"]
    
    EXPOSE 3306

    mysql/init-db/init.sql (ejemplo con credenciales débiles y tabla)

    CREATE DATABASE empleados;
    
    USE empleados;
    
    CREATE TABLE usuarios ( id INT AUTO_INCREMENT PRIMARY KEY, usuario VARCHAR(50), password VARCHAR(50) );
    
    INSERT INTO usuarios (usuario, password) VALUES ('admin','admin123');

    Construir y ejecutar:

    cd mysql
    
    docker build -t ctf-mysql:1.0 . docker run -d --name mysql --network cybernet \
    
    -e MYSQL_ROOT_PASSWORD=rootroot \
    
    -v mysql_data:/var/lib/mysql \
    
    ctf-mysql:1.0

    B) Web vulnerable (PHP + Apache) (web/Dockerfile)

    FROM php:8.2-apache
    
    # Copiamos el código vulnerable
    
    COPY html/ /var/www/html/
    
    # Habilitamos mod_rewrite por si hace falta
    
    RUN a2enmod rewrite
    
    EXPOSE 80

    web/html/index.php (vulnerable ejemplo: login sin sanitizar — para SQLi)

    <?php
    $conn = new mysqli('mysql','root','rootroot','empleados');
    if(isset($_GET['user'])){
        $user = $_GET['user'];
        $res = $conn->query("SELECT * FROM usuarios WHERE usuario = '$user'");
        if($res && $res->num_rows){
            echo "Usuario encontrado<br>";
        } else {
            echo "No encontrado";
        }
    } else {
        echo '<a href="?user=admin">Comprobar usuario admin</a>';
    }
    ?>

    web/html/secret/flag.txt

    FLAG{has_encontrado_la_bandera_web}

    Construir y ejecutar:

    cd web
    docker build -t ctf-web:1.0 .
    docker run -d --name web --network cybernet \
      -p 8080:80 \
      -v web_html:/var/www/html \
      ctf-web:1.0
    # Copiar el html local al volumen (una sola vez si quieres)
    docker cp html/. web:/var/www/html/

    Nota: en index.php se conecta a host mysql (nombre del contenedor) — así los containers se resuelven por nombre dentro de la red cybernet.


    C) FTP (ftp/Dockerfile) — ejemplo simple con vsftpd

    FROM alpine:3.18
    RUN apk add --no-cache vsftpd openrc
    COPY ftp-config/vsftpd.conf /etc/vsftpd/vsftpd.conf
    # Creamos usuario demo
    RUN adduser -D -h /home/usuario ftpuser && \
        echo "ftpuser:ftp123" | chpasswd && \
        mkdir -p /home/usuario/ftp_upload && chown ftpuser:ftpuser /home/usuario/ftp_upload
    EXPOSE 21 20
    CMD ["sh", "-c", "vsftpd /etc/vsftpd/vsftpd.conf"]

    ftp-config/vsftpd.conf puede ser muy simple para prácticas. (Puedes proveerlo a los alumnos o generar uno básico.)

    Construir y ejecutar:

    cd ftp
    
    docker build -t ctf-ftp:1.0 .
    
    docker run -d --name ftp --network cybernet -p 2121:21 ctf-ftp:1.0

    (Nota: FTP activo/pasivo considera puertos extra; para la práctica usarlo localmente y documentar.)


    D) Maildev (mail server de pruebas) — Dockerfile mínimo

    FROM maildev/maildev:latest 
    EXPOSE 1080 25

    Construir y ejecutar:

    cd mail
    
    docker build -t ctf-mail:1.0 .
    
    docker run -d --name mail --network cybernet -p 1080:1080 ctf-mail:1.0

    Maildev ofrece UI web en :1080 para ver emails enviados por la aplicación vulnerable.


    E) Logger / ELK ligero (logger/Dockerfile) — ejemplo usando busybox+fluentd sería largo.

    Puedes usar Fluentd o simplemente montar un contenedor que recoja docker logs desde fuera. Para simplicidad, propondré un contenedor fluentd base:

    FROM fluent/fluentd:v1.14-1
    
    EXPOSE 24224

    Construir y ejecutar:

    cd logger
    
    docker build -t ctf-logger:1.0 .
    
    docker run -d --name logger --network cybernet -p 24224:24224 ctf-logger:1.0

    F) Contenedor atacante (Kali) (attacker/Dockerfile)

    Instalamos unas herramientas útiles (nmap, nikto, sqlmap, hydra).

    FROM kalilinux/kali-rolling
    RUN apt update && apt install -y nmap nikto sqlmap hydra netcat-openbsd curl && apt clean
    WORKDIR /root
    CMD ["/bin/bash"]

    Construir y ejecutar (modo interactivo):

    cd attacker
    
    docker build -t ctf-kali:1.0 .
    
    docker run -it --name kali --network cybernet ctf-kali:1.0

    Dentro del contenedor atacas la red cybernet (ej. nmap -sV -p- web).


    4) Notas sobre volúmenes y persistencia

    Para copiar archivos iniciales al volumen, puedes docker cp como mostré.

    Mysql usa -v mysql_data:/var/lib/mysql para persistencia.

    Web usamos -v web_html:/var/www/html para poder editar fuera y reflejar cambios.

    RECUERDA

    # Ver containers
    docker ps -a

    # Logs de un contenedor
    docker logs web

    # Entrar a un contenedor
    docker exec -it mysql bash
    docker exec -it kali bash

    # Consultar red
    docker network inspect cybernet

    # Inspeccionar variables de entorno de un contenedor
    docker inspect -f ‘{{range .Config.Env}}{{println .}}{{end}}’ mysql

    Retos

    1. Recon: desde kali hacer nmap -sV -p- --open 172.18.0.0/16 y documentar servicios.
    2. FTP-cred: conectarse a FTP (ftp user:ftpuser/ftp123) y buscar creds.txt.
    3. DB login: usar credenciales encontradas para entrar en MySQL (mysql -u admin -p admin123).
    4. SQLi: Explorar index.php y realizar un SQL injection básico para sacar datos o la flag.txt.
      • Ejemplo payload: '?user=admin' vs '?user=admin' OR '1'='1'
    5. Mail: desde web, forzar envío de correo a Maildev y ver en la UI :1080.
    6. Detección: activar logs/monitor y buscar patrones sospechosos (pings, intentos SQL).
    7. Hardening: cambiar la configuración, cerrar puertos, poner contraseñas robustas y documentar.
  • Despliegue JEE (Servlet + HTML/JS) + MySQL en Docker

    Despliegue JEE (Servlet + HTML/JS) + MySQL en Docker

    Levantar una aplicación Java (Servlet) desplegada en Tomcat y conectada a MySQL, todo en contenedores Docker, sin frameworks y sin docker-compose.

    Requisitos

    • Docker instalado y funcionando.
    • Tu aplicación empaquetada como WAR (ej: miapp.war).
    • Una carpeta de proyecto en tu PC (host).

    Estructura del proyecto (en el host)

    Crea una carpeta de trabajo, por ejemplo:

    mkdir -p ~/proyecto-jee-docker
    cd ~/proyecto-jee-docker
    mkdir -p deploy

    Copia tu WAR dentro de deploy/:

    cp /RUTA/AL/WAR/miapp.war ./deploy/

    Crear una red Docker para que se vean por nombre

    Esto permite que Tomcat encuentre MySQL usando el nombre del contenedor.

    docker network create red-jee

    Levantar MySQL con volumen (persistencia)

    2.1 Crear volumen para MySQL

    docker volume create mysql_data

    2.2 Lanzar el contenedor MySQL

    ⚠️ Cambia passwords y nombre de BD si quieres, pero respeta la idea.

    docker run -d \
      --name mysql-jee \
      --network red-jee \
      -e MYSQL_ROOT_PASSWORD=root1234 \
      -e MYSQL_DATABASE=appdb \
      -e MYSQL_USER=appuser \
      -e MYSQL_PASSWORD=app1234 \
      -v mysql_data:/var/lib/mysql \
      -p 3307:3306 \
      mysql:8.0

    Qué hace esto:

    • --network red-jee → MySQL estará accesible desde Tomcat por hostname mysql-jee
    • -v mysql_data:/var/lib/mysql → los datos sobreviven aunque borres el contenedor
    • -p 3307:3306 → MySQL será accesible desde tu PC en localhost:3307 (evita conflictos si tu 3306 está ocupado)

    2.3 Comprobar que MySQL está arriba

    docker psdocker logs mysql-jee --tail30

    Crear tablas y datos de prueba (dentro del contenedor)

    Entra a MySQL dentro del contenedor:

    docker exec -it mysql-jee mysql -u root -p

    Escribe la contraseña root1234.

    Luego (ejemplo genérico), crea una tabla y mete algo.


    Preparar el contenedor Tomcat y desplegar el WAR

    Ejecutar Tomcat y montar tu WAR (sin construir imagen propia)

    Opción simple: montas la carpeta deploy en webapps.

    docker run -d \
      --name tomcat-jee \
      --network red-jee \
      -p 8080:8080 \
      -v "$PWD/deploy:/usr/local/tomcat/webapps" \
      tomcat:10.1-jdk17

    Qué hace esto:

    • -p 8080:8080 → accedes en http://localhost:8080
    • -v .../webapps → Tomcat “ve” tu WAR y lo despliega al arrancar

    4.2 Ver logs de Tomcat (muy importante)

    docker logs -f tomcat-jee

    Busca líneas tipo “Deploying web application archive … miapp.war”.


    5) Configurar la conexión a MySQL en tu Java (punto clave)

    Dentro de Docker, NO uses localhost para MySQL desde Tomcat.
    Debes usar el nombre del contenedor:

    • Host: mysql-jee
    • Puerto interno: 3306
    • BD: appdb
    • User: appuser
    • Pass: app1234

    Ejemplo de URL JDBC:

    jdbc:mysql://mysql-jee:3306/appdb?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC

    ✅ Si tu app usa un fichero de configuración (properties), ajusta eso.

    Ejemplo (si usas Properties):

    db.url=jdbc:mysql://mysql-jee:3306/appdb?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC
    db.user=appuser
    db.pass=app1234

    Nota: si tu WAR ya estaba compilado con localhost, toca recompilar con mysql-jee.


    6) Probar la aplicación desde el navegador

    1. Entra a tu app (depende del nombre del WAR):
    • Si el WAR se llama miapp.war, normalmente será:
      • http://localhost:8080/miapp/
    1. Prueba el endpoint del servlet que devuelve JSON:
    • Ejemplo:
      • http://localhost:8080/miapp/api/items

    7) Verificar el fetch() y el DOM (JS)

    Tu JavaScript hará algo así:

    fetch('/miapp/api/items')
      .then(r => r.json())
      .then(data => {
        // pintar DOM
      });

    Errores típicos

    • 404 en fetch → la ruta no coincide con el url-pattern del servlet.
    • El fetch devuelve HTML en vez de JSON → estás pegando a una ruta que devuelve una página, no el endpoint.
    • CORS: si sirves el HTML desde Tomcat y llamas al servlet en el mismo host/puerto, normalmente no hay CORS.
      CORS aparece si llamas a otro host/puerto distinto.

    8) Comandos útiles de operación (vida real)

    Ver contenedores

    docker ps
    docker ps-a

    Parar / arrancar

    docker stop tomcat-jee mysql-jee
    docker start mysql-jee
    docker start tomcat-jee

    Entrar a un contenedor

    docker exec -it tomcat-jee bash
    docker exec -it mysql-jee bash

    Ver redes y comprobar nombres

    docker network ls
    docker network inspect red-jee

    9) Reinicio limpio (sin perder datos)

    Si quieres borrar Tomcat y recrearlo (sin perder DB):

    docker rm -f tomcat-jee
    docker run -d \
      --name tomcat-jee \
      --network red-jee \
      -p 8080:8080 \
      -v "$PWD/deploy:/usr/local/tomcat/webapps" \
      tomcat:10.1-jdk17

    La BD se conserva porque está en el volumen mysql_data.

    Resumen de pasos a realizar:

    • He creado red-jee
    • He creado el volumen mysql_data
    • He levantado mysql-jee y responde
    • He creado tablas y datos en appdb
    • He levantado tomcat-jee con el WAR montado
    • La app carga en http://localhost:8080/...
    • El servlet devuelve JSON válido
    • El fetch() recibe JSON y pinta en el DOM
  • Práctica – Juego de retos de Docker

    Práctica – Juego de retos de Docker

    Reto1

    – Crear un contenedor docker de ubuntu.
    – Instalar python, la libreria request y de mysql
    – Crear una imgane personalizada con el contenedor

    Reto 2

    • Crear un contenedor nuevo con la imagen personalizada  de docker
    • Este contenedor tendra un volumen con una ruta en el disco del anfitrión (Bind)

    Reto 3

    • Crear un repositorio git en la carpeta del anfitrión y unirlo con un repositorio en Github

    Reto 4

    • Crear un contenedor mysql.
    • Crear una base de datos, para almacenar Coches. Los campos seran id, marca, modelo, color, km y precio
    • añadir almenos 10 coches a modo de contenido de muestra.

    Reto 5 

    • Crear en el repositorio Local un programa en python que se conecte a la base de datos y obtenga los registros de la mase de datos.
      – El programa debe listar los datos de los coches guardados en la base de datos de forma estetica.

    ID    MARCA          MODELO         COLOR     KM        PRECIO    
    ————————————————————————
    1     Toyota             Corolla              Blanco    20000     15000     
    2     Honda              Civic                Rojo        30000     17000     
    3     Ford                  Focus               Azul       25000     16000     

    Reto 6

    • Almacenar los datos de conexion a la base de datos en un archivo JSON y que el programa Python los lea de dicho archivo. 
      * crear un nuevo archivo para la modificación.
      * Realiza un commit en cada paso
    • crear el .gitignore para que no suba el archivo con los datos de conexión.

    Reto 7

    Formatear la tabla para que quede mas estetica con la libreria 

    +—-+——–+———+——-+————-+——–+
    | ID | Marca  | Modelo  | Color | Kilometraje  | Precio |
    +—-+——–+———+——-+————-+——–+
    | 1  | Toyota | Corolla | Rojo  | 25000        | 15000  |
    | 2  | Honda  | Civic   | Azul  | 30000        | 18000  |
    | 3  | Ford   | Focus   | Blanco| 40000        | 17000  |
    +—-+——–+———+——-+————-+——–+

    Reto 8

    Crear un conetenedor Mongo y conectarse desde la terminal y utilizando MongoDB
    Crea la una bd e inserta en una colecci’on coches con el criterio de de campos del reto anterior
    Crear un Script de Python  para leer los datos de colecciones de MongoDB y los imprima en una tabla.

    Reto 9

    Subir una imagen personaliza de nuestro contenedor mongodb al  hub de Docker.
    En el Linux Parrot montar un contenedor con la imagen subida al hub.

    Documento de ayuda y pistas para los retos


    🚀 RETO 1 — Tu primer contenedor personalizado

    Objetivo

    Crear un contenedor Ubuntu con Python y librerías necesarias, y convertirlo en una imagen reutilizable.

    Pistas

    Según la guía visual del PDF (pág. 1), debes:

    • Ejecutar un contenedor Ubuntu
    • Instalar:
      • python3
      • pip
      • requests
      • cliente MySQL

    Después crea una imagen personalizada con:

    docker commit <contenedor> mi-imagen-personalizada
    

    ✅ Checkpoint

    Debes poder ver tu imagen con:

    docker images
    

    🚀 RETO 2 — Persistencia con Bind Mount

    Objetivo

    Crear un nuevo contenedor usando la imagen anterior y asociarlo a una carpeta del host.

    📌 En la página 2 del PDF se muestra cómo activar modo interactivo y configurar el volumen.

    Ejemplo conceptual:

    docker run -it -v /ruta/host:/ruta/contenedor mi-imagen
    

    ¿Por qué es importante?

    Si borras el contenedor, los datos seguirán vivos.

    Bienvenido al mundo real.

    ✅ Checkpoint

    Crea un archivo dentro del contenedor y verifica que aparece en el host.


    🚀 RETO 3 — Control de versiones

    Objetivo

    Versionar tu carpeta de trabajo y conectarla a GitHub.

    Tal como aparece en la página 3 del PDF:

    1️⃣ Inicializa Git
    2️⃣ Crea un repositorio en GitHub
    3️⃣ Ejecuta:

    git add .
    git commit -m "Inicio del laboratorio"
    git remote add origin <repo>
    git push -u origin main
    

    ✅ Checkpoint

    Tu repositorio debe contener al menos:

    • scripts
    • documentación
    • archivos de configuración

    🚀 RETO 4 — MySQL en contenedor

    Objetivo

    Levantar un contenedor MySQL y crear una base de datos de coches.

    Campos:

    • id
    • marca
    • modelo
    • color
    • km
    • precio

    Inserta mínimo 10 registros.

    📌 La página 4 del PDF muestra la configuración del contenedor.

    ✅ Checkpoint

    Debe funcionar:

    SELECT * FROM coches;
    

    🚀 RETO 5 — Python contra la base de datos

    Objetivo

    Crear un script que lea los coches desde MySQL.

    Las páginas 5–7 del PDF enseñan:

    • conexión con mysql.connector
    • uso de cursor
    • fetchall()

    Ejemplo conceptual:

    cursor.execute("SELECT * FROM coches")
    for coche in cursor.fetchall():
        print(coche)
    

    ⚠️ Problema común

    En la página 9 se menciona un conflicto con el conector de MySQL en Ubuntu.

    Solución alternativa:

    pip install mysql-connector-python --break-system-packages
    

    ✅ Checkpoint

    El script debe mostrar registros sin errores.


    🚀 RETO 6 — Seguridad básica

    Objetivo

    Separar credenciales en un archivo JSON.

    Después:

    ✔ Leer ese archivo desde Python
    ✔ Crear .gitignore
    ✔ Evitar subir credenciales

    ✅ Checkpoint

    Tu repo NO debe contener:

    config.json
    .env
    credenciales
    

    🚀 RETO 7 — Salida profesional

    Objetivo

    Mostrar los datos en formato tabla.

    En la página 10 se recomienda usar:

    apt install python3-prettytable
    

    Ejemplo:

    from prettytable import PrettyTable
    

    ✅ Checkpoint

    La salida debe parecer una tabla real, no texto desordenado.


    🚀 RETO 8 — MongoDB entra en escena

    Ahora pasamos a bases de datos NoSQL.

    Según las páginas 12–16:

    Debes:

    • Crear contenedor Mongo
    • Definir usuario y contraseña
    • Crear BD
    • Insertar coches
    • Consultar datos

    Acceso desde terminal:

    docker exec -it <contenedor> bash
    mongosh -u usuario -p password --authenticationDatabase admin
    

    ✅ Checkpoint

    Debe funcionar:

    db.coches.find().pretty()
    

    🚀 RETO 9 — Python + MongoDB

    Instala pymongo (pág. 18):

    apt install python3-pymongo
    

    Conecta y recorre la colección.

    Después muestra los datos en tabla (pág. 20).

    ✅ Checkpoint

    Debes tener:

    • script funcional
    • salida formateada
    • conexión estable

    🚀 RETO FINAL — Publica tu imagen

    Sube tu imagen de Mongo personalizada a Docker Hub.

    Después:

    👉 descárgala desde otra máquina (por ejemplo Parrot)
    👉 levanta el contenedor

    Si funciona…

    Acabas de reproducir un flujo profesional real.

  • MySQL + phpMyAdmin con Docker Compose

    MySQL + phpMyAdmin con Docker Compose

    • Desplegar MySQL y phpMyAdmin con docker compose
    • Usar volumen para persistencia
    • Verificar conexión y datos
    • Practicar variables de entorno, puertos y reinicios

    1) Estructura del proyecto

    Crea una carpeta de práctica:

    mkdir mysql-pma-compose
    cd mysql-pma-compose
    

    2) Crear el archivo docker-compose.yml

    Crea el fichero:

    
    # Define los servicios (contenedores) que vamos a ejecutar
    services:
    
      # ----- SERVICIO MYSQL -----
      mysql:
        image: mysql:8.0              # Imagen oficial que se descargará de Docker Hub
        container_name: mysql1        # Nombre fijo del contenedor (opcional, pero útil)
        restart: unless-stopped       # Reinicia el contenedor automáticamente salvo que lo pares tú
    
        # Variables de entorno que usa la imagen de MySQL en su primer arranque
        environment:
          MYSQL_ROOT_PASSWORD: "RootPass_123!"   # Contraseña del usuario root
          MYSQL_DATABASE: "academia"             # Base de datos que se crea automáticamente
          MYSQL_USER: "alumno"                   # Usuario adicional que se creará
          MYSQL_PASSWORD: "AlumnoPass_123!"      # Contraseña del usuario alumno
    
        # Volúmenes → permiten persistencia (los datos sobreviven aunque borres el contenedor)
        volumes:
          - mysql_data:/var/lib/mysql   # Guarda los datos reales de MySQL fuera del contenedor
    
        # Conecta el contenedor a una red Docker interna
        networks:
          - red-bbdd
    
    
      # ----- SERVICIO PHPMYADMIN -----
      phpmyadmin:
        image: phpmyadmin:latest        # Imagen oficial de phpMyAdmin
        container_name: pma1
        restart: unless-stopped
    
        # Indica que MySQL debe arrancar antes (solo orden, no espera a que esté listo)
        depends_on:
          - mysql
    
        # Variables que indican a phpMyAdmin a qué servidor MySQL conectarse
        environment:
          PMA_HOST: "mysql"   # Nombre del servicio MySQL dentro de la red Docker
          PMA_PORT: 3306      # Puerto interno de MySQL
    
        # Publica puertos → expone el servicio al exterior
        ports:
          - "8080:80"         # Puerto HOST:PUERTO_CONTENEDOR → http://localhost:8080
    
        # Conecta phpMyAdmin a la misma red que MySQL para que puedan verse
        networks:
          - red-bbdd
    
    
    # ----- DEFINICIÓN DE VOLÚMENES -----
    volumes:
      mysql_data:   # Volumen gestionado por Docker (no es carpeta local visible normalmente)
    
    
    # ----- DEFINICIÓN DE REDES -----
    networks:
      red-bbdd:     # Red interna donde los contenedores se comunican por nombre (DNS interno)
    

    Qué está pasando aquí (muy importante)

    • services: dos contenedores, uno para MySQL y otro para phpMyAdmin.
    • volumes: mysql_data guarda la base de datos fuera del contenedor (persistencia).
    • networks: red privada para que phpmyadmin pueda acceder a mysql por nombre.
    • depends_on: arranca primero MySQL (ojo: no garantiza “ready”, solo orden de arranque).

    3) Levantar los contenedores

    docker compose up -d
    

    Verifica:

    docker compose ps
    docker compose logs mysql --tail 30
    docker compose logs phpmyadmin --tail 30
    

    4) Acceder a phpMyAdmin

    Abre en el navegador:

    • http://localhost:8080

    Credenciales:

    • Usuario: root (o alumno)
    • Password: RootPass_123! (o AlumnoPass_123!)

    Comprueba:

    • Existe la BD academia

    5) Crear tabla de prueba (verificación)

    Opción A: desde phpMyAdmin (SQL)
    Ejecuta:

    USE academia;
    CREATE TABLE prueba (
      id INT PRIMARY KEY AUTO_INCREMENT,
      texto VARCHAR(50)
    );
    INSERT INTO prueba (texto) VALUES ("hola compose");
    SELECT * FROM prueba;
    

    Opción B: desde terminal (dentro del contenedor)

    docker compose exec mysql mysql -u root -p
    



    Comandos útiles para alumnos

    docker compose ps
    docker compose logs -f
    docker compose exec mysql bash
    docker compose exec phpmyadmin bash
    docker compose down
    docker compose down -v
    

  • [Reto] – Despliegue de servicios con Docker y Docker Compose

    [Reto] – Despliegue de servicios con Docker y Docker Compose

    1. Contexto de la práctica

    En esta práctica vas a desplegar una pequeña infraestructura de servicios utilizando Docker y Docker Compose.

    El objetivo no es únicamente “levantar contenedores”, sino comprender cómo se organizan los servicios, cómo se comunican entre ellos, cómo se almacenan los datos, cómo se exponen puertos al exterior y cómo se puede documentar y justificar una instalación técnica.

    Durante la práctica se trabajará con servicios similares a los vistos en clase:

    • Servidor web HTTP con Apache.
    • PHP para ejecutar código dinámico.
    • MySQL como base de datos relacional.
    • phpMyAdmin como herramienta de administración.
    • MongoDB como base de datos NoSQL.
    • Mongo Express como herramienta de administración.
    • Redes Docker.
    • Volúmenes persistentes.
    • Variables de entorno.
    • Logs y comprobaciones.
    • Copias de seguridad básicas.
    • Documentación técnica.

    2. Objetivos de aprendizaje

    Al finalizar la práctica, el alumno deberá ser capaz de:

    1. Explicar qué es Docker y para qué se utiliza.
    2. Diferenciar una imagen de un contenedor.
    3. Crear y ejecutar contenedores mediante Docker Compose.
    4. Configurar servicios conectados entre sí.
    5. Usar volúmenes para conservar datos.
    6. Usar redes Docker para aislar servicios.
    7. Configurar variables de entorno.
    8. Desplegar una aplicación PHP conectada a MySQL.
    9. Desplegar y consultar una base de datos MongoDB.
    10. Comprobar el estado de los servicios mediante comandos.
    11. Interpretar errores básicos mediante logs.
    12. Documentar una instalación técnica de forma clara.
    13. Defender oralmente el trabajo realizado.

    3. Resultado final esperado

    Al terminar la práctica deberás tener funcionando una infraestructura compuesta por varios contenedores:

    ServicioTecnologíaPuerto externo sugeridoFunción
    Web PHPApache + PHP8080Servidor web principal
    MySQLMySQL3306 o solo internoBase de datos relacional
    phpMyAdminphpMyAdmin8081Administración de MySQL
    MongoDBMongoDB27017 o solo internoBase de datos NoSQL
    Mongo ExpressMongo Express8082Administración de MongoDB

    La aplicación PHP deberá mostrar una página web con información del alumno, conexión a MySQL y, opcionalmente, conexión a MongoDB.


    4. Normas de entrega

    El alumno deberá entregar:

    1. Carpeta completa del proyecto.
    2. Archivo docker-compose.yml.
    3. Archivo .env.
    4. Código fuente de la aplicación PHP.
    5. Capturas de pantalla.
    6. Documento de memoria en PDF.
    7. Comandos utilizados.
    8. Respuestas a las preguntas de reflexión.
    9. Evidencias de funcionamiento.
    10. Breve vídeo opcional de demostración.

    5. Estructura recomendada del proyecto

    Crea una carpeta llamada:

    practica-docker-servicios-nombre-apellido

    Dentro de ella deberás crear una estructura similar a esta:

    practica-docker-servicios-nombre-apellido/

    ├── docker-compose.yml
    ├── .env
    ├── README.md

    ├── web/
    │ ├── index.php
    │ ├── conexion_mysql.php
    │ ├── insertar.php
    │ ├── listar.php
    │ └── estilos.css

    ├── mysql/
    │ └── init.sql

    ├── evidencias/
    │ ├── captura_01_docker_version.png
    │ ├── captura_02_contenedores.png
    │ ├── captura_03_web_funcionando.png
    │ ├── captura_04_phpmyadmin.png
    │ ├── captura_05_mongo_express.png
    │ └── captura_06_logs.png

    └── memoria/
    └── memoria_practica.pdf

    6. Fase 1 — Preparación del entorno

    6.1. Comprobar Docker

    Ejecuta los siguientes comandos:

    docker --version
    docker compose version

    Guarda una captura de pantalla donde se vea el resultado.

    Preguntas de reflexión

    Responde en tu memoria:

    1. ¿Qué diferencia hay entre Docker y Docker Compose?
    2. ¿Qué problema soluciona Docker cuando trabajamos con varios servicios?
    3. ¿Por qué puede ser útil usar Docker en un entorno educativo o de laboratorio?

    6.2. Comprobar que Docker funciona

    Ejecuta:

    docker run hello-world

    Evidencia obligatoria

    Incluye una captura del resultado del comando.

    Pregunta de reflexión

    Explica con tus palabras qué ha ocurrido al ejecutar hello-world.

    Debes mencionar:

    • Si Docker ha usado una imagen local o la ha descargado.
    • Qué es un contenedor.
    • Por qué el contenedor termina después de ejecutarse.

    7. Fase 2 — Primer diseño de la infraestructura

    Antes de escribir el docker-compose.yml, realiza un pequeño esquema de la infraestructura.

    Puedes hacerlo con una tabla, un dibujo o un diagrama sencillo.

    Debe aparecer:

    • Servicio web.
    • Base de datos MySQL.
    • phpMyAdmin.
    • MongoDB.
    • Mongo Express.
    • Red interna.
    • Volúmenes de datos.
    • Puertos publicados.

    Ejemplo conceptual:

    Navegador
    |
    | puerto 8080
    v
    Apache + PHP
    |
    | red interna docker
    v
    MySQL

    Navegador
    |
    | puerto 8081
    v
    phpMyAdmin
    |
    v
    MySQL

    Navegador
    |
    | puerto 8082
    v
    Mongo Express
    |
    v
    MongoDB

    Preguntas de reflexión

    1. ¿Por qué no es recomendable instalar todos los servicios dentro del mismo contenedor?
    2. ¿Qué ventajas tiene separar web, base de datos y herramientas de administración?
    3. ¿Qué servicios deberían ser accesibles desde el navegador y cuáles deberían quedar solo dentro de la red Docker?

    8. Fase 3 — Creación del archivo .env

    Crea un archivo llamado .env en la raíz del proyecto.

    Ejemplo:

    MYSQL_ROOT_PASSWORD=rootpassword
    MYSQL_DATABASE=asir_db
    MYSQL_USER=asir_user
    MYSQL_PASSWORD=asir_password

    MONGO_INITDB_ROOT_USERNAME=admin
    MONGO_INITDB_ROOT_PASSWORD=adminpassword
    MONGO_DATABASE=asir_mongo

    WEB_PORT=8080
    PHPMYADMIN_PORT=8081
    MONGO_EXPRESS_PORT=8082

    Importante

    El archivo .env permite separar configuración del archivo docker-compose.yml.

    No deberías escribir directamente las contraseñas dentro del docker-compose.yml si puedes evitarlo.

    Preguntas de reflexión

    1. ¿Qué ventaja tiene usar variables de entorno?
    2. ¿Por qué puede ser peligroso subir un archivo .env real a un repositorio público?
    3. ¿Qué información sensible contiene este archivo?

    9. Fase 4 — Creación del docker-compose.yml

    Crea el archivo docker-compose.yml.

    Propuesta base:

    services:

    web:
    image: php:8.2-apache
    container_name: asir_web_php
    ports:
    - "${WEB_PORT}:80"
    volumes:
    - ./web:/var/www/html
    depends_on:
    - mysql
    - mongodb
    networks:
    - red_asir

    mysql:
    image: mysql:8.0
    container_name: asir_mysql
    restart: always
    environment:
    MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
    MYSQL_DATABASE: ${MYSQL_DATABASE}
    MYSQL_USER: ${MYSQL_USER}
    MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
    - mysql_data:/var/lib/mysql
    - ./mysql/init.sql:/docker-entrypoint-initdb.d/init.sql
    networks:
    - red_asir

    phpmyadmin:
    image: phpmyadmin:latest
    container_name: asir_phpmyadmin
    restart: always
    ports:
    - "${PHPMYADMIN_PORT}:80"
    environment:
    PMA_HOST: mysql
    PMA_PORT: 3306
    depends_on:
    - mysql
    networks:
    - red_asir

    mongodb:
    image: mongo:latest
    container_name: asir_mongodb
    restart: always
    environment:
    MONGO_INITDB_ROOT_USERNAME: ${MONGO_INITDB_ROOT_USERNAME}
    MONGO_INITDB_ROOT_PASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
    volumes:
    - mongo_data:/data/db
    networks:
    - red_asir

    mongo-express:
    image: mongo-express:latest
    container_name: asir_mongo_express
    restart: always
    ports:
    - "${MONGO_EXPRESS_PORT}:8081"
    environment:
    ME_CONFIG_MONGODB_ADMINUSERNAME: ${MONGO_INITDB_ROOT_USERNAME}
    ME_CONFIG_MONGODB_ADMINPASSWORD: ${MONGO_INITDB_ROOT_PASSWORD}
    ME_CONFIG_MONGODB_SERVER: mongodb
    depends_on:
    - mongodb
    networks:
    - red_asir

    volumes:
    mysql_data:
    mongo_data:

    networks:
    red_asir:
    driver: bridge

    9.1. Preguntas sobre el docker-compose.yml

    Responde en tu memoria:

    1. ¿Qué significa services?
    2. ¿Qué diferencia hay entre image y container_name?
    3. ¿Para qué sirve ports?
    4. ¿Qué diferencia hay entre el puerto de la izquierda y el de la derecha en esta línea?
    - "8080:80"
    1. ¿Para qué sirve volumes?
    2. ¿Qué pasaría con los datos de MySQL si no usásemos un volumen?
    3. ¿Qué hace depends_on?
    4. ¿Qué limitación tiene depends_on?
    5. ¿Para qué sirve definir una red propia llamada red_asir?
    6. ¿Por qué el servicio web puede conectarse a MySQL usando el nombre mysql?

    10. Fase 5 — Script inicial de MySQL

    Dentro de la carpeta mysql, crea el archivo init.sql.

    Ejemplo:

    CREATE TABLE IF NOT EXISTS alumnos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    apellido VARCHAR(100) NOT NULL,
    curso VARCHAR(100) NOT NULL,
    fecha_registro TIMESTAMP DEFAULT CURRENT_TIMESTAMP
    );

    INSERT INTO alumnos (nombre, apellido, curso)
    VALUES
    ('Alumno', 'Ejemplo', 'ASIR'),
    ('Docker', 'Compose', 'Servicios');

    Este script se ejecutará automáticamente cuando se cree por primera vez el contenedor de MySQL y el volumen esté vacío.

    Preguntas de reflexión

    1. ¿Cuándo se ejecutan los scripts dentro de /docker-entrypoint-initdb.d/?
    2. Si modificas init.sql después de haber creado el volumen, ¿se vuelve a ejecutar automáticamente?
    3. ¿Qué tendrías que hacer para reiniciar completamente la base de datos desde cero?
    4. ¿Por qué hay que tener cuidado al borrar volúmenes?

    11. Fase 6 — Crear la aplicación PHP

    Dentro de la carpeta web, crea el archivo index.php.

    Ejemplo:

    <?php
    $host = 'mysql';
    $db = 'asir_db';
    $user = 'asir_user';
    $password = 'asir_password';

    $conexionCorrecta = false;
    $error = '';

    try {
    $pdo = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $password);
    $conexionCorrecta = true;
    } catch (PDOException $e) {
    $error = $e->getMessage();
    }
    ?>

    <!DOCTYPE html>
    <html lang="es">
    <head>
    <meta charset="UTF-8">
    <title>Práctica Docker ASIR</title>
    <link rel="stylesheet" href="estilos.css">
    </head>
    <body>

    <main>
    <h1>Práctica Docker, Docker Compose y Servicios</h1>

    <section>
    <h2>Datos del alumno</h2>
    <p><strong>Nombre:</strong> Escribe aquí tu nombre</p>
    <p><strong>Curso:</strong> ASIR</p>
    <p><strong>Módulo:</strong> Implantación de aplicaciones / Servicios / Sistemas</p>
    </section>

    <section>
    <h2>Estado de la conexión con MySQL</h2>

    <?php if ($conexionCorrecta): ?>
    <p class="ok">Conexión correcta con MySQL.</p>
    <?php else: ?>
    <p class="error">Error de conexión con MySQL.</p>
    <pre><?php echo $error; ?></pre>
    <?php endif; ?>
    </section>

    <section>
    <h2>Listado de alumnos desde MySQL</h2>

    <?php
    if ($conexionCorrecta) {
    $consulta = $pdo->query("SELECT * FROM alumnos");

    echo "<table>";
    echo "<tr><th>ID</th><th>Nombre</th><th>Apellido</th><th>Curso</th><th>Fecha</th></tr>";

    while ($fila = $consulta->fetch(PDO::FETCH_ASSOC)) {
    echo "<tr>";
    echo "<td>" . htmlspecialchars($fila['id']) . "</td>";
    echo "<td>" . htmlspecialchars($fila['nombre']) . "</td>";
    echo "<td>" . htmlspecialchars($fila['apellido']) . "</td>";
    echo "<td>" . htmlspecialchars($fila['curso']) . "</td>";
    echo "<td>" . htmlspecialchars($fila['fecha_registro']) . "</td>";
    echo "</tr>";
    }

    echo "</table>";
    }
    ?>
    </section>
    </main>

    </body>
    </html>

    11.1. Archivo CSS

    Crea estilos.css:

    body {
    font-family: Arial, sans-serif;
    background-color: #f4f4f4;
    margin: 0;
    padding: 0;
    }

    main {
    width: 80%;
    margin: 40px auto;
    background-color: white;
    padding: 30px;
    border-radius: 10px;
    }

    h1 {
    color: #333;
    }

    section {
    margin-bottom: 30px;
    }

    .ok {
    color: green;
    font-weight: bold;
    }

    .error {
    color: red;
    font-weight: bold;
    }

    table {
    width: 100%;
    border-collapse: collapse;
    }

    th {
    background-color: #333;
    color: white;
    }

    td, th {
    padding: 10px;
    border: 1px solid #ccc;
    }

    11.2. Problema intencionado

    Con la imagen oficial php:8.2-apache, puede ocurrir que PHP no tenga activada la extensión necesaria para conectarse a MySQL mediante PDO.

    Si al abrir la web aparece un error relacionado con MySQL o PDO, deberás investigarlo y solucionarlo.

    Pista

    Puede ser necesario crear un Dockerfile personalizado para el servicio web.


    12. Fase 7 — Crear una imagen personalizada para PHP

    Crea un archivo llamado Dockerfile dentro de la carpeta web.

    FROM php:8.2-apache

    RUN docker-php-ext-install pdo pdo_mysql mysqli

    Ahora modifica el servicio web en el docker-compose.yml.

    Antes:

    web:
    image: php:8.2-apache

    Después:

    web:
    build: ./web

    El servicio completo quedaría así:

    web:
    build: ./web
    container_name: asir_web_php
    ports:
    - "${WEB_PORT}:80"
    volumes:
    - ./web:/var/www/html
    depends_on:
    - mysql
    - mongodb
    networks:
    - red_asir

    Preguntas de reflexión

    1. ¿Qué diferencia hay entre usar image y usar build?
    2. ¿Para qué sirve un Dockerfile?
    3. ¿Qué hace esta línea?
    RUN docker-php-ext-install pdo pdo_mysql mysqli
    1. ¿Por qué necesitamos instalar extensiones de PHP?
    2. ¿Qué ventaja tiene crear nuestra propia imagen personalizada?

    13. Fase 8 — Levantar los servicios

    Ejecuta:

    docker compose up -d

    Después comprueba:

    docker compose ps

    También puedes usar:

    docker ps

    Evidencias obligatorias

    Incluye capturas donde se vea:

    • Los contenedores levantados.
    • Los puertos publicados.
    • El estado de cada contenedor.

    Preguntas de reflexión

    1. ¿Qué significa ejecutar Docker Compose con -d?
    2. ¿Qué diferencia hay entre docker compose ps y docker ps?
    3. ¿Qué significa que un contenedor esté en estado Up?
    4. ¿Qué harías si un contenedor aparece como Restarting?

    14. Fase 9 — Comprobación de servicios en navegador

    Accede desde el navegador a:

    http://localhost:8080

    Deberías ver la aplicación PHP.

    Accede también a:

    http://localhost:8081

    Deberías ver phpMyAdmin.

    Accede a:

    http://localhost:8082

    Deberías ver Mongo Express.

    Evidencias obligatorias

    Incluye capturas de:

    1. Página PHP funcionando.
    2. phpMyAdmin mostrando la base de datos.
    3. Tabla alumnos dentro de MySQL.
    4. Mongo Express funcionando.

    Preguntas de reflexión

    1. ¿Por qué accedemos a la web usando localhost:8080 y no localhost:80?
    2. ¿Qué usuario y contraseña has utilizado para entrar en phpMyAdmin?
    3. ¿Qué base de datos aparece creada automáticamente?
    4. ¿Qué diferencia hay entre administrar MySQL desde línea de comandos y hacerlo desde phpMyAdmin?
    5. ¿Qué riesgos tendría dejar phpMyAdmin abierto en un servidor real?

    15. Fase 10 — Insertar datos desde PHP

    Crea un archivo llamado insertar.php.

    <?php
    $host = 'mysql';
    $db = 'asir_db';
    $user = 'asir_user';
    $password = 'asir_password';

    try {
    $pdo = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $password);

    $sql = "INSERT INTO alumnos (nombre, apellido, curso) VALUES (:nombre, :apellido, :curso)";
    $stmt = $pdo->prepare($sql);

    $stmt->execute([
    ':nombre' => 'NombreAlumno',
    ':apellido' => 'ApellidoAlumno',
    ':curso' => 'ASIR'
    ]);

    echo "Registro insertado correctamente.";
    } catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
    }

    Accede a:

    http://localhost:8080/insertar.php

    Después vuelve a:

    http://localhost:8080/index.php

    Comprueba que el nuevo registro aparece en la tabla.

    Trabajo obligatorio

    Modifica el archivo para que inserte tus propios datos.

    Preguntas de reflexión

    1. ¿Qué ocurre cada vez que recargas insertar.php?
    2. ¿Por qué puede ser peligroso insertar datos directamente sin un formulario controlado?
    3. ¿Qué diferencia hay entre una consulta normal y una consulta preparada?
    4. ¿Qué problema de seguridad ayudan a evitar las consultas preparadas?

    16. Fase 11 — Crear formulario de inserción

    Crea una página formulario.php.

    <!DOCTYPE html>
    <html lang="es">
    <head>
    <meta charset="UTF-8">
    <title>Formulario de alumnos</title>
    <link rel="stylesheet" href="estilos.css">
    </head>
    <body>

    <main>
    <h1>Insertar alumno</h1>

    <form action="guardar.php" method="post">
    <label>Nombre:</label><br>
    <input type="text" name="nombre" required><br><br>

    <label>Apellido:</label><br>
    <input type="text" name="apellido" required><br><br>

    <label>Curso:</label><br>
    <input type="text" name="curso" required><br><br>

    <button type="submit">Guardar</button>
    </form>
    </main>

    </body>
    </html>

    Crea ahora guardar.php.

    <?php
    $host = 'mysql';
    $db = 'asir_db';
    $user = 'asir_user';
    $password = 'asir_password';

    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $nombre = $_POST['nombre'] ?? '';
    $apellido = $_POST['apellido'] ?? '';
    $curso = $_POST['curso'] ?? '';

    try {
    $pdo = new PDO("mysql:host=$host;dbname=$db;charset=utf8", $user, $password);

    $sql = "INSERT INTO alumnos (nombre, apellido, curso) VALUES (:nombre, :apellido, :curso)";
    $stmt = $pdo->prepare($sql);

    $stmt->execute([
    ':nombre' => $nombre,
    ':apellido' => $apellido,
    ':curso' => $curso
    ]);

    echo "Alumno guardado correctamente.";
    echo "<br><a href='index.php'>Volver al listado</a>";

    } catch (PDOException $e) {
    echo "Error: " . $e->getMessage();
    }
    }

    Evidencia obligatoria

    Incluye captura del formulario y del registro insertado.

    Preguntas de reflexión

    1. ¿Qué método HTTP utiliza el formulario?
    2. ¿Qué diferencia hay entre GET y POST?
    3. ¿Dónde llegan los datos enviados por el formulario?
    4. ¿Qué validaciones mínimas debería tener este formulario?
    5. ¿Por qué no deberíamos confiar nunca únicamente en la validación del navegador?

    17. Fase 12 — Trabajo con MongoDB

    MongoDB es una base de datos NoSQL. En lugar de trabajar con tablas y filas, trabaja con bases de datos, colecciones y documentos.

    Accede a Mongo Express:

    http://localhost:8082

    Crea una base de datos llamada:

    asir_mongo

    Crea una colección llamada:

    inventario

    Inserta al menos tres documentos similares a estos:

    {
    "nombre": "Servidor web",
    "tipo": "contenedor",
    "servicio": "Apache PHP",
    "puerto": 8080
    }
    {
    "nombre": "Base de datos relacional",
    "tipo": "contenedor",
    "servicio": "MySQL",
    "puerto": 3306
    }
    {
    "nombre": "Base de datos NoSQL",
    "tipo": "contenedor",
    "servicio": "MongoDB",
    "puerto": 27017
    }

    Evidencias obligatorias

    Incluye capturas de:

    • Base de datos creada.
    • Colección creada.
    • Documentos insertados.

    Preguntas de reflexión

    1. ¿Qué diferencia hay entre una tabla de MySQL y una colección de MongoDB?
    2. ¿Qué diferencia hay entre una fila y un documento?
    3. ¿Qué ventajas puede tener MongoDB frente a MySQL?
    4. ¿Qué ventajas puede tener MySQL frente a MongoDB?
    5. ¿En qué tipo de proyecto usarías cada uno?

    18. Fase 13 — Comandos de administración

    Ejecuta y documenta los siguientes comandos.

    Ver contenedores activos

    docker ps

    Ver todos los contenedores

    docker ps -a

    Ver imágenes

    docker images

    Ver redes

    docker network ls

    Ver volúmenes

    docker volume ls

    Ver logs del servicio web

    docker compose logs web

    Ver logs de MySQL

    docker compose logs mysql

    Entrar dentro del contenedor web

    docker exec -it asir_web_php bash

    Dentro del contenedor, ejecuta:

    ls -la /var/www/html

    Después sal:

    exit

    Evidencias obligatorias

    Incluye capturas de al menos cinco comandos anteriores.

    Preguntas de reflexión

    1. ¿Para qué sirve docker ps?
    2. ¿Qué información muestra docker images?
    3. ¿Qué diferencia hay entre una imagen y un contenedor?
    4. ¿Qué utilidad tienen los logs?
    5. ¿Para qué puede servir entrar dentro de un contenedor?
    6. ¿Por qué no deberíamos modificar manualmente demasiadas cosas dentro de un contenedor en producción?

    19. Fase 14 — Persistencia de datos

    Vas a comprobar que los datos sobreviven aunque los contenedores se detengan.

    Ejecuta:

    docker compose down

    Después vuelve a levantar:

    docker compose up -d

    Comprueba:

    • Que la web sigue funcionando.
    • Que los datos de MySQL siguen estando.
    • Que los documentos de MongoDB siguen estando.

    Ahora ejecuta:

    docker compose down -v

    Vuelve a levantar:

    docker compose up -d

    Comprueba qué ha ocurrido con los datos.

    Mucho cuidado

    El parámetro -v elimina los volúmenes asociados al proyecto.

    Preguntas de reflexión

    1. ¿Qué diferencia hay entre docker compose down y docker compose down -v?
    2. ¿Por qué se han conservado los datos en el primer caso?
    3. ¿Por qué se han perdido los datos en el segundo caso?
    4. ¿Qué papel tienen los volúmenes en Docker?
    5. ¿Por qué la persistencia es fundamental en bases de datos?

    20. Fase 15 — Copia de seguridad de MySQL

    Realiza una copia de seguridad de la base de datos MySQL.

    Ejemplo:

    docker exec asir_mysql mysqldump -u root -p asir_db > backup_asir_db.sql

    El sistema pedirá la contraseña de root.

    Después comprueba que se ha creado el archivo:

    ls -lh backup_asir_db.sql

    Evidencia obligatoria

    Incluye captura del archivo generado.

    Preguntas de reflexión

    1. ¿Qué es mysqldump?
    2. ¿Por qué es importante hacer copias de seguridad?
    3. ¿Dónde se ha guardado el archivo SQL?
    4. ¿Qué información contiene ese archivo?
    5. ¿Sería suficiente esta copia de seguridad en un entorno real? Justifica la respuesta.

    21. Fase 16 — Restauración básica de MySQL

    Para comprobar la restauración, puedes crear una nueva base de datos desde phpMyAdmin o desde consola e importar el archivo.

    Ejemplo orientativo:

    docker exec -i asir_mysql mysql -u root -p asir_db < backup_asir_db.sql

    Preguntas de reflexión

    1. ¿Qué diferencia hay entre exportar e importar una base de datos?
    2. ¿Qué riesgos existen al restaurar una copia sobre una base de datos que ya contiene información?
    3. ¿Qué medidas tomarías antes de restaurar una copia en producción?

    22. Fase 17 — Comprobación de red entre contenedores

    Entra en el contenedor web:

    docker exec -it asir_web_php bash

    Instala herramientas de red si fuera necesario:

    apt update
    apt install -y iputils-ping

    Prueba conectividad:

    ping mysql
    ping mongodb

    Sal del contenedor:

    exit

    Preguntas de reflexión

    1. ¿Por qué podemos hacer ping mysql y no necesitamos conocer la IP del contenedor?
    2. ¿Qué papel hace el DNS interno de Docker?
    3. ¿Cambiaría la IP del contenedor si lo eliminamos y lo volvemos a crear?
    4. ¿Por qué es mejor usar nombres de servicio que direcciones IP?

    23. Fase 18 — Seguridad básica

    Analiza la configuración actual.

    Responde:

    1. ¿Qué puertos están expuestos al equipo anfitrión?
    2. ¿Qué servicios son accesibles desde el navegador?
    3. ¿Tiene sentido exponer MySQL directamente al exterior?
    4. ¿Tiene sentido exponer MongoDB directamente al exterior?
    5. ¿Qué contraseñas son débiles?
    6. ¿Qué cambiarías si esto fuese un servidor real?
    7. ¿Qué riesgos tiene usar root para administrar la base de datos?
    8. ¿Qué riesgos tiene dejar herramientas como phpMyAdmin o Mongo Express accesibles públicamente?

    Mejora obligatoria

    Modifica el docker-compose.yml para que MySQL y MongoDB no publiquen puertos hacia el exterior.

    Es decir, evita configuraciones como:

    ports:
    - "3306:3306"

    o:

    ports:
    - "27017:27017"

    Los servicios internos deben comunicarse por la red Docker, no necesariamente por puertos públicos.

    Pregunta de reflexión

    Si MySQL no tiene puerto publicado, ¿por qué phpMyAdmin puede seguir conectando con él?


    24. Fase 19 — Personalización del proyecto

    El alumno deberá personalizar la práctica.

    Como mínimo deberá:

    1. Cambiar el nombre de los contenedores.
    2. Cambiar el nombre de la base de datos.
    3. Cambiar usuario y contraseña.
    4. Personalizar la página principal.
    5. Insertar datos propios.
    6. Añadir una nueva tabla en MySQL.
    7. Añadir una nueva colección en MongoDB.
    8. Documentar los cambios realizados.

    Nueva tabla obligatoria

    Crea una tabla llamada servicios.

    Ejemplo:

    CREATE TABLE IF NOT EXISTS servicios (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(100) NOT NULL,
    imagen VARCHAR(100) NOT NULL,
    puerto VARCHAR(20),
    descripcion TEXT
    );

    Inserta al menos cinco servicios:

    • Apache/PHP.
    • MySQL.
    • phpMyAdmin.
    • MongoDB.
    • Mongo Express.

    Preguntas de reflexión

    1. ¿Qué campos has elegido para la tabla servicios?
    2. ¿Por qué has elegido esos campos?
    3. ¿Qué relación existe entre esta tabla y la infraestructura desplegada?
    4. ¿Cómo podrías mostrar esta tabla desde PHP?

    25. Fase 20 — Ampliación opcional

    Para subir nota, puedes realizar una o varias de estas mejoras.

    Opción A — Añadir Nginx como proxy inverso

    Añade un contenedor Nginx que actúe como punto de entrada.

    Debe permitir acceder a la aplicación PHP desde otro puerto o ruta.

    Opción B — Añadir Adminer

    Añade Adminer como alternativa a phpMyAdmin.

    Opción C — Añadir Redis

    Añade Redis como servicio adicional y explica para qué se utiliza.

    Opción D — Crear una aplicación PHP más completa

    La aplicación deberá permitir:

    • Insertar alumnos.
    • Listar alumnos.
    • Borrar alumnos.
    • Editar alumnos.