Add fsGroupChangePolicy option to Kubernetes backend (#5416)

Co-authored-by: Lilly Sell <sell@b1-systems.de>
This commit is contained in:
Robert Kaussow 2025-08-15 10:28:38 +02:00 committed by GitHub
parent 8912f8989c
commit dc7795e64b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 41 additions and 24 deletions

View file

@ -250,6 +250,15 @@ backend_options:
localhostProfile: k8s-apparmor-example-deny-write localhostProfile: k8s-apparmor-example-deny-write
``` ```
or configure a specific `fsGroupChangePolicy` (Kubernetes defaults to 'Always')
```yaml
backend_options:
kubernetes:
securityContext:
fsGroupChangePolicy: OnRootMismatch
```
:::note :::note
The feature requires Kubernetes v1.30 or above. The feature requires Kubernetes v1.30 or above.
::: :::

View file

@ -2,6 +2,7 @@ package kubernetes
import ( import (
"github.com/go-viper/mapstructure/v2" "github.com/go-viper/mapstructure/v2"
v1 "k8s.io/api/core/v1"
backend "go.woodpecker-ci.org/woodpecker/v3/pipeline/backend/types" backend "go.woodpecker-ci.org/woodpecker/v3/pipeline/backend/types"
) )
@ -55,6 +56,7 @@ type SecurityContext struct {
RunAsUser *int64 `mapstructure:"runAsUser"` RunAsUser *int64 `mapstructure:"runAsUser"`
RunAsGroup *int64 `mapstructure:"runAsGroup"` RunAsGroup *int64 `mapstructure:"runAsGroup"`
FSGroup *int64 `mapstructure:"fsGroup"` FSGroup *int64 `mapstructure:"fsGroup"`
FsGroupChangePolicy *v1.PodFSGroupChangePolicy `mapstructure:"fsGroupChangePolicy"`
SeccompProfile *SecProfile `mapstructure:"seccompProfile"` SeccompProfile *SecProfile `mapstructure:"seccompProfile"`
ApparmorProfile *SecProfile `mapstructure:"apparmorProfile"` ApparmorProfile *SecProfile `mapstructure:"apparmorProfile"`
} }

View file

@ -466,6 +466,7 @@ func podSecurityContext(sc *SecurityContext, secCtxConf SecurityContextConfig, s
user *int64 user *int64
group *int64 group *int64
fsGroup *int64 fsGroup *int64
fsGroupChangePolicy *v1.PodFSGroupChangePolicy
seccomp *v1.SeccompProfile seccomp *v1.SeccompProfile
apparmor *v1.AppArmorProfile apparmor *v1.AppArmorProfile
) )
@ -505,6 +506,7 @@ func podSecurityContext(sc *SecurityContext, secCtxConf SecurityContextConfig, s
seccomp = seccompProfile(sc.SeccompProfile) seccomp = seccompProfile(sc.SeccompProfile)
apparmor = apparmorProfile(sc.ApparmorProfile) apparmor = apparmorProfile(sc.ApparmorProfile)
fsGroupChangePolicy = sc.FsGroupChangePolicy
} }
if nonRoot == nil && user == nil && group == nil && fsGroup == nil && seccomp == nil && apparmor == nil { if nonRoot == nil && user == nil && group == nil && fsGroup == nil && seccomp == nil && apparmor == nil {
@ -516,6 +518,7 @@ func podSecurityContext(sc *SecurityContext, secCtxConf SecurityContextConfig, s
RunAsUser: user, RunAsUser: user,
RunAsGroup: group, RunAsGroup: group,
FSGroup: fsGroup, FSGroup: fsGroup,
FSGroupChangePolicy: fsGroupChangePolicy,
SeccompProfile: seccomp, SeccompProfile: seccomp,
AppArmorProfile: apparmor, AppArmorProfile: apparmor,
} }

View file

@ -293,6 +293,7 @@ func TestFullPod(t *testing.T) {
"runAsGroup": 101, "runAsGroup": 101,
"runAsNonRoot": true, "runAsNonRoot": true,
"fsGroup": 101, "fsGroup": 101,
"fsGroupChangePolicy": "OnRootMismatch",
"appArmorProfile": { "appArmorProfile": {
"type": "Localhost", "type": "Localhost",
"localhostProfile": "k8s-apparmor-example-deny-write" "localhostProfile": "k8s-apparmor-example-deny-write"
@ -348,12 +349,14 @@ func TestFullPod(t *testing.T) {
{Number: 2345, Protocol: "tcp"}, {Number: 2345, Protocol: "tcp"},
{Number: 3456, Protocol: "udp"}, {Number: 3456, Protocol: "udp"},
} }
fsGroupChangePolicy := v1.PodFSGroupChangePolicy("OnRootMismatch")
secCtx := SecurityContext{ secCtx := SecurityContext{
Privileged: newBool(true), Privileged: newBool(true),
RunAsNonRoot: newBool(true), RunAsNonRoot: newBool(true),
RunAsUser: newInt64(101), RunAsUser: newInt64(101),
RunAsGroup: newInt64(101), RunAsGroup: newInt64(101),
FSGroup: newInt64(101), FSGroup: newInt64(101),
FsGroupChangePolicy: &fsGroupChangePolicy,
SeccompProfile: &SecProfile{ SeccompProfile: &SecProfile{
Type: "Localhost", Type: "Localhost",
LocalhostProfile: "profiles/audit.json", LocalhostProfile: "profiles/audit.json",