DevSecOps en la práctica con Gitlab

Andres Felipe Polo
BLOG DE TECNOLOGÍA DE DEVCO
7 min readMay 28, 2020

En DevOps, ya los equipos de desarrollo y operaciones se fusionan para colaborar en todo el ciclo de vida del software, desde el diseño, desarrollo y pruebas, hasta el despliegue y operaciones, permitiendo a las organizaciones entregar productos y nuevas funcionalidades rápidamente. Sin embargo, aún no se tiene la confianza de que el producto entregado es seguro. Cuando aparece la seguridad como un atributo importante para el cumplimiento de entrega del software, es necesario que haga parte fundamental en el proceso de desarrollo, dando origen a la práctica denominada DevSecOps.

DevSecOps

A nivel cultural DevSecOps busca fusionar a los profesionales en seguridad con los equipos que ya colaboran en DevOps, al incluir este rol como parte esencial para generar valor al producto. Por otra parte, también se enfoca en automatizar los flujos de trabajo de seguridad y cumplimiento para establecer un proceso que promueva la creación de código seguro y la colaboración entre los equipos de desarrollo y seguridad.

Con DevSecOps no solo nos aseguramos que los desarrolladores eviten integrar código sin cobertura o deuda técnica, sino que también se verifica que el nuevo código sea seguro y no inyecte vulnerabilidades. Podemos decir que DevSecOps es la evolución natural de DevOps.

¿Cómo empezar?

Inicialmente implica tener conocimiento de las prácticas de seguridad recomendadas por OWASP e implementar en el proceso de desarrollo en una etapa temprana de forma automatizada, esto llevado a la práctica en un contexto de flujo de trabajo con Git se traduce en ejecutar jobs automáticamente en cada Pull Request para detectar problemas de seguridad y vulnerabilidades en el código y dependencias antes de la integración con la rama base del proyecto.

Dicho esto, revisemos brevemente unas de las prácticas de seguridad más comunes:

Pruebas estáticas de seguridad del software

Escanea el código fuente de la aplicación y los archivos binarios para detectar posibles vulnerabilidades.

Pruebas dinámicas de seguridad del software

Escanea la aplicación en tiempo de ejecución para detectar vulnerabilidades como:

  • SQL injection
  • DoS attack
  • XSS

Es necesario que la aplicación se este ejecutando en un entorno de laboratorio, donde se realizan las pruebas de afuera hacia adentro sin tener acceso al código fuente. También hacen parte de los tipos de pruebas conocidas como de caja negra.

Escaneo de dependencias

Analiza las dependencias externas del proyecto como npm, ruby gem, composer, entre otras para detectar vulnerabilidades en cada commit de código. Por ejemplo, verificar que no estamos utilizando librerías de código obsoletas o deprecadas.

Cumplimiento de licencias

Busca automáticamente en las dependencias del proyecto las licencias aprobadas o en lista negra de su organización para evitar utilizar software pirata.

Al mismo tiempo, es muy importante trabajar en generar un cambio en la cultura de la organización, donde la seguridad se convierte en responsabilidad de todos, no solo del equipo de seguridad. También para que las personas puedan colaborar en espacios de intercambio de conocimiento para mejorar las prácticas de codificación.

Ahora que sabemos que acción tomar para adoptar DevSecOps, veamos cómo es su implementación con una herramienta que nos facilite realizar esta tarea.

Apoyarse en las herramientas adecuadas. Gitlab es nuestro mejor aliado.

Si bien en el mercado existe un sin número de herramientas para automatización y DevOps, Gitlab es una plataforma que provee un enfoque de aplicación completa en el SDLC que ofrece herramientas integradas en el proceso de CI/CD para implementar DevSecOps de forma rápida y con un menor esfuerzo.

Flujo de trabajo en DevSecOps con Gitlab https://about.gitlab.com/images/secure/security-diagram.svg

Principalmente en Gitlab nos enfocamos en dos momentos en el proceso de desarrollo, el primero cuando se escanea y analiza el nuevo código que el equipo de desarrollo por medio de Pull Request quiere integrar al proyecto base y segundo cuando el equipo de seguridad revisa el estado general del proyecto mediante el Security Dashboard. Es relevante mencionar que aunque pareciera que los dos momentos separan a los equipos, la verdad es que deben colaborar continuamente, por ejemplo en los PR’s el rol de seguridad atiende junto con los desarrolladores los issues encontrados en los reportes de pruebas.

Con el entendimiento logrado hasta ahora sobre el flujo de trabajo en DevSecOps con Gitlab, llegamos a un buen termino para empezar a construir nuestro primer pipeline que podemos llamar “Continuous Security Testing”.

Continuous Security Testing — Pipeline

Antes de empezar a escribir nuestro Pipeline As Code, revisemos el plano que ilustra la imagen anterior, inicialmente el pipeline va a tener tres Jobs asociados a las prácticas en DevSecOps, el escaneo de contenedores y dependencias, y las pruebas estáticas y dinámicas de seguridad del software. También destacar que en este mismo vamos ejecutar Jobs de Continuous Integration como son Build, Unit Test y Code Quality.

Consideraciones: los pasos a continuación omiten en gran parte la creación del repositorio en Gitlab y otras configuraciones.

Para escribir nuestro pipeline en Gitlab simplemente creamos un archivo llamado .gitlab-ci.yml en la raíz del proyecto. A partir de este archivo Gitlab orquesta y ejecuta los pipelines que codifiquemos sobre su infraestructura. Posteriormente agregamos las siguientes líneas de código:

image: docker:latest

