13/11/2018
En el vertiginoso mundo del desarrollo de software y la infraestructura, la eficiencia es clave. Alpine Linux se ha consolidado como una de las distribuciones favoritas para la creación de imágenes de Docker gracias a su increíble ligereza y su enfoque minimalista. Utilizando Busybox y la biblioteca Musl Libc, las imágenes basadas en Alpine tienen una huella de memoria significativamente menor en comparación con otras distribuciones como Ubuntu o CentOS. Sin embargo, esta naturaleza minimalista trae consigo ciertos desafíos, especialmente en la gestión de permisos. Por defecto, el contenedor se ejecuta con el usuario root, una práctica desaconsejada por motivos de seguridad. El verdadero reto surge cuando creamos un usuario sin privilegios y nos encontramos con el temido error "Permission denied".

Este artículo es una guía completa para navegar y resolver los problemas de permisos en Alpine Docker. Exploraremos no solo cómo crear un usuario, sino cómo otorgarle los privilegios necesarios de una manera limpia, segura y fiel a la filosofía de Alpine, utilizando herramientas como `doas` en lugar del tradicional `sudo`.
- El Primer Paso: Crear un Usuario en el Dockerfile
- El Problema Central: La Falta de Permisos
- La Solución Ligera: `doas` como Alternativa a `sudo`
- Mejores Prácticas: Gestión de Permisos por Grupos
- Tabla Comparativa: `doas` vs. `sudo`
- Opciones Avanzadas de Configuración
- Método Alternativo: Creación de Usuario con un Script `ENTRYPOINT`
- Preguntas Frecuentes (FAQ)
- Conclusión
El Primer Paso: Crear un Usuario en el Dockerfile
La base de una imagen de Docker segura es evitar el uso del usuario `root`. El primer paso lógico es crear un usuario dedicado dentro de nuestro `Dockerfile`. Alpine Linux, a diferencia de otras distribuciones, no incluye `useradd` por defecto, pero nos proporciona `adduser`, una utilidad de BusyBox perfectamente capaz.
Para crear un usuario, utilizamos el comando `RUN adduser`. La opción `-D` es particularmente útil en este contexto, ya que crea un usuario sin contraseña, lo cual es ideal para entornos de contenedores donde el inicio de sesión interactivo no es la norma.
Una vez creado el usuario, debemos indicarle a Docker que lo utilice por defecto para las siguientes instrucciones del `Dockerfile` y para cuando el contenedor se inicie. Esto se logra con la instrucción `USER`.
Veamos un `Dockerfile` básico que implementa esto:
FROM alpine:latest # Creamos un usuario llamado 'appuser' sin contraseña RUN adduser -D appuser # Establecemos 'appuser' como el usuario por defecto para las # instrucciones posteriores y para la ejecución del contenedor USER appuser # Un comando para mantener el contenedor en ejecución CMD ["sleep", "infinity"]Para construir y verificar nuestra imagen, ejecutaríamos los siguientes comandos:
# Construir la imagen docker build -t alpine-permisos . # Ejecutar un contenedor y abrir una shell docker run --rm -it alpine-permisos sh # Dentro del contenedor, verificar el usuario actual /home/appuser$ whoami appuserComo podemos ver, el comando `whoami` confirma que ahora estamos operando como `appuser`, no como `root`. Hemos dado el primer paso hacia un contenedor más seguro.
El Problema Central: La Falta de Permisos
Hemos creado nuestro usuario, pero pronto descubriremos sus limitaciones. Si intentamos realizar cualquier tarea administrativa, como instalar un paquete con el gestor de paquetes de Alpine (`apk`), nos toparemos con un muro.
/home/appuser$ apk add curl ERROR: Unable to lock database: Permission denied ERROR: Failed to open apk database: Permission deniedEste error es completamente esperado. Nuestro `appuser` es un usuario estándar sin privilegios elevados. En un sistema Debian o Ubuntu, la solución instintiva sería usar `sudo`. Sin embargo, si intentamos ejecutar `sudo apk add curl`, descubriremos que el comando `sudo` no existe. Alpine no lo incluye en su imagen base para mantenerla lo más pequeña posible.
La Solución Ligera: `doas` como Alternativa a `sudo`
Aquí es donde entra en juego la filosofía de Alpine. En lugar de instalar el paquete `sudo`, que es grande y complejo, podemos optar por una alternativa mucho más ligera y simple: `doas`. `doas` (Do as) es una utilidad originaria de OpenBSD diseñada para ser una sustitución minimalista y segura de `sudo`.
La configuración de `doas` es notablemente más sencilla que el `sudoers` file. Para implementarlo, seguiremos estos pasos en nuestro `Dockerfile`:
- Instalar el paquete `doas`.
- Crear el usuario.
- Crear y configurar el archivo de configuración de `doas`.
El archivo de configuración principal se encuentra en `/etc/doas.conf`, aunque es una buena práctica crear configuraciones personalizadas en `/etc/doas.d/`. La sintaxis es muy intuitiva: `permit
Modifiquemos nuestro `Dockerfile` para incluir `doas` y darle a nuestro `appuser` la capacidad de ejecutar comandos como `root`:
FROM alpine:latest # Instalar doas y crear el usuario en un solo paso para optimizar las capas RUN apk add --no-cache doas && \ adduser -D appuser && \ # Crear el archivo de configuración para permitir a 'appuser' actuar como root echo 'permit appuser as root' > /etc/doas.d/doas.conf USER appuser CMD ["sleep", "infinity"]Ahora, si construimos y ejecutamos esta nueva imagen, nuestro `appuser` podrá instalar paquetes usando `doas`:
/home/appuser$ doas apk add curl fetch https://dl-cdn.alpinelinux.org/alpine/v3.18/main/x86_64/APKINDEX.tar.gz ... OK: 15 MiB in 31 packages¡Éxito! Hemos resuelto el problema de permisos de una manera eficiente y alineada con los principios de Alpine.
Mejores Prácticas: Gestión de Permisos por Grupos
Otorgar permisos directamente a un usuario funciona, pero una práctica más robusta y escalable es gestionar los permisos a través de grupos. En muchos sistemas tipo Unix, el grupo `wheel` es el grupo administrativo estándar. Podemos añadir nuestro usuario a este grupo y luego configurar `doas` para que otorgue privilegios a todos los miembros del grupo `wheel`.
Esto tiene dos ventajas principales:
- Escalabilidad: Si necesitamos crear más usuarios con privilegios, simplemente los añadimos al grupo `wheel` sin tener que modificar el archivo de configuración de `doas`.
- Claridad: Es una convención conocida que indica claramente qué usuarios tienen privilegios administrativos.
Para implementar esto, usamos la opción `-G` de `adduser` para añadir el usuario a un grupo. En la configuración de `doas`, los nombres de grupo van precedidos por dos puntos (`:`).
Nuestro `Dockerfile` recomendado se vería así:
FROM alpine:latest # Instalar doas, crear el grupo 'wheel' si no existe, y crear el usuario RUN apk add --no-cache doas && \ addgroup -S wheel && \ adduser -D -G wheel appuser && \ # Configurar doas para permitir a todos en el grupo 'wheel' actuar como root echo 'permit :wheel as root' > /etc/doas.d/doas.conf USER appuser CMD ["sleep", "infinity"]Esta configuración es más segura, más limpia y sigue las mejores prácticas de administración de sistemas.
Tabla Comparativa: `doas` vs. `sudo`
Para entender mejor por qué `doas` es la opción preferida en Alpine, aquí hay una tabla comparativa:
| Característica | `sudo` | `doas` |
|---|---|---|
| Origen | Proyecto independiente, omnipresente en Linux. | OpenBSD, enfocado en simplicidad y seguridad. |
| Complejidad de Configuración | Alta. El archivo `sudoers` tiene una sintaxis compleja y potente. | Muy baja. Sintaxis simple y directa en `doas.conf`. |
| Tamaño / Dependencias | Más grande, con más dependencias. | Extremadamente ligero, ideal para imágenes minimalistas. |
| Filosofía de Diseño | Ofrecer control granular y exhaustivo sobre los permisos. | Hacer una cosa y hacerla bien: ejecutar un comando como otro usuario. |
| Superficie de Ataque | Mayor debido a su complejidad y cantidad de funcionalidades. | Menor debido a su base de código simple y enfocada. |
Opciones Avanzadas de Configuración
`doas` y `adduser` ofrecen algunas opciones adicionales que pueden ser útiles en ciertos escenarios.
`doas` sin Contraseña (`nopass`)
En entornos automatizados como scripts de CI/CD, solicitar una contraseña no es viable. `doas` permite la ejecución sin contraseña con la opción `nopass`. Úsese con extrema precaución, ya que reduce la seguridad.
# En el Dockerfile, modifica la línea de configuración de doas: echo 'permit nopass :wheel as root' > /etc/doas.d/doas.conf`doas` con Contraseña Persistente (`persist`)
Para sesiones interactivas, puede ser tedioso escribir la contraseña para cada comando. La opción `persist` hace que `doas` recuerde la autenticación durante un breve período de tiempo.
# En el Dockerfile: echo 'permit persist :wheel as root' > /etc/doas.d/doas.confCreación de un Usuario de Sistema
Si el usuario que estás creando no es para un humano, sino para ejecutar un servicio o una aplicación en segundo plano, es una buena práctica crearlo como un usuario de sistema. Estos usuarios suelen tener UIDs en un rango diferente y no están destinados a inicios de sesión interactivos. Se crean con la opción `-S`.
# Crear un usuario de sistema llamado 'nginx_runner' RUN adduser -S nginx_runnerMétodo Alternativo: Creación de Usuario con un Script `ENTRYPOINT`
Hasta ahora, hemos integrado la creación del usuario en el proceso de construcción de la imagen. Existe un enfoque alternativo: crear el usuario dinámicamente cuando el contenedor se inicia, utilizando un script en el `ENTRYPOINT`.
Este método es útil en escenarios avanzados, por ejemplo, cuando necesitas que el UID/GID del usuario dentro del contenedor coincida con el del usuario en el host para evitar problemas de permisos con volúmenes montados.
Primero, creamos un script, por ejemplo, `entrypoint.sh`:
#!/bin/sh # Instalar paquetes necesarios apk add --no-cache doas # Crear usuario y grupo addgroup -S wheel adduser -D -G wheel appuser # Configurar doas echo 'permit persist :wheel as root' > /etc/doas.d/doas.conf # Cambiar al nuevo usuario y ejecutar el comando pasado al contenedor exec su-exec appuser "$@"Luego, el `Dockerfile` simplemente copia y ejecuta este script:
FROM alpine:latest COPY entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] # El comando por defecto que ejecutará el entrypoint como 'appuser' CMD ["sh"]Este enfoque añade una pequeña sobrecarga al inicio del contenedor, pero ofrece una flexibilidad mucho mayor para casos de uso específicos.
Preguntas Frecuentes (FAQ)
¿Por qué no usar simplemente el usuario `root` en Docker?
Usar `root` por defecto es un riesgo de seguridad significativo. Si un atacante logra comprometer un proceso dentro de tu contenedor, tendrá privilegios de superusuario inmediatamente. Esto podría permitirle escalar el ataque, intentar escapar del contenedor al host y causar un daño mucho mayor. Seguir el "Principio de Mínimo Privilegio" es fundamental en la seguridad de contenedores.
¿Cuál es la diferencia real entre `adduser` y `useradd`?
`useradd` es una utilidad binaria de bajo nivel para crear usuarios, mientras que `adduser` es típicamente un script de más alto nivel (en Alpine, es parte de BusyBox) que ofrece una interfaz más amigable y realiza tareas adicionales, como crear el directorio de inicio por defecto. La imagen base de Alpine incluye `adduser` pero no `useradd`.
¿Es `doas` tan seguro como `sudo`?
Se considera que `doas` es extremadamente seguro, precisamente por su simplicidad. Una base de código más pequeña significa menos lugares donde pueden esconderse los errores y las vulnerabilidades (una menor superficie de ataque). Si bien `sudo` es muy potente, esa complejidad ha sido fuente de vulnerabilidades de seguridad en el pasado. Para el 99% de los casos de uso en un contenedor, `doas` es más que suficiente y potencialmente más seguro.
¿Puedo usar `sudo` en Alpine si lo prefiero?
Sí, absolutamente. Puedes instalarlo con `apk add --no-cache sudo`. Sin embargo, esto va en contra del espíritu de usar Alpine. Estarías añadiendo un paquete relativamente grande y complejo a una imagen que fue elegida por ser pequeña y simple. Aprender a usar las herramientas nativas y ligeras del ecosistema, como `doas`, es generalmente la mejor aproximación.
Conclusión
Dominar la gestión de usuarios y permisos en Alpine Linux es una habilidad esencial para cualquiera que busque construir imágenes de Docker eficientes y seguras. Aunque el enfoque minimalista de Alpine puede presentar un desafío inicial, las soluciones que ofrece están perfectamente alineadas con su filosofía. Al abandonar la práctica de usar el usuario `root`, crear usuarios con privilegios mínimos y emplear herramientas ligeras y seguras como `doas`, no solo resolvemos los molestos errores de "Permission denied", sino que también elevamos significativamente la postura de seguridad de nuestras aplicaciones en contenedores. La próxima vez que inicies un `Dockerfile` con `FROM alpine`, tendrás la confianza y el conocimiento para hacerlo de la manera correcta.
Si quieres conocer otros artículos parecidos a Alpine Docker: Solución a Permisos Faltantes puedes visitar la categoría Automovilismo.

