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
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"
) )
@@ -50,13 +51,14 @@ const (
) )
type SecurityContext struct { type SecurityContext struct {
Privileged *bool `mapstructure:"privileged"` Privileged *bool `mapstructure:"privileged"`
RunAsNonRoot *bool `mapstructure:"runAsNonRoot"` RunAsNonRoot *bool `mapstructure:"runAsNonRoot"`
RunAsUser *int64 `mapstructure:"runAsUser"` RunAsUser *int64 `mapstructure:"runAsUser"`
RunAsGroup *int64 `mapstructure:"runAsGroup"` RunAsGroup *int64 `mapstructure:"runAsGroup"`
FSGroup *int64 `mapstructure:"fsGroup"` FSGroup *int64 `mapstructure:"fsGroup"`
SeccompProfile *SecProfile `mapstructure:"seccompProfile"` FsGroupChangePolicy *v1.PodFSGroupChangePolicy `mapstructure:"fsGroupChangePolicy"`
ApparmorProfile *SecProfile `mapstructure:"apparmorProfile"` SeccompProfile *SecProfile `mapstructure:"seccompProfile"`
ApparmorProfile *SecProfile `mapstructure:"apparmorProfile"`
} }
type SecProfile struct { type SecProfile struct {

View File

@@ -462,12 +462,13 @@ func toleration(backendToleration Toleration) v1.Toleration {
func podSecurityContext(sc *SecurityContext, secCtxConf SecurityContextConfig, stepPrivileged bool) *v1.PodSecurityContext { func podSecurityContext(sc *SecurityContext, secCtxConf SecurityContextConfig, stepPrivileged bool) *v1.PodSecurityContext {
var ( var (
nonRoot *bool nonRoot *bool
user *int64 user *int64
group *int64 group *int64
fsGroup *int64 fsGroup *int64
seccomp *v1.SeccompProfile fsGroupChangePolicy *v1.PodFSGroupChangePolicy
apparmor *v1.AppArmorProfile seccomp *v1.SeccompProfile
apparmor *v1.AppArmorProfile
) )
if secCtxConf.RunAsNonRoot { if secCtxConf.RunAsNonRoot {
@@ -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 {
@@ -512,12 +514,13 @@ func podSecurityContext(sc *SecurityContext, secCtxConf SecurityContextConfig, s
} }
securityContext := &v1.PodSecurityContext{ securityContext := &v1.PodSecurityContext{
RunAsNonRoot: nonRoot, RunAsNonRoot: nonRoot,
RunAsUser: user, RunAsUser: user,
RunAsGroup: group, RunAsGroup: group,
FSGroup: fsGroup, FSGroup: fsGroup,
SeccompProfile: seccomp, FSGroupChangePolicy: fsGroupChangePolicy,
AppArmorProfile: apparmor, SeccompProfile: seccomp,
AppArmorProfile: apparmor,
} }
log.Trace().Msgf("pod security context that will be used: %v", securityContext) log.Trace().Msgf("pod security context that will be used: %v", securityContext)
return securityContext return securityContext

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",