Skip to content

fix: harden validation container volume mounts and support private sigstore instances - #190

Open
sampras343 wants to merge 4 commits into
sigstore:mainfrom
sampras343:fix/readonly-volume-mounts
Open

sampras343 wants to merge 4 commits into
sigstore:mainfrom
sampras343:fix/readonly-volume-mounts

Conversation

@sampras343

@sampras343 sampras343 commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

What

Hardens volume mounts on injected validation containers and fixes two bugs discovered when testing with readOnlyRootFilesystem against both public Sigstore and private RHTAS instances.

Changes

Volume mount restrictions (commits 1-2)

  1. ReadOnly enforcement — all copied volume mounts now have ReadOnly: true set, preventing the validation agent from writing to any application volume.
  2. Mount filtering — instead of copying every volume mount from every application container, only mounts whose mountPath is a prefix of a needed path are included. Needed paths are: model.Path, model.SignaturePath, config.PkiConfig.CertificateAuthority, config.PublicKeyConfig.KeyPath, and config.ClientTrustConfig.TrustConfigPath.

Bug fixes for readOnlyRootFilesystem (commit 3)

The security hardening in #178 set readOnlyRootFilesystem: true on injected validation containers, which broke sigstore verification:

  1. TUF cache write failure — the sigstore-go TUF client needs to write trust root metadata to /.sigstore. When sigstoreConfig is used, inject an emptyDir volume at /.sigstore to provide a writable cache without compromising the read-only root filesystem.
  2. Missing trust config mount — collectNeededPaths did not include clientTrustConfig.trustConfigPath, so filterVolumeMounts never copied the trust config volume into the init container. This broke verification against private sigstore instances (e.g. RHTAS with Keycloak).

Why

The webhook was copying all volume mounts from all application containers into the validation init container verbatim, exposing service account tokens, TLS keys, database credentials, and other secrets. The validation agent only needs to read model files, signatures, and trust configuration.

The TUF cache and trust config path bugs were caught during end-to-end testing with both public Sigstore and a private RHTAS instance on an OpenShift cluster with enforced pod security standards.

Testing

Verified on OpenShift 4.22 with:

  • Public Sigstore: model signed via GitHub Actions OIDC, verified against public Fulcio/Rekor
  • Private RHTAS: model signed with Keycloak OIDC token against private Fulcio/Rekor, verified using clientTrustConfig with the private instance's trust root

Both modes: init container completes successfully (exit 0), pod transitions to Running, ModelValidation CR shows correct injection status.

Fixes #191
Fixes #192
Fixes #193

The webhook copies all volume mounts from application containers into
the injected validation container. These were inherited with their
original read/write permissions, violating least-privilege — the
validation agent only reads model files and signatures.

Signed-off-by: Sachin Sampras M <sampras343@gmail.com>
Instead of copying all volume mounts from all app containers into the
validation container, only mount volumes whose mountPath is a prefix of
a path the agent actually needs (model path, signature path, CA cert,
public key). Prevents read access to unrelated secrets, tokens, and TLS
keys if the validation-agent image is compromised.

Signed-off-by: Sachin Sampras M <sampras343@gmail.com>
@sampras343
sampras343 force-pushed the fix/readonly-volume-mounts branch from 92dd437 to 4702126 Compare September 23, 2026 09:51
@sampras343 sampras343 changed the title fix: restrict validation container volume mounts to model-relevant paths fix: harden validation container volume mounts and support private sigstore instances Sep 25, 2026
…iners

The security hardening in 9a4ffeb set readOnlyRootFilesystem on injected
validation containers, which broke sigstore verification in two ways:

1. The sigstore-go TUF client needs to write to /.sigstore for trust root
   metadata. Inject an emptyDir volume at /.sigstore when sigstoreConfig
   is used.

2. collectNeededPaths did not include clientTrustConfig.trustConfigPath,
   so filterVolumeMounts never copied the trust config volume into the
   init container. Add it to the needed paths.

Signed-off-by: Sachin Sampras M <sampras343@gmail.com>
@sampras343
sampras343 force-pushed the fix/readonly-volume-mounts branch from 174c486 to a5ccb0c Compare September 25, 2026 10:57
1. Set a 10Mi sizeLimit on the sigstore-tuf-cache emptyDir volume to
   prevent a compromised agent from filling the node's ephemeral storage.

2. Reject mountPath "/" in filterVolumeMounts and require proper
   directory prefix matching (trailing slash). A container with
   mountPath "/" would previously match every neededPath, leaking
   access to the entire volume into the validation container.

Add unit tests for filterVolumeMounts covering prefix matching, root
path rejection, partial directory name rejection, exact path matching,
deduplication, and multi-path scenarios.

Signed-off-by: Sachin Sampras M <sampras343@gmail.com>
@sampras343
sampras343 force-pushed the fix/readonly-volume-mounts branch from 3eac0da to a0716bf Compare September 25, 2026 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant