¿Cuál es la ventaja de separar el entorno de ejecución de contenedores en componentes instrumentales separados? En particular, que se pueden comenzar a combinar estos instrumentos para que se protejan entre sí.

A muchos les atrae la idea de construir imágenes OCI en el marco de o un sistema similar. Supongamos que tenemos CI/CD, que constantemente compila imágenes, entonces algo como /Kubernetes было бы весьма полезно с точки зрения распределения нагрузки при сборке. До недавних пор большинство людей просто давали контейнерам доступ к Docker-сокету и разрешали выполнять команду docker build. , que esto es muy inseguro, de hecho, es incluso peor que dar acceso root sin contraseña o sudo.
Por eso la gente constantemente intenta ejecutar Buildah dentro de un contenedor. En resumen, creamos lo que, en nuestra opinión, es la mejor manera de ejecutar Buildah dentro de un contenedor y publicamos las imágenes correspondientes en . Empecemos...
Configuración
Estas imágenes están construidas a partir de Dockerfiles que se pueden encontrar en el repositorio de Buildah en la carpeta .
Aquí revisaremos .
# stable/Dockerfile
#
# Build a Buildah container image from the latest
# stable version of Buildah on the Fedoras Updates System.
# https://bodhi.fedoraproject.org/updates/?search=buildah
# This image can be used to create a secured container
# that runs safely with privileges within the container.
#
FROM fedora:latest
# Don't include container-selinux and remove
# directories used by dnf that are just taking
# up space.
RUN yum -y install buildah fuse-overlayfs --exclude container-selinux; rm -rf /var/cache /var/log/dnf* /var/log/yum.*
# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' /etc/containers/storage.conf
En lugar de OverlayFS, implementado en el nivel del núcleo de Linux del host, usamos dentro del contenedor el programa , ya que en este momento OverlayFS solo puede montar si se le otorgan los permisos SYS_ADMIN a través de las capacidades de Linux. Y queremos ejecutar nuestros contenedores Buildah sin ningún privilegio de nivel root. Fuse-overlay funciona bastante rápido y tiene mejor rendimiento que el controlador de almacenamiento VFS. Note que al ejecutar un contenedor Buildah que usa Fuse, se debe proporcionar el dispositivo /dev/fuse.
podman run --device /dev/fuse quay.io/buildahctr ...
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock
A continuación, creamos un directorio para los almacenes adicionales. soporta la concepto de adjuntar almacenes de imágenes de solo lectura adicionales. Por ejemplo, se puede configurar un área de almacenamiento en superposición en una máquina y luego montarla en otra máquina utilizando NFS y usar las imágenes de ella sin tener que descargarlas mediante pull. Necesitamos este almacenamiento para poder montar, como volumen, algún almacenamiento de imágenes desde el host y utilizarlo dentro del contenedor.
# Set up environment variables to note that this is
# not starting with user namespace and default to
# isolate the filesystem with chroot.
ENV _BUILDAH_STARTED_IN_USERNS="" BUILDAH_ISOLATION=chroot
Y finalmente, utilizando la variable de entorno BUILDAH_ISOLATION, indicamos que por defecto el contenedor de Buildah debe ejecutarse con aislamiento chroot. No se requiere aislamiento adicional aquí, ya que estamos trabajando en un contenedor. Para que Buildah pueda crear sus propios contenedores con separación de espacios de nombres, se necesita privilegio SYS_ADMIN, y para ello será necesario relajar las reglas de SELinux y SECCOMP para el contenedor, lo que contradice nuestra configuración de realizar la construcción desde un contenedor seguro.
Ejecutando Buildah dentro del contenedor
El esquema de imagen del contenedor de Buildah considerado anteriormente permite variar de manera flexible las formas de ejecutar dichos contenedores.
Velocidad frente a seguridad
La seguridad informática siempre es un compromiso entre la velocidad de ejecución del proceso y cuánta protección se ha implementado a su alrededor. Esta afirmación es cierta también al construir contenedores, por lo que a continuación examinaremos las opciones para este tipo de compromiso.
La imagen de contenedor discutida anteriormente mantendrá su almacenamiento en /var/lib/containers. Por lo tanto, necesitamos montar contenido en esta carpeta, y la forma en que lo haremos tendrá un impacto significativo en la velocidad de construcción de las imágenes de contenedores.
Consideremos tres opciones.
Opción 1. Si se requiere la máxima seguridad, se puede crear una carpeta para cada contenedor para containers/image y conectarla al contenedor a través de un volumen montado. Además, se puede ubicar el directorio de contexto dentro del contenedor, en la carpeta /build:
# mkdir /var/lib/containers1
# podman run -v ./build:/build:z -v /var/lib/containers1:/var/lib/containers:Z quay.io/buildah/stable
buildah -t image1 bud /build
# podman run -v /var/lib/containers1:/var/lib/containers:Z quay.io/buildah/stable buildah push image1 registry.company.com/myuser
# rm -rf /var/lib/containers1
Seguridad. Construir con Buildah en dicho contenedor ofrece la máxima seguridad: no se le otorgan privilegios de root mediante capacidades, y se aplican todas las restricciones de SECOMP y SELinux. Este contenedor incluso se puede ejecutar con aislamiento de espacio de usuario, añadiendo una opción como —uidmap 0:100000:10000.
Rendimiento. Sin embargo, el rendimiento aquí es mínimo, ya que cualquier imagen de los registros de contenedores se copia en cada ocasión al host, y la caché no funciona en absoluto. Al finalizar su trabajo, el contenedor de Buildah debe enviar la imagen al registro y destruir el contenido en el host. Cuando se construya la imagen del contenedor la próxima vez, deberá descargarse nuevamente del registro, ya que para ese momento ya no quedará nada en el host.
Opción 2. Si se necesita rendimiento al nivel de Docker, se puede montar container/storage del host directamente en el contenedor.
# podman run -v ./build:/build:z -v /var/lib/containers:/var/lib/containers --security-opt label:disabled quay.io/buildah/stable buildah -t image2 bud /build
# podman run -v /var/lib/containers:/var/lib/containers --security-opt label:disabled quay.io/buildah/stable buildah push image2 registry.company.com/myuser
Seguridad. Este es el método menos seguro para construir contenedores, ya que aquí se permite al contenedor modificar el almacenamiento en el host, lo que podría potencialmente introducir una imagen maliciosa a Podman o CRI-O. Además, será necesario desactivar la separación de SELinux para que los procesos en el contenedor de Buildah puedan interactuar con el almacenamiento en el host. Tenga en cuenta que esta opción sigue siendo mejor que el socket de Docker, ya que el contenedor está bloqueado por las funciones de seguridad restantes y no puede simplemente ejecutar algún contenedor en el host.
Rendimiento. Aquí es máxima, ya que se utiliza completamente la caché. Si Podman o CRI-O ya han descargado la imagen necesaria en el host, entonces el proceso de Buildah dentro del contenedor no necesitará descargarla nuevamente, y las siguientes construcciones basadas en esta imagen también podrán tomar lo necesario de la caché.
Opción 3. La esencia de este método es combinar varias imágenes en un solo proyecto con una carpeta común para las imágenes de contenedor.
# mkdir /var/lib/project3
# podman run --security-opt label_level=s0:C100, C200 -v ./build:/build:z
-v /var/lib/project3:/var/lib/containers:Z quay.io/buildah/stable buildah -t image3 bud /build
# podman run --security-opt label_level=s0:C100, C200
-v /var/lib/project3:/var/lib/containers quay.io/buildah/stable buildah push image3 registry.company.com/myuser
En este ejemplo, no eliminamos la carpeta del proyecto (\/var\/lib\/project3) entre ejecuciones, por lo que todas las siguientes construcciones dentro del proyecto se benefician de la caché.
Seguridad. Algo intermedio entre las opciones 1 y 2. Por un lado, los contenedores no tienen acceso al contenido en el host y, por lo tanto, no pueden introducir algo malo en el almacenamiento de imágenes de Podman\/CRI-O. Por otro lado, dentro de su proyecto, el contenedor puede interferir en la construcción de otros contenedores.
Rendimiento. Aquí es peor que al usar caché compartida a nivel de host, ya que no se pueden usar imágenes que ya se hayan descargado anteriormente por Podman\/CRI-O. Sin embargo, una vez que Buildah descarga la imagen, esta imagen se puede utilizar en cualquier construcción posterior dentro del proyecto.
Almacenes adicionales
En Hay una cosa genial llamada almacenes adicionales (additional stores), gracias a la cual, al iniciar y construir contenedores, los motores de contenedores pueden utilizar almacenes externos de imágenes en modo de superposición de solo lectura. En esencia, se puede agregar uno o varios almacenes 'solo lectura' en el archivo storage.conf, para que luego, al iniciar un contenedor, el motor de contenedores busque la imagen necesaria en ellos. De hecho, solo descargará la imagen del registro si no la encuentra en ninguno de estos almacenes. El motor de contenedores podrá escribir solo en almacenes accesibles para escritura...
Si desplazamos hacia arriba y miramos el Dockerfile que estamos utilizando para construir la imagen quay.io/buildah/stable, hay líneas como estas:
# Adjust storage.conf to enable Fuse storage.
RUN sed -i -e 's|^#mount_program|mount_program|g' -e '/additionalimage.*/a "/var/lib/shared",' /etc/containers/storage.conf
RUN mkdir -p /var/lib/shared/overlay-images /var/lib/shared/overlay-layers; touch /var/lib/shared/overlay-images/images.lock; touch /var/lib/shared/overlay-layers/layers.lock
En la primera línea, modificamos /etc/containers/storage.conf dentro de la imagen del contenedor, indicando al controlador de almacenamiento que use 'additionalimagestores' en la carpeta /var/lib/shared. En la siguiente línea, creamos una carpeta compartida y añadimos un par de archivos de bloqueo para evitar conflictos con containers/storage. En esencia, simplemente estamos creando un almacén vacío de imágenes de contenedores.
Si montamos containers/storage un nivel por encima de esta carpeta, Buildah podrá usar las imágenes.
Ahora regresemos a la Opción 2 mencionada anteriormente, donde el contenedor de Buildah puede leer y escribir en containers/store en los hosts y, por tanto, tiene un rendimiento máximo gracias a la caché de imágenes a nivel de Podman/CRI-O, pero ofrece mínimo seguridad, ya que puede escribir directamente en los almacenes. Y ahora sumemos almacenes adicionales y obtengamos lo mejor de ambos mundos.
# mkdir /var/lib/containers4
# podman run -v ./build:/build:z -v /var/lib/containers/storage:/var/lib/shared:ro -v /var/lib/containers4:/var/lib/containers:Z quay.io/buildah/stable
buildah -t image4 bud /build
# podman run -v /var/lib/containers/storage:/var/lib/shared:ro
-v >/var/lib/containers4:/var/lib/containers:Z quay.io/buildah/stable buildah push image4 registry.company.com/myuser
# rm -rf /var/lib/continers4
Tenga en cuenta que /var/lib/containers/storage del host está montada en /var/lib/shared dentro del contenedor en modo de solo lectura. Por lo tanto, al trabajar en el contenedor, Buildah puede usar cualquier imagen que haya sido descargada previamente a través de Podman/CRI-O (hola, velocidad), pero puede escribir solo en su propio almacén (hola, seguridad). También tenga en cuenta que esto se hace sin desactivar la separación de SELinux para el contenedor.
Un detalle importante
De ninguna manera se deben eliminar imágenes del almacén subyacente. De lo contrario, el contenedor de Buildah podría fallar.
Y estas no son todas las ventajas
Las capacidades de los almacenes adicionales no se limitan solo al escenario descrito anteriormente. Por ejemplo, se puede almacenar todas las imágenes de contenedores en un almacén de red compartido y otorgar acceso a todos los contenedores de Buildah. Supongamos que tenemos cientos de imágenes que nuestro sistema CI/CD utiliza regularmente para construir imágenes de contenedores. Concentramos todas estas imágenes en un solo host-almacén y luego, utilizando los medios preferidos de almacenamiento en red (NFS, Gluster, Ceph, ISCSI, S3…), abrimos el acceso a este almacén a todos los nodos de Buildah o Kubernetes.
Ahora solo es necesario montar este almacén de red en el contenedor de Buildah en /var/lib/shared y listo: los contenedores de Buildah ya no tendrán que descargar imágenes a través de pull. Así, eliminamos la fase de pre-población y estamos listos para desplegar contenedores de inmediato.
Y, por supuesto, esto se puede utilizar en el marco de un sistema Kubernetes existente o una infraestructura de contenedores, para ejecutar y operar contenedores en cualquier lugar sin descargar imágenes a través de pull. Además, el registro de contenedores, al recibir una petición push para cargar una imagen actualizada, puede enviar automáticamente esta imagen al almacén de red compartido, donde se hace disponible de inmediato a todos los nodos.
Los tamaños de las imágenes de los contenedores a veces pueden alcanzar varios gigabytes. La funcionalidad de almacenes adicionales permite evitar la clonación de estas imágenes en los nodos y hace que el lanzamiento de contenedores sea prácticamente instantáneo.
Además, actualmente estamos trabajando en una nueva función de montajes de volúmenes superpuestos, que hará que la construcción de contenedores sea aún más rápida.
Conclusión
Ejecutar Buildah dentro de un contenedor en un entorno Kubernetes/CRI-O, Podman o incluso en Docker es completamente posible; además, es simple y mucho más seguro que usar docker.socket. Hemos aumentado significativamente la flexibilidad en el trabajo con imágenes, y ahora puede ejecutarlas de varias maneras para lograr un equilibrio óptimo entre seguridad y rendimiento.
La funcionalidad de almacenes adicionales permite acelerar o incluso eliminar por completo la descarga de imágenes en los nodos.
Fuente: habr.com
