Skip to content

Commit 9db9c71

Browse files
authored
Improve security context handling (#3482)
1 parent 03c891e commit 9db9c71

File tree

2 files changed

+96
-26
lines changed

2 files changed

+96
-26
lines changed

pipeline/backend/kubernetes/pod.go

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ func podSpec(step *types.Step, config *config, options BackendOptions) (v1.PodSp
122122
HostAliases: hostAliases(step.ExtraHosts),
123123
NodeSelector: nodeSelector(options.NodeSelector, step.Environment["CI_SYSTEM_PLATFORM"]),
124124
Tolerations: tolerations(options.Tolerations),
125-
SecurityContext: podSecurityContext(options.SecurityContext, config.SecurityContext),
125+
SecurityContext: podSecurityContext(options.SecurityContext, config.SecurityContext, step.Privileged),
126126
}
127127
spec.Volumes, err = volumes(step.Volumes)
128128
if err != nil {
@@ -339,7 +339,7 @@ func toleration(backendToleration Toleration) v1.Toleration {
339339
}
340340
}
341341

342-
func podSecurityContext(sc *SecurityContext, secCtxConf SecurityContextConfig) *v1.PodSecurityContext {
342+
func podSecurityContext(sc *SecurityContext, secCtxConf SecurityContextConfig, stepPrivileged bool) *v1.PodSecurityContext {
343343
var (
344344
nonRoot *bool
345345
user *int64
@@ -348,21 +348,31 @@ func podSecurityContext(sc *SecurityContext, secCtxConf SecurityContextConfig) *
348348
seccomp *v1.SeccompProfile
349349
)
350350

351-
if sc != nil && sc.RunAsNonRoot != nil {
352-
if *sc.RunAsNonRoot {
353-
nonRoot = sc.RunAsNonRoot // true
354-
}
355-
} else if secCtxConf.RunAsNonRoot {
356-
nonRoot = &secCtxConf.RunAsNonRoot // true
351+
if secCtxConf.RunAsNonRoot {
352+
nonRoot = newBool(true)
357353
}
358354

359355
if sc != nil {
360-
user = sc.RunAsUser
361-
group = sc.RunAsGroup
362-
fsGroup = sc.FSGroup
363-
}
356+
// only allow to set user if its not root or step is privileged
357+
if sc.RunAsUser != nil && (*sc.RunAsUser != 0 || stepPrivileged) {
358+
user = sc.RunAsUser
359+
}
360+
361+
// only allow to set group if its not root or step is privileged
362+
if sc.RunAsGroup != nil && (*sc.RunAsGroup != 0 || stepPrivileged) {
363+
group = sc.RunAsGroup
364+
}
365+
366+
// only allow to set fsGroup if its not root or step is privileged
367+
if sc.FSGroup != nil && (*sc.FSGroup != 0 || stepPrivileged) {
368+
fsGroup = sc.FSGroup
369+
}
370+
371+
// only allow to set nonRoot if it's not set globally already
372+
if nonRoot == nil && sc.RunAsNonRoot != nil {
373+
nonRoot = sc.RunAsNonRoot
374+
}
364375

365-
if sc != nil {
366376
seccomp = seccompProfile(sc.SeccompProfile)
367377
}
368378

@@ -398,23 +408,19 @@ func seccompProfile(scp *SecProfile) *v1.SeccompProfile {
398408
}
399409

400410
func containerSecurityContext(sc *SecurityContext, stepPrivileged bool) *v1.SecurityContext {
401-
var privileged *bool
402-
403-
if sc != nil && sc.Privileged != nil && *sc.Privileged {
404-
privileged = sc.Privileged // true
405-
} else if stepPrivileged {
406-
privileged = &stepPrivileged // true
407-
}
408-
409-
if privileged == nil {
411+
if !stepPrivileged {
410412
return nil
411413
}
412414

413-
securityContext := &v1.SecurityContext{
414-
Privileged: privileged,
415+
if sc != nil && sc.Privileged != nil && *sc.Privileged {
416+
securityContext := &v1.SecurityContext{
417+
Privileged: newBool(true),
418+
}
419+
log.Trace().Msgf("container security context that will be used: %v", securityContext)
420+
return securityContext
415421
}
416-
log.Trace().Msgf("container security context that will be used: %v", securityContext)
417-
return securityContext
422+
423+
return nil
418424
}
419425

420426
func apparmorAnnotation(containerName string, scp *SecProfile) (*string, *string) {

pipeline/backend/kubernetes/pod_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020

2121
"github.com/kinbiko/jsonassert"
2222
"github.com/stretchr/testify/assert"
23+
v1 "k8s.io/api/core/v1"
2324

2425
"go.woodpecker-ci.org/woodpecker/v2/pipeline/backend/types"
2526
)
@@ -348,3 +349,66 @@ func TestFullPod(t *testing.T) {
348349
ja := jsonassert.New(t)
349350
ja.Assertf(string(podJSON), expected)
350351
}
352+
353+
func TestPodPrivilege(t *testing.T) {
354+
createTestPod := func(stepPrivileged, globalRunAsRoot bool, secCtx SecurityContext) (*v1.Pod, error) {
355+
return mkPod(&types.Step{
356+
Name: "go-test",
357+
Image: "golang:1.16",
358+
Privileged: stepPrivileged,
359+
}, &config{
360+
Namespace: "woodpecker",
361+
SecurityContext: SecurityContextConfig{RunAsNonRoot: globalRunAsRoot},
362+
}, "wp-01he8bebctabr3kgk0qj36d2me-0", "linux/amd64", BackendOptions{
363+
SecurityContext: &secCtx,
364+
})
365+
}
366+
367+
// securty context is requesting user and group 101 (non-root)
368+
secCtx := SecurityContext{
369+
RunAsUser: newInt64(101),
370+
RunAsGroup: newInt64(101),
371+
FSGroup: newInt64(101),
372+
}
373+
pod, err := createTestPod(false, false, secCtx)
374+
assert.NoError(t, err)
375+
assert.Equal(t, int64(101), *pod.Spec.SecurityContext.RunAsUser)
376+
assert.Equal(t, int64(101), *pod.Spec.SecurityContext.RunAsGroup)
377+
assert.Equal(t, int64(101), *pod.Spec.SecurityContext.FSGroup)
378+
379+
// securty context is requesting root, but step is not privileged
380+
secCtx = SecurityContext{
381+
RunAsUser: newInt64(0),
382+
RunAsGroup: newInt64(0),
383+
FSGroup: newInt64(0),
384+
}
385+
pod, err = createTestPod(false, false, secCtx)
386+
assert.NoError(t, err)
387+
assert.Nil(t, pod.Spec.SecurityContext)
388+
assert.Nil(t, pod.Spec.Containers[0].SecurityContext)
389+
390+
// step is not privileged, but security context is requesting privileged
391+
secCtx = SecurityContext{
392+
Privileged: newBool(true),
393+
}
394+
pod, err = createTestPod(false, false, secCtx)
395+
assert.NoError(t, err)
396+
assert.Nil(t, pod.Spec.SecurityContext)
397+
assert.Nil(t, pod.Spec.Containers[0].SecurityContext)
398+
399+
// step is privileged and security context is requesting privileged
400+
secCtx = SecurityContext{
401+
Privileged: newBool(true),
402+
}
403+
pod, err = createTestPod(true, false, secCtx)
404+
assert.NoError(t, err)
405+
assert.Equal(t, true, *pod.Spec.Containers[0].SecurityContext.Privileged)
406+
407+
// global runAsNonRoot is true and override is requested value by security context
408+
secCtx = SecurityContext{
409+
RunAsNonRoot: newBool(false),
410+
}
411+
pod, err = createTestPod(false, true, secCtx)
412+
assert.NoError(t, err)
413+
assert.Equal(t, true, *pod.Spec.SecurityContext.RunAsNonRoot)
414+
}

0 commit comments

Comments
 (0)