Day 21: DevSecOps in GitLab — Built-in Security Scanners & Pipeline Integration
This chapter covers the core principles of DevSecOps, focusing on how to transition from traditional, late-stage security audits to an integrated, automated "Shift Left" security model. We will explore GitLab's native security scanning capabilities—including Static Application Security Testing (SAST), dependency scanning, and container scanning—and implement them through hands-on pipeline configurations.
1. The Paradigm Shift: From Traditional CI/CD to DevSecOps
In traditional software development lifecycles, security is treated as an isolated, final phase executed right before deployment. This siloed approach introduces significant bottlenecks and financial risks.
The Traditional Pipeline Flow
If the security team identifies a critical vulnerability at the end of this flow, the entire release must be halted. The code must then be sent back to the development team, restarting the entire cycle.
The DevSecOps Pipeline Flow
DevSecOps integrates automated security checks directly into every stage of the continuous integration and continuous delivery (CI/CD) pipeline.
By embedding security checks at every level, security becomes a shared, continuous responsibility rather than a final gatekeeping step.
2. The Core Mental Model: "Shift Left" Security
The fundamental philosophy of DevSecOps is Shift Left Security (or "Security Left"). This principle dictates that security testing should occur as early as possible in the software development lifecycle (SDLC)—which is represented on the "left" side of a linear timeline.
Real-Life Analogy: Structural Engineering
Imagine building a multi-story house. If you discover a structural flaw in the foundation design while it is still a blueprint (on the "left"), correcting it requires only a pencil and an eraser.
However, if you discover that same foundation flaw after the entire house has been built, painted, and furnished (on the "right"), fixing it requires tearing down the entire structure. This is incredibly expensive, time-consuming, and disruptive.
Similarly, finding software vulnerabilities during the coding phase is cheap and fast; finding them in production can cost millions of dollars in breaches, downtime, and emergency patches.
3. GitLab's Built-In Security Scanning Ecosystem
One of GitLab's greatest strengths is its out-of-the-box security capabilities. Developers do not need to provision, license, or configure complex third-party security platforms. GitLab provides built-in scanners that run natively within your runner environments.
4. Lab 1: Implementing Static Application Security Testing (SAST)
In this lab, we will write a vulnerable Python script and configure GitLab's native SAST template to detect the vulnerability automatically.
Step 1: Create the Project
- Create a new blank project in GitLab.
- Name the project
day-21-security. - Set the visibility level to Public.
Step 2: Add Vulnerable Code (app.py)
Create a file named app.py containing a classic command injection vulnerability. This vulnerability occurs because user input is passed directly to the system shell without sanitization.
import subprocess
# Vulnerable Python Code
user_input = input("Enter the host to ping: ")
# DANGER: shell=True allows arbitrary command execution
subprocess.call(user_input, shell=True)
Step 3: Configure the GitLab CI/CD Pipeline (.gitlab-ci.yml)
Create a .gitlab-ci.yml file. Instead of writing custom scanning scripts, we will include GitLab's official, pre-built SAST template.
stages:
- test
# Include the official GitLab SAST template
include:
- template: Security/SAST.gitlab-ci.yml
How the Template Works
When the pipeline runs, GitLab evaluates the codebase, detects that it contains Python files, and dynamically provisions the appropriate analyzer job (e.g., semgrep-sast) under the hood.
5. Analyzing SAST Reports and Artifacts
Once the pipeline completes, you can inspect the security findings in several ways.
Viewing the Vulnerability Report
- Navigate to Build > Pipelines and click on your completed pipeline.
- Click on the automatically generated SAST job (e.g.,
semgrep-sast). - The console output will display a summary:
TEXT
Scan Summary: Scan Completed. Findings: 1 Total rules utilized: 81 - To view the detailed report:
- Via Artifacts: Click Browse or Download on the right side of the job page to retrieve
gl-sast-report.json. - Via Browser Pretty Print: Click directly on the artifact file in the GitLab UI to view an interactive, formatted report detailing the exact file, line number, and CVE description of the command injection vulnerability.
- Via Artifacts: Click Browse or Download on the right side of the job page to retrieve
6. Lab 2: Dependency Scanning with pip-audit
Applications rely heavily on open-source libraries. If these libraries contain known vulnerabilities, your application becomes vulnerable. In this lab, we will use pip-audit to scan a Python dependency manifest.
Step 1: Create a Vulnerable Dependency Manifest (requirements.txt)
Create a requirements.txt file in your repository containing outdated, vulnerable versions of popular libraries:
Django==1.2
requests==2.19
Step 2: Configure the Dependency Scanning Job
Update your .gitlab-ci.yml file to include a dedicated security stage that runs pip-audit inside a Python container:
stages:
- security
dependency_scan:
stage: security
image: python:3.11
script:
- pip install pip-audit
- pip-audit -r requirements.txt
Step 3: Pipeline Execution and Failure Behavior
When this pipeline runs, pip-audit checks the specified versions of Django and requests against the PyPI vulnerability database.
Note: GitLab treats any non-zero exit code (such as
1returned bypip-auditwhen vulnerabilities are present) as a job failure. This prevents vulnerable code from progressing further down the deployment pipeline.
7. Multi-Language Dependency Scanning Reference
Different programming languages use different package managers and scanning tools. Use the table below to configure dependency scanning for your specific tech stack:
8. Lab 3: Filesystem Scanning with Trivy
Trivy (by Aqua Security) is an extremely fast, versatile, and comprehensive security scanner. It can scan filesystems, git repositories, and container images.
In this lab, we will configure Trivy to scan our repository's filesystem for configuration issues and vulnerable files.
Pipeline Configuration (.gitlab-ci.yml)
Add the following job to your pipeline:
stages:
- security
filesystem_scan:
stage: security
image:
name: aquasec/trivy:latest
entrypoint: [""]
script:
- trivy fs .
Analyzing the Output
Trivy will scan the current directory (.) and output a clean, tabular report directly in the GitLab runner console:
The output highlights the severity level, the installed version, the fixed version, and a link to the vulnerability details.
9. Lab 4: Container Image Scanning with Trivy
In production environments, applications are packaged as Docker images. Scanning the application code is not enough; you must also scan the base operating system and system libraries packaged inside the container image.
In this lab, we will configure Trivy to scan a public container image (nginx) to simulate scanning a production artifact.
Pipeline Configuration (.gitlab-ci.yml)
Update your pipeline configuration to target a container image:
stages:
- security
container_image_scan:
stage: security
image:
name: aquasec/trivy:latest
entrypoint: [""]
script:
# Scan a public container image (e.g., nginx)
- trivy image nginx
Production Tip: In a real-world enterprise pipeline, you would replace
nginxwith your freshly built application image tag (e.g., trivy image CI_COMMIT_SHA).
10. Overriding Image Entrypoints in GitLab CI
You might have noticed the following configuration block in both Trivy labs:
image:
name: aquasec/trivy:latest
entrypoint: [""]
Why is entrypoint: [""] necessary?
By default, many official Docker images define a default entrypoint command. For example, the aquasec/trivy image is configured to run the trivy binary immediately upon startup.
If you do not override this entrypoint, the GitLab Runner will attempt to start the container and pass its own internal shell scripts to it. Because the container's entrypoint is expecting Trivy arguments rather than shell commands, the job will crash immediately with an execution error.
Setting entrypoint: [""] clears the image's default entrypoint, allowing the GitLab Runner to start an interactive shell session inside the container and execute your custom script commands sequentially.
Summary and Key Takeaways
- DevSecOps Integration: Security is no longer a post-deployment phase. It must be integrated directly into the CI/CD pipeline to identify vulnerabilities early.
- Shift Left Philosophy: Finding and fixing security issues during the initial coding and commit phases is significantly cheaper and faster than fixing them in production.
- GitLab Native SAST: GitLab provides built-in templates (such as
Security/SAST.gitlab-ci.yml) that automatically detect project languages and run appropriate static analyzers without requiring third-party tools. - Pipeline Failure on Vulnerability: Tools like
pip-auditreturn non-zero exit codes when vulnerabilities are found, allowing GitLab to automatically fail the pipeline and block insecure deployments. - Trivy Versatility: Trivy is a powerful tool for scanning both filesystems (
trivy fs .) and container images (trivy image <image_name>). - Entrypoint Overrides: When using specialized utility containers (like Trivy) in GitLab CI, you must set
entrypoint: [""]to allow the runner to execute custom shell commands.