Skip to main content

Trivy: Una navaja suiza para seguridad

trivy

Documentación

Github

Es un escáner de seguridad muy potente y versátil desarrollado por Aqua Security. Extremadamente fácil de usar, una línea de comandos All In One que genera informes de CVEs para los siguientes recursos:

  • Imágenes de Contenedores
  • Filesystem
  • Repositorios Git
  • Imágenes de VMs
  • Kubernetes
  • AWS

Soporta los principales lenguajes, como:

  • Golang
  • .Net
  • Java
  • NodeJs
  • Rust
  • Python

Además de todo esto podemos analizar los archivos de declaraciones de IaC en Terraform, Helm, manifiestos de kubernetes y vulnerabilidades del clúster y Dockerfiles. Claro que existen herramientas más específicas para este tema, pero de forma básica hace un excelente trabajo convirtiendo a Trivy en una de las navajas suizas en el mundo de la seguridad.

Como veremos más abajo usando Trivy podemos escanear varias cosas. Cada comando actuará de forma diferente.

alt text

Instalación

Podemos instalar la cli o ejecutar directamente desde un contenedor.

No vale la pena reinventar la rueda. Podemos instalar usando el gestor de paquetes de las principales distros, descargando el binario, usando asdf, usando el script y clonando el repositorio. Consulta aquí cómo instalar.

Ya que es solamente una CLI podemos usar docker. La imagen ya tiene el entrypoint trivy preparado para recibir los parámetros de la CLI.

Solo es necesario mapear los volúmenes -v /var/run/docker.sock:/var/run/docker.sock -v $HOME/Library/Caches:/root/.cache si se va a hacer scan de imágenes.

# docker run  aquasec/trivy también funciona

root@master:~# docker run -v /var/run/docker.sock:/var/run/docker.sock -v $HOME/Library/Caches:/root/.cache/ aquasec/trivy:0.55.0
Usage:
trivy [global flags] command [flags] target
trivy [command]

Examples:
# Scan a container image
$ trivy image python:3.4-alpine

# Scan a container image from a tar archive
$ trivy image --input ruby-3.1.tar

# Scan local filesystem
$ trivy fs .

# Run in server mode
$ trivy server


########### SEPARACIÓN DE LAS FUNCIONALIDADES ##################
Scanning Commands
config Scan config files for misconfigurations
filesystem Scan local filesystem
image Scan a container image
kubernetes [EXPERIMENTAL] Scan kubernetes cluster
repository Scan a repository
rootfs Scan rootfs
sbom Scan SBOM for vulnerabilities and licenses
vm [EXPERIMENTAL] Scan a virtual machine image
###########################################################

Management Commands
module Manage modules
plugin Manage plugins
vex [EXPERIMENTAL] VEX utilities

Utility Commands
clean Remove cached files
completion Generate the autocompletion script for the specified shell
convert Convert Trivy JSON report into a different format
help Help about any command
server Server mode
version Print the version

Flags:
--cache-dir string cache directory (default "/root/.cache/trivy")
-c, --config string config path (default "trivy.yaml")
-d, --debug debug mode
-f, --format string version format (json)
--generate-default-config write the default config to trivy-default.yaml
-h, --help help for trivy
--insecure allow insecure server connections
-q, --quiet suppress progress bar and log output
--timeout duration timeout (default 5m0s)
-v, --version show version

Use "trivy [command] --help" for more information about a command.

Habilita el completion para verificar varios posibles parámetros.

Análisis de Contenedores

Vamos a empezar por el escaneo básico de vulnerabilidades en imágenes. Podemos hacer el análisis de una imagen que ya tenemos o la descargará.

trivy image <parametros> es el subcomando usado para activar el scan de imágenes.

# Usando la última versión del trivy:latest escaneando la última versión del alpine.

# Cada vez descargará la base de datos, así que vamos a quitar un poco de salida para mejorar la lectura
root@master:~# docker run aquasec/trivy image alpine
2024-09-04T01:14:19Z INFO [db] Need to update DB
2024-09-04T01:14:19Z INFO [db] Downloading DB... repository="ghcr.io/aquasecurity/trivy-db:2"
16.98 MiB / 52.71 MiB [------------------->_________________________________________] 32.21% ? p/s ?34.67 MiB / 52.71 MiB s ETA 0s52.71 MiB / 52.71 MiB [-------------------------------------------------] 100.00% 13.57 MiB p/s 4.1s2024-09-04T01:14:23Z INFO [vuln] Vulnerability scanning is enabled
2024-09-04T01:14:23Z INFO [secret] Secret scanning is enabled
2024-09-04T01:14:23Z INFO [secret] If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2024-09-04T01:14:23Z INFO [secret] Please see also https://aquasecurity.github.io/trivy/v0.55/docs/scanner/secret#recommendation for faster secret detection
2024-09-04T01:14:25Z INFO Detected OS family="alpine" version="3.20.2"
2024-09-04T01:14:25Z INFO [alpine] Detecting vulnerabilities... os_version="3.20" repository="3.20" pkg_num=14
2024-09-04T01:14:25Z INFO Number of language-specific files num=0

alpine (alpine 3.20.2)
======================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)
# ¡Alpine es increíble!

Haz una prueba en otras imágenes como por ejemplo Ubuntu.

Vamos a kubernetes a escanear el apiserver 1.30.4

root@cks-master:~# docker run  aquasec/trivy image registry.k8s.io/kube-apiserver:v1.30.4
2024-09-04T01:20:19Z INFO [db] Need to update DB
2024-09-04T01:20:19Z INFO [db] Downloading DB... repository="ghcr.io/aquasecurity/trivy-db:2"
15.68 MiB / 52.71 MiB [------------------>__________________________________________] 29.74% ? p/s ?37.33 MiB / 52.71 MiB [-------------------------------------------------] 100.00% 16.18 MiB p/s 3.5s2024-09-04T01:20:23Z INFO [vuln] Vulnerability scanning is enabled
2024-09-04T01:20:23Z INFO [secret] Secret scanning is enabled
2024-09-04T01:20:23Z INFO [secret] If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2024-09-04T01:20:23Z INFO [secret] Please see also https://aquasecurity.github.io/trivy/v0.55/docs/scanner/secret#recommendation for faster secret detection
2024-09-04T01:20:26Z INFO Detected OS family="debian" version="12.5"
2024-09-04T01:20:26Z INFO [debian] Detecting vulnerabilities... os_version="12" pkg_num=3
2024-09-04T01:20:26Z INFO Number of language-specific files num=2
2024-09-04T01:20:26Z INFO [gobinary] Detecting vulnerabilities...

registry.k8s.io/kube-apiserver:v1.30.4 (debian 12.5)
====================================================
Total: 0 (UNKNOWN: 0, LOW: 0, MEDIUM: 0, HIGH: 0, CRITICAL: 0)


usr/local/bin/kube-apiserver (gobinary)
=======================================
Total: 2 (UNKNOWN: 0, LOW: 0, MEDIUM: 1, HIGH: 1, CRITICAL: 0)

┌──────────────────────────────────────────────────────────────┬────────────────┬──────────┬──────────┬───────────────────┬───────────────┬─────────────────────────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├──────────────────────────────────────────────────────────────┼────────────────┼──────────┼──────────┼───────────────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│ go.opentelemetry.io/contrib/instrumentation/google.golang.o- │ CVE-2023-47108 │ HIGH │ fixed │ v0.42.0 │ 0.46.0 │ opentelemetry-go-contrib: DoS vulnerability in otelgrpc due │
│ rg/grpc/otelgrpc │ │ │ │ │ │ to unbound cardinality metrics │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2023-47108 │
├──────────────────────────────────────────────────────────────┼────────────────┼──────────┼──────────┼───────────────────┼───────────────┼─────────────────────────────────────────────────────────────┤
│ gopkg.in/square/go-jose.v2 │ CVE-2024-28180 │ MEDIUM │ affected │ v2.6.0 │ │ jose-go: improper handling of highly compressed data │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-28180 │
└──────────────────────────────────────────────────────────────┴────────────────┴──────────┴──────────┴───────────────────┴───────────────┴─────────────────────────────────────────────────────────────┘
# Opa encontramos cositas...

Bueno, eso es todo, allí ya se muestran los CVEs encontrados, si es posible corregir o no. Si quieres ignorar los que no es posible corregir solo pasa la flag -ignore-unfixed.

Si quieres ver bastantes CVEs haz una prueba ahí en nginx. Esta vez voy a ejecutar usando la cli de trivy.

# --ignore-unfixed reducirá la salida solo para cves conocidos y reparables.
trivy image nginx --ignore-unfixed
2024-09-03T22:32:41-03:00 INFO [vuln] Vulnerability scanning is enabled
2024-09-03T22:32:41-03:00 INFO [secret] Secret scanning is enabled
2024-09-03T22:32:41-03:00 INFO [secret] If your scanning is slow, please try '--scanners vuln' to disable secret scanning
2024-09-03T22:32:41-03:00 INFO [secret] Please see also https://aquasecurity.github.io/trivy/v0.54/docs/scanner/secret#recommendation for faster secret detection
2024-09-03T22:32:43-03:00 INFO Detected OS family="debian" version="12.6"
2024-09-03T22:32:43-03:00 INFO [debian] Detecting vulnerabilities... os_version="12" pkg_num=149
2024-09-03T22:32:43-03:00 INFO Number of language-specific files num=0
2024-09-03T22:32:43-03:00 WARN Using severities from other vendors for some vulnerabilities. Read https://aquasecurity.github.io/trivy/v0.54/docs/scanner/vulnerability#severity-selection for details.

nginx (debian 12.6)

Total: 9 (UNKNOWN: 0, LOW: 2, MEDIUM: 6, HIGH: 0, CRITICAL: 1)

┌──────────┬───────────────┬──────────┬────────┬───────────────────┬───────────────────┬───────────────────────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├──────────┼───────────────┼──────────┼────────┼───────────────────┼───────────────────┼───────────────────────────────────────────────────────────┤
curl │ CVE-2024-7264 │ MEDIUM │ fixed │ 7.88.1-10+deb12u6 │ 7.88.1-10+deb12u7 │ curl: libcurl: ASN.1 date parser overread │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-7264 │
├──────────┼───────────────┼──────────┤ ├───────────────────┼───────────────────┼───────────────────────────────────────────────────────────┤
│ libaom3 │ CVE-2024-5171 │ CRITICAL │ │ 3.6.0-1 │ 3.6.0-1+deb12u1 │ libaom: Integer overflow in internal │
│ │ │ │ │ │ │ function img_alloc_helper │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-5171 │
├──────────┼───────────────┼──────────┤ ├───────────────────┼───────────────────┼───────────────────────────────────────────────────────────┤
│ libcurl4 │ CVE-2024-7264 │ MEDIUM │ │ 7.88.1-10+deb12u6 │ 7.88.1-10+deb12u7 │ curl: libcurl: ASN.1 date parser overread │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-7264 │
├──────────┼───────────────┤ │ ├───────────────────┼───────────────────┼───────────────────────────────────────────────────────────┤
│ libssl3 │ CVE-2024-4603 │ │ │ 3.0.13-1~deb12u1 │ 3.0.14-1~deb12u1 │ openssl: Excessive time spent checking DSA keys and │
│ │ │ │ │ │ │ parameters │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-4603 │
│ ├───────────────┤ │ │ │ ├───────────────────────────────────────────────────────────┤
│ │ CVE-2024-4741 │ │ │ │ │ openssl: Use After Free with SSL_free_buffers │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-4741 │
│ ├───────────────┼──────────┤ │ │ ├───────────────────────────────────────────────────────────┤
│ │ CVE-2024-2511 │ LOW │ │ │ │ openssl: Unbounded memory growth with session handling in
│ │ │ │ │ │ │ TLSv1.3 │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-2511 │
├──────────┼───────────────┼──────────┤ │ │ ├───────────────────────────────────────────────────────────┤
│ openssl │ CVE-2024-4603 │ MEDIUM │ │ │ │ openssl: Excessive time spent checking DSA keys and │
│ │ │ │ │ │ │ parameters │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-4603 │
│ ├───────────────┤ │ │ │ ├───────────────────────────────────────────────────────────┤
│ │ CVE-2024-4741 │ │ │ │ │ openssl: Use After Free with SSL_free_buffers │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-4741 │
│ ├───────────────┼──────────┤ │ │ ├───────────────────────────────────────────────────────────┤
│ │ CVE-2024-2511 │ LOW │ │ │ │ openssl: Unbounded memory growth with session handling in
│ │ │ │ │ │ │ TLSv1.3 │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-2511 │
└──────────┴───────────────┴──────────┴────────┴───────────────────┴───────────────────┴───────────────────────────────────────────────────────────┘

Analizando IaC

Dentro del directorio un Dockerfile simple y un deployment.yaml

cat Dockerfile
FROM alpine
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y golang-go
COPY app.go .
RUN go build app.go
CMD ["./app"]

#Creando el deployment.yaml
❯ kubectl create deployment nginx --image=nginx -oyaml --dry-run=client > deployment.yaml

cat deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: nginx
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
resources: {}
status: {}

Vamos a analizar la carpeta actual en la que estamos. Voy a comentar esta salida.

❯ trivy config .
2024-09-03T23:31:25-03:00 INFO [misconfig] Misconfiguration scanning is enabled
2024-09-03T23:31:26-03:00 INFO Detected config files num=2

# Encontró un dockerfile
Dockerfile (dockerfile)

Tests: 27 (SUCCESSES: 23, FAILURES: 4, EXCEPTIONS: 0)
Failures: 4 (UNKNOWN: 0, LOW: 1, MEDIUM: 1, HIGH: 2, CRITICAL: 0)

MEDIUM: Specify a tag in the 'FROM' statement for image 'alpine' # Bien, porque no definimos ninguna versión específica
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
When using a 'FROM' statement you should use a specific tag to avoid uncontrolled behavior when the image is updated.

See https://avd.aquasec.com/misconfig/ds001
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Dockerfile:1
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 [ FROM alpine
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

# Bien, porque no definimos ningún USER
HIGH: Specify at least 1 USER command in Dockerfile with non-root user as argument
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Running containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.

See https://avd.aquasec.com/misconfig/ds002
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

# Bueno porque no es buena práctica tener el HEALTHCHECK del contenedor.
LOW: Add HEALTHCHECK instruction in your Dockerfile
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
You should add HEALTHCHECK instruction in your docker container images to perform the health check on running containers.

See https://avd.aquasec.com/misconfig/ds026
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

# Muy bueno....
HIGH: '--no-install-recommends' flag is missed: 'apt-get update && apt-get install -y golang-go'
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
'apt-get' install should use '--no-install-recommends' to minimize image size.

See https://avd.aquasec.com/misconfig/ds029
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Dockerfile:3
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
3 [ RUN apt-get update && apt-get install -y golang-go
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

# Ahora analizando el otro archivo...

deployment.yaml (kubernetes)

Tests: 94 (SUCCESSES: 80, FAILURES: 14, EXCEPTIONS: 0)
Failures: 14 (UNKNOWN: 0, LOW: 9, MEDIUM: 4, HIGH: 1, CRITICAL: 0)

# Muy bueno, kube-bench acusaría lo mismo
MEDIUM: Container 'nginx' of Deployment 'nginx' should set 'securityContext.allowPrivilegeEscalation' to false
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node.

See https://avd.aquasec.com/misconfig/ksv001
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.yaml:21-23
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
21 ┌ - image: nginx
22 │ name: nginx
23 └ resources: {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

# Excelente, kube bench también acusaría esto.
LOW: Container 'nginx' of Deployment 'nginx' should add 'ALL' to 'securityContext.capabilities.drop'
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
The container should drop all default capabilities and add only those that are needed for its execution.

See https://avd.aquasec.com/misconfig/ksv003
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.yaml:21-23
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
21 ┌ - image: nginx
22 │ name: nginx
23 └ resources: {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

# Realmente faltan límites en este contenedor
LOW: Container 'nginx' of Deployment 'nginx' should set 'resources.limits.cpu'
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Enforcing CPU limits prevents DoS via resource exhaustion.

See https://avd.aquasec.com/misconfig/ksv011
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.yaml:21-23
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
21 ┌ - image: nginx
22 │ name: nginx
23 └ resources: {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

# También sería acusado por kube bench
MEDIUM: Container 'nginx' of Deployment 'nginx' should set 'securityContext.runAsNonRoot' to true
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Force the running image to run as a non-root user to ensure least privileges.

See https://avd.aquasec.com/misconfig/ksv012
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.yaml:21-23
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
21 ┌ - image: nginx
22 │ name: nginx
23 └ resources: {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

# Perfecto, porque no debemos ejecutar con la tag latest
MEDIUM: Container 'nginx' of Deployment 'nginx' should specify an image tag
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
It is best to avoid using the ':latest' image tag when deploying containers in production. Doing so makes it hard to track which version of the image is running, and hard to roll back the version.

See https://avd.aquasec.com/misconfig/ksv013
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.yaml:21-23
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
21 ┌ - image: nginx
22 │ name: nginx
23 └ resources: {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

# Acusaría también en kube bench
HIGH: Container 'nginx' of Deployment 'nginx' should set 'securityContext.readOnlyRootFilesystem' to true
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
An immutable root file system prevents applications from writing to their local disk. This can limit intrusions, as attackers will not be able to tamper with the file system or write foreign executables to disk.

See https://avd.aquasec.com/misconfig/ksv014
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.yaml:21-23
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
21 ┌ - image: nginx
22 │ name: nginx
23 └ resources: {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

# Misma situación de los límites arriba, pero para requests de cpu y memoria
LOW: Container 'nginx' of Deployment 'nginx' should set 'resources.requests.cpu'
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
When containers have resource requests specified, the scheduler can make better decisions about which nodes to place pods on, and how to deal with resource contention.

See https://avd.aquasec.com/misconfig/ksv015
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.yaml:21-23
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
21 ┌ - image: nginx
22 │ name: nginx
23 └ resources: {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


LOW: Container 'nginx' of Deployment 'nginx' should set 'resources.requests.memory'
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
When containers have memory requests specified, the scheduler can make better decisions about which nodes to place pods on, and how to deal with resource contention.

See https://avd.aquasec.com/misconfig/ksv016
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.yaml:21-23
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
21 ┌ - image: nginx
22 │ name: nginx
23 └ resources: {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


LOW: Container 'nginx' of Deployment 'nginx' should set 'resources.limits.memory'
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Enforcing memory limits prevents DoS via resource exhaustion.

See https://avd.aquasec.com/misconfig/ksv018
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.yaml:21-23
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
21 ┌ - image: nginx
22 │ name: nginx
23 └ resources: {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

# Lo mismo de kube-bench, que también pide poner user y group por encima de 10000. Ya estoy empezando a pensar que trivy tiene kube bench embebido.
LOW: Container 'nginx' of Deployment 'nginx' should set 'securityContext.runAsUser' > 10000
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Force the container to run with user ID > 10000 to avoid conflicts with the host's user table.

See https://avd.aquasec.com/misconfig/ksv020
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.yaml:21-23
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
21 ┌ - image: nginx
22 │ name: nginx
23 └ resources: {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


LOW: Container 'nginx' of Deployment 'nginx' should set 'securityContext.runAsGroup' > 10000
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Force the container to run with group ID > 10000 to avoid conflicts with the host's user table.

See https://avd.aquasec.com/misconfig/ksv021
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.yaml:21-23
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
21 ┌ - image: nginx
22 │ name: nginx
23 └ resources: {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


LOW: Either Pod or Container should set 'securityContext.seccompProfile.type' to 'RuntimeDefault'
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
According to pod security standard 'Seccomp', the RuntimeDefault seccomp profile must be required, or allow specific additional profiles.

See https://avd.aquasec.com/misconfig/ksv030
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.yaml:21-23
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
21 ┌ - image: nginx
22 │ name: nginx
23 └ resources: {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


MEDIUM: container "nginx" of deployment "nginx" in "default" namespace should specify a seccomp profile
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
A program inside the container can bypass Seccomp protection policies.

See https://avd.aquasec.com/misconfig/ksv104
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


LOW: container should drop all
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Containers must drop ALL capabilities, and are only permitted to add back the NET_BIND_SERVICE capability.

See https://avd.aquasec.com/misconfig/ksv106
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
deployment.yaml:21-23
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
21 ┌ - image: nginx
22 │ name: nginx
23 └ resources: {}
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

Bueno, y vimos que trivy también hace un buen trabajo con archivos declarativos de IaC. ¿Y un helm funcionará?

helm repo add aqua https://aquasecurity.github.io/helm-charts/
helm pull aqua/trivy --untar

trivy config trivy
2024-09-03T23:45:45-03:00 INFO [misconfig] Misconfiguration scanning is enabled
2024-09-03T23:45:46-03:00 INFO Detected config files num=7

# Archivos encontrados...
templates/statefulset.yaml (helm)

Tests: 94 (SUCCESSES: 87, FAILURES: 7, EXCEPTIONS: 0)
Failures: 7 (UNKNOWN: 0, LOW: 5, MEDIUM: 2, HIGH: 0, CRITICAL: 0)

MEDIUM: Container 'main' of StatefulSet 'trivy' should set 'securityContext.allowPrivilegeEscalation' to false
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
A program inside the container can elevate its own privileges and run as root, which might give the program control over the container and node.

See https://avd.aquasec.com/misconfig/ksv001
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
templates/statefulset.yaml:45-92
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
45 ┌ - name: main
46 │ image: docker.io/aquasec/trivy:0.37.2
47 │ imagePullPolicy: "IfNotPresent"
48 │ securityContext:
49 │ privileged: false
50 │ readOnlyRootFilesystem: true
51 │ args:
52 │ - server
53 └ envFrom:
..
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


LOW: Container 'main' of StatefulSet 'trivy' should add 'ALL' to 'securityContext.capabilities.drop'
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
The container should drop all default capabilities and add only those that are needed for its execution.

See https://avd.aquasec.com/misconfig/ksv003
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
templates/statefulset.yaml:45-92
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
45 ┌ - name: main
46 │ image: docker.io/aquasec/trivy:0.37.2
47 │ imagePullPolicy: "IfNotPresent"
48 │ securityContext:
49 │ privileged: false
50 │ readOnlyRootFilesystem: true
51 │ args:
52 │ - server
53 └ envFrom:
..
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


LOW: Container 'main' of StatefulSet 'trivy' should set 'securityContext.runAsUser' > 10000
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Force the container to run with user ID > 10000 to avoid conflicts with the host's user table.

See https://avd.aquasec.com/misconfig/ksv020
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
templates/statefulset.yaml:45-92
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
45 ┌ - name: main
46 │ image: docker.io/aquasec/trivy:0.37.2
47 │ imagePullPolicy: "IfNotPresent"
48 │ securityContext:
49 │ privileged: false
50 │ readOnlyRootFilesystem: true
51 │ args:
52 │ - server
53 └ envFrom:
..
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


LOW: Container 'main' of StatefulSet 'trivy' should set 'securityContext.runAsGroup' > 10000
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Force the container to run with group ID > 10000 to avoid conflicts with the host's user table.

See https://avd.aquasec.com/misconfig/ksv021
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
templates/statefulset.yaml:45-92
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
45 ┌ - name: main
46 │ image: docker.io/aquasec/trivy:0.37.2
47 │ imagePullPolicy: "IfNotPresent"
48 │ securityContext:
49 │ privileged: false
50 │ readOnlyRootFilesystem: true
51 │ args:
52 │ - server
53 └ envFrom:
..
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


LOW: Either Pod or Container should set 'securityContext.seccompProfile.type' to 'RuntimeDefault'
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
According to pod security standard 'Seccomp', the RuntimeDefault seccomp profile must be required, or allow specific additional profiles.

See https://avd.aquasec.com/misconfig/ksv030
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
templates/statefulset.yaml:45-92
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
45 ┌ - name: main
46 │ image: docker.io/aquasec/trivy:0.37.2
47 │ imagePullPolicy: "IfNotPresent"
48 │ securityContext:
49 │ privileged: false
50 │ readOnlyRootFilesystem: true
51 │ args:
52 │ - server
53 └ envFrom:
..
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


MEDIUM: container "main" of statefulset "trivy" in "default" namespace should specify a seccomp profile
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
A program inside the container can bypass Seccomp protection policies.

See https://avd.aquasec.com/misconfig/ksv104
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


LOW: container should drop all
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Containers must drop ALL capabilities, and are only permitted to add back the NET_BIND_SERVICE capability.

See https://avd.aquasec.com/misconfig/ksv106
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
templates/statefulset.yaml:45-92
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
45 ┌ - name: main
46 │ image: docker.io/aquasec/trivy:0.37.2
47 │ imagePullPolicy: "IfNotPresent"
48 │ securityContext:
49 │ privileged: false
50 │ readOnlyRootFilesystem: true
51 │ args:
52 │ - server
53 └ envFrom:
..

Análisis del FS

No es necesario crear la imagen de la aplicación para verificar el proyecto en el que se está trabajando. Obviamente después de que montemos la imagen tendremos una herencia de la base elegida, pero podemos solo verificar el propio proyecto.

trivy fs <ruta al directorio del proyecto>

En este ejemplo voy a escanear el propio proyecto de docusaurus, este mismo en el que estoy escribiendo.

# Pasé solo lo que quiero escanear para no coger secrets que están en el proyecto que son de ejemplos.

❯ trivy fs . --scanners vuln,misconfig
2024-09-04T09:36:23-03:00 INFO [vuln] Vulnerability scanning is enabled
2024-09-04T09:36:23-03:00 INFO [misconfig] Misconfiguration scanning is enabled
2024-09-04T09:36:26-03:00 INFO Number of language-specific files num=1
2024-09-04T09:36:26-03:00 INFO [npm] Detecting vulnerabilities...
2024-09-04T09:36:26-03:00 INFO Detected config files num=3

node_modules/@surma/rollup-plugin-off-main-thread/Dockerfile (dockerfile)

Tests: 27 (SUCCESSES: 23, FAILURES: 4, EXCEPTIONS: 0)
Failures: 4 (UNKNOWN: 0, LOW: 1, MEDIUM: 1, HIGH: 2, CRITICAL: 0)

MEDIUM: Specify a tag in the 'FROM' statement for image 'selenium/node-chrome'
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
When using a 'FROM' statement you should use a specific tag to avoid uncontrolled behavior when the image is updated.

See https://avd.aquasec.com/misconfig/ds001
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
node_modules/@surma/rollup-plugin-off-main-thread/Dockerfile:1
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
1 [ FROM selenium/node-chrome:latest
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


HIGH: Last USER command in Dockerfile should not be 'root'
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
Running containers with 'root' user can lead to a container escape situation. It is a best practice to run containers as non-root users, which can be done by adding a 'USER' statement to the Dockerfile.

See https://avd.aquasec.com/misconfig/ds002
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
node_modules/@surma/rollup-plugin-off-main-thread/Dockerfile:3
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
3 [ USER root
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


HIGH: The instruction 'RUN <package-manager> update' should always be followed by '<package-manager> install' in the same RUN statement.
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
The instruction 'RUN <package-manager> update' should always be followed by '<package-manager> install' in the same RUN statement.

See https://avd.aquasec.com/misconfig/ds017
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
node_modules/@surma/rollup-plugin-off-main-thread/Dockerfile:5-8
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
5 ┌ RUN apt-get update -qqy \
6&& rm -rf /var/lib/apt/lists/* /var/cache/apt/* \
7&& rm /bin/sh && ln -s /bin/bash /bin/sh \
8&& chown seluser /usr/local
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────


LOW: Add HEALTHCHECK instruction in your Dockerfile
══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
You should add HEALTHCHECK instruction in your docker container images to perform the health check on running containers.

See https://avd.aquasec.com/misconfig/ds026
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────

# También podemos verificar licencias
trivy fs . --scanners license

El escaneo local permite evaluar los módulos que están instalados mientras se está construyendo el proyecto. Diferentemente si hiciéramos el scan para un repositorio en el que ignoramos usando .gitignore varios archivos del build.

También es útil para entrar en un contenedor y hacer el análisis interno.

Análisis de Repositorio

Si hacemos el mismo análisis arriba, podemos observar que no tendremos node_modules, porque no estará en el repositorio.

Observe que los análisis están mirando las versiones de los paquetes y no los binarios.

❯ trivy repository https://gitlab.com/davidpuziol/devsecops.git --scanners vulnerability,misconfig
2024-09-04T09:47:14-03:00 INFO [vuln] Vulnerability scanning is enabled
2024-09-04T09:47:14-03:00 INFO [misconfig] Misconfiguration scanning is enabled
Enumerating objects: 1371, done.
Counting objects: 100% (1371/1371), done.
Compressing objects: 100% (1281/1281), done.
Total 1371 (delta 32), reused 1166 (delta 28), pack-reused 0 (from 0)
2024-09-04T09:47:21-03:00 INFO [npm] To collect the license information of packages, "npm install" needs to be performed beforehand dir="node_modules"
2024-09-04T09:47:22-03:00 INFO Number of language-specific files num=1
2024-09-04T09:47:22-03:00 INFO [npm] Detecting vulnerabilities...
2024-09-04T09:47:22-03:00 INFO Detected config files num=2

package-lock.json (npm)

Total: 2 (UNKNOWN: 0, LOW: 0, MEDIUM: 2, HIGH: 0, CRITICAL: 0)

┌────────────┬────────────────┬──────────┬────────┬───────────────────┬───────────────┬────────────────────────────────────────────────────────┐
│ Library │ Vulnerability │ Severity │ Status │ Installed Version │ Fixed Version │ Title │
├────────────┼────────────────┼──────────┼────────┼───────────────────┼───────────────┼────────────────────────────────────────────────────────┤
│ micromatch │ CVE-2024-4067 │ MEDIUM │ fixed │ 4.0.5 │ 4.0.8 │ micromatch: vulnerable to Regular Expression Denial of │
│ │ │ │ │ │ │ Service │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-4067 │
├────────────┼────────────────┤ │ ├───────────────────┼───────────────┼────────────────────────────────────────────────────────┤
│ webpack │ CVE-2024-43788 │ │ │ 5.91.0 │ 5.94.0 │ webpack: DOM Clobbering vulnerability in
│ │ │ │ │ │ │ AutoPublicPathRuntimeModule │
│ │ │ │ │ │ │ https://avd.aquasec.com/nvd/cve-2024-43788 │
└────────────┴────────────────┴──────────┴────────┴───────────────────┴───────────────┴────────────────────────────────────────────────────────┘