Skip to content

Publish CI/release automation; extension-loaded minion images; run ev… - #3

Merged
saifuddinrangwala merged 2 commits into
mainfrom
saifuddin/non-root-images-and-publishing
Sep 8, 2026
Merged

Publish CI/release automation; extension-loaded minion images; run ev…#3
saifuddinrangwala merged 2 commits into
mainfrom
saifuddin/non-root-images-and-publishing

Conversation

@saifuddinrangwala

Copy link
Copy Markdown
Collaborator

…erything non-root

Build/publish (GHCR, per-component release tags, see docs/releasing.md):

  • CI + release workflows for salt-master, salt-minion-vcf, salt-minion-kubernetes images and all 3 charts (OCI push), gated on a matching CHANGELOG.md entry.
  • Per-component CHANGELOG.md recording exactly which Salt/extension versions each release tag carries (tags are independent semver, not Salt versions).

salt-minion-kubernetes becomes a full project (like salt-minion-vcf): a new self-contained Dockerfile preloaded with saltext.vault + saltext.kubernetes (pip-installed from PyPI), replacing the old docker/salt-minion, which turned out to depend on a saltext-kubernetes checkout as build context rather than building from this repo. salt-minion-vcf additionally gains saltext.bmc and saltext.kubernetes.

Non-root throughout: salt-master, salt-minion-vcf, and salt-minion-kubernetes now all run as the 'salt' system user (uid 999) instead of root or an invented user, verified with runAsNonRoot. Fixed along the way:

  • /var/run gets remounted root-owned by the container runtime on every pod start regardless of image layer chown, so salt-master's entrypoint now keeps pidfile/sock_dir under /var/cache/salt/master instead.
  • A volume mounted at /etc/salt/pki/* always comes back root-owned regardless of fsGroup, and this cluster's container runtime doesn't populate the ambient capability set for non-root containers (confirmed via live testing), so capabilities.add can't fix it either. Added a root initContainer (reuses the app's own image, chowns once) to all 3 charts; the long-running container is never root.
  • salt-minion-kubernetes's optional install-kubectl init container (curlimages/curl) needed its actual numeric uid/gid pinned explicitly for the same runAsNonRoot reason.

Also: master-key pre-seeding support (agent.masterKeySecretName) in salt-master-kubernetes, mirroring salt-minion-vcf's existing minion-key pattern. SALT_VERSION default corrected to 3008.2 (3008.3 doesn't exist in the Broadcom Salt apt repo, confirmed via a real build failure).

Tested end-to-end on a live Rancher cluster: built+pushed all 3 images, deployed all 3 charts, confirmed master<->minion test.ping for both minion variants, confirmed non-root identity and PKI persistence for all three.

Known gap (not fixed here): docs/kubernetes-compliance-guide.md's kube_bench_cache execution module doesn't exist anywhere - not in the public saltext.kubernetes package, not in any internal fork - so that compliance workflow was never actually functional.

Saifuddin Rangwala and others added 2 commits September 7, 2026 22:06
…erything non-root

Build/publish (GHCR, per-component release tags, see docs/releasing.md):
- CI + release workflows for salt-master, salt-minion-vcf, salt-minion-kubernetes
  images and all 3 charts (OCI push), gated on a matching CHANGELOG.md entry.
- Per-component CHANGELOG.md recording exactly which Salt/extension versions
  each release tag carries (tags are independent semver, not Salt versions).

salt-minion-kubernetes becomes a full project (like salt-minion-vcf): a new
self-contained Dockerfile preloaded with saltext.vault + saltext.kubernetes
(pip-installed from PyPI), replacing the old docker/salt-minion, which turned
out to depend on a saltext-kubernetes checkout as build context rather than
building from this repo. salt-minion-vcf additionally gains saltext.bmc and
saltext.kubernetes.

Non-root throughout: salt-master, salt-minion-vcf, and salt-minion-kubernetes
now all run as the 'salt' system user (uid 999) instead of root or an
invented user, verified with runAsNonRoot. Fixed along the way:
- /var/run gets remounted root-owned by the container runtime on every pod
  start regardless of image layer chown, so salt-master's entrypoint now
  keeps pidfile/sock_dir under /var/cache/salt/master instead.
- A volume mounted at /etc/salt/pki/* always comes back root-owned
  regardless of fsGroup, and this cluster's container runtime doesn't
  populate the ambient capability set for non-root containers (confirmed via
  live testing), so capabilities.add can't fix it either. Added a root
  initContainer (reuses the app's own image, chowns once) to all 3 charts;
  the long-running container is never root.
- salt-minion-kubernetes's optional install-kubectl init container
  (curlimages/curl) needed its actual numeric uid/gid pinned explicitly for
  the same runAsNonRoot reason.

Also: master-key pre-seeding support (agent.masterKeySecretName) in
salt-master-kubernetes, mirroring salt-minion-vcf's existing minion-key
pattern. SALT_VERSION default corrected to 3008.2 (3008.3 doesn't exist in
the Broadcom Salt apt repo, confirmed via a real build failure).

Tested end-to-end on a live Rancher cluster: built+pushed all 3 images,
deployed all 3 charts, confirmed master<->minion test.ping for both minion
variants, confirmed non-root identity and PKI persistence for all three.

Known gap (not fixed here): docs/kubernetes-compliance-guide.md's
kube_bench_cache execution module doesn't exist anywhere - not in the public
saltext.kubernetes package, not in any internal fork - so that compliance
workflow was never actually functional.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
hadolint (failure-threshold: info, so any finding fails the job):
- DL3008: ignore for OS-level packages (ca-certificates, curl, gnupg,
  tini, iproute2, dnsutils, netcat-openbsd) across all 4 Dockerfiles -
  these intentionally track the base image's own security-patch channel;
  only Salt's own packages are version-pinned via SALT_VERSION.
- DL3003 (salt-master): replace `(cd /tmp && sha256sum -c ...)` with a
  checksum file rewritten to reference the absolute path, avoiding the
  subshell cd entirely.
- DL4006: rewrite `curl | gpg --dearmor` pipelines (in salt-minion-vcf,
  its dev-master image, and salt-minion-kubernetes) and the kubectl
  `echo | sha256sum -c -` pipeline as separate steps through a temp file.
  This isn't just lint cosmetics: without pipefail, a failed curl on the
  left side of a pipe doesn't fail the RUN, so the image could silently
  build with an empty/corrupt keyring or an unverified binary.
- SC2174 (mkdir -m -p only applies -m to the deepest new directory):
  split into separate mkdir -p / chmod steps.
- DL3064: ignore for ENV blocks - these are empty placeholder defaults
  meant to be supplied at container start, not baked-in secrets.

shellcheck SC3028: both docker-entrypoint.sh scripts have a #!/bin/sh
shebang but referenced ${HOSTNAME}, which is a bash-only auto-variable
and undefined under POSIX sh (dash, the actual /bin/sh on Debian/
Ubuntu). Under `set -u` this would abort the script instead of falling
back to the hostname as intended. Replaced with `$(hostname)`, which
works under any POSIX shell.

Verified: hadolint and shellcheck now pass on all 4 Dockerfiles/2
scripts locally; helm lint unaffected; checksum/gpg logic re-tested
end-to-end against the real download URLs.

@prawintiru prawintiru left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM

@saifuddinrangwala
saifuddinrangwala merged commit aca966f into main Sep 8, 2026
4 checks passed
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