Published on: September 9, 2024
11 min read
Learn how GitLab + Chainguard can help deliver secure containerized applications faster. This tutorial includes easy-to-follow code examples.
Container technology, which creates consistent environments and streamlines deployment processes, is incredibly beneficial for software development. Containers contribute to faster development cycles, more efficient resource utilization, and greater flexibility in application management.
Some of that efficiency can be lost, though, if organizations reinvent the wheel with each software development project. Instead, a base image should serve as the starting point for building other container images. These base images contain a bare minimum OS, essential tools, ensured compatibility, reduced image size, and other advantages.
While base images provide a lot of value, they do have risks. It’s easy for your application to be compromised due to:
GitLab and Chainguard provide several solutions to address these risks, including Hardened Base Images, Container Signing, and Vulnerability Scanning and Management. In this article, you'll learn how these features can be implemented to prevent breaches via containerized applications.
Chainguard Images offer several key benefits that make them essential for organizations prioritizing security:
Chainguard provides more than 833 minimal, hardened images that can be easily built, shipped, and run. Container images can all be stored and managed directly in GitLab Container Registry. These solutions greatly minimize container security complexity.
The Chainguard directory provides hardened, minimal container images to help developers build software from the onset. With 97.6% fewer vulnerabilities than the average image, Chainguard Images help organizations swiftly reach container security compliance goals like NIST 800-53, FedRAMP, or PCI-DSS.
These images can be accessed directly from the Chainguard Directory. All images have the following features:
Chainguard container images can be used in the following ways:
To use a Chainguard image in a specific GitLab job, within your gitlab-ci.yml
, simply set the image
directive under the job definition to the image you wish to use. For example, the following job named unit-tests
uses cgr.dev/chainguard/go:latest
as the container image to run the job.
stages:
- test
unit-tests:
image: cgr.dev/chainguard/go:latest
stage: test
before_script:
- go mod download
script:
- go test -coverprofile=coverage.out
artifacts:
paths:
- coverage.out
To use a Chainguard image within a Dockerfile, simply create a Dockerfile in the root directory of your GitLab project. Then set the base image of the Dockerfile to the Chainguard image you wish to use, and add any other required commands:
FROM cgr.dev/chainguard/go:latest
WORKDIR /app
COPY . .
RUN go mod download
RUN go build -o /main .
CMD [“/main”]
Then, you can create a job in the .gitlab-ci.yml
to log in to the built-in GitLab Container Registry and push the image:
build-app-image:
stage: build
image: docker:latest
services:
- docker:dind
variables:
IMAGE: $CI_REGISTRY_IMAGE/$CI_DEFAULT_BRANCH:latest
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $IMAGE .
- docker push $IMAGE
Once the job completes, you can see the pushed images in GitLab Container Registry by selecting Deploy > Container Registry from your project’s side tab.
Note: GitLab makes it easy to authenticate with the built-in container registry via reserved CI/CD variables as seen above.
Signing container images is a critical security measure to prevent tampering by verifying their authenticity, trust, and integrity:
The Sigstore project provides a CLI called Cosign, which can be used for keyless signing of container images. This eliminates the need to manage safeguards and rotate the private key that will be signing the image. GitLab provides container-signing by allowing you to generate a private key via a token obtained from the GitLab server using the OIDC identity of the user who ran the job. The token includes unique claims that certify that a CI/CD pipeline generated the token.
GitLab stores the container signature details in the container registry. A job can then be created to validate the signature against the certificate issuer using Cosign.
As you add more application dependencies to a hardened base image to achieve your goals, over time you may introduce vulnerabilities. By enabling security scanning provided by GitLab, you can address these risks as they come and reduce them. Additionally, when these vulnerabilities arise, vulnerability management tools are crucial for managing your security posture.
It's necessary to regularly run security scans to avoid data breaches, reduce service downtime, and prevent loss of brand reputation. Some benefits of running security scans before code is deployed to production include:
GitLab provides several analyzers to scan various parts of your application for security vulnerabilities:
Scanner Type | Description |
---|---|
Static Application Security Testing (SAST) | Scans static source code for known vulnerabilities (C/C++, Java, Python, Go, JavaScript, and many more languages) |
Dynamic Application Security Testing (DAST) | Runs automated penetration tests to find vulnerabilities in your web applications and APIs as they are running |
Infrastructure as Code Scanning (IaC) | Scans infrastructure definition files for known vulnerabilities (Terraform, Ansible, AWS Cloudformation, Kubernetes, and many more) |
Container Scanning (including image dependencies and licenses) | Scans container images for known vulnerabilities, including GitLab Container Registry, external container registries, Kubernetes cluster. Container image dependencies and licenses are also scanned and compared to policy |
Dependency Scanning and License Compliance | Scans your application’s dependencies for known vulnerabilities, including NuGet, Gradle, Maven, pip, npm, yarn, and more. Dependency licenses are also scanned and compared to policy. |
Secret Detection | Scans your repository for secrets, such as keys and passwords. Scans all text files regardless of language or framework. Can be set to reject pushes if a secret is detected and can run in browser to warn if you are about to post a potential secret. |
Web API Fuzzing | Sets operation parameters to unexpected values to cause unexpected behavior and errors in the API backend |
Coverage-guided Fuzzing | Sends random inputs to an instrumented version of your application to cause unexpected behavior |
These scanners can be easily added to your pipeline by simply importing the appropriate scanner template in your .gitlab-ci.yml
. For example, to enable SAST, simply add the following to your .gitlab-ci.yml
:
stages:
- test
include:
- template: Jobs/SAST.gitlab-ci.yml
Once you've enabled the scanners, whenever you create a merge request to commit code from a feature branch into another branch, scanner results will display directly within the MR:
These results allow developers to quickly assess, prioritize, and mitigate or remediate vulnerabilities by providing the following information:
Additional actions can be taken on a vulnerability, such as:
Note: Scanners can also be configured and/or extended using variables and pipeline directives, just like any other GitLab job.
The scanners mentioned above can be used along with security policies to prevent insecure code from being merged into production and to ensure that the scanners are run on every pipeline. GitLab provides the following security policy types:
Implementing these policies ensures that when creating an MR, security scans and custom compliance jobs will be run, and that approval will be required if vulnerabilities or incompatible licenses are detected:
Detecting vulnerabilities before they make it to production is important, but it is equally important to determine and manage vulnerabilities that make their way into production, so that they can be mitigated accordingly.
GitLab Vulnerability Report provides information on all the detected vulnerabilities from scans of the default branch (which may be your staging or production branch):
If you select a vulnerability, you’ll be taken to its vulnerability page, which displays the same vulnerability details as you would see in the MR view. You can use this view to quickly assess, prioritize, and mitigate or remediate vulnerabilities:
The security team can manage vulnerabilities by setting their status to one of the following:
A software bill of materials (SBOM) is a comprehensive inventory that lists all the components, dependencies, and associated metadata of a software application. SBOMs are vital for organizations to effectively manage software security, compliance, and supply chain risks.
Chainguard provides high-quality, out-of-the-box SBOMs for their container images in SPDX format. The SBOM can be converted into CycloneDX format and loaded into or compared with the results of GitLab’s dependency list. The dependency list is an SBOM generated from an artifact or the results of the dependency, container, and license scanners:
Chainguard images meet SLSA Level 2 requirements and are verified, signed, and attested with signatures. Furthermore, GitLab CI can generate and produce attestation/provenance metadata for all build artifacts. By using Chainguard with GitLab, you can prevent tampering and provide additional build integrity guarantees.
To learn more about GitLab and Chainguard, and how we can help enhance your security posture, check out the following resources: