Usando Trivy desde Azure DevOps

Publicado por Jose Maria Hidalgo Garcia el

DevSecOpsAzure DevOpsSeguridadTrivy

La seguridad es un aspecto crítico a tener en cuenta en el ciclo de vida del desarrollo de software.

Desde hace tiempo, y con la implantación de modelos DevOps en las organizaciones, el análisis de vulnerabilidades en el código y sus dependencias se encuentra automatizada en los pipelines de construcción de software.

Pero en la actualidad con el uso de contenedores, surge la necesidad que esa búsqueda o detección de vulnerabilidades deba extenderse también a las imágenes base donde se ejecutan nuestras aplicaciones.

Por enumerar algunas opciones disponibles open-source para análisis de vulnerabilidades en imágenes tendríamos:

+-----------------+---------+
| OSS CVE Scanner | Vendor  |
+-----------------+---------+
| Clair           | Red Hat |
| Anchore         | Anchore |
| Trivy           | Aquasec |
+-----------------+---------+

Tanto Anchore como Clair son excelentes herramientas, pero el objetivo de esta entrada está centrado en Trivy de la compañía Aqua y su uso dentro de un pipeline de Azure DevOps.

Dispones de todo el código en el siguiente repo publico: https://github.com/jhidalgo3/azure-pipeline-trivy

Principales características de Trivy

1.- Es una herramienta sencilla capaz de escanear y detectar vulnerabilidades en:

  • Paquetes de SO (Alpine, RHEL, CentOS, etc)
  • Dependencias para diferentes lenguajes (Composer, npm, Java, etc)
  • Dockerfile
  • Kubernetes yamls
  • Terraform

2.- Puede escanear diferentes artefactos:

  • Imágenes Docker en repositorios públicos y privados
  • Repositorios Git remotos

3.- Sencillo su uso sobre plataformas de CI: GitHub Actions, Jenkins, Azure DevOps, etc. Al ser un único binario y ofrecer también una imagen Docker mantenida por el propio fabricante.

4.- El uso de templating (JSON, HTML, JUnit, Table) para la generación de informes de resultados lo hace muy versátil.  A continuación el "junit.tpl" que usaremos en el ejemplo para poder publicar el resultado del análisis y la lista de las vulnerabilidades encontradas como si fueran resultado de test:

<?xml version="1.0" ?>  
<testsuites>  
{{- range . -}}
{{- $failures := len .Vulnerabilities }}
    <testsuite tests="{{ $failures }}" failures="{{ $failures }}" name="{{  .Target }}" errors="0" skipped="0" time="">
    {{- if not (eq .Type "") }}
        <properties>
            <property name="type" value="{{ .Type }}"></property>
        </properties>
        {{- end -}}
        {{ range .Vulnerabilities }}
        <testcase classname="{{ .PkgName }}-{{ .InstalledVersion }}" name="[{{ .Vulnerability.Severity }}] {{ .VulnerabilityID }}" time="">
            <failure message="{{ escapeXML .Title }}" type="description">{{ escapeXML .Description }}</failure>
        </testcase>
    {{- end }}
    </testsuite>
{{- end }}
</testsuites>  

Ejecución en local de Trivy

Como ya se ha comentado anteriormente para usar Trivy tan solo es necesario ejecutar su imagen Docker. A continuación se muestra el comando necesario para analizar un repositorio git público alojado en GitHub. Este proyecto es Javascript y Trivy analizará si existen vulnerabilidades a nivel de dependencias NPM.

Lanzamos el análisis, para localizar solo las que sean HIGH y CRITICAL:

docker run --rm aquasec/trivy repo --severity HIGH,CRITICAL --no-progress https://github.com/jhidalgo3/jenkins-cli  

Una vez finalizado, el análisis arroja que este proyecto tiene las siguientes vulnerabilidades:

package-lock.json (npm)  
=======================
Total: 16 (HIGH: 12, CRITICAL: 4)  

En el resumen, Trivy ofrece también la version con el parche que soluciona dicha vulnerabilidad:

Ejecución de Trivy en Azure DevOps

Para el ejemplo usaré los agentes efímeros proporcionados por Microsoft pero que son suficientes para analizar la imagen pública jhidalgo3/python-webhook-echo en aproximadamente 30 segundos.

Vamos con la creación del pipeline. Accedemos a Azure DevOps https://dev.azure.com/

Una vez dentro de Azure DevOps pulsamos en crear un Pipeline de tipo GitHub Yaml y seleccionamos All Repositories buscando en la caja de filtro el repo jhidalgo3/azure-pipeline-trivy:

En la siguiente pantalla seleccionar Existing Azure Pipelines YAML file, ya que vamos a usar un fichero yaml que define el pipeline existente. Lo seleccionamos:

Por ultimo, se mostrará el contenido del yaml azure-pipeline-build-trivy.yaml:

variables:  
  trivyVersion: 0.21.0
  IMAGE: "jhidalgo3/python-webhook-echo"

trigger:  
- main

pool:  
  vmImage: 'ubuntu-latest'

steps:

- task: CmdLine@2
  displayName: Scan
  inputs:
    script: |
      docker run -v /var/run/docker.sock:/var/run/docker.sock -v $PWD:/app --rm aquasec/trivy image --severity HIGH,CRITICAL --no-progress --format template --template "@contrib/junit.tpl" -o /app/junit-report.xml ${IMAGE}
      cp junit-report.xml $(Build.ArtifactStagingDirectory)/junit-report.xml

- task: PublishBuildArtifacts@1
  inputs:
    PathtoPublish: '$(Build.ArtifactStagingDirectory)'
    ArtifactName: 'drop'
    publishLocation: 'Container'

#Publicación de los resultados del análisis
- task: PublishTestResults@2
  inputs:
    testResultsFormat: 'JUnit'
    testResultsFiles: '**/junit-*.xml'
    mergeTestResults: true
    failTaskOnFailedTests: true
    testRunTitle: 'Trivy'
  condition: 'always()'

Al pulsar en RUN, Azure comenzará a ejecutar el pipeline:

El pipeline se marcará como error, esto es así porque estamos presentando las vulnerabilidades como un report de Test y en el pipeline hemos marcado la opcion failTaskOnFailedTests: true:

Se puede comprobar el detalle de todas las vulnerabilidades encontradas en la pestaña de Test de la ejecución del pipeline:

Con esta solución disponemos de una solución integrada que nos permite la navegación por cada una de las vulnerabilidades para poder revisar su detalle.

Conclusiones

Trivy tiene una gran versatilidad y capacidades para identificar vulnerabilidades de seguridad en nuestras imágenes Docker. Al integrarse fácilmente dentro de los pipeline CI/CD desde la fase de construcción se puede detectar e impedir que esas vulnerabilidades sean desplegadas en producción.

Es una opción valida para incorporar en los procesos y así reforzar la seguridad de nuestros desarrollos.

Si te surge cualquier comentario o duda, estaré encantado de resolverla. Y si te ha gustado, ¡síguenos en Twitter para estar al día de próximos posts!

Autor

Jose Maria Hidalgo Garcia

Arquitecto software y apasionado por todo lo que rodea al movimiento DevOps. Me encanta el terminal y tener todo automatizado en los proyectos en los que participo.
Twitter: @jhidalgo3