Skip to content

[security] Default PodSecurityContext sets RunAsGroup=0 and FSGroup=0 even with non-root UID — privilege seam on shared volumes #1054

Description

@CryptoJones

Finding

GetPodSecurityContext() in internal/controller/humiocluster_defaults.go (lines 727–737) returns a default PodSecurityContext that runs the container as non-root UID 65534, which is great — but pairs it with RunAsGroup: 0 and FSGroup: 0:

return &corev1.PodSecurityContext{
    RunAsUser:    helpers.Int64Ptr(65534),
    RunAsNonRoot: helpers.BoolPtr(true),
    RunAsGroup:   helpers.Int64Ptr(0), // TODO: We probably want to move away from this.
    FSGroup:      helpers.Int64Ptr(0), // TODO: We probably want to move away from this.
}

The two TODO comments next to each line indicate this is already on the team's radar — opening this issue mainly to give it a tracking surface and (hopefully) help prioritize.

Why it matters

  1. RunAsGroup: 0 means the process's primary group ID is root (GID 0). Even though the user is non-root, anything in the container that checks "is this process in the root group" (and there are surprising consumers of that — e.g., default-group ownership of files written by the process, some capability-aware tools) treats this process as privileged for group-based decisions. Combined with bind-mounted host paths or shared persistent volumes, files written by the Humio pod end up 0:root group-owned.

  2. FSGroup: 0 is the bigger one. The kubelet applies FSGroup as a recursive chgrp (and bit-or of 02770 on the mode) on all writable volumes mounted into the pod. FSGroup: 0 makes every volume mount group-owned by root and group-writable, which:

    • Breaks the principle of least privilege for the pod itself
    • Opens a lateral path when volumes are shared across pods (HostPath, ReadWriteMany PVs): another pod in the cluster whose process happens to run as GID 0 (very common — many images default to root group) can read or modify Humio's on-disk state without any RBAC permission, because the access decision happens at the kernel level on the volume, not the API server.

For an operator that manages a log-store cluster (Kafka logs, cluster state, potentially crashdumps), an unauthenticated lateral read of those files is a real exposure if a sibling workload is ever compromised.

Suggested fix

The cleanest path, in increasing order of disruption:

  1. Pick a non-zero GID for both fields. UID 65534's matching group on Debian/Alpine is nogroup = GID 65534. Setting RunAsGroup: 65534 and FSGroup: 65534 keeps the same identity, just on a non-privileged group:

    RunAsGroup: helpers.Int64Ptr(65534),
    FSGroup:    helpers.Int64Ptr(65534),

    The Humio container image needs to be confirmed to be group-65534-writable on its data directories — if it isn't today, the image's existing Dockerfile lines would need a chgrp -R 65534 /data (or equivalent).

  2. If 65534 is wrong for the image, pick whatever GID the image's data directories are actually owned by, and document it as a hard constraint of the operator's contract with the Humio image.

  3. Open up a CR-level override. The existing humioNodeSpec.PodSecurityContext is honored on the next line, so cluster operators can already specify their own — but the default is what most deployments will live with. Defaults should be safe.

Scope

  • Only affects the default branch (hnp.humioNodeSpec.PodSecurityContext == nil). Anyone who already specifies a podSecurityContext in their HumioCluster CR is unaffected.
  • Should pair with a clear upgrade note in the release that lands this — existing clusters will get a fresh chgrp recursive walk on their data volumes on the first rolling restart after the change, which can take a while on large clusters.

Happy to send a PR if useful — would want to confirm the image's expected GID with someone from the Humio side first.

Proudly Made in Nebraska. Go Big Red! 🌽 https://xkcd.com/2347/

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions