Skip to main content

Herramientas de línea de comandos para contenedores: cómo usar Snyk con Buildah, Podman y Skopeo

Escrito por

9 de diciembre de 2020

0 minutos de lectura

A medida que el ecosistema de contenedores ha madurado, algo que no nos falta son opciones, tanto en cuanto al software que usamos como a la forma de integrarlo todo.

Una de estas opciones es la combinación de Buildah, Podman y Skopeo: tres herramientas de línea de comandos de código abierto que surgieron en el ecosistema de RedHat. Como su nombre indica, Buildah ofrece una amplia variedad de funciones para crear contenedores e imágenes compatibles con OCI; Podman permite administrar y ejecutar contenedores OCI, mientras que Skopeo es una herramienta para trabajar con registros de contenedores.

En esta publicación del blog veremos cómo el uso de estándares abiertos permite que el análisis de contenedores de Snyk funcione con todas estas tecnologías.

Primero, veamos Skopeo. Es una herramienta de línea de comandos para mover imágenes de contenedores entre repositorios y también puede guardar imágenes localmente. Skopeo permite generar imágenes como archivos Docker o archivos OCI, dos formatos que Snyk admite al analizar desde el sistema de archivos. Usemos Skopeo para descargar una imagen de Quay.io y analizarla localmente desde nuestro sistema de archivos:

$ skopeo copy docker://quay.io/tutum/hello-world oci-archive:hello-world.tar
$ snyk container test oci-archive:hello-world.tar 

Testing oci-archive:hello-world.tar…

{-----OUTPUT SNIPPED-----}

✗ High severity vulnerability found in openssl
  Description: Out-of-Bounds
  Info: https://snyk.io/vuln/SNYK-UBUNTU1210-OPENSSL-374571
  Introduced through: openssl@1.0.1c-3ubuntu2.6, ca-certificates@20120623, ssl-cert@1.0.32, meta-common-packages@meta
  From: openssl@1.0.1c-3ubuntu2.6
  From: ca-certificates@20120623 > openssl@1.0.1c-3ubuntu2.6
  From: ssl-cert@1.0.32 > openssl@1.0.1c-3ubuntu2.6
  and 1 more...
  Fixed in: 1.0.1c-3ubuntu2.7

Organization:      matt-jarvis-snyk
Package manager:   deb
Project name:      docker-image|hello-world.tar
Docker image:      oci-archive:hello-world.tar
Licenses:          enabled

Tested 237 dependencies for known issues, found 25 issues.

Ubuntu 12.10 is no longer supported by the Ubuntu maintainers. Vulnerability detection may be affected by a lack of security updates.

Como podemos ver, esta imagen usa una versión de Ubuntu que llegó al fin de su vida útil y, por lo tanto, contiene varias vulnerabilidades que no se corregirán. Así, con Skopeo podemos descargar imágenes de cualquier registro de contenedores que cumpla con los estándares, en formatos compatibles con Snyk, y analizarlas localmente.

Buildah

Ahora veamos Buildah. Buildah es una herramienta de línea de comandos que crea imágenes, pero no las ejecuta, por lo que está pensada para usarse junto con Podman. También está diseñada para funcionar sin privilegios de root: utiliza espacios de nombres de usuario en el kernel de Linux para aislar un shell similar al de root que solo tiene privilegios de usuario. Esto permite que los administradores faculten a los desarrolladores para crear y mantener imágenes de contenedores y, al mismo tiempo, seguir los principios de privilegios mínimos en el entorno de compilación.

Aunque Buildah puede trabajar con Dockerfiles de forma similar a Docker, también permite desarrollar contenedores e imágenes con otros flujos de trabajo, por ejemplo, usando Bash o Python. Esto puede ofrecer ventajas en flexibilidad y facilitar la depuración de las compilaciones de contenedores. Buildah permite, por ejemplo, montar el contenedor intermedio durante el proceso de compilación, para que los scripts puedan verificar que todo se ejecute correctamente antes de continuar, compilar software de forma externa o incluso solicitar datos al usuario. Esto ofrece una alternativa a los procesos de compilación de varias etapas, a veces complicados, que se han convertido en la práctica recomendada al usar Docker. 

Veamos un ejemplo de cómo usar Buildah.

Lo primero que debes saber sobre Buildah es que podemos usar Dockerfiles para crear nuestros contenedores, si ese es el flujo de trabajo que te funciona. Tomemos este Dockerfile como ejemplo:

$ cat Dockerfile 
FROM centos:8
LABEL maintainer Matt Jarvis <matt@mattjarvis.org.uk>

RUN dnf install -y git gcc findutils make help2man texinfo gperf gettext-devel autoconf automake && dnf clean all

RUN git clone https://git.savannah.gnu.org/git/hello.git /opt/hello

WORKDIR /opt/hello

RUN ./bootstrap --skip-po && ./configure && make && make install

RUN ./hello
ENTRYPOINT "/usr/local/bin/hello"

Aquí usamos la imagen de CentOS 8 como base, clonamos el código fuente de GNU Hello, instalamos algunos requisitos previos y luego compilamos e instalamos el binario.

Podemos compilar esto automáticamente con Buildah y la opción bud (build-using-dockerfile):

$ buildah bud

Esto claramente no es una buena práctica, ya que incluimos tanto el código fuente como los requisitos previos de compilación en nuestra imagen final, y esta imagen incumple la mayoría de las prácticas recomendadas para desarrollar imágenes seguras. Con Docker, la práctica recomendada en este caso sería una compilación de varias etapas. Podríamos usar un Dockerfile de varias etapas con Buildah, pero es más interesante que también podemos usar comandos nativos de Buildah y escribir scripts para hacerlo. Para replicar exactamente lo que hicimos con el Dockerfile anterior, ejecutaríamos el siguiente script:

$ cat single-stage.sh 
#!/usr/bin/env bash

set -o errexit

# Create a container
container=$(buildah from centos:8)

# Labels are part of the "buildah config" command
buildah config --label maintainer="Matt Jarvis <matt@mattjarvis.org.uk>" $container

# Install pre-reqs
buildah run $container dnf install -y git gcc findutils make help2man texinfo gperf gettext-devel autoconf automake
buildah run $container dnf clean all

# Clone the git repository
buildah run $container git clone https://git.savannah.gnu.org/git/hello.git /opt/hello

# Workingdir is also a "buildah config" command
buildah config --workingdir /opt/hello $container

buildah run $container ./bootstrap --skip-po
buildah run $container ./configure
buildah run $container make
buildah run $container make install

# Entrypoint is also a “buildah config” command
buildah config --entrypoint /usr/local/bin/hello $container

# Finally save the running container to an image
buildah commit --format docker $container hello:latest

Sin embargo, Buildah también nos permite montar el contenedor intermedio durante el proceso de compilación. Así, podemos compilar el código fuente en el host y simplemente copiar el binario resultante en el contenedor antes de crear la imagen. De esta forma, podemos aprovechar los recursos del host durante las compilaciones y hacer otras cosas, como usar el administrador de paquetes del host para instalar paquetes en el contenedor. Tampoco necesitamos acceso de root para hacerlo, ya que Buildah admite el uso de espacios de nombres de usuario para permitir el montaje sin privilegios mediante el comando unshare:

$ buildah unshare
$ cat host-mount.sh 
#!/usr/bin/env bash

set -o errexit

# Create a container
container=$(buildah from centos:8)
mountpoint=$(buildah mount $container)

buildah config --label maintainer="Matt Jarvis <matt@mattjarvis.org.uk>" $container

# Clone the git repository to the host
git clone https://git.savannah.gnu.org/git/hello.git hello

pushd hello
./bootstrap --skip-po
./configure
make
# Install to the container
make install DESTDIR=${mountpoint}
popd

chroot $mountpoint bash -c "/usr/local/bin/hello"

buildah config --entrypoint "/usr/local/bin/hello" $container
buildah commit --format docker $container hello
buildah unmount $container

Como puedes ver en el ejemplo anterior, después de ejecutar el comando unshare tenemos un shell de root, aunque está en un espacio de nombres de usuario sin privilegios completos de root. Esto nos permite montar el sistema de archivos del contenedor durante el proceso de compilación. Como se trata de un script de Bash, también podemos aprovechar toda la funcionalidad que ofrece un lenguaje de programación: podríamos usar condicionales y bucles, e incluso solicitar datos al usuario durante el proceso.

Podman

Una vez que ejecutamos este script y creamos nuestra imagen, también podemos analizarla con Snyk; aquí es donde entra en juego Podman. Podman es un motor de contenedores sin daemon para desarrollar, administrar y ejecutar contenedores OCI. Como Buildah y Podman se basan en estándares abiertos, Snyk siempre ha podido analizar imágenes creadas o descargadas con Podman: basta con usar Podman para guardar la imagen en el disco y analizarla desde el sistema de archivos. Podman permite guardar imágenes tanto como archivos Docker estándar como en formato de archivo OCI, y Snyk admite ambos.

[root@localhost buildah_test]# podman images
REPOSITORY                         TAG     IMAGE ID      CREATED         SIZE
localhost/hello                    latest  975f60fc1f19  15 minutes ago  223 MB
[root@localhost buildah_test]# podman save 975f60fc1f19 -o hello.tar
[root@localhost buildah_test]# snyk container test docker-archive:hello.tar

Testing docker-archive:hello.tar...

{-----OUTPUT SNIPPED-----}

✗ High severity vulnerability found in librepo
  Description: RHSA-2020:3658
  Info: https://snyk.io/vuln/SNYK-CENTOS8-LIBREPO-610057
  Introduced through: librepo@1.11.0-2.el8
  From: librepo@1.11.0-2.el8
  Fixed in: 0:1.11.0-3.el8_2

Organization:      matt-jarvis-snyk
Package manager:   rpm
Project name:      docker-image|hello.tar
Docker image:      docker-archive:hello.tar
Platform:          linux/amd64
Licenses:          enabled

Tested 172 dependencies for known issues, found 28 issues.

Pro tip: use `--file` option to get base image remediation advice.
Example: $ snyk test --docker docker-archive:hello.tar --file=path/to/Dockerfile

To remove this message in the future, please run `snyk config set disableSuggestions=true`

Ahora bien, como nuestra imagen base es pública y está disponible en Dockerhub, si queremos analizarla con Snyk CLI, también podemos hacer lo siguiente:

[root@localhost buildah_test]# snyk container test centos:8

En este caso, Snyk se comunicará directamente con Dockerhub y analizará la imagen de origen.

Sin embargo, ninguna de esas opciones funcionará si la imagen solo existe localmente en Podman o si está en un repositorio privado de Dockerhub que requiere iniciar sesión. Por ejemplo, intentemos analizar nuestra imagen compilada directamente desde Podman:

[matt@localhost buildah_test]# podman images
REPOSITORY                         TAG     IMAGE ID      CREATED         SIZE
localhost/hello                    latest  975f60fc1f19  20 minutes ago  223 MB

[matt@localhost buildah_test]# snyk container test localhost/hello
connect ECONNREFUSED 127.0.0.1:443

Para estos casos, Snyk usa el cliente local de Docker, ya sea mediante la API de Registry o el socket de Docker. Como el sistema no tiene el binario de Docker, Snyk intentó usar la API de Registry y no pudo conectarse. De forma predeterminada, Snyk no sabe que esas imágenes existen localmente en Podman ni cómo usar Podman para comunicarse con el origen.

Por suerte, Podman ofrece algunas funciones de compatibilidad sencillas mediante el paquete podman-docker. Este proporciona un binario falso de Docker, que básicamente es un contenedor de shell para el binario de Podman, y agrega un enlace simbólico entre el socket de la API de Podman y la ubicación predeterminada del archivo de socket de Docker. De forma predeterminada, este paquete crea un enlace simbólico a /var/run/podman/podman.sock, que es el valor predeterminado cuando se ejecuta la API de Podman como root.

[matt@localhost buildah_test]$ sudo dnf install podman-docker

Now let’s try and scan our local image again :

[matt@localhost buildah_test]$ snyk container test localhost/hello
connect EACCES /var/run/docker.sock

Esto sigue sin funcionar, pero esta vez tenemos un error distinto. Snyk detectó un binario llamado docker y ahora intenta conectarse a la ubicación predeterminada del socket de Docker con privilegios. Como todavía no estamos ejecutando la API de Podman, no puede conectarse y vuelve a fallar.

Como vimos antes, el paquete podman-docker crea un enlace simbólico desde /var/run/podman/podman.sock hasta /var/run/docker.sock, suponiendo que la API de Podman se ejecutará con privilegios.

[matt@localhost buildah_test]$ ls -l /var/run/docker.sock 
lrwxrwxrwx. 1 root root 23 Nov 25 07:51 /var/run/docker.sock -> /run/podman/podman.sock

Uno de los principales problemas del socket de Docker es que, de forma predeterminada, se ejecuta con privilegios, lo que se considera un riesgo de seguridad en ciertos entornos. En la mayoría de los entornos de desarrollo local no necesitamos ejecutar la API de Podman de esa manera; podemos usar un socket sin privilegios en otra ubicación. También podemos comunicarle esa ubicación a Snyk mediante la variable de entorno DOCKER_HOST, que admite los formatos de URL tcp:// y unix://.

En otra terminal, ejecutemos la API de Podman con un socket sin privilegios:

[matt@localhost ~]$ podman system service --time=0 unix://home/matt/podman.sock

El proceso se ejecutará en primer plano, así que tendrás que presionar Ctrl+C para finalizarlo cuando termines la prueba. Ahora tenemos que establecer la variable de entorno DOCKER_HOST:

[matt@localhost buildah_test]$ export DOCKER_HOST=unix:///home/matt/podman.sock

Por último, intentemos analizar de nuevo nuestra imagen local con Snyk:

[matt@localhost buildah_test]$ snyk container test localhost/hello

Testing localhost/hello...

{ ----SNIPPED OUTPUT----}

✗ High severity vulnerability found in librepo
  Description: RHSA-2020:3658
  Info: https://snyk.io/vuln/SNYK-CENTOS8-LIBREPO-610057
  Introduced through: librepo@1.11.0-2.el8
  From: librepo@1.11.0-2.el8
  Fixed in: 0:1.11.0-3.el8_2

Organization:      matt-jarvis-snyk
Package manager:   rpm
Project name:      docker-image|localhost/hello
Docker image:      localhost/hello
Platform:          linux/amd64
Licenses:          enabled

Tested 172 dependencies for known issues, found 28 issues.

Pro tip: use `--file` option to get base image remediation advice.
Example: $ snyk test --docker localhost/hello --file=path/to/Dockerfile

To remove this message in the future, please run `snyk config set disableSuggestions=true`

¡Listo! Snyk ahora funciona correctamente y se comunica con Podman mediante el socket sin privilegios.

Para configurar DOCKER_HOST de forma permanente en tu shell, puedes agregar el comando export al archivo .bashrc para que el valor se mantenga.

Por último, nos gustaría que toda esta configuración estuviera disponible automáticamente cada vez que iniciamos sesión. Para lograrlo, podemos aprovechar las funciones de activación de sockets de systemd, que iniciarán automáticamente nuestro socket cuando iniciemos sesión e intentemos conectarnos a él.

Primero, tenemos que configurar el socket:

[matt@localhost ~]$ cat .config/systemd/user/podman.socket
[Unit]
Description=Podman API Socket
Documentation=man:podman-api(1)

[Socket]
ListenStream=/home/matt/podman.sock
SocketMode=0660

[Install]
WantedBy=sockets.target

Luego, asociamos el socket con un servicio:

[matt@localhost ~]$ cat .config/systemd/user/podman.service
[Unit]
Description=Podman API Service
Requires=podman.socket
After=podman.socket
Documentation=man:podman-api(1)
StartLimitIntervalSec=0

[Service]
Type=oneshot
Environment=REGISTRIES_CONFIG_PATH=/etc/containers/registries.conf
ExecStart=/usr/bin/podman system service unix:///home/matt/podman.sock
TimeoutStopSec=30
KillMode=process

[Install]
WantedBy=multi-user.target
Also=podman.socket

Por último, volvemos a cargar la configuración de systemd y habilitamos el servicio.

[matt@localhost ~]$ systemctl --user daemon-reload
[matt@localhost ~]$ systemctl --user enable --now podman.socket

Conclusión

Y así es como el análisis de imágenes de Snyk CLI funciona con Podman igual que con Docker, lo que permite a los desarrolladores analizar de forma integral y sencilla imágenes locales de Docker u OCI como parte de su flujo de trabajo, sin necesitar privilegios elevados. También vimos cómo usar Skopeo y Buildah con Snyk gracias al poder de los estándares abiertos.

¿Todavía no tienes una cuenta de Snyk? Regístrate gratis y usa Snyk para analizar tanto imágenes de contenedores como dependencias de código abierto.

Empieza con los retos de Capture the Flag

Aprende a resolver retos de Capture the Flag viendo nuestro taller virtual de nivel básico a pedido.

Leer más

feature customer snowflake
Article

Seguridad desde el inicio: presentamos la integración de Snyk Studio con Snowflake Cortex Code

Snyk Studio se integra con Snowflake Cortex Code para analizar código generado por IA, dependencias y contenedores en busca de vulnerabilidades durante el desarrollo.

blog feature pypi spoof
Article

Toda la Snyk AI Security Platform, gratis para quienes mantienen proyectos de código abierto

Quienes mantienen proyectos de código abierto reciben reportes de vulnerabilidades reales sin parar y necesitan ayuda para priorizarlas, corregirlas y publicar soluciones más rápido. El Secure Developer Program de Snyk ofrece a los proyectos que cumplen los requisitos acceso gratis a Snyk AI Security Platform.

feature insights announcement
Blog

Compromiso de la cadena de suministro de node-gyp: un gusano de npm que se propaga solo y se oculta en binding.gyp

Un nuevo gusano de npm abusa de binding.gyp para activar node-gyp durante la instalación y permitir que paquetes maliciosos ejecuten código sin scripts del ciclo de vida. Roba credenciales, persiste en GitHub y se propaga entre mantenedores.