Problema
En entornos de producción auto‑gestionados es frecuente que el acceso SSH se mantenga mediante claves públicas/privadas, mientras que la autenticación sudo sigue basada en una contraseña local. Cuando esa contraseña se pierde, el administrador queda bloqueado para ejecutar tareas que requieren privilegios de root. La solución tradicional implica arrancar el servidor en modo de recuperación o usar un live‑CD, lo que requiere acceso físico y tiempo de inactividad. En clústers Docker Swarm o Kubernetes, sin embargo, el propio motor de contenedores ya tiene la capacidad de montar el sistema de archivos del host y ejecutar con privilegios elevados. Esa característica, si se usa sin restricciones, permite modificar directamente archivos críticos como /etc/shadow y restaurar el acceso sin tocar el hardware.
Causa
El punto débil no es el contenedor en sí, sino la combinación de dos configuraciones que se encuentran en muchas instalaciones:
- Contenedores privilegiados (
--privilegedo capacidades ampliadas) que otorgan al proceso dentro del contenedor prácticamente todas las capacidades del kernel del host. - Montaje del filesystem del host (
-v /:/host) que expone la raíz completa del servidor al contenedor. - Política de autenticación basada en contraseña para sudo, que depende exclusivamente del hash almacenado en /etc/shadow. Si el hash se pierde, no hay otro mecanismo de recuperación.
Cuando cualquiera de estos elementos está presente sin una capa de control de acceso (por ejemplo, políticas de AppArmor/SELinux o un escaneo de vulnerabilidades), un atacante que consiga ejecutar código dentro del contenedor puede escalar a root del host con un simple chroot o edición de archivos.
Solución
La estrategia consiste en usar un contenedor privilegiado de forma controlada para reemplazar el hash de la contraseña en /etc/shadow. El proceso se divide en tres fases:
- Preparar una imagen mínima que incluya
awk,sedyopenssl(cualquier distro base sirve). - Ejecutar el contenedor con los flags necesarios:
--privilegedpara obtener todas las capacidades del kernel.-v /:/hostpara montar la raíz del host bajo/host.- Opcionalmente, limitar la red (
--network none) para evitar exfiltración.
- Dentro del contenedor, generar un nuevo hash con
openssl passwd -6(SHA‑512) y sobrescribir la línea correspondiente en/host/etc/shadow.
Este método es aplicable tanto a nodos individuales como a workers de un Swarm, siempre que el nodo siga aceptando despliegues de contenedores. La clave está en ejecutar la operación una única vez y luego eliminar el contenedor para cerrar la brecha.
Alternativas prácticas
- Uso de
nsenter: si el host permitensenterdesde el contenedor, se puede entrar al namespace de PID 1 y ejecutarpasswddirectamente, evitando la edición manual del archivo. - Recuperación mediante
systemd‑boot: en servidores consystemdy acceso a la consola, añadirinit=/bin/bashal kernel es otra vía, pero requiere reinicio físico. - Gestión de contraseñas con LDAP/SSSD: migrar a autenticación centralizada elimina la dependencia de un hash local y reduce la superficie de ataque.
Cuándo aplicar esta solución
- Síntomas claros: el usuario puede iniciar sesión por SSH con claves, pero
sudorechaza la contraseña y no hay otro método de escalado. - Entorno Docker/Kubernetes: el nodo forma parte de un clúster y acepta despliegues de contenedores con permisos elevados.
- Acceso físico limitado: no se dispone de consola o medio de arranque externo.
No aplicar si:
- El clúster está configurado con políticas de seguridad estrictas (AppArmor, SELinux) que bloquean
--privilegedo el montaje de/. - El host está bajo un esquema de autenticación externa (LDAP, Kerberos) donde el hash local no es relevante.
- La pérdida de contraseña es parte de un incidente de seguridad mayor; en ese caso, es preferible reinstalar o aislar el nodo.
Código
# 1. Crear una imagen temporal (Dockerfile)
cat > Dockerfile <<'EOF'
FROM alpine:latest
RUN apk add --no-cache openssl
EOF
docker build -t pwd-reset:latest .
# 2. Ejecutar el contenedor privilegiado
docker run --rm -i \
--privileged \
-v /:/host \
--network none \
pwd-reset:latest sh -c '
NEW_HASH=$(openssl passwd -6 "NuevaContraseñaSegura")
USER=$(grep "^$(whoami):" /host/etc/passwd | cut -d: -f1)
# Sustituir el hash en /etc/shadow
sed -i "s|^\($USER:[^:]*:\)[^:]*|\1$NEW_HASH|" /host/etc/shadow
echo "Hash de $USER actualizado"
'
Verificación
- Salir del contenedor y volver a la sesión SSH original.
- Ejecutar
sudo -k && sudo -lpara forzar la petición de contraseña. - Introducir la nueva contraseña definida en el script.
- Si
sudoconcede privilegios, la operación fue exitosa. - Opcional: revisar el archivo
/etc/shadowdel host para confirmar que el hash corresponde a la nueva contraseña (grep "^$(whoami):" /etc/shadow).
Notas adicionales
- Eliminar el contenedor inmediatamente después de la operación. Mantener un contenedor privilegiado activo es una puerta trasera permanente.
- Auditar los logs de Docker (
/var/log/docker.log) y del sistema (/var/log/auth.log) para detectar intentos no autorizados. - Restringir la capacidad de lanzar contenedores privilegiados mediante políticas de autorización en Docker Swarm (
docker swarm update --task-history-limitno ayuda directamente, pero combinar con RBAC y certificados firmados reduce el riesgo). - Rotar la contraseña de sudo y actualizar cualquier script de automatización que dependa de ella.
- Considerar el uso de
sudosin contraseña para usuarios de confianza dentro de entornos de contenedores, siempre que se acompañe de una auditoría de comandos permitidos (/etc/sudoers.d/). - Backup de /etc/shadow: mantener una copia cifrada fuera del host permite restaurar rápidamente sin necesidad de contenedores privilegiados.
Con este enfoque se recupera el acceso administrativo sin interrumpir los servicios críticos y, al mismo tiempo, se evidencia la necesidad de reforzar la política de contenedores privilegiados en cualquier infraestructura auto‑gestionada.