Skip to content

RavenDB-27110: harden RavenDB pod security context, fix PVC permissions#40

Open
poissoncorp wants to merge 1 commit into
mainfrom
RavenDB-27110-pod-security
Open

RavenDB-27110: harden RavenDB pod security context, fix PVC permissions#40
poissoncorp wants to merge 1 commit into
mainfrom
RavenDB-27110-pod-security

Conversation

@poissoncorp

Copy link
Copy Markdown
Contributor

Summary

The operator ran RavenDB containers as uid/gid 999 (the image's USER ravendb) but never told Kubernetes to prepare mounted volumes for that identity. On storage providers that mount freshly provisioned PVCs root-owned (e.g. Longhorn), RavenDB fails at startup with:

Could not access some of the specified paths.
Key: 'DataDir' Path: '/var/lib/ravendb/data' Error: 'Access ... is denied.'

Reported twice, independently: #37 (Kyverno fsGroup workaround) and #39 (fsGroup patch). This addresses the root instead of the symptom, and hardens the pods while we are in there.

What changed

  • Single source of truth for the runtime identity. pkg/common.RavenDBUID/RavenDBGID = 999, mirroring the image USER. No more bare 999 copied across files.
  • Shared, hardened security-context builders (pkg/resource/security.go), compatible with the PodSecurity restricted profile:
    • pod: fsGroup: 999 (+ fsGroupChangePolicy: OnRootMismatch), runAsNonRoot, seccompProfile: RuntimeDefault
    • container: runAsUser/runAsGroup 999, runAsNonRoot, allowPrivilegeEscalation: false, drop ALL capabilities, seccompProfile: RuntimeDefault
  • Applied to both workloads. StatefulSet pod/container and the bootstrapper Job pod/container. The Job container previously had no security context at all.
  • Dropping ALL caps is safe for port 443: the pod keeps the safe sysctl net.ipv4.ip_unprivileged_port_start=0, so NET_BIND_SERVICE is not needed. The StatefulSet keeps the sysctl; the Job does not (it binds no low ports).

Why fsGroup (root cause)

The image ships /var/lib/ravendb/data as root:ravendb 0770, so gid 999 can write. A PVC mount masks that with the volume root (often root:root), destroying the group-writable property. fsGroup: 999 restores it. It cannot be inferred from the image, so the value is restated in code (see the CI guard below).

Tests & guards

  • Rendered-spec unit tests assert the context is wired through BuildStatefulSet and BuildJob (not the helpers in isolation), including that pod fsGroup matches the container identity. CI now runs ./pkg/... too.
  • Image-drift guard: hack/verify-image-uid.sh + .github/workflows/image-uid-guard.yml assert the upstream image still uses uid/gid 999, so a UID change fails in CI, not on a user's cluster.

Not verified in CI here

Live PodSecurity restricted admission and the original root-owned-PVC failure need a specific storage backend; the original bug was validated on Longhorn (#39). The e2e suite still exercises that pods start.

Docs

SECURITY.md (posture + supported-version bump), operator chart README "Pod security" section, CLAUDE.md convention note.

Closes #37.
Supersedes #39. Credit to @jkossis, whose #39 seeded the fsGroup fix (co-authored on the commit).

The operator ran containers as uid/gid 999 (the RavenDB image's USER) but
never told Kubernetes to prepare mounted volumes for that identity. On storage
providers that mount freshly provisioned PVCs root-owned (e.g. Longhorn),
RavenDB failed at startup with a DataDir permission error. Reported twice
independently: issue #37 (Kyverno workaround) and PR #39 (fsGroup patch).

Address the root instead of the symptom:

- pkg/common: single source of truth for the runtime identity
  (RavenDBUID/RavenDBGID = 999), mirroring the image USER. No more bare 999.
- pkg/resource/security.go: shared container and pod security-context builders,
  compatible with the PodSecurity "restricted" profile (runAsNonRoot, fsGroup
  with OnRootMismatch, allowPrivilegeEscalation false, drop ALL capabilities,
  seccompProfile RuntimeDefault). Dropping ALL is safe because the low-port
  sysctl makes 443 unprivileged.
- Apply to both the StatefulSet pod/container and the bootstrapper Job
  pod/container. The Job container previously had no security context at all.
- Rendered-spec unit tests assert the context is wired through BuildStatefulSet
  and BuildJob, not just the helpers in isolation. CI now runs ./pkg/... too.
- hack/verify-image-uid.sh + image-uid-guard.yml: CI guard asserting the
  upstream image still uses uid/gid 999, so drift fails in CI, not on a cluster.
- Docs: SECURITY.md posture + supported version bump, operator chart README
  "Pod security" section, CLAUDE.md convention note.

Closes #37. Supersedes #39.

Co-authored-by: Jason Kossis <jkossis@gmail.com>
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.

File permission issues

1 participant