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!