# Secure your Python image supply chain


## Prerequisites

Complete [Configure CI/CD for your Python application](/guides/python/secure-supply-chain/configure-github-actions/).

## Overview

When you ship a container image, what's inside it and where it came from
matters. Supply chain attestations are signed records that answer questions
like which packages are in the image, what vulnerabilities affect them, how
the image was built, and what security checks it passed.

In this section, you'll inspect the attestations that ship with your Docker
Hardened Image base, generate your own SBOM and provenance attestations
during CI, and pin the base image by digest so your builds are reproducible.

The inspection commands in this topic are shown manually so you can see what
each one returns. In a real workflow you'd automate these checks with
[Docker Scout](/scout/), which runs the same scans on every push,
enforces policies in CI, and surfaces results in your registry and pull
requests.

## Inspect the base image attestations

Docker Hardened Images are built to SLSA Build Level 3 and ship with a set of
signed attestations covering bill-of-materials, vulnerabilities, build
provenance, and security scans. See
[DHI attestations](/dhi/core-concepts/attestations/) for the full
list of types and how to verify their signatures with Cosign.

List all the attestations available on the Python DHI:

```console
$ docker scout attest list registry://dhi.io/python:3.12
```

View the SBOM:

```console
$ docker scout sbom registry://dhi.io/python:3.12
```

Check known vulnerabilities:

```console
$ docker scout cves registry://dhi.io/python:3.12
```

> [!NOTE]
>
> The `registry://` prefix forces `docker scout` to fetch the image and its
> attestations from the registry instead of reading a locally pulled copy. If
> you've already pulled or built against the base image, the local copy
> doesn't have the attached attestations, so the prefix is required to see
> them.

When you base your own image on a DHI image, these attestations stay attached to the base layer in the registry. Tools that inspect your image can follow the chain back to the DHI source.

## Generate attestations for your image

Update your GitHub Actions workflow to attach SBOM and provenance attestations to the image you push.

Edit `.github/workflows/build.yml` and update the build-and-push step:

```yaml {hl_lines="6-7"}
- name: Build and push Docker image
  uses: docker/build-push-action@v6
  with:
    context: .
    push: true
    sbom: true
    provenance: mode=max
    tags: ${{ steps.meta.outputs.tags }}
```

- `sbom: true` tells BuildKit to scan the built image and attach an SBOM attestation.
- `provenance: mode=max` records detailed build provenance, including the source repository, commit, and build parameters.

The next time your workflow runs, the pushed image will carry these attestations alongside the image manifest in the registry.

## Inspect your pushed image's attestations

After your workflow pushes the image, inspect it the same way you inspected the base image:

```console
$ docker scout attest list registry://DOCKER_USERNAME/REPO_NAME:latest
$ docker scout sbom registry://DOCKER_USERNAME/REPO_NAME:latest
```

The SBOM includes packages from every layer, including those inherited from `dhi.io/python:3.12`. The provenance record references the DHI base image by digest, so consumers of your image can trace the build chain back to the DHI source.

## Pin the base image by digest

Image tags like `dhi.io/python:3.12` move over time as new patches land. For reproducible builds, pin to an immutable digest.

The Dockerfile uses two tags, `dhi.io/python:3.12-dev` in the builder stage
and `dhi.io/python:3.12` in the runtime stage. Each tag has its own digest,
so look up both:

```console
$ docker buildx imagetools inspect dhi.io/python:3.12-dev --format "{{ .Manifest.Digest }}"
sha256:4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945
$ docker buildx imagetools inspect dhi.io/python:3.12 --format "{{ .Manifest.Digest }}"
sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
```

Each digest is a 64-character hex string. Update your `Dockerfile` to reference
each digest on the matching `FROM` line:

```dockerfile
FROM dhi.io/python:3.12-dev@sha256:4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945 AS builder
# ...
FROM dhi.io/python:3.12@sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
```

> [!TIP]
>
> Pinning by digest also pins you to that image's vulnerabilities. Use [Dependabot](https://docs.github.com/en/code-security/dependabot) or [Renovate](https://docs.renovatebot.com/) to automate digest updates so you get a PR when a new patched image is available, with a changelog to review before merging.

## Summary

In this section, you learned how to:

- Inspect the supply chain attestations that ship with the DHI base image, including SBOMs, CVE reports, VEX statements, and scan results
- Generate SBOM and provenance attestations for your own image in CI
- Pin base images by digest for reproducible builds

Related information:

- [DHI attestations](/dhi/core-concepts/attestations/)
- [Verify a Docker Hardened Image](/dhi/how-to/verify/)
- [Docker Scout](/scout/)
- [Build attestations](/build/metadata/attestations/)

## Next steps

In the next section, you'll deploy your application to Kubernetes.

