diff --git a/keycloak/templates/_helpers.tpl b/keycloak/templates/_helpers.tpl index c547f68..f7daa01 100644 --- a/keycloak/templates/_helpers.tpl +++ b/keycloak/templates/_helpers.tpl @@ -353,7 +353,7 @@ chart's defaults, not the repo standard - see the uid/gid note. {{- $defaults := dict "runAsNonRoot" true "runAsUser" 1000 - "runAsGroup" 0 + "runAsGroup" 1000 "fsGroup" 1000 "fsGroupChangePolicy" "Always" "seccompProfile" (dict "type" "RuntimeDefault") -}} diff --git a/keycloak/values.schema.json b/keycloak/values.schema.json index d758528..7ab8e86 100644 --- a/keycloak/values.schema.json +++ b/keycloak/values.schema.json @@ -248,7 +248,7 @@ } }, "securityContext": { - "description": "May be null / empty at any level: the chart then falls back to its hardening defaults (issue #99). This chart's fallback reproduces ITS defaults, which deviate from the repo's usual 20000/1000 pair - the published image declares keycloak:x:1000:0 in /etc/passwd, puts that user in the ROOT GROUP and copies the whole install tree with --chown=1000:0, so uid 1000 / gid 0 is what the image expects and what makes the mounted build volume writable. Every container in the pod, both init containers included, uses the same container context: readOnlyRootFilesystem: true holds throughout, which is the reason the chart has two init containers rather than one writable one.", + "description": "May be null / empty at any level: the chart then falls back to its hardening defaults (issue #99). This chart's fallback reproduces ITS defaults, which deviate from the repo's usual 20000/1000 pair: the published image declares keycloak:x:1000:0 in /etc/passwd and copies the whole install tree with --chown=1000:0, so uid 1000 is the OWNER of everything the process reads. The GROUP is 1000 and not the image's own 0 - counted across the image layer, 521 of 522 entries are owned by 1000:0 with every mode granting the owner full access, so group 0 (upstream's OpenShift arbitrary-uid pattern) buys nothing once the owning uid is pinned, and leaving it out keeps a root GID out of the pod. Every container in the pod, both init containers included, uses the same container context: readOnlyRootFilesystem: true holds throughout, which is the reason the chart has two init containers rather than one writable one.", "type": ["object", "null"], "additionalProperties": false, "properties": { diff --git a/keycloak/values.yaml b/keycloak/values.yaml index 6df0a18..afdf5ae 100644 --- a/keycloak/values.yaml +++ b/keycloak/values.yaml @@ -418,18 +418,33 @@ keycloak: securityContext: pod: runAsNonRoot: true - # 1000 AND GROUP 0, which is not this repo's 20000/1000 default and not a - # free choice: the published image declares "keycloak:x:1000:0" in - # /etc/passwd, adds that user to the ROOT GROUP in /etc/group, and copies - # the whole install tree with --chown=1000:0. Everything under - # /opt/keycloak is therefore owned by 1000:0 and group-writable, and the - # emptyDir mounted over /opt/keycloak/lib/quarkus has to be writable by - # the same pair or the build writes nothing. + # uid 1000, NOT this repo's 20000, and that is the image's requirement + # rather than a free choice: it declares "keycloak:x:1000:0" in + # /etc/passwd and copies the whole install tree with --chown=1000:0, so + # 1000 is the OWNER of everything the process reads and executes. Read + # off the image rather than guessed - `USER 1000` and the chown are in + # the config blob's history of quay.io/keycloak/keycloak:26.8.0. # - # Read off the image rather than guessed: `USER 1000` and the chown are - # in the config blob's history of quay.io/keycloak/keycloak:26.8.0. + # GROUP 1000 AND NOT 0, WHICH IS WHERE THIS CHART DIVERGES FROM THE + # IMAGE'S OWN CONVENTION - deliberately, and measured before deciding. + # Upstream puts the keycloak user in the ROOT GROUP and makes the install + # tree group-writable (mode 775/664, owner 1000, group 0). That is the + # OpenShift arbitrary-uid pattern: it lets ANY uid that happens to be in + # group 0 write there. We do not need it, because we pin the uid that + # OWNS the files - counted across the image layer, 521 of 522 entries are + # owned by 1000:0 and every mode grants the owner full access, so uid + # 1000 gets what it needs through the owner bits whatever its group is. + # The one remaining entry is /opt itself, which needs only traverse. + # + # What that buys: no root primary or supplementary GID in this pod, i.e. + # no AVD-KSV-0116 finding and no suppression for it. A suppression would + # have had to cover the whole chart, serving container included, to + # silence a check that is making a fair point. + # + # If this is ever changed back to 0, the reason has to be a path that + # uid 1000 does NOT own - and there is none in this image today. runAsUser: 1000 - runAsGroup: 0 + runAsGroup: 1000 fsGroup: 1000 fsGroupChangePolicy: Always seccompProfile: