BeastCoding
By Tobias Kriebisch
on

Keeping container images secure is a critical task in any modern DevOps workflow. Two popular tools that help achieve this are Grype and Syft. With these tools, you can automatically generate a Software Bill of Materials (SBOM) and perform vulnerability scans against the CVE Database for your container images. However, a common challenge arises when running these tools inside GitLab CI: the containers provided by their vendors are distroless and cannot be used by GitLab Runner (which often requires a full Linux distribution in order to run Docker-in-Docker or other tasks).

What Are Grype & Syft?

The Challenge

When running scans in GitLab CI, you might try to pull the official Grype or Syft containers only to discover they’re minimal “distroless” images, which are incompatible with the default GitLab Runner. Also by default they try to use docker or podman. Typically, using containers that require certain privileges (like Docker-in-Docker) or a full Linux distribution can be problematic or insecure in a Gitlab Runner.


The Solution: Nix

By using Nix, you can simply use Grype and Syft in a plain, unprivileged environment. Furthermore, by using a “special prefix,” you can pull images—even from private registries—without needing special setups for Docker or Podman.

Below are two example jobs in GitLab CI that will illustrate how to automatically create SBOMs and scan for vulnerabilities, all without privileged containers.


Example Job: Generating an SBOM with Syft

syft-container-scaning:
  stage: build
  image:
    name: nixos/nix:2.25.3
    entrypoint: [""]
  variables:
    SYFT_REGISTRY_AUTH_USERNAME: ${CI_REGISTRY_USER}
    SYFT_REGISTRY_AUTH_PASSWORD: ${CI_REGISTRY_PASSWORD}
  script:
    - mkdir reports
    - nix-shell -p syft --run "syft scan registry:your-container-image:vx.x.x --output cyclonedx-json=reports/container-sbom.json --output cyclonedx-xml=reports/container-sbom.xml"
  artifacts:
    paths:
      - reports/**.json
      - reports/**.xml
    when: on_success
    expire_in: "30 days"
  only:
    - tags

Example Job: Vulnerability Scanning with Grype

grype:
  stage: build
  image:
    name: nixos/nix:2.25.3
    entrypoint: [""]
  needs:
    - job: syft-container-scaning
      artifacts: true
  artifacts:
    paths:
      - reports/container-vulnerability-report.json
    when: always
    expire_in: "30 days"
  script:
    - nix-shell -p grype --run 'grype --fail-on High sbom:reports/container-sbom.json -o cyclonedx-json=reports/container-vulnerability-report.json -o table'
  only:
    - tags
``

## Conclusion

By leveraging **Nix** to install Grype and Syft, I can seamlessly integrate container scanning into my GitLab CI pipeline without resorting to privileged containers. This approach helps maintain security best practices while providing all the benefits of generating an SBOM and detecting vulnerabilities in your images.

With these two example jobs, I can now confidently automate my container scanning. As soon as your images are built and tagged, Syft generates an SBOM, Grype checks for vulnerabilities, and the pipeline fails (or warns you) when critical issues arise. This helps shift left on security by catching problems early—giving you peace of mind that your container images are secure and up to date.

Ideally I should introduce a maybe daily job to repeat the scanning to find newly discovered vulnerabilites.