¿Cómo manejar permisos en Docker?

DockerDockerBeginner
Practicar Ahora

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

Introducción

Manejar los permisos en Docker es un aspecto crucial para la gestión de sus aplicaciones en contenedores. Este tutorial lo guiará a través de la comprensión de los permisos de archivos en Docker, la configuración de permisos en contenedores Docker y la exploración de las mejores prácticas para la gestión de permisos. Al final, tendrá el conocimiento necesario para manejar eficazmente los permisos en sus proyectos basados en Docker.

En este laboratorio (lab), aprenderá cómo funcionan los permisos de los contenedores Docker, cómo crear y usar usuarios no root en contenedores y cómo gestionar los permisos al compartir datos entre el host y los contenedores.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL docker(("Docker")) -.-> docker/VolumeOperationsGroup(["Volume Operations"]) docker(("Docker")) -.-> docker/DockerfileGroup(["Dockerfile"]) docker(("Docker")) -.-> docker/ContainerOperationsGroup(["Container Operations"]) docker/ContainerOperationsGroup -.-> docker/run("Run a Container") docker/ContainerOperationsGroup -.-> docker/rm("Remove Container") docker/ContainerOperationsGroup -.-> docker/exec("Execute Command in Container") docker/ContainerOperationsGroup -.-> docker/logs("View Container Logs") docker/ContainerOperationsGroup -.-> docker/create("Create Container") docker/VolumeOperationsGroup -.-> docker/volume("Manage Volumes") docker/DockerfileGroup -.-> docker/build("Build Image from Dockerfile") subgraph Lab Skills docker/run -.-> lab-415866{{"¿Cómo manejar permisos en Docker?"}} docker/rm -.-> lab-415866{{"¿Cómo manejar permisos en Docker?"}} docker/exec -.-> lab-415866{{"¿Cómo manejar permisos en Docker?"}} docker/logs -.-> lab-415866{{"¿Cómo manejar permisos en Docker?"}} docker/create -.-> lab-415866{{"¿Cómo manejar permisos en Docker?"}} docker/volume -.-> lab-415866{{"¿Cómo manejar permisos en Docker?"}} docker/build -.-> lab-415866{{"¿Cómo manejar permisos en Docker?"}} end

Comprensión de los permisos predeterminados de Docker

En Docker, comprender cómo funcionan los permisos es fundamental para mantener contenedores seguros. Comencemos por explorar la configuración de permisos predeterminados en los contenedores Docker.

Verificación del usuario predeterminado en Docker

Por defecto, Docker ejecuta procesos dentro de los contenedores como el usuario root. Esto puede generar posibles problemas de seguridad si el contenedor se ve comprometido. Para observar este comportamiento, creemos un contenedor simple y verifiquemos qué usuario ejecuta los procesos.

Primero, asegúrese de estar en el directorio de su proyecto:

cd ~/project

Ahora, ejecute un contenedor básico de Ubuntu y verifique el usuario actual:

docker run -it --rm ubuntu:22.04 whoami

Debería ver la siguiente salida:

root

Esto confirma que Docker utiliza el usuario root por defecto. Ahora, verifiquemos el ID de usuario (UID) y el ID de grupo (GID):

docker run -it --rm ubuntu:22.04 id

La salida debería ser similar a:

uid=0(root) gid=0(root) groups=0(root)

El uid=0 y gid=0 indican que el contenedor se está ejecutando como el usuario y grupo root, que tienen acceso completo a todos los recursos del contenedor.

Exploración de los permisos de archivos dentro de un contenedor

Examinemos cómo funcionan los permisos de archivos dentro de un contenedor Docker. Creemos un archivo simple dentro de un contenedor y verifiquemos sus permisos.

Primero, creemos un contenedor que se ejecutará en segundo plano:

docker run -d --name permissions-demo ubuntu:22.04 sleep 3600

Ahora, ejecutemos comandos dentro de este contenedor para crear un archivo y verificar sus permisos:

docker exec permissions-demo touch /test-file
docker exec permissions-demo ls -l /test-file

Debería ver una salida similar a:

-rw-r--r-- 1 root root 0 May 15 12:34 /test-file

Note que el archivo es propiedad del usuario y grupo root. Esto demuestra que todos los archivos creados dentro de un contenedor Docker por defecto son propiedad del usuario root.

Creemos un directorio y verifiquemos sus permisos también:

docker exec permissions-demo mkdir /test-directory
docker exec permissions-demo ls -ld /test-directory

La salida debería ser similar a:

drwxr-xr-x 2 root root 4096 May 15 12:35 /test-directory

Nuevamente, el directorio es propiedad del usuario y grupo root.

Limpiemos el contenedor antes de pasar al siguiente paso:

docker stop permissions-demo
docker rm permissions-demo

Esto demuestra el comportamiento predeterminado de Docker con respecto a los permisos de usuario. En el siguiente paso, aprenderemos cómo crear y usar usuarios no root en contenedores Docker para mejorar la seguridad.

Creación y uso de usuarios no root en Docker

Ejecutar aplicaciones como el usuario root dentro de contenedores Docker presenta riesgos de seguridad. Si un atacante compromete un contenedor que se ejecuta como root, podría obtener privilegios elevados en el sistema host. En este paso, aprenderemos cómo crear y usar usuarios no root en contenedores Docker.

Creación de un Dockerfile con un usuario no root

Creemos un Dockerfile que defina un usuario no root y lo establezca como el usuario predeterminado para ejecutar comandos.

Primero, cree un nuevo directorio para nuestro proyecto:

mkdir -p ~/project/non-root-user
cd ~/project/non-root-user

Ahora, cree un Dockerfile utilizando el editor de texto nano:

nano Dockerfile

Agregue el siguiente contenido al Dockerfile:

FROM ubuntu:22.04

## Create a new user called 'appuser' with user ID 1000
RUN useradd -m -u 1000 appuser

## Create a directory for the application and set ownership
RUN mkdir -p /app && chown -R appuser:appuser /app

## Set the working directory to /app
WORKDIR /app

## Switch to the non-root user
USER appuser

## Create a test file
RUN touch test-file.txt

## Command to run when the container starts
CMD ["bash", "-c", "echo 'Running as user:' && whoami && echo 'File ownership:' && ls -l test-file.txt && tail -f /dev/null"]

Guarde el archivo presionando Ctrl+O, luego Enter, y salga del editor con Ctrl+X.

Este Dockerfile hace lo siguiente:

  1. Crea un nuevo usuario llamado appuser con UID 1000
  2. Crea un directorio para la aplicación y le otorga la propiedad al usuario appuser
  3. Establece el directorio de trabajo en /app
  4. Cambia al usuario no root para los comandos posteriores
  5. Crea un archivo de prueba que será propiedad del usuario appuser
  6. Establece un comando que mostrará información sobre el usuario y la propiedad del archivo

Construcción y ejecución del contenedor con usuario no root

Ahora, construyamos la imagen Docker a partir de nuestro Dockerfile:

docker build -t non-root-image .

Debería ver una salida que indique que Docker está construyendo la imagen. Una vez completada la construcción, ejecute un contenedor a partir de esta imagen:

docker run --name non-root-container -d non-root-image

Ahora, veamos la salida de nuestro contenedor:

docker logs non-root-container

Debería ver una salida similar a:

Running as user:
appuser
File ownership:
-rw-r--r-- 1 appuser appuser 0 May 15 12:45 test-file.txt

Esto confirma que el contenedor se está ejecutando como el usuario appuser, y el archivo de prueba es propiedad de appuser.

Prueba de los permisos del usuario no root

Conectémonos al contenedor en ejecución y exploremos los permisos de nuestro usuario no root:

docker exec -it non-root-container bash

Ahora debería estar dentro del contenedor como el usuario appuser. Verifiquemos esto:

whoami

Debería ver:

appuser

Verifique el ID de usuario y el ID de grupo:

id

La salida debería ser similar a:

uid=1000(appuser) gid=1000(appuser) groups=1000(appuser)

Ahora, intente crear un archivo en el directorio raíz:

touch /root-test-file

Debería ver un error de permiso denegado:

touch: cannot touch '/root-test-file': Permission denied

Esto se debe a que el usuario no root no tiene permisos de escritura en el directorio raíz. Sin embargo, nuestro usuario puede escribir en el directorio /app:

touch /app/user-test-file
ls -l /app/user-test-file

Debería ver que el nuevo archivo es propiedad de appuser:

-rw-r--r-- 1 appuser appuser 0 May 15 12:50 /app/user-test-file

Salga de la shell del contenedor:

exit

Limpiemos el contenedor antes de pasar al siguiente paso:

docker stop non-root-container
docker rm non-root-container

Este paso demostró cómo crear y usar un usuario no root en un contenedor Docker. El uso de un usuario no root ayuda a mejorar la seguridad de sus aplicaciones en contenedores al restringir los permisos disponibles para la aplicación.

Gestión de permisos de montaje de volúmenes

Los volúmenes de Docker permiten compartir datos entre el host y los contenedores. Sin embargo, gestionar correctamente los permisos al utilizar volúmenes es importante para evitar problemas. En este paso, aprenderemos cómo manejar los permisos al utilizar montajes de volúmenes.

Comprensión de los problemas de permisos de montaje de volúmenes

El principal desafío con los montajes de volúmenes de Docker es que los IDs de usuario dentro del contenedor pueden no coincidir con los IDs de usuario en el sistema host. Esto puede provocar problemas de permisos al acceder a archivos en volúmenes montados.

Demostremos este problema con un ejemplo sencillo.

Primero, cree un nuevo directorio en el host que montaremos en un contenedor:

mkdir -p ~/project/host-data
cd ~/project/host-data

Cree un archivo de prueba en este directorio:

echo "This is a test file created on the host" > host-file.txt

Verifique la propiedad de este archivo:

ls -l host-file.txt

Debería ver que el archivo es propiedad del usuario labex (su usuario actual en el host):

-rw-r--r-- 1 labex labex 39 May 15 13:00 host-file.txt

Ahora, ejecutemos un contenedor que monte este directorio y tratemos de modificar el archivo:

docker run -it --rm -v ~/project/host-data:/container-data ubuntu:22.04 bash

Ahora está dentro del contenedor. Verifiquemos la propiedad del archivo montado:

ls -l /container-data/host-file.txt

Puede ver una salida similar a:

-rw-r--r-- 1 1000 1000 39 May 15 13:00 /container-data/host-file.txt

Note que el archivo muestra IDs numéricos en lugar de nombres de usuario porque el contenedor no conoce al usuario labex del host.

Intente crear un nuevo archivo en el directorio montado:

echo "This is a test file created in the container" > /container-data/container-file.txt

Ahora, verifique la propiedad de este nuevo archivo:

ls -l /container-data/container-file.txt

Debería ver:

-rw-r--r-- 1 root root 47 May 15 13:05 /container-data/container-file.txt

El archivo es propiedad del usuario root dentro del contenedor (ya que estamos ejecutando como root por defecto).

Salga del contenedor:

exit

Ahora, verifique la propiedad de ambos archivos en el host:

ls -l ~/project/host-data/

Verá que el archivo creado desde dentro del contenedor es propiedad de root en el host:

-rw-r--r-- 1 root  root  47 May 15 13:05 container-file.txt
-rw-r--r-- 1 labex labex 39 May 15 13:00 host-file.txt

Esto puede provocar problemas de permisos si un usuario no root en el host necesita acceder o modificar estos archivos.

Solución de problemas de permisos de volúmenes

Hay varias formas de resolver los problemas de permisos de volúmenes. Exploremos algunos enfoques comunes.

Enfoque 1: Establecer el usuario en el Dockerfile para que coincida con el usuario del host

Cree un nuevo directorio para este ejemplo:

mkdir -p ~/project/volume-permissions
cd ~/project/volume-permissions

Cree un nuevo Dockerfile:

nano Dockerfile

Agregue el siguiente contenido:

FROM ubuntu:22.04

## Create a user with the same UID as the host user
RUN useradd -m -u 1000 appuser

## Create app directory and set ownership
RUN mkdir -p /app/data && chown -R appuser:appuser /app

## Set working directory
WORKDIR /app

## Switch to appuser
USER appuser

## Command to run
CMD ["bash", "-c", "echo 'I can write to the mounted volume' > /app/data/test.txt && tail -f /dev/null"]

Guarde y salga del editor.

Construya la imagen:

docker build -t volume-permissions-image .

Cree un directorio en el host para realizar pruebas:

mkdir -p ~/project/volume-permissions/host-data

Ejecute un contenedor con el volumen montado:

docker run -d --name volume-test -v ~/project/volume-permissions/host-data:/app/data volume-permissions-image

Después de un momento, verifique la propiedad del archivo creado en el host:

ls -l ~/project/volume-permissions/host-data/

Debería ver que el archivo es propiedad de un usuario con UID 1000, que debería coincidir con el UID de su usuario en el host:

-rw-r--r-- 1 labex labex 35 May 15 13:15 test.txt

Este enfoque funciona porque creamos un usuario en el contenedor con el mismo UID que el usuario del host.

Enfoque 2: Utilizar la bandera --user

Otro enfoque es utilizar la bandera --user para especificar qué ID de usuario utilizar al ejecutar el contenedor.

Primero, limpie el contenedor anterior:

docker stop volume-test
docker rm volume-test

Ahora ejecute un contenedor con la bandera --user:

docker run -d --name user-flag-test --user "$(id -u):$(id -g)" -v ~/project/volume-permissions/host-data:/data ubuntu:22.04 bash -c "echo 'Created with --user flag' > /data/user-flag-test.txt && sleep 3600"

Este comando ejecuta el contenedor con su ID de usuario y ID de grupo actuales.

Verifique la propiedad del nuevo archivo:

ls -l ~/project/volume-permissions/host-data/user-flag-test.txt

Debería ver que el archivo es propiedad de su usuario en el host:

-rw-r--r-- 1 labex labex 23 May 15 13:20 user-flag-test.txt

Limpiemos antes de pasar al siguiente paso:

docker stop user-flag-test
docker rm user-flag-test

Estos enfoques demuestran cómo gestionar los permisos al utilizar volúmenes de Docker. Al hacer coincidir los IDs de usuario entre el contenedor y el host, se pueden evitar problemas de permisos al compartir datos entre ellos.

Implementación de las mejores prácticas de permisos

Pongamos en práctica todo lo que hemos aprendido creando un ejemplo más realista de un contenedor Docker que siga las mejores prácticas de permisos. Crearemos un contenedor de una aplicación web sencilla que siga las mejores prácticas de seguridad en cuanto a permisos.

Creación de un contenedor de aplicación web seguro

Primero, cree un nuevo directorio para nuestro ejemplo:

mkdir -p ~/project/secure-app
cd ~/project/secure-app

Creemos una aplicación web sencilla. Primero, cree un archivo app.py:

nano app.py

Agregue el siguiente código Python para crear un servidor web Flask sencillo:

from flask import Flask
import os

app = Flask(__name__)

@app.route('/')
def hello():
    return 'Hello from a secure container!'

@app.route('/whoami')
def whoami():
    return os.popen('id').read()

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080)

Guarde y salga del editor.

Ahora, cree un archivo requirements.txt para las dependencias de Python:

nano requirements.txt

Agregue el siguiente contenido:

flask==2.0.1

Guarde y salga del editor.

Ahora, cree un Dockerfile que siga las mejores prácticas de permisos:

nano Dockerfile

Agregue el siguiente contenido:

FROM python:3.10-slim

## Create a non-root user to run the application
RUN groupadd -g 1000 appgroup \
  && useradd -u 1000 -g appgroup -s /bin/bash -m appuser

## Set working directory and create necessary directories
WORKDIR /app

## Copy requirements first to leverage Docker cache
COPY requirements.txt .

## Install dependencies as root
RUN pip install --no-cache-dir -r requirements.txt

## Copy application code
COPY app.py .

## Create a data directory that the application can write to
RUN mkdir -p /app/data \
  && chown -R appuser:appgroup /app

## Set proper permissions
RUN chmod -R 755 /app

## Switch to non-root user
USER appuser

## Expose the port the app will run on
EXPOSE 8080

## Command to run the application
CMD ["python", "app.py"]

Guarde y salga del editor.

Construyamos la imagen Docker:

docker build -t secure-web-app .

Ahora, ejecutemos el contenedor:

docker run -d --name secure-app -p 8080:8080 secure-web-app

Probemos la aplicación. Primero, verifiquemos si el contenedor está en ejecución:

docker ps

Debería ver su contenedor secure-app en la lista. Ahora, use curl para probar la aplicación:

curl http://localhost:8080/

Debería ver:

Hello from a secure container!

Veamos como usuario se está ejecutando la aplicación:

curl http://localhost:8080/whoami

Debería ver una salida similar a:

uid=1000(appuser) gid=1000(appgroup) groups=1000(appgroup)

Esto confirma que nuestra aplicación se está ejecutando como el usuario no root appuser.

Análisis de seguridad

Analicemos las mejoras de seguridad en nuestro Dockerfile:

  1. Usuario no root: Creamos un usuario dedicado (appuser) para ejecutar la aplicación, lo que reduce el riesgo si el contenedor es comprometido.

  2. Permisos mínimos: Establecimos solo los permisos necesarios para que la aplicación funcione.

  3. Propiedad clara: Todos los archivos y directorios en el contenedor tienen una propiedad claramente definida.

  4. Estructura de directorios adecuada: Creamos una estructura de directorios dedicada para la aplicación y sus datos.

Estas prácticas ayudan a garantizar que, incluso si un atacante logra explotar una vulnerabilidad en la aplicación, tendrá un acceso limitado al contenedor y al sistema host.

Implementación de permisos de volúmenes con la aplicación segura

Agreguemos un volumen a nuestra aplicación segura para demostrar cómo manejar adecuadamente los permisos con volúmenes.

Primero, detenga y elimine el contenedor existente:

docker stop secure-app
docker rm secure-app

Cree un directorio de datos en el host:

mkdir -p ~/project/secure-app/host-data

Establezca los permisos correctos en el directorio del host:

sudo chown 1000:1000 ~/project/secure-app/host-data

Ahora, ejecute el contenedor con el volumen montado:

docker run -d --name secure-app-with-volume \
  -p 8080:8080 \
  -v ~/project/secure-app/host-data:/app/data \
  secure-web-app

Conectémonos al contenedor y creemos un archivo en el volumen montado:

docker exec -it secure-app-with-volume bash

Ahora, dentro del contenedor, cree un archivo de prueba en el volumen montado:

echo "Test file created from inside the container" > /app/data/container-file.txt

Verifique la propiedad del archivo:

ls -l /app/data/container-file.txt

Debería ver que el archivo es propiedad de appuser:

-rw-r--r-- 1 appuser appgroup 43 May 15 13:30 /app/data/container-file.txt

Salga del contenedor:

exit

Ahora, verifique la propiedad del archivo en el host:

ls -l ~/project/secure-app/host-data/container-file.txt

Debería ver que el archivo es propiedad del UID 1000 (que corresponde a su usuario en el host):

-rw-r--r-- 1 labex labex 43 May 15 13:30 container-file.txt

Esto demuestra que, con una configuración adecuada de permisos, los archivos creados dentro del contenedor en un volumen montado tendrán la propiedad correcta en el host.

Limpiemos antes de concluir:

docker stop secure-app-with-volume
docker rm secure-app-with-volume

Siguiendo estas mejores prácticas de permisos de Docker, puede crear aplicaciones en contenedores seguras y confiables que gestionen adecuadamente los permisos de archivos tanto dentro del contenedor como al compartir datos con el sistema host.

Resumen

En este laboratorio, has aprendido técnicas esenciales para gestionar permisos en contenedores Docker:

  1. Comprensión de los permisos predeterminados de Docker: Has explorado cómo los contenedores Docker se ejecutan como root por defecto y cómo esto puede generar riesgos de seguridad potenciales.

  2. Creación y uso de usuarios no root: Has aprendido cómo crear usuarios no root personalizados en contenedores Docker, lo cual es una práctica de seguridad fundamental.

  3. Gestión de permisos de montaje de volúmenes: Has descubierto cómo manejar los permisos al compartir datos entre el host y los contenedores utilizando volúmenes, abordando problemas comunes de permisos.

  4. Implementación de las mejores prácticas de permisos: Has aplicado todos estos conceptos para crear un contenedor de aplicación web seguro que siga las mejores prácticas de permisos de Docker.

Al aplicar estas técnicas en tus proyectos de Docker, puedes mejorar significativamente la seguridad y la confiabilidad de tus aplicaciones en contenedores. Recuerda, ejecutar contenedores con los privilegios mínimos necesarios es un principio de seguridad fundamental que siempre debe seguirse.

Algunos puntos clave a recordar:

  • Utiliza siempre usuarios no root en tus contenedores.
  • Haz coincidir los IDs de usuario entre el host y el contenedor cuando uses volúmenes.
  • Establece permisos adecuados para archivos y directorios.
  • Sigue el principio de privilegio mínimo.

Estas prácticas te ayudarán a construir contenedores Docker más seguros y a evitar problemas comunes relacionados con los permisos.