Repository: runatlantis/atlantis (language: go)

Issue:
Start signing Atlantis containers and providing signatures/SBOMs for new releases
<!--- Please keep this note for the community --->

### Community Note

- Please vote on this issue by adding a 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to the original issue to help the community and maintainers prioritize this request. Searching for pre-existing feature requests helps us consolidate datapoints for identical requirements into a single place, thank you!
- Please do not leave "+1" or other comments that do not add relevant new information or questions, they generate extra noise for issue followers and do not help prioritize the request.
- If you are interested in working on this issue or have submitted a pull request, please leave a comment.

<!--- Thank you for keeping this note for the community --->

---

- [X] I'd be willing to implement this feature ([contributing guide](https://github.com/runatlantis/atlantis/blob/main/CONTRIBUTING.md))

**Describe the user story**

As a security engineer, I want to ensure that the containers I deploy into my environment include high-quality software. That software includes dependencies of the app destined to run within a container. I'd also like to implement signature verification into my container import workflow so that I can attest to the provenance of my container ecosystem. Without assurance that the containers that I run are the same ones which were imported from upstream, I may find myself at risk in the future.

**Describe the solution you'd like**

I would like to implement the following:

* New container release workflow which signs Atlantis images when they are built and provides the signatures to downstream users for verification. 
* SBOM generation workflow which produces a [CycloneDX](https://cyclonedx.org/) formatted bill of materials for each new image version.

**Describe the drawbacks of your solution**

Image attestation is hard. One needs to maintain a private key, which increases the level of trust placed in project maintainers. Project maintenance cost may be slightly increased given that the signature workflow will require additional Actions minutes.

**Describe alternatives you've considered**

We considered signing our own copy of Atlantis but signing containers without doing anything else is pointless. Part of the supply chain security manifesto, if you can call it that, involves attesting to the quality and safety of the software within an image - not just the provenance of the image. That doesn't just mean the software that the image is intended to run. It means that software, its dependencies, and those dependencies’ transient dependencies. Since we don't fork Atlantis we don't gain anything by signing a copy of it.




Discussion / hints:

Fix patch:
diff --git a/.github/workflows/atlantis-image.yml b/.github/workflows/atlantis-image.yml
index fa892287f4..02f0f2dcec 100644
--- a/.github/workflows/atlantis-image.yml
+++ b/.github/workflows/atlantis-image.yml
@@ -45,6 +45,11 @@ jobs:
     needs: [changes]
     if: needs.changes.outputs.should-run-build == 'true'
     name: Build Image
+    permissions:
+      contents: read
+      id-token: write
+      packages: write
+      attestations: write
     strategy:
       matrix:
         image_type: [alpine, debian]
@@ -129,6 +134,7 @@ jobs:
       run: echo "RELEASE_VERSION=${{ startsWith(github.ref, 'refs/tags/') && '${GITHUB_REF#refs/*/}' || 'dev' }}" >> $GITHUB_ENV
 
     - name: "Build ${{ env.PUSH == 'true' && 'and push' || '' }} ${{ env.DOCKER_REPO }} image"
+      id: build
       if: contains(fromJson('["push", "pull_request"]'), github.event_name)
       uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 # v6
       with:
@@ -147,6 +153,14 @@ jobs:
         labels: ${{ steps.meta.outputs.labels }}
         outputs: type=image,name=target,annotation-index.org.opencontainers.image.description=${{ fromJSON(steps.meta.outputs.json).labels['org.opencontainers.image.description'] }}
 
+    - name: "Sign and Attest Image"
+      if: env.PUSH == 'true'
+      uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb # v2.1.0
+      with:
+        subject-digest: ${{ steps.build.outputs.digest }}
+        subject-name: ghcr.io/${{ github.repository }}
+        push-to-registry: true
+
   test:
     needs: [changes]
     if: needs.changes.outputs.should-run-build == 'true'
@@ -201,4 +215,4 @@ jobs:
         image_type: [alpine, debian]
     runs-on: ubuntu-24.04
     steps:
-      - run: 'echo "No build required"'
+      - run: 'echo "No build required"'
\ No newline at end of file


Test patch: