CNCF Score project improved its security posture by joining the Docker-Sponsored Open Source Program

Docker ·

CNCF Score project improved its security posture by joining the Docker-Sponsored Open Source Program

See how CNCF Score improved container security with Docker Scout, Hardened Images, and the Docker-Sponsored Open Source Program.

Score, a CNCF Sandbox project, recently joined the Docker-Sponsored Open Source (DSOS) Program to strengthen the security of its container images and release pipeline. In this post, the Score maintainers share what we adopted (Docker Hub, Docker Scout, Docker Hardened Images, the Docker GitHub Builder, and OIDC connections), the results we measured, and how other open source projects, within the CNCF and beyond, can apply the same practices by default.

In September 2025, the CNCF announced a partnership with Docker to expand infrastructure support for project maintainers through the Docker-Sponsored Open Source (DSOS) Program. The partnership gives CNCF projects access to Docker’s security scanning tools, container registry, and best-practice resources at no cost.

This aligned closely with the discussions held at the Maintainer Summit NA 2025 in Atlanta , which centered on increasing collaboration across CNCF projects and demonstrating security best practices.

The Docker-Sponsored Open Source (DSOS) Program supports non-commercial open source projects with Docker tooling and infrastructure.

Eligible projects receive:

Before: Score publishes different container images: the CLIs of score-compose , score-k8s , score-helm and score-radius ; and the sample-score-app used for demos and testing. We previously published them in the GitHub Container Registry here .

After: Through the DSOS Program, we now publish these images on Docker Hub, which increases their visibility and gives the community a verified, trusted source.

All the container images are now available on Docker Hub .

The program also provides usage analytics that show how the community uses these images. Here is an example:

Before: As a CNCF project, Score follows the OpenSSF best practices . In addition, the OpenSSF Scorecard GitHub Action runs continuously. This was a solid foundation, but we had no dedicated security scanning tool in place.

After: The DSOS Program gave us access to Docker Scout, which we now use in our pull requests and on our container registry (Docker Hub). This gives us clear visibility into the CVEs introduced or fixed by each pull request and release.

The following GitHub Actions step uses docker/scout-action on every pull request to compare the image built from the PR branch with the one built from the main branch:

- name: Docker Scout Comparison between main branch and current PR branch uses: docker/scout-action@v1 with: command: compare image: local://score-compose:pr-${{ github.event.number }} to: local://score-compose:main write-comment: true github-token: ${{ secrets.GITHUB_TOKEN }} organization: ${{ secrets.DOCKER_HUB_ORG }} Here is an example of a comment posted by this docker/scout-action step in a recent Pull Request:

The comment details the comparison between the two images (before versus after): size, packages updated, added or removed, CVEs introduced and resolved, and compliance with our policies in place (run as non-root, no critical CVEs, no packages with AGPL v3 licences).

The analysis goes beyond the images themselves: it also covers the Go libraries and binaries they contain.

The Docker Scout console also provides a single view of CVEs, packages, base images, and policies across all of our container images, which makes it easy to see what is used where, and in which version.

When Docker made Hardened Images freely available under Apache 2.0 , we saw an opportunity to simplify our security posture while staying compatible with our existing Dockerfiles and workflows, even though we were already using multi-stage builds and distroless base images.

Docker Hardened Images are minimal, security-focused container base images designed to reduce inherited risk in downstream projects. They are built and maintained with security and transparency as defaults, and offer:

The migration was straightforward.

Take the score-radius CLI, which used a golang:alpine and a distroless images and now uses dhi.io/golang and dhi.io/static images. The changes required in the Dockerfile were minimal:

In the associated PR here , image size dropped by 0.3 MB while the number of packages stayed the same and the CVE count remained at 0. We could have stayed with Debian, but chose to move to an Alpine base image (DHI provides the two options ):

The same applies to the NodeJS sample-score-app, which used nodejs on top of an alpine image and now uses a dhi.io/node image. The changes required in the Dockerfile were minimal:

In the associated PR here , 22 packages were removed (including the package manager and the shell), along with 2 CVEs:

Note: We were able to present a short walkthrough of our DHI integration during the CNCF webinar held on January 23, 2026, which accompanied the Docker Hardened Images announcement for CNCF projects .

Although not directly part of the DSOS Program, we took the opportunity to adopt the new docker/github-builder GitHub reusable workflow to release our container images.

This model provides a build pipeline maintained in the Docker organization, with a version-controlled BuildKit environment, multi-platform builds distributed across runners where beneficial, and signed SLSA provenance recording both the source commit and the builder identity.

The trade-off is intentional. You retain control over when the build runs and which inputs it uses, while the build implementation itself lives in the Docker-maintained workflow rather than in per-repository job steps.

Before , the GitHub Actions workflow to release our container images was using this sequence of actions:

release-container-image: runs-on: ubuntu-latest permissions: id-token: write packages: write steps: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub (docker.io) uses: docker/login-action@v4 with: registry: docker.io username: ${{ vars.DOCKER_HUB_ORG }} password: ${{ secrets.DOCKER_HUB_RELEASE_TOKEN }} - name: Login to GitHub Container Registry uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build and push docker image id: build-push-container uses: docker/build-push-action@v6 with: context: . platforms: linux/amd64,linux/arm64 push: true provenance: mode=max sbom: true tags: | ghcr.io/score-spec/score-compose:${{ github.ref_name }} ghcr.io/score-spec/score-compose:latest docker.io/scorespec/score-compose:${{ github.ref_name }} docker.io/scorespec/score-compose:latest build-args: | "VERSION=${{ github.ref_name }}" - name: Sign container image run: | cosign sign --yes ghcr.io/score-spec/score-compose@${{ steps.build-push-container.outputs.digest }} cosign sign --yes scorespec/score-compose@${{ steps.build-push-container.outputs.digest }} After , this same workflow is now only using the reusable workflow docker/github-builder/.github/workflows/build.yml :

release-container-image: uses: docker/github-builder/.github/workflows/build.yml@v1 permissions: contents: read id-token: write packages: write with: output: image push: true platforms: linux/amd64,linux/arm64 sbom: true cache: true context: . set-meta-labels: true set-meta-annotations: true build-args: | "VERSION=${{ github.ref_name }}" meta-images: | ghcr.io/score-spec/score-compose scorespec/score-compose meta-tags: | type=ref,event=tag latest registry-identities: | - type: dockerhub registry: docker.io username: ${{ vars.DOCKER_HUB_ORG }} connection_id: ${{ vars.DOCKER_HUB_OIDC_CONNECTION_ID }} secrets: registry-auths: | - registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} Docker GitHub Builder gives us a Docker-maintained build pipeline with signed SLSA provenance and SBOM attestations, helping consumers verify the builder identity and trace images back to their source commit. Signing uses GitHub OIDC, so we don’t need to manage long-lived signing keys. With caching enabled in our workflow, cache entries are also signed and verified before reuse, helping protect against cache poisoning. See the Docker GitHub Builder documentation for more information about its security model.

The following command verifies the signature on the attestation manifest for the selected image platform:

TAG=latest PLATFORM=amd64 SIGNED_DIGEST=$(crane manifest scorespec/score-compose:$TAG \ | jq -r --arg platformdigest \ "$(crane digest --platform linux/$PLATFORM scorespec/score-compose:$TAG)" \ '.manifests[] | select(.annotations["vnd.docker.reference.digest"] == $platformdigest) | .digest') cosign verify \ --experimental-oci11 \ --new-bundle-format \ --certificate-oidc-issuer https://token.actions.githubusercontent.com \ --certificate-identity-regexp '^https://github\.com/docker/github-builder/\.github/workflows/build\.yml@.*$' \ scorespec/score-compose@$SIGNED_DIGEST OIDC Connections Most recently, with an update released on July 21, 2026, Docker introduced OIDC connections . This feature allows pulls and pushes to Docker Hub to use a short-lived token instead of a long-lived Organization Access Token (OAT).

An OIDC connection establishes a trust relationship between Docker and a trusted third party, so you no longer need to maintain long-lived credentials. Docker exchanges short-lived tokens with the other vendor, which can grant fine-grained access to your Docker resources.

Before:

With docker/login-action :

steps: - uses: docker/login-action@v4 with: username: ${{ vars.DOCKER_HUB_ORG }} password: ${{ secrets.DOCKER_HUB_TOKEN }} Or with docker/github-builder :

uses: docker/github-builder/.github/workflows/build.yml@v1 with: ... secrets: registry-auths: | - username: ${{ vars.DOCKER_HUB_ORG }} password: ${{ secrets.DOCKER_HUB_TOKEN }} After:

With docker/login-action :

permissions: id-token: write steps: - uses: docker/login-action@v4 # v4.5.0+ env: DOCKERHUB_OIDC_CONNECTIONID: ${{ vars.DOCKER_HUB_OIDC_CONNECTION_ID }} with: username: ${{ vars.DOCKER_HUB_ORG }} Or with docker/github-builder :

uses: docker/github-builder/.github/workflows/build.yml@v1 # v1.15.0+ permissions: id-token: write with: ... registry-identities: | - type: dockerhub registry: docker.io username: ${{ vars.DOCKER_HUB_ORG }} connection_id: ${{ vars.DOCKER_HUB_OIDC_CONNECTION_ID }} By replacing a static, long-lived token with a short-lived one and making it issued on a per-workflow basis, we removed a credential that could otherwise be stolen and misused.

Participating in the Docker-Sponsored Open Source (DSOS) Program has been rewarding, and it has helped Score adopt a security-by-default foundation. Using Docker Scout , Docker Hardened Images , OIDC connections , and the Docker GitHub Builder has strengthened the security posture of our container images and brought broader benefits to the Score projects and repositories.

If you maintain a CNCF project or any open source project, we encourage you to explore the Docker-Sponsored Open Source (DSOS) Program . The security improvements were measurable, and the onboarding process was straightforward.

The full implementation across the Score repositories is tracked in GitHub issue #376 .

Attending KubeCon NA 2026 in Salt Lake City? Feel free to connect with the Score maintainers during the Maintainer Summit on Sunday, Nov 8th and at the Kiosk #P-17B on Thursday, Nov 12th between 10:15am and 2pm, you will see Score in actions with the new Score Labspace !

Note: This work was completed after I joined Docker. It was, however, an initiative we wanted to pursue as CNCF project maintainers from the moment it was announced in September 2025 , both to improve our security posture and to share what we learned with the broader community.

We hope this post helps you strengthen the security of your own projects.

Источник: Docker