include:
- template: Code-Quality.gitlab-ci.yml
- template: Dependency-Scanning.gitlab-ci.yml
- template: SAST.gitlab-ci.yml
- template: Container-Scanning.gitlab-ci.yml

stages:
- build
- test

Por defecto Gitlab ejecuta los pipelines bajo la capa de docker. Para controlar que imagen se utiliza en nuestro pipeline, asignamos el nombre de la imagen como valor del parámetro image.

Como mencioné anteriormente con Gitlab podemos implementar DevSecOps con un menor esfuerzo, al incluir las plantillas de Jobs que Gitlab mantiene oficialmente podemos tener integrados servicios en cuestión de minutos. En particular acabamos de agregar los templates para escaneo de dependencias y contenedores, y pruebas estáticas de seguridad del software con los parámetros include y template. Para los más curiosos es posible acceder al código fuente de cada template. A continuación dejo el enlace para Container-Scanning.gitlab-ci.yml

En último lugar, tenemos el parámetro stages que recibe una lista con los nombres de cada etapa en el pipeline. Hasta aquí hemos cubierto gran parte de la automatización de las prácticas de seguridad, pero ¿Qué sucede si queremos agregar nuestros propios Jobs y además configurar los Scanners?. ¡Excelente pregunta! y la respuesta es aún mejor… que sí es posible.

build:
stage: build
services:
- docker:dind
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
before_script:
- docker login -u $CI_REGISTRY_U -p $CI_REGISTRY_PW $CI_REGISTRY
script:
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG

code_quality:
stage: test
artifacts:
paths: [gl-code-quality-report.json]

dependency_scanning:
stage: test
variables:
CI_DEBUG_TRACE: "true"

sast:
stage: test
variables:
CI_DEBUG_TRACE: "true"
artifacts:
reports:
sast: gl-sast-report.json

container_scanning:
stage: test
variables:
GIT_STRATEGY: fetch
KLAR_TRACE: "true"
CLAIR_TRACE: "true"

Como muestra el código escribimos la estructura para cada Job qué recibe como primer parámetro el nombre y seguidamente los parámetros del propio Job, el stage indica en que etapa se va a ejecutar y mediante variables de entorno controlamos las configuraciones de cada servicio, por ejemplo: el Job “container_scanning” establece la variable CLAIR_TRACE en true para habilitar una salida más detallada del servicio que realiza el escáner. Así mismo, decir que estamos haciendo un override de los Jobs que se incluyeron como templates de Gitlab, a excepción del Job “build” que es una implementación propia con scripts para construir una imagen de Docker y publicar en un Registry.

Finalmente, nuestro archivo .gitlab-ci,yml se vería de la siguiente manera:

image: docker:latest

include:
- template: Code-Quality.gitlab-ci.yml
- template: Dependency-Scanning.gitlab-ci.yml
- template: SAST.gitlab-ci.yml
- template: Container-Scanning.gitlab-ci.yml

stages:
- build
- test
build:
stage: build
services:
- docker:dind
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_SLUG:$CI_COMMIT_SHA
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG

code_quality:
stage: test
artifacts:
paths: [gl-code-quality-report.json]

dependency_scanning:
stage: test
variables:
CI_DEBUG_TRACE: "true"

sast:
stage: test
variables:
CI_DEBUG_TRACE: "true"
artifacts:
reports:
sast: gl-sast-report.json

container_scanning:
stage: test
variables:
GIT_STRATEGY: fetch
KLAR_TRACE: "true"
CLAIR_TRACE: "true"
deploy_lab:
stage: deploy
variables:
SSH_KEY_ACCESS: "True"
script:
- echo "Deploying..."

dast:
stage: secure
variables:
DNS_HOSTNAME: "ec2.12.2312.12.com"
script:
- echo "Dinamic application segurity testing"

Una vez terminado nuestro Pipeline As Code es hora de visualizar el resultado en un Pull Request donde vamos a poder revisar el estado del pipeline, approvals y los reportes de pruebas que generaron los Jobs.

Gitlab Merge Request

Primero verificamos que el pipeline haya terminado exitosamente, luego podemos visualizar una sección donde se encuentran las aprobaciones requeridas para el Merge, comúnmente encontramos solo el grupo de probadores y desarrolladores pero al adoptar DevSecOps aparece en escena el grupo de seguridad con sus respectivos miembros. En cuanto a los aprobadores cada organización puede ser tan estricta como quiera debido a que no hay una regla estándar, pero por lo general con una o tres aprobaciones en cada grupo bastaría para hacer Merge.

Gitlab Merge Request — Reporte de pruebas de seguridad

Un poco más abajo en el Merge Request podemos revisar los reportes de las pruebas de seguridad ejecutadas en el pipeline, como pueden ver los escáner han encontrado demasiadas vulnerabilidades y esto es debido a que para esta demostración agregué código escrito hace más de dos años por lo cual podemos decir que en su mayoría está deprecado, más allá de eso lo importante en este punto es analizar cada una de las incidencias en conjunto con la persona en el rol de profesional de seguridad y establecer un plan de acción con prioridades para resolver cada issue encontrado.

Hasta este punto, además de hablar conceptualmente de lo que denominamos como DevSecOps hemos cubierto con un enfoque práctico los primeros pasos para empezar a adoptar esta práctica, apoyándonos en una plataforma como Gitlab. Muy seguramente en tu organización encontrarás retos desafiantes para evolucionar el proceso de desarrollo, y tal vez lo más importante sea comunicar y compartir con toda la organización en reuniones y presentaciones formales el plan de acción y estrategia de adopción, que a veces ayuda a resaltar preocupaciones o riesgos compartidos que otros miembros pueden no haber considerado y que en última instancia solidifican mejor la evolución del proceso en la organización.

--

--