Métodos y ejemplos de implementación de las utilidades de comprobación de seguridad de Docker

Métodos y ejemplos de implementación de las utilidades de comprobación de seguridad de Docker
¡Hola, Habr!

En la realidad moderna, debido al papel cada vez mayor de la contenerización en los procesos de desarrollo, la cuestión de garantizar la seguridad de las distintas etapas y entidades asociadas con los contenedores no es la cuestión menos importante. Realizar comprobaciones manuales lleva mucho tiempo, por lo que sería una buena idea dar al menos los pasos iniciales para automatizar este proceso.

En este artículo, compartiré scripts listos para implementar varias utilidades de seguridad de Docker e instrucciones sobre cómo implementar una pequeña demostración para probar este proceso. Puede utilizar los materiales para experimentar cómo organizar el proceso de prueba de seguridad de las imágenes e instrucciones de Dockerfile. Está claro que la infraestructura de desarrollo e implementación de cada persona es diferente, por lo que a continuación brindaré varias opciones posibles.

Utilidades de control de seguridad

Existe una gran cantidad de aplicaciones y scripts de ayuda diferentes que realizan comprobaciones en diversos aspectos de la infraestructura de Docker. Algunos de ellos ya han sido descritos en el artículo anterior (https://habr.com/ru/company/swordfish_security/blog/518758/#docker-security), y en este material me gustaría centrarme en tres de ellos, que cubren la mayor parte de los requisitos de seguridad para las imágenes de Docker creadas durante el proceso de desarrollo. Además, también mostraré un ejemplo de cómo estas tres utilidades se pueden conectar en una sola tubería para realizar controles de seguridad.

hadolin
https://github.com/hadolint/hadolint

Una utilidad de consola bastante simple que ayuda, como primera aproximación, a evaluar la exactitud y seguridad de las instrucciones de Dockerfile (por ejemplo, usando solo registros de imágenes autorizados o usando sudo).

Métodos y ejemplos de implementación de las utilidades de comprobación de seguridad de Docker

muelle
https://github.com/goodwithtech/dockle

Una utilidad de consola que trabaja con una imagen (o con un archivo tar guardado de una imagen), que verifica la exactitud y seguridad de una imagen en particular como tal, analizando sus capas y configuración: qué usuarios se crean, qué instrucciones se utilizan, qué volúmenes montados, presencia de una contraseña vacía, etc. D. Hasta ahora, el número de comprobaciones no es muy grande y se basa en varias de nuestras propias comprobaciones y recomendaciones. Punto de referencia CIS (Centro para la seguridad de Internet) para Docker.
Métodos y ejemplos de implementación de las utilidades de comprobación de seguridad de Docker

curiosidades
https://github.com/aquasecurity/trivy

Esta utilidad tiene como objetivo encontrar dos tipos de vulnerabilidades: problemas con las compilaciones del sistema operativo (compatibles con Alpine, RedHat (EL), CentOS, Debian GNU, Ubuntu) y problemas con las dependencias (Gemfile.lock, Pipfile.lock, compositor.lock, paquete -lock.json, hilo.lock, cargo.lock). Trivy puede escanear tanto una imagen en el repositorio como una imagen local, y también puede escanear en función del archivo .tar transferido con la imagen de Docker.

Métodos y ejemplos de implementación de las utilidades de comprobación de seguridad de Docker

Opciones para implementar utilidades.

Para probar las aplicaciones descritas en un entorno aislado, proporcionaré instrucciones para instalar todas las utilidades en un proceso algo simplificado.

La idea principal es demostrar cómo se puede implementar la verificación automática de contenido de Dockerfiles e imágenes de Docker que se crean durante el desarrollo.

El cheque en sí consta de los siguientes pasos:

  1. Comprobación de la exactitud y seguridad de las instrucciones de Dockerfile utilizando una utilidad linter hadolin
  2. Comprobación de la exactitud y seguridad de las imágenes finales e intermedias mediante una utilidad. muelle
  3. Comprobación de la presencia de vulnerabilidades conocidas públicamente (CVE) en la imagen base y una serie de dependencias, utilizando la utilidad curiosidades

Más adelante en el artículo daré tres opciones para implementar estos pasos:
La primera es configurar la canalización de CI/CD usando GitLab como ejemplo (con una descripción del proceso de generación de una instancia de prueba).
El segundo es utilizar un script de shell.
El tercero implica crear una imagen de Docker para escanear imágenes de Docker.
Podrás elegir la opción que más te convenga, trasladarla a tu infraestructura y adaptarla a tus necesidades.

Todos los archivos necesarios e instrucciones adicionales también se encuentran en el repositorio: https://github.com/Swordfish-Security/docker_cicd

Integración en GitLab CI/CD

En la primera opción, veremos cómo implementar controles de seguridad usando el sistema de repositorio GitLab como ejemplo. Aquí seguiremos los pasos y descubriremos cómo instalar un entorno de prueba con GitLab desde cero, crearemos un proceso de escaneo e iniciaremos utilidades para verificar el Dockerfile de prueba y una imagen aleatoria: la aplicación JuiceShop.

Instalación de GitLab
1. Instale Docker:

sudo apt-get update && sudo apt-get install docker.io

2. Agregue el usuario actual al grupo de Docker para que pueda trabajar con Docker sin usar sudo:

sudo addgroup <username> docker

3. Encuentra tu IP:

ip addr

4. Instale e inicie GitLab en el contenedor, reemplazando la dirección IP en el nombre de host por la suya propia:

docker run --detach 
--hostname 192.168.1.112 
--publish 443:443 --publish 80:80 
--name gitlab 
--restart always 
--volume /srv/gitlab/config:/etc/gitlab 
--volume /srv/gitlab/logs:/var/log/gitlab 
--volume /srv/gitlab/data:/var/opt/gitlab 
gitlab/gitlab-ce:latest

Esperamos hasta que GitLab complete todos los procedimientos de instalación necesarios (puede monitorear el proceso a través de la salida del archivo de registro: docker logs -f gitlab).

5. Abra su IP local en el navegador y vea una página que le pide que cambie la contraseña del usuario root:
Métodos y ejemplos de implementación de las utilidades de comprobación de seguridad de Docker
Establezca una nueva contraseña y vaya a GitLab.

6. Cree un nuevo proyecto, por ejemplo cicd-test e inicialícelo con el archivo de inicio. README.md:
Métodos y ejemplos de implementación de las utilidades de comprobación de seguridad de Docker
7. Ahora necesitamos instalar GitLab Runner: un agente que ejecutará todas las operaciones necesarias a pedido.
Descarga la última versión (en este caso, para Linux de 64 bits):

sudo curl -L --output /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-amd64

8. Hazlo ejecutable:

sudo chmod +x /usr/local/bin/gitlab-runner

9. Agregue un usuario de sistema operativo para Runner e inicie el servicio:

sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash
sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
sudo gitlab-runner start

Debería verse algo como esto:

local@osboxes:~$ sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
Runtime platform arch=amd64 os=linux pid=8438 revision=0e5417a3 version=12.0.1
local@osboxes:~$ sudo gitlab-runner start
Runtime platform arch=amd64 os=linux pid=8518 revision=0e5417a3 version=12.0.1

10. Ahora registramos el Runner para que pueda interactuar con nuestra instancia de GitLab.
Para hacer esto, abra la página Configuración-CI/CD (http://OUR_IP_ADDRESS/root/cicd-test/-/settings/ci_cd) y en la pestaña Corredores busque la URL y el token de registro:
Métodos y ejemplos de implementación de las utilidades de comprobación de seguridad de Docker
11. Registre a Runner sustituyendo la URL y el token de registro:

sudo gitlab-runner register 
--non-interactive 
--url "http://<URL>/" 
--registration-token "<Registration Token>" 
--executor "docker" 
--docker-privileged 
--docker-image alpine:latest 
--description "docker-runner" 
--tag-list "docker,privileged" 
--run-untagged="true" 
--locked="false" 
--access-level="not_protected"

Como resultado, obtenemos un GitLab listo para usar, al que debemos agregar instrucciones para iniciar nuestras utilidades. En esta demostración no tenemos los pasos para construir la aplicación y contenerla, pero en un entorno real estos precederían a los pasos de escaneo y generarían imágenes y un Dockerfile para el análisis.

configuración de tubería

1. Agregar archivos al repositorio midockerfile.df (este es un Dockerfile de prueba que verificaremos) y el archivo de configuración del proceso GitLab CI/CD .gitlab-cicd.yml, que enumera instrucciones para escáneres (tenga en cuenta el punto en el nombre del archivo).

El archivo de configuración YAML contiene instrucciones para ejecutar tres utilidades (Hadolint, Dockle y Trivy) que analizarán el Dockerfile seleccionado y la imagen especificada en la variable DOCKERFILE. Todos los archivos necesarios se pueden tomar del repositorio: https://github.com/Swordfish-Security/docker_cicd/

Extracto de midockerfile.df (Este es un archivo abstracto con un conjunto de instrucciones arbitrarias solo para demostrar el funcionamiento de la utilidad). Enlace directo al archivo: midockerfile.df

Contenido de mydockerfile.df

FROM amd64/node:10.16.0-alpine@sha256:f59303fb3248e5d992586c76cc83e1d3700f641cbcd7c0067bc7ad5bb2e5b489 AS tsbuild
COPY package.json .
COPY yarn.lock .
RUN yarn install
COPY lib lib
COPY tsconfig.json tsconfig.json
COPY tsconfig.app.json tsconfig.app.json
RUN yarn build
FROM amd64/ubuntu:18.04@sha256:eb70667a801686f914408558660da753cde27192cd036148e58258819b927395
LABEL maintainer="Rhys Arkins <[email protected]>"
LABEL name="renovate"
...
COPY php.ini /usr/local/etc/php/php.ini
RUN cp -a /tmp/piik/* /var/www/html/
RUN rm -rf /tmp/piwik
RUN chown -R www-data /var/www/html
ADD piwik-cli-setup /piwik-cli-setup
ADD reset.php /var/www/html/
## ENTRYPOINT ##
ADD entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
USER root

La configuración YAML se ve así (el archivo en sí se puede encontrar a través del enlace directo aquí: .gitlab-ci.yml):

Contenido de .gitlab-ci.yml

variables:
    DOCKER_HOST: "tcp://docker:2375/"
    DOCKERFILE: "mydockerfile.df" # name of the Dockerfile to analyse   
    DOCKERIMAGE: "bkimminich/juice-shop" # name of the Docker image to analyse
    # DOCKERIMAGE: "knqyf263/cve-2018-11235" # test Docker image with several CRITICAL CVE
    SHOWSTOPPER_PRIORITY: "CRITICAL" # what level of criticality will fail Trivy job
    TRIVYCACHE: "$CI_PROJECT_DIR/.cache" # where to cache Trivy database of vulnerabilities for faster reuse
    ARTIFACT_FOLDER: "$CI_PROJECT_DIR"
 
services:
    - docker:dind # to be able to build docker images inside the Runner
 
stages:
    - scan
    - report
    - publish
 
HadoLint:
    # Basic lint analysis of Dockerfile instructions
    stage: scan
    image: docker:git
 
    after_script:
    - cat $ARTIFACT_FOLDER/hadolint_results.json
 
    script:
    - export VERSION=$(wget -q -O - https://api.github.com/repos/hadolint/hadolint/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/1/')
    - wget https://github.com/hadolint/hadolint/releases/download/v${VERSION}/hadolint-Linux-x86_64 && chmod +x hadolint-Linux-x86_64
     
    # NB: hadolint will always exit with 0 exit code
    - ./hadolint-Linux-x86_64 -f json $DOCKERFILE > $ARTIFACT_FOLDER/hadolint_results.json || exit 0
 
    artifacts:
        when: always # return artifacts even after job failure       
        paths:
        - $ARTIFACT_FOLDER/hadolint_results.json
 
Dockle:
    # Analysing best practices about docker image (users permissions, instructions followed when image was built, etc.)
    stage: scan   
    image: docker:git
 
    after_script:
    - cat $ARTIFACT_FOLDER/dockle_results.json
 
    script:
    - export VERSION=$(wget -q -O - https://api.github.com/repos/goodwithtech/dockle/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/1/')
    - wget https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.tar.gz && tar zxf dockle_${VERSION}_Linux-64bit.tar.gz
    - ./dockle --exit-code 1 -f json --output $ARTIFACT_FOLDER/dockle_results.json $DOCKERIMAGE   
     
    artifacts:
        when: always # return artifacts even after job failure       
        paths:
        - $ARTIFACT_FOLDER/dockle_results.json
 
Trivy:
    # Analysing docker image and package dependencies against several CVE bases
    stage: scan   
    image: docker:git
 
    script:
    # getting the latest Trivy
    - apk add rpm
    - export VERSION=$(wget -q -O - https://api.github.com/repos/knqyf263/trivy/releases/latest | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/1/')
    - wget https://github.com/knqyf263/trivy/releases/download/v${VERSION}/trivy_${VERSION}_Linux-64bit.tar.gz && tar zxf trivy_${VERSION}_Linux-64bit.tar.gz
     
    # displaying all vulnerabilities w/o failing the build
    - ./trivy -d --cache-dir $TRIVYCACHE -f json -o $ARTIFACT_FOLDER/trivy_results.json --exit-code 0 $DOCKERIMAGE    
    
    # write vulnerabilities info to stdout in human readable format (reading pure json is not fun, eh?). You can remove this if you don't need this.
    - ./trivy -d --cache-dir $TRIVYCACHE --exit-code 0 $DOCKERIMAGE    
 
    # failing the build if the SHOWSTOPPER priority is found
    - ./trivy -d --cache-dir $TRIVYCACHE --exit-code 1 --severity $SHOWSTOPPER_PRIORITY --quiet $DOCKERIMAGE
         
    artifacts:
        when: always # return artifacts even after job failure
        paths:
        - $ARTIFACT_FOLDER/trivy_results.json
 
    cache:
        paths:
        - .cache
 
Report:
    # combining tools outputs into one HTML
    stage: report
    when: always
    image: python:3.5
     
    script:
    - mkdir json
    - cp $ARTIFACT_FOLDER/*.json ./json/
    - pip install json2html
    - wget https://raw.githubusercontent.com/shad0wrunner/docker_cicd/master/convert_json_results.py
    - python ./convert_json_results.py
     
    artifacts:
        paths:
        - results.html

Si es necesario, también puede escanear imágenes guardadas en forma de archivo .tar (sin embargo, deberá cambiar los parámetros de entrada para las utilidades en el archivo YAML)

NB: Trivy requiere estar instalado rpm и git. De lo contrario, generará errores al escanear imágenes basadas en RedHat y recibir actualizaciones de la base de datos de vulnerabilidades.

2. Después de agregar archivos al repositorio, de acuerdo con las instrucciones en nuestro archivo de configuración, GitLab comenzará automáticamente el proceso de compilación y escaneo. En la pestaña CI/CD → Pipelines puede ver el progreso de las instrucciones.

Como resultado, tenemos cuatro tareas. Tres de ellos se ocupan directamente del escaneo y el último (Informe) recopila un informe simple de archivos dispersos con los resultados del escaneo.
Métodos y ejemplos de implementación de las utilidades de comprobación de seguridad de Docker
De forma predeterminada, Trivy deja de ejecutarse si se detectan vulnerabilidades CRÍTICAS en la imagen o las dependencias. Al mismo tiempo, Hadolint siempre devuelve un código de éxito porque siempre genera comentarios, lo que hace que la compilación se detenga.

Dependiendo de sus requisitos específicos, puede configurar un código de salida para que cuando estas utilidades detecten problemas de cierta importancia, también detengan el proceso de compilación. En nuestro caso, la compilación se detendrá solo si Trivy detecta una vulnerabilidad con la criticidad que especificamos en la variable SHOWSTOPPER en .gitlab-ci.yml.
Métodos y ejemplos de implementación de las utilidades de comprobación de seguridad de Docker

El resultado de cada utilidad se puede ver en el registro de cada tarea de escaneo, directamente en los archivos json en la sección de artefactos o en un informe HTML simple (más sobre esto a continuación):
Métodos y ejemplos de implementación de las utilidades de comprobación de seguridad de Docker

3. Para presentar los informes de utilidad en una forma un poco más legible para los humanos, se utiliza un pequeño script de Python para convertir tres archivos JSON en un archivo HTML con una tabla de defectos.
Este script se inicia mediante una tarea de Informe independiente y su artefacto final es un archivo HTML con un informe. La fuente del script también se encuentra en el repositorio y puede adaptarse a sus necesidades, colores, etc.
Métodos y ejemplos de implementación de las utilidades de comprobación de seguridad de Docker

script de shell

La segunda opción es adecuada para casos en los que necesita verificar imágenes de Docker fuera del sistema CI/CD o necesita tener todas las instrucciones en un formato que pueda ejecutarse directamente en el host. Esta opción está cubierta por un script de shell listo para usar que se puede ejecutar en una máquina virtual (o incluso real) limpia. El script ejecuta las mismas instrucciones que gitlab-runner descrito anteriormente.

Para que el script se ejecute correctamente, Docker debe estar instalado en el sistema y el usuario actual debe estar en el grupo de Docker.

El script en sí se puede encontrar aquí: docker_sec_check.sh

Al principio del archivo, las variables especifican qué imagen debe escanearse y qué defectos críticos harán que la utilidad Trivy salga con el código de error especificado.

Durante la ejecución del script, todas las utilidades se descargarán al directorio herramientas_docker, los resultados de su trabajo están en el directorio herramientas_docker/json, y el HTML con el informe estará en el archivo resultados.html.

Ejemplo de salida de script

~/docker_cicd$ ./docker_sec_check.sh

[+] Setting environment variables
[+] Installing required packages
[+] Preparing necessary directories
[+] Fetching sample Dockerfile
2020-10-20 10:40:00 (45.3 MB/s) - ‘Dockerfile’ saved [8071/8071]
[+] Pulling image to scan
latest: Pulling from bkimminich/juice-shop
[+] Running Hadolint
...
Dockerfile:205 DL3015 Avoid additional packages by specifying `--no-install-recommends`
Dockerfile:248 DL3002 Last USER should not be root
...
[+] Running Dockle
...
WARN    - DKL-DI-0006: Avoid latest tag
        * Avoid 'latest' tag
INFO    - CIS-DI-0005: Enable Content trust for Docker
        * export DOCKER_CONTENT_TRUST=1 before docker pull/build
...
[+] Running Trivy
juice-shop/frontend/package-lock.json
=====================================
Total: 3 (UNKNOWN: 0, LOW: 1, MEDIUM: 0, HIGH: 2, CRITICAL: 0)

+---------------------+------------------+----------+---------+-------------------------+
|       LIBRARY       | VULNERABILITY ID | SEVERITY | VERSION |             TITLE       |
+---------------------+------------------+----------+---------+-------------------------+
| object-path         | CVE-2020-15256   | HIGH     | 0.11.4  | Prototype pollution in  |
|                     |                  |          |         | object-path             |
+---------------------+------------------+          +---------+-------------------------+
| tree-kill           | CVE-2019-15599   |          | 1.2.2   | Code Injection          |
+---------------------+------------------+----------+---------+-------------------------+
| webpack-subresource | CVE-2020-15262   | LOW      | 1.4.1   | Unprotected dynamically |
|                     |                  |          |         | loaded chunks           |
+---------------------+------------------+----------+---------+-------------------------+

juice-shop/package-lock.json
============================
Total: 20 (UNKNOWN: 0, LOW: 1, MEDIUM: 6, HIGH: 8, CRITICAL: 5)

...

juice-shop/package-lock.json
============================
Total: 5 (CRITICAL: 5)

...
[+] Removing left-overs
[+] Making the output look pretty
[+] Converting JSON results
[+] Writing results HTML
[+] Clean exit ============================================================
[+] Everything is done. Find the resulting HTML report in results.html

Imagen de Docker con todas las utilidades

Como tercera alternativa, compilé dos Dockerfiles simples para crear una imagen con utilidades de seguridad. Un Dockerfile ayudará a crear un conjunto para escanear una imagen desde un repositorio, el segundo (Dockerfile_tar) ayudará a crear un conjunto para escanear un archivo tar con una imagen.

1. Tome el archivo Docker y los scripts correspondientes del repositorio. https://github.com/Swordfish-Security/docker_cicd/tree/master/Dockerfile.
2. Lo lanzamos para su montaje:

docker build -t dscan:image -f docker_security.df .

3. Una vez completado el ensamblaje, creamos un contenedor a partir de la imagen. Al mismo tiempo pasamos la variable de entorno DOCKERIMAGE con el nombre de la imagen que nos interesa y montamos el Dockerfile que queremos analizar desde nuestra máquina al archivo /archivo Docker (tenga en cuenta que se requiere la ruta absoluta a este archivo):

docker run --rm -v $(pwd)/results:/results -v $(pwd)/docker_security.df:/Dockerfile -e DOCKERIMAGE="bkimminich/juice-shop" dscan:image


[+] Setting environment variables
[+] Running Hadolint
/Dockerfile:3 DL3006 Always tag the version of an image explicitly
[+] Running Dockle
WARN    - DKL-DI-0006: Avoid latest tag
        * Avoid 'latest' tag
INFO    - CIS-DI-0005: Enable Content trust for Docker
        * export DOCKER_CONTENT_TRUST=1 before docker pull/build
INFO    - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
        * not found HEALTHCHECK statement
INFO    - DKL-LI-0003: Only put necessary files
        * unnecessary file : juice-shop/node_modules/sqlite3/Dockerfile
        * unnecessary file : juice-shop/node_modules/sqlite3/tools/docker/architecture/linux-arm64/Dockerfile
        * unnecessary file : juice-shop/node_modules/sqlite3/tools/docker/architecture/linux-arm/Dockerfile
[+] Running Trivy
...
juice-shop/package-lock.json
============================
Total: 20 (UNKNOWN: 0, LOW: 1, MEDIUM: 6, HIGH: 8, CRITICAL: 5)
...
[+] Making the output look pretty
[+] Starting the main module ============================================================
[+] Converting JSON results
[+] Writing results HTML
[+] Clean exit ============================================================
[+] Everything is done. Find the resulting HTML report in results.html

resultados

Solo analizamos un conjunto básico de utilidades para escanear artefactos de Docker que, en mi opinión, cubre de manera muy efectiva una parte decente de los requisitos de seguridad de imágenes. También hay una gran cantidad de herramientas gratuitas y de pago que pueden realizar las mismas comprobaciones, generar bonitos informes o trabajar exclusivamente en modo consola, cubrir sistemas de gestión de contenedores, etc. Una descripción general de estas herramientas y cómo integrarlas puede aparecer un poco más adelante. .

Lo bueno del conjunto de herramientas descritas en este artículo es que todas son de código abierto y puedes experimentar con ellas y otras herramientas similares para encontrar la que se adapte a tus necesidades e infraestructura. Por supuesto, todas las vulnerabilidades que se encuentren deben estudiarse para determinar su aplicabilidad en condiciones específicas, pero este es un tema para un artículo extenso en el futuro.

Espero que esta guía, scripts y utilidades le ayuden y se conviertan en un punto de partida para crear una infraestructura más segura en el área de la contenedorización.

Fuente: habr.com

Añadir un comentario