Container images
Scan the container image your pipeline builds, for each environment (dev, staging, production), and fail the build on vulnerabilities
Container images
Add one step after your pipeline builds an image. It scans that image on the runner and records
it in Safeguard under the image's repository (for example ghcr.io/acme/api), one version per
environment and tag, such as prod-1.4.2.
safeguard ci scan --image "ghcr.io/acme/api:$TAG" --environment prod --wait --fail-on criticalIn the app, every container registry card under Integrations (Amazon ECR, Docker Hub, Azure Container Registry, Google Artifact Registry, Harbor, JFrog, Nexus, Quay and the rest) has a Set up CI/CD button. Pick your CI, the image and the environments, and it gives you the steps.
Setup
- Get a secret key with
scans:triggerandscans:read. Admins create one in the Set up CI/CD sheet or under Settings → Developer; others ask an admin. - Store it in your CI as
SAFEGUARD_API_KEY, the same way as for code scans: GitHub Actions, GitLab CI/CD, Bitbucket Pipelines, Jenkins, Azure Pipelines. - Add the step after the image is built, in each environment's pipeline.
Environments
Use one step per environment, each with its own --environment: dev, staging, prod, or
any name of letters, digits and dashes. Each environment's build is its own version, so you can
see what runs where, and fail only where it matters, for example --fail-on critical in
production and no --fail-on in development.
| Image | --environment | Version |
|---|---|---|
ghcr.io/acme/api:1.4.2 | prod | prod-1.4.2 |
ghcr.io/acme/api:1.4.2 | staging | staging-1.4.2 |
ghcr.io/acme/api:latest | dev | dev-<commit> (a latest or missing tag uses the build's commit) |
Where the image is read from
- The local Docker daemon, when the step runs on the machine that built the image. Nothing to set up.
- A registry, otherwise. Set
SAFEGUARD_REGISTRY_USERNAMEandSAFEGUARD_REGISTRY_PASSWORDas CI secrets so the scan can pull it. On GitLab's own registry use$CI_REGISTRY_USERand$CI_REGISTRY_PASSWORD.
Only the results leave the runner. No repository integration is needed.
Example: GitHub Actions
- name: Build
run: docker build -t "ghcr.io/acme/api:${{ github.sha }}" .
- name: Safeguard image scan (prod)
env:
SAFEGUARD_API_KEY: ${{ secrets.SAFEGUARD_API_KEY }}
SAFEGUARD_INSTALL_DIR: ${{ runner.temp }}/safeguard-bin
IMAGE: ghcr.io/acme/api:${{ github.sha }}
run: |
set -eu
curl -fsSL https://cli.safeguard.sh/install | bash
"$SAFEGUARD_INSTALL_DIR/safeguard" ci scan --image "$IMAGE" --environment prod --wait --fail-on criticalOptions
| Flag | What it does |
|---|---|
--image <ref> | The image to scan. Implies an on-runner scan. |
--environment <name> | The environment this build is for; names the version. |
--fail-on <severity> | With --wait, fail on a vulnerability at or above critical, high, medium or low. |
--project <name> | Record under another project instead of the image repository. |
Troubleshooting
| Message | What to do |
|---|---|
reading image … | The image is not in the local daemon and could not be pulled. Set the registry credentials, or run the step where the image was built. |
BAD_ENVIRONMENT | Use letters, digits and dashes, like prod. |
--image and --path are two different targets | Pass one: --image for an image, --local --path for code. |
Other errors are listed in CI/CD Integration.