Docker: Lista de Materiales de Software o Software Bill Of Materials (SBOM)
Software Bill Of Materials (SBOM), es un inventario de los componentes de software y dependencias de un sistema, información de dichos componentes y la relación entre ellos. Este inventario se mantiene en un formato que es fácilmente legible por máquinas. Últimamente se han visto muchas vulnerabilidades relacionadas con la cadena de suministro de dependencias, una de las más sonadas el famoso log4shell entre otras. Cuando aparece una vulnerabilidad de este tipo, la pregunta es ¿Estamos afectado por dicha vulnerabilidad? ¿Alguno de nuestros sistemas usa la librería X? Y Docker nos puede ayudar.
La única forma de poder responder a este tipo de preguntas con cierta confianza es manteniendo el inventario, actualizado, de los componentes y dependencias que nuestros sistemas usan. Hay que tener en cuenta también que dicho inventario no sólo contenga los componentes y dependencias usadas directamente en nuestros sistemas, sino también las dependencias heredadas por dichos componentes y dependencias, también conocidas como dependencias transitivas.
Aunque SBOM no es algo nuevo, se puso muy de moda el año pasado cuando la Casa Blanca lanzó una orden ejecutiva en la requería que cualquier empresa que hiciera negocios con el gobierno norteamericano (venta de software, desarrollo o venta de licencias de uso) fuera capaz de proveer de esta lista de materiales con todos los componentes del software. De esta forma han sido muchas de las empresas que han incluido la posibilidad de la creación de SBOMs en sus herramientas, entre ellas Docker, dentro de su producto Docker Desktop.
Figura 1: Docker. Lista de Materiales de Software
o Software Bill Of Materials (SBOM)
La única forma de poder responder a este tipo de preguntas con cierta confianza es manteniendo el inventario, actualizado, de los componentes y dependencias que nuestros sistemas usan. Hay que tener en cuenta también que dicho inventario no sólo contenga los componentes y dependencias usadas directamente en nuestros sistemas, sino también las dependencias heredadas por dichos componentes y dependencias, también conocidas como dependencias transitivas.
Aunque SBOM no es algo nuevo, se puso muy de moda el año pasado cuando la Casa Blanca lanzó una orden ejecutiva en la requería que cualquier empresa que hiciera negocios con el gobierno norteamericano (venta de software, desarrollo o venta de licencias de uso) fuera capaz de proveer de esta lista de materiales con todos los componentes del software. De esta forma han sido muchas de las empresas que han incluido la posibilidad de la creación de SBOMs en sus herramientas, entre ellas Docker, dentro de su producto Docker Desktop.
En la versión 4.7.0, justo después de la publicación de la segunda la edición de nuestro libro Docker: SecDevOps, Docker añadió un plugin por defecto que permite la generación de ficheros SBOM y de los que vamos a ver algunos ejemplos, que ya hemos visto un poco qué es SBOM y por qué es importante.
Una cosa para recalcar, como se comentó antes, esto no es algo nuevo, y además tampoco es un estándar, por lo que existen varias iniciativas y formatos. Aunque no son los únicos que existen, los siguientes son los formatos que más fuerza o repercusión tienen actualmente:
- SPDX (Software Package Data Exchange), cuyo principal promotor es la Linux Foundation. Actualmente existen muchas herramientas que permite generar el SBOM en este formato.
- CycloneDX, éste tiene el apoyo de varias empresas y organizaciones, entre ellas OWASP. Al igual que con SPDX, existen muchas herramientas que permite generar el SBOM en este formato.
- SWID (Software Identification tags), este es un formato estandarizado en XML que identifica y contextualiza los componentes de un producto de software. Ojo, este no es un estándar SBOM, pero sí es un formato estandarizado (ISO/IEC 19770-2:2015). Al igual que los anteriores existen herramientas para la generación de ficheros SBOM en este formato.
Como comenté anteriormente, en la versión 4.7.0, Docker añadió una opción nueva (realmente un plugin) al comando *docker* llamada *sbom*. Este plugin usa por detrás un proyecto de Anchore llamado [syft](https://github.com/anchore/syft). Éste analiza las capas de la imagen y extrae la información sobre que comoponentes hay en cada capa. Si ejecutamos dicho comando obtendremos la siguiente salida:
$ docker sbom Usage: docker sbom [OPTIONS] COMMAND View the packaged-based Software Bill Of Materials (SBOM) for an image. EXPERIMENTAL: The flags and outputs of this command may change. Leave feedback on https://github.com/docker/sbom-cli-plugin. Examples: docker sbom alpine:latest a summary of discovered packages docker sbom alpine:latest --format syft-json show all possible cataloging details docker sbom alpine:latest --output sbom.txt write report output to a file docker sbom alpine:latest --exclude /lib --exclude '**/*.db' ignore one or more paths/globs in the image Options: -D, --debug show debug logging --exclude stringArray exclude paths from being scanned using a glob expression --format string report output format, options=[syft-json cyclonedx-xml cyclonedx-json github-0-json spdx-tag-value spdx-json table text] (default "table") --layers string [experimental] selection of layers to catalog, options=[squashed all] (default "squashed") -o, --output string file to write the default report output to (default is STDOUT) --platform string an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux') --quiet suppress all non-report output -v, --version version for sbom Commands: version Show Docker sbom version information Run 'docker sbom COMMAND --help' for more information on a command. an image argument is requiredDicho plugin permite la generación de ficheros SBOM en formato SPDX y CycloneDX. Veamos a continuación la salida por defecto que se produciría sobre una imagen Alpine:
$ docker sbom alpine Syft v0.43.0 ✔ Loaded image ✔ Parsed image ✔ Cataloged packages [14 packages] NAME VERSION TYPE alpine-baselayout 3.2.0-r16 apk alpine-keys 2.3-r1 apk apk-tools 2.12.7-r0 apk busybox 1.33.1-r3 apk ca-certificates-bundle 20191127-r5 apk libc-utils 0.7.2-r3 apk libcrypto1.1 1.1.1l-r0 apk libretls 3.3.3p1-r2 apk libssl1.1 1.1.1l-r0 apk musl 1.2.2-r3 apk musl-utils 1.2.2-r3 apk scanelf 1.3.2-r0 apk ssl_client 1.33.1-r3 apk zlib 1.2.11-r3 apkEn este caso se puede ver la lista de los componentes de la imagen *alpine:latest*. Ahora veamos el mismo ejemplo, pero en formato SPDX:
$ docker sbom --format spdx alpine Syft v0.43.0 ✔ Loaded image ✔ Parsed image ✔ Cataloged packages [14 packages] SPDXVersion: SPDX-2.2 DataLicense: CC0-1.0 SPDXID: SPDXRef-DOCUMENT DocumentName: alpine-latest DocumentNamespace: https://anchore.com/syft/image/alpine-latest-b478dc4d-bced-406a-adf9-6f7b7ea6d231 LicenseListVersion: 3.16 Creator: Organization: Anchore, Inc Creator: Tool: syft-[not provided] Created: 2022-04-15T20:19:57Z ##### Package: alpine-baselayout PackageName: alpine-baselayout SPDXID: SPDXRef-Package-apk-alpine-baselayout-ed18f2a986e77aab PackageVersion: 3.2.0-r16 PackageDownloadLocation: NOASSERTION FilesAnalyzed: false PackageLicenseConcluded: GPL-2.0-only PackageLicenseDeclared: GPL-2.0-only PackageCopyrightText: NOASSERTION ExternalRef: SECURITY cpe23Type cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r16:*:*:*:*:*:*:* ExternalRef: SECURITY cpe23Type cpe:2.3:a:alpine-baselayout:alpine_baselayout:3.2.0-r16:*:*:*:*:*:*:* ExternalRef: SECURITY cpe23Type cpe:2.3:a:alpine_baselayout:alpine-baselayout:3.2.0-r16:*:*:*:*:*:*:* ExternalRef: SECURITY cpe23Type cpe:2.3:a:alpine_baselayout:alpine_baselayout:3.2.0-r16:*:*:*:*:*:*:* ExternalRef: SECURITY cpe23Type cpe:2.3:a:alpine:alpine-baselayout:3.2.0-r16:*:*:*:*:*:*:* ExternalRef: SECURITY cpe23Type cpe:2.3:a:alpine:alpine_baselayout:3.2.0-r16:*:*:*:*:*:*:* ExternalRef: PACKAGE_MANAGER purl pkg:alpine/alpine-baselayout@3.2.0-r16?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.14.2 ##### Package: alpine-keys ...La salida es mucho más larga, pero a modo de ejemplo se puede ver uno de los componentes encontrados, con nombre, licencia, referencias externas, etcétera. También es posible generar la salida en formato *json*:
$ docker sbom --format spdx-json alpine Syft v0.43.0 ✔ Loaded image ✔ Parsed image ✔ Cataloged packages [14 packages] { "SPDXID": "SPDXRef-DOCUMENT", "name": "alpine-latest", "spdxVersion": "SPDX-2.2", "creationInfo": { "created": "2022-04-15T21:15:23.057754Z", "creators": [ "Organization: Anchore, Inc", "Tool: syft-[not provided]" ], "licenseListVersion": "3.16" }, "dataLicense": "CC0-1.0", "documentNamespace": "https://anchore.com/syft/image/alpine-latest-2ea1fa9e-099e-4939-bdd8-d41fab29f1b0", "packages": [ { "SPDXID": "SPDXRef-ed18f2a986e77aab", ...Para ver la salida en formato CycloneDX:
$ docker sbom --format cyclonedx alpine Syft v0.43.0 ✔ Loaded image ✔ Parsed image ✔ Cataloged packages [14 packages] [0000] WARN unable to convert relationship from CycloneDX 1.3 JSON, dropping: {From:Pkg(name="musl" version="1.2.2-r3" type="apk" id="304c2239a7d1d6b7") To:Location<RealPath="/lib/ld-musl-x86_64.so.1" ... <?xml version="1.0" encoding="UTF-8"?> <bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:faaf4f40-e332-4f0e-8acc-3fadcae62d25" version="1"> <metadata> <timestamp>2022-04-15T17:17:00-04:00</timestamp> <tools> <tool> <vendor>anchore</vendor> <name>syft</name> <version>[not provided]</version> </tool> </tools> <component bom-ref="e33e1cadf7cfbd1e" type="container"> <name>alpine:latest</name> <version>sha256:a2f19fdc3f78ddc201c5bb37e302fed8b3487695056215b5577113d3938196bf</version> </component> </metadata> <components> <component type="library"> <publisher>Natanael Copa <ncopa@alpinelinux.org></publisher> <name>alpine-baselayout</name> <version>3.2.0-r16</version> <description>Alpine base dir structure and init scripts</description> <licenses> <license> <id>GPL-2.0-only</id> </license> </licenses> <cpe>cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r16:*:*:*:*:*:*:*</<pe> <purl>pkg:alpine/alpine-baselayout@3.2.0-r16?arch=x86_64&upstream=alpine-baselayout&distro=alpine-3.14.2</purl> <externalReferences> <reference type="distribution"> <url>https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout</url> </reference> </externalReferences> <properties> <property name="syft:package:foundBy">apkdb-cataloger</property> <property name="syft:package:metadataType">ApkMetadata</property> <property name="syft:package:type">apk</property> <property name="syft:cpe23">cpe:2.3:a:alpine-baselayout:alpine_baselayout:3.2.0-r16:*:*:*:*:*:*:*</property> <property name="syft:cpe23">cpe:2.3:a:alpine_baselayout:alpine-baselayout:3.2.0-r16:*:*:*:*:*:*:*</property> <property name="syft:cpe23">cpe:2.3:a:alpine_baselayout:alpine_baselayout:3.2.0-r16:*:*:*:*:*:*:*</property> <property name="syft:cpe23">cpe:2.3:a:alpine:alpine-baselayout:3.2.0-r16:*:*:*:*:*:*:*</property> <property name="syft:cpe23">cpe:2.3:a:alpine:alpine_baselayout:3.2.0-r16:*:*:*:*:*:*:*</property> <property name="syft:location:0:layerID">sha256:e2eb06d8af8218cfec8210147357a68b7e13f7c485b991c288c2d01dc228bb68</property> <property name="syft:location:0:path">/lib/apk/db/installed</property> <property name="syft:metadata:gitCommitOfApkPort">8a8c96a0ea2fcd824c361aa4438763fa33ee8ca0</property> <property name="syft:metadata:installedSize">413696</property> <property name="syft:metadata:originPackage">alpine-baselayout</property> <property name="syft:metadata:pullChecksum">Q1UJtB9cNV4r+/VbxySkEei++qbho=</property> <property name="syft:metadata:pullDependencies">/bin/sh so:libc.musl-x86_64.so.1</property> <property name="syft:metadata:size">20716</property> </properties> </component> ... ...Como se pueden ver los campos generados son distintos que SPDX y el formato, por defecto, es XML, pero al final la información que se recolecta es prácticamente la misma. También es posible la generación en formato *json*:
$ docker sbom --format cyclonedx-json alpine Syft v0.43.0 ✔ Loaded image ✔ Parsed image ✔ Cataloged packages [14 packages] [0000] WARN unable to convert relationship from CycloneDX 1.3 JSON, dropping: {From:Pkg(name="musl" version="1.2.2-r3" type="apk" id="304c2239a7d1d6b7") To:Location<RealPath="/lib/ld-musl-x86_6 ... { "bomFormat": "CycloneDX", "specVersion": "1.4", "serialNumber": "urn:uuid:95cef6a2-edcb-4b82-91a4-3ed026f1670f", "version": 1, "metadata": { "timestamp": "2022-04-15T17:21:34-04:00", "tools": [ { "vendor": "anchore", "name": "syft", "version": "[not provided]" } ], "component": { "bom-ref": "e33e1cadf7cfbd1e", "type": "container", "name": "alpine:latest", "version": "sha256:a2f19fdc3f78ddc201c5bb37e302fed8b3487695056215b5577113d3938196bf" } }, "components": [ { "type": "library", "publisher": "Natanael Copa \u003cncopa@alpinelinux.org\u003e", "name": "alpine-baselayout", "version": "3.2.0-r16", "description": "Alpine base dir structure and init scripts", "licenses": [ { "license": { "id": "GPL-2.0-only" } } ], "cpe": "cpe:2.3:a:alpine-baselayout:alpine-baselayout:3.2.0-r16:*:*:*:*:*:*:*", "purl": "pkg:alpine/alpine-baselayout@3.2.0-r16?arch=x86_64\u0026upstream=alpine-baselayout\u0026distro=alpine-3.14.2", "externalReferences": [ { "url": "https://git.alpinelinux.org/cgit/aports/tree/main/alpine-baselayout", "type": "distribution" } ], "properties": [ { "name": "syft:package:foundBy", "value": "apkdb-cataloger" }, { "name": "syft:package:metadataType", ...La lista de los formatos soportados actualmente son:
* syft-json * cyclonedx-xml * cyclonedx-json * github-0-json * spdx-tag-value * spdx-json * table (formato por defecto) * textSi en vez mandar el contenido del fichero a la consola o salida estándar, queréis que se guarde en un fichero, se puede usar la opción -o o --output:
$ docker sbom --format spdx-json -o alpine.json alpine Syft v0.43.0 ✔ Loaded image ✔ Parsed image ✔ Cataloged packages [14 packages]La inclusión de este plugin en Docker está en modo experimental y puede que en futuras versiones éste cambie. Si tienes que generar SBOMs en entornos de producción, se podría usar la herramienta syft directamente. Esta herramienta es mucho más completa que lo que ofrece actualmente el plugin de Docker.
Happy Hacking!
Autor: Rafael Troncoso (@tuxotron) es Senior Software Engineer en SAP Concur, co-autor del libro "Microhistorias: Anécdotas y Curiosidades de la historia de la informática (y los hackers)", del libro "Docker: SecDevOps" además del blog CyberHades. Puedes contactar con Rafael Troncoso en MyPublicInbox.
No hay comentarios:
Publicar un comentario