CoursesSecure CI/CD with GitLabSecurity scanning stages

Container image scanning

Trivy: report all, fail on critical.

Intermediate12 min · lesson 11 of 17

If you build container images, the image itself is an artifact to scan — its base OS packages and language libraries carry CVEs just like your dependency tree. Container scanning (Trivy is the standard, and GitLab ships a built-in template) inspects the built image before it ships. The workflow is: build the image, scan it, and only push it to the registry if it passes the gate — so a vulnerable image never reaches a cluster.

.gitlab-ci.yml
container-scan:
stage: test
image: { name: aquasec/trivy:0.53.0, entrypoint: [""] }
variables: { TRIVY_CACHE_DIR: .trivycache/ } # cache the vuln DB (~40MB)
cache: { paths: [.trivycache/] }
script:
- trivy image --exit-code 0 --severity LOW,MEDIUM,HIGH "$IMAGE" # report all
- trivy image --exit-code 1 --severity CRITICAL --ignore-unfixed "$IMAGE" # gate

The two-pass pattern, again

The same shape recurs across scanners because it works: one pass reports every severity with exit code 0 (nothing fails, full visibility), a second pass fails the build with exit code 1 on only the severities you would actually page for, plus --ignore-unfixed so you never block on a CVE with no patch. A scanner that blocks every merge gets turned off; one that blocks only actionable criticals stays on.

Keep scans fast, or they get skipped

A scan that adds three minutes to every pipeline is a scan developers route around. Two moves keep it fast: cache Trivy’s vulnerability database (about 40MB) between runs so it is not re-downloaded each time, and pin the scanner image version so the job is reproducible and the DB cache stays valid. A sub-30-second scan is one nobody minds. And of course, the smaller the image (distroless, from the container course), the less there is to scan and fewer CVEs to triage.

Pin the scanner; scan before you push
Two disciplines. Pin the scanner image (aquasec/trivy:0.53.0, not :latest) so your security gate cannot change under you — a scanner that updates itself is the supply-chain problem you are trying to prevent. And put the scan before the registry push, so a failing image is never published; scanning after push means the vulnerable artifact is already out there.