Skip to content

chore(security): harden release pipeline + container distribution - #42

Merged
malickyeu merged 2 commits into
mainfrom
hardening/supply-chain-and-container
Jun 15, 2026
Merged

chore(security): harden release pipeline + container distribution#42
malickyeu merged 2 commits into
mainfrom
hardening/supply-chain-and-container

Conversation

@malickyeu

Copy link
Copy Markdown
Contributor

Summary

Acts on the code-review + security-review of the distribution work (#41), ahead of broader distribution (container registry, and later MS Store / Debian). No application/Go code changes — supply-chain and container-posture hardening only.

Supply chain (release.yml + ci.yml)

  • Pin every GitHub Actions step to a commit SHA (version in a trailing comment) so a moved tag can't inject code into a run holding id-token / attestations / packages write.
  • Cover the SBOM with the signed checksumsdockercmd.sbom.spdx.json is appended to SHA256SUMS before the cosign sign-blob, so it's no longer published without integrity. sbom-action upload-release-assets: false (the release already ships it).
  • Sign the image recursively (cosign sign -r) so each per-platform manifest is signed, not just the index.
  • Anchor the cosign verify identity to …/release.yml@refs/tags/v (was @refs/tags/).

Container posture (Dockerfile + README)

  • Hardened docker run: required --group-add (in the command), --read-only + --tmpfs /tmp, --cap-drop ALL, --security-opt no-new-privileges, digest pinning, an explicit socket = host-root-equivalent warning, and a bind-mount ownership note (uid 65532).
  • Document image verification (cosign verify / gh attestation verify oci://…).
  • Dockerfile: drop the redundant build-stage chown (the COPY --chown sets ownership).

Type of change

  • Bug fix
  • New feature
  • Docs only
  • Refactor / chore (security hardening)

Checklist

  • go test -short ./... and go vet ./... pass — N/A (no Go changed)
  • gofmt gate is clean (no .go files touched)
  • Frontend type-checks — N/A (no UI change)
  • Rebuilt and committed web/dist — N/A (no web/src change)
  • Added/updated tests — N/A (CI/Docker/docs; validated by a real local build + run, see notes)
  • Updated docs/ and added a CHANGELOG.md entry

Notes for reviewers

  • Validated locally: the hardened docker run (read-only + tmpfs + cap-drop ALL + no-new-privileges + --group-add docker + socket + named volume) starts cleanly, connects to Docker, and writes /data as uid 65532 (docker-commander.db{,-shm,-wal} owned 65532:65532). Both workflows pass yaml.safe_load and carry no unpinned @vN action tags.
  • Action SHAs were resolved from each action's current major tag (gh api repos/<a>/commits/<vN>); Dependabot can keep them current.
  • The release-time steps (cosign, SBOM-in-checksums, recursive image sign) still first execute on the next v* tag.
  • Out of scope (need provisioned secrets): Authenticode signing for the Windows .exe (MS Store) and dpkg-sig/GPG for a Debian package — separate follow-ups once certs/keys exist.

Acts on the code + security review of the distribution work:

- Pin every GitHub Actions step (release.yml + ci.yml) to a commit SHA, so a
  moved tag can't inject code into a run holding id-token / attestations /
  packages write.
- Cover the SBOM with the signed SHA256SUMS (it shipped unsigned); set
  sbom-action upload-release-assets: false (the release already uploads it).
- Sign the image recursively (cosign sign -r) so each per-platform manifest is
  signed, not just the index.
- Anchor the cosign verify identity to .../release.yml@refs/tags/v.
- README docker run hardened: required --group-add, --read-only + --tmpfs,
  --cap-drop ALL, --security-opt no-new-privileges, digest pinning, a
  socket=host-root warning + bind-mount ownership note; document image
  verification (cosign verify / gh attestation verify oci://).
- Dockerfile: drop the redundant build-stage chown (COPY --chown sets it).

Validated locally: the hardened `docker run` (read-only + tmpfs + cap-drop +
named volume + socket) starts cleanly and writes /data as uid 65532; both
workflows YAML-lint and carry no unpinned action tags.
Copilot AI review requested due to automatic review settings June 15, 2026 15:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens the project’s supply-chain posture and container distribution/docs without changing application (Go) code, focusing on securing GitHub Actions releases and improving container runtime guidance for users.

Changes:

  • Pins GitHub Actions used in CI/release workflows to commit SHAs; enhances release signing coverage (SBOM included in signed checksums) and recursively signs multi-arch images.
  • Hardens Docker runtime documentation (read-only filesystem, dropped capabilities, no-new-privileges, socket warning) and adds container image verification commands.
  • Simplifies Dockerfile build stage by removing redundant ownership changes while keeping runtime ownership via COPY --chown.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
README.md Strengthens Docker run guidance and adds image verification instructions.
Dockerfile Removes redundant build-stage chown while preserving runtime ownership setup.
CHANGELOG.md Records security hardening changes in the Unreleased section.
.github/workflows/release.yml Pins actions by SHA, includes SBOM in signed checksums, and recursively signs images.
.github/workflows/ci.yml Pins CI actions by SHA for supply-chain hardening.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread README.md
Comment on lines 118 to 126
docker run -d --name dockercmd \
-p 127.0.0.1:8470:8470 \
--group-add "$(getent group docker | cut -d: -f3)" \
--read-only --tmpfs /tmp \
--security-opt no-new-privileges \
--cap-drop ALL \
-v /var/run/docker.sock:/var/run/docker.sock \
-v dockercmd-data:/data \
ghcr.io/koduj-dev/docker-commander:latest
Comment thread README.md Outdated
Comment on lines +135 to +137
expose it unauthenticated. The `--group-add` line hands the non-root user the
host's `docker` group so it can read the socket; on **rootless / Docker
Desktop** (user-owned socket, no `docker` group) drop that line.
Comment thread README.md Outdated
The container image is signed and carries SLSA provenance + an SBOM as well:

```bash
IMAGE=ghcr.io/koduj-dev/docker-commander:1.4.0
…age verify

- README docker run derives the socket's owning GID via `stat` (GNU `-c %g`
  with a BSD `-f %g` fallback) instead of `getent group docker`, so it works
  without a `docker` group; updated the accompanying note to match.
- The image verification example now uses an immutable `@sha256:<digest>`
  reference, consistent with the digest-pinning guidance just above it.
@malickyeu
malickyeu merged commit f9f0896 into main Jun 15, 2026
3 checks passed
@malickyeu
malickyeu deleted the hardening/supply-chain-and-container branch June 15, 2026 15:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants