#!/usr/bin/env bash
# =============================================================================
#  demo-01 · Diagnóstico de tu motor
# =============================================================================
#  A qué punto del guion sirve: puntos 1, 2, 10 y 11
#    · 1  «Dos mundos: Podman en mi portátil, Docker en el servidor»
#         → `podman --version` y `docker --version`: dos binarios, dos diseños.
#    · 2  «Y ahora quiero el servidor sin root»
#         → cómo se sabe si tu contenedor corre rootless: `podman info` /
#           `docker info`, y el mapa de identificadores del espacio de usuario.
#    · 10 «Qué cambia rootless de verdad»
#         → la línea `rootlessPortForwarder` de `podman info`: el reenviador
#           que hace de proxy y se come la IP de origen.
#    · 11 «No es cosa de Podman: es cosa de rootless»
#         → el mismo dato, buscado en `docker info`.
#
#  Qué se ve en pantalla: cuatro bloques, uno por idea. Cada bloque lanza el
#  comando exacto y comenta en una línea lo que significa lo que ha salido.
#
#  Estado:
#    · Podman (versiones, rootless, mapa de UID, reenviador) → VERIFICADO en
#      local con Podman 6.1.4 rootless. La salida real está en
#      `verificacion/demo-01-salida.txt`.
#    · Docker → NO VERIFICADO aquí: en esta máquina no hay binario `docker`.
#      El bloque está escrito para que funcione si lo hay, y si no, imprime la
#      salida ESPERADA y qué línea hay que buscar.
#
#  Seguridad de la demo: solo LEE. No crea contenedores, no toca la red del
#  sistema y no modifica nada. `podman unshare` abre un espacio de nombres de
#  usuario, ejecuta `cat` y sale.
# =============================================================================

set -u

titulo() { printf '\n\033[1m── %s\033[0m\n' "$1"; }
orden()  { printf '\n\033[1m$ %s\033[0m\n' "$1"; }
nota()   { printf '   ↳ %s\n' "$1"; }
esperado() { printf '\n\033[1m[ESPERADO, no ejecutado aquí]\033[0m\n'; }

printf '\033[1mDiagnóstico de tu motor de contenedores\033[0m  (puntos 1, 2, 10 y 11)\n'
printf 'Máquina: %s · %s\n' "$(hostname)" "$(date -Is)"

# -----------------------------------------------------------------------------
titulo "1 · ¿Qué motor tengo?  ·  punto 1"
# -----------------------------------------------------------------------------
# La versión dice mucho más de lo que parece: aquí no se elige "Podman o
# Docker" por gusto, son dos arquitecturas. Podman no tiene demonio; Docker sí.
if command -v podman >/dev/null 2>&1; then
    orden "podman --version"
    podman --version
    nota "Un binario más que contesta. No hay servicio con privilegios detrás:"
    nota "el contenedor es un hijo de esta sesión y muere con ella."
else
    nota "no hay 'podman' en el PATH de esta máquina."
fi

if command -v docker >/dev/null 2>&1; then
    orden "docker --version"
    docker --version
    nota "Mismo software por debajo, distinto modelo: hay un 'dockerd'"
    nota "arrancado como root al que le pides el contenedor."
else
    esperado
    printf '$ docker --version\n'
    printf 'Docker version 28.x.y, build xxxxxxx\n'
    nota "La versión en sí no te dice si es rootless: para eso, el bloque 2."
fi

# -----------------------------------------------------------------------------
titulo "2 · ¿Mi motor es rootless?  ·  punto 2"
# -----------------------------------------------------------------------------
# Pregunta útil, sobre todo en el servidor: si es rootless, el usuario cero
# dentro del contenedor NO es el root de la máquina.
if command -v podman >/dev/null 2>&1; then
    orden "podman info --format '{{.Host.Security.Rootless}}'"
    podman info --format '{{.Host.Security.Rootless}}'
    nota "'true' = rootless. 'false' = los contenedores corren como root de verdad."

    orden "podman info | grep -i rootless"
    podman info 2>/dev/null | grep -i rootless
    nota "Tres líneas, y ninguna sobra: 'rootlessNetworkCmd' (con qué se monta la"
    nota "red: aquí 'pasta'), 'rootlessPortForwarder' (quiénes publican los puertos:"
    nota "aquí 'rootlessport') y 'rootless: true', que es la tercera y la misma"
    nota "respuesta del comando anterior. El reenviador del bloque 10 es la segunda."
else
    nota "no hay 'podman' en el PATH: se salta la comprobación rootless."
fi

# -----------------------------------------------------------------------------
titulo "3 · Qué significa rootless por dentro  ·  punto 2"
# -----------------------------------------------------------------------------
# El truco es del kernel: espacios de nombres de usuario. Dentro, el proceso se
# cree root; fuera, es un usuario sin privilegios con un rango de identificadores
# prestados. Se ve en el mapa de UID.
# Se usa 'podman unshare' para NO tener que crear ningún contenedor: abre el
# espacio de nombres, ejecuta 'cat' y sale.
if command -v podman >/dev/null 2>&1; then
    orden "podman unshare cat /proc/self/uid_map"
    podman unshare cat /proc/self/uid_map 2>&1
    nota "El 0 de la izquierda (dentro) es un 1000 cualquiera (fuera):"
    nota "quien escape del contenedor no encuentra el root del host, encuentra un don nadie."
    nota "En un contenedor ya en marcha, el mismo dato se ve con:"
    nota "  cat /proc/self/uid_map   (dentro del contenedor)"
else
    nota "sin podman no hay espacio de nombres que enseñar."
fi

# -----------------------------------------------------------------------------
titulo "4 · El reenviador: donde se pierde la IP  ·  punto 10"
# -----------------------------------------------------------------------------
# En rootless nadie abre el puerto en el kernel por ti: lo abre un proceso tuyo
# que escucha y reenvía hacia el contenedor. Ese proceso es un proxy, y por eso
# dentro del contenedor la conexión no llega del visitante sino del mensajero.
if command -v podman >/dev/null 2>&1; then
    orden "podman info --format '{{.Host.RootlessPortForwarder}}'"
    podman info --format '{{.Host.RootlessPortForwarder}}' 2>&1
    orden "podman info --format '{{.Host.RootlessNetworkCmd}}'"
    podman info --format '{{.Host.RootlessNetworkCmd}}' 2>&1
    nota "Esos dos nombres son la pieza de la que habla el guion: el reenvío"
    nota "de puertos ocurre en el espacio de usuario, no en el NAT del kernel."
else
    nota "sin podman no hay reenviador que enseñar."
fi

# -----------------------------------------------------------------------------
titulo "5 · ¿Y en Docker? Exactamente lo mismo  ·  punto 11"
# -----------------------------------------------------------------------------
# La frase del episodio: no es cosa de Podman, es cosa de rootless. Docker
# rootless arrastra el mismo comportamiento porque se apoya en RootlessKit.
if command -v docker >/dev/null 2>&1; then
    orden "docker info --format '{{.SecurityOptions}}'"
    docker info --format '{{.SecurityOptions}}' 2>&1
    nota "Si aparece 'rootless' en la lista, es rootless. Si no aparece, no lo es."

    orden "docker context inspect --format '{{.Endpoints.docker.Host}}'"
    docker context inspect --format '{{.Endpoints.docker.Host}}' 2>&1
    nota "Segunda pista: un socket en /run/user/<uid>/docker.sock es rootless;"
    nota "unix:///var/run/docker.sock es el demonio de root de toda la vida."
else
    esperado
    printf '$ docker info --format '"'"'{{.SecurityOptions}}'"'"'\n'
    printf '[name=seccomp,profile=builtin name=rootless name=cgroupns]\n\n'
    printf '$ docker context inspect --format '"'"'{{.Endpoints.docker.Host}}'"'"'\n'
    printf 'unix:///run/user/1000/docker.sock\n'
    nota "Docker rootless se instala aparte y se arranca como usuario;"
    nota "si no lo has hecho, tu Docker es rootful aunque no te lo diga la versión."
fi

# -----------------------------------------------------------------------------
titulo "Cierre del bloque"
# -----------------------------------------------------------------------------
printf '   · Dos binarios, dos diseños: sin demonio (Podman) / con demonio (Docker).\n'
printf '   · Rootless se comprueba con %s, no con la versión.\n' "'info'"
printf '   · Y en rootless los puertos los publica un reenviador: ese es el que\n'
printf '     hace que dentro del contenedor veas al mensajero y no al visitante.\n\n'
exit 0
