Skip to content

Commit 3d4c343

Browse files
committed
SecurityContextConstraints: pass effective runAsNonRoot and runAsUser to user validation interface.
1 parent b5a8497 commit 3d4c343

10 files changed

+72
-65
lines changed

pkg/security/securitycontextconstraints/provider.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ func (s *simpleProvider) ValidateContainerSecurityContext(pod *api.Pod, containe
309309
}
310310

311311
sc := container.SecurityContext
312-
allErrs = append(allErrs, s.runAsUserStrategy.Validate(pod, container)...)
312+
allErrs = append(allErrs, s.runAsUserStrategy.Validate(fldPath.Child("securityContext"), pod, container, sc.RunAsNonRoot, sc.RunAsUser)...)
313313
allErrs = append(allErrs, s.seLinuxStrategy.Validate(fldPath.Child("seLinuxOptions"), pod, container, sc.SELinuxOptions)...)
314314
allErrs = append(allErrs, s.seccompStrategy.ValidateContainer(pod, container)...)
315315

pkg/security/securitycontextconstraints/provider_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ func TestValidateContainerSecurityContextFailures(t *testing.T) {
416416
"failUserSCC": {
417417
pod: failUserPod,
418418
scc: failUserSCC,
419-
expectedError: "does not match required UID",
419+
expectedError: "runAsUser: Invalid value",
420420
},
421421
"failSELinuxSCC": {
422422
pod: failSELinuxPod,

pkg/security/securitycontextconstraints/user/mustrunas.go

+7-13
Original file line numberDiff line numberDiff line change
@@ -35,24 +35,18 @@ func (s *mustRunAs) Generate(pod *api.Pod, container *api.Container) (*int64, er
3535
}
3636

3737
// Validate ensures that the specified values fall within the range of the strategy.
38-
func (s *mustRunAs) Validate(pod *api.Pod, container *api.Container) field.ErrorList {
38+
func (s *mustRunAs) Validate(fldPath *field.Path, _ *api.Pod, _ *api.Container, runAsNonRoot *bool, runAsUser *int64) field.ErrorList {
3939
allErrs := field.ErrorList{}
4040

41-
securityContextPath := field.NewPath("securityContext")
42-
if container.SecurityContext == nil {
43-
detail := fmt.Sprintf("unable to validate nil security context for container %s", container.Name)
44-
allErrs = append(allErrs, field.Invalid(securityContextPath, container.SecurityContext, detail))
45-
return allErrs
46-
}
47-
if container.SecurityContext.RunAsUser == nil {
48-
detail := fmt.Sprintf("unable to validate nil runAsUser for container %s", container.Name)
49-
allErrs = append(allErrs, field.Invalid(securityContextPath.Child("runAsUser"), container.SecurityContext.RunAsUser, detail))
41+
if runAsUser == nil {
42+
allErrs = append(allErrs, field.Required(fldPath.Child("runAsUser"), ""))
5043
return allErrs
5144
}
5245

53-
if *s.opts.UID != *container.SecurityContext.RunAsUser {
54-
detail := fmt.Sprintf("UID on container %s does not match required UID. Found %d, wanted %d", container.Name, *container.SecurityContext.RunAsUser, *s.opts.UID)
55-
allErrs = append(allErrs, field.Invalid(securityContextPath.Child("runAsUser"), *container.SecurityContext.RunAsUser, detail))
46+
if *s.opts.UID != *runAsUser {
47+
detail := fmt.Sprintf("must be: %v", *s.opts.UID)
48+
allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsUser"), *runAsUser, detail))
49+
return allErrs
5650
}
5751

5852
return allErrs

pkg/security/securitycontextconstraints/user/mustrunas_test.go

+14-7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package user
22

33
import (
4+
"fmt"
5+
"strings"
46
"testing"
57

68
api "k8s.io/kubernetes/pkg/apis/core"
@@ -58,19 +60,24 @@ func TestMustRunAsValidate(t *testing.T) {
5860
if err != nil {
5961
t.Fatalf("unexpected error initializing NewMustRunAs %v", err)
6062
}
61-
container := &api.Container{
62-
SecurityContext: &api.SecurityContext{
63-
RunAsUser: &badUID,
64-
},
63+
64+
errs := mustRunAs.Validate(nil, nil, nil, nil, nil)
65+
expectedMessage := "runAsUser: Required value"
66+
if len(errs) == 0 {
67+
t.Errorf("expected errors from nil runAsUser but got none")
68+
} else if !strings.Contains(errs[0].Error(), expectedMessage) {
69+
t.Errorf("expected error to contain %q but it did not: %v", expectedMessage, errs)
6570
}
6671

67-
errs := mustRunAs.Validate(nil, container)
72+
errs = mustRunAs.Validate(nil, nil, nil, nil, &badUID)
73+
expectedMessage = fmt.Sprintf("runAsUser: Invalid value: %d: must be: %d", badUID, uid)
6874
if len(errs) == 0 {
6975
t.Errorf("expected errors from mismatch uid but got none")
76+
} else if !strings.Contains(errs[0].Error(), expectedMessage) {
77+
t.Errorf("expected error to contain %q but it did not: %v", expectedMessage, errs)
7078
}
7179

72-
container.SecurityContext.RunAsUser = &uid
73-
errs = mustRunAs.Validate(nil, container)
80+
errs = mustRunAs.Validate(nil, nil, nil, nil, &uid)
7481
if len(errs) != 0 {
7582
t.Errorf("expected no errors from matching uid but got %v", errs)
7683
}

pkg/security/securitycontextconstraints/user/mustrunasrange.go

+11-19
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ import (
99
securityapi "github.com/openshift/origin/pkg/security/apis/security"
1010
)
1111

12-
// mustRunAs implements the RunAsUserSecurityContextConstraintsStrategy interface
12+
// mustRunAsRange implements the RunAsUserSecurityContextConstraintsStrategy interface
1313
type mustRunAsRange struct {
1414
opts *securityapi.RunAsUserStrategyOptions
1515
}
1616

17-
// NewMustRunAs provides a strategy that requires the container to run as a specific UID in a range.
17+
var _ RunAsUserSecurityContextConstraintsStrategy = &mustRunAsRange{}
18+
19+
// NewMustRunAsRange provides a strategy that requires the container to run as a specific UID in a range.
1820
func NewMustRunAsRange(options *securityapi.RunAsUserStrategyOptions) (RunAsUserSecurityContextConstraintsStrategy, error) {
1921
if options == nil {
2022
return nil, fmt.Errorf("MustRunAsRange requires run as user options")
@@ -36,28 +38,18 @@ func (s *mustRunAsRange) Generate(pod *api.Pod, container *api.Container) (*int6
3638
}
3739

3840
// Validate ensures that the specified values fall within the range of the strategy.
39-
func (s *mustRunAsRange) Validate(pod *api.Pod, container *api.Container) field.ErrorList {
41+
func (s *mustRunAsRange) Validate(fldPath *field.Path, _ *api.Pod, _ *api.Container, runAsNonRoot *bool, runAsUser *int64) field.ErrorList {
4042
allErrs := field.ErrorList{}
4143

42-
securityContextPath := field.NewPath("securityContext")
43-
if container.SecurityContext == nil {
44-
detail := fmt.Sprintf("unable to validate nil security context for container %s", container.Name)
45-
allErrs = append(allErrs, field.Invalid(securityContextPath, container.SecurityContext, detail))
46-
return allErrs
47-
}
48-
if container.SecurityContext.RunAsUser == nil {
49-
detail := fmt.Sprintf("unable to validate nil RunAsUser for container %s", container.Name)
50-
allErrs = append(allErrs, field.Invalid(securityContextPath.Child("runAsUser"), container.SecurityContext.RunAsUser, detail))
44+
if runAsUser == nil {
45+
allErrs = append(allErrs, field.Required(fldPath.Child("runAsUser"), ""))
5146
return allErrs
5247
}
5348

54-
if *container.SecurityContext.RunAsUser < *s.opts.UIDRangeMin || *container.SecurityContext.RunAsUser > *s.opts.UIDRangeMax {
55-
detail := fmt.Sprintf("UID on container %s does not match required range. Found %d, required min: %d max: %d",
56-
container.Name,
57-
*container.SecurityContext.RunAsUser,
58-
*s.opts.UIDRangeMin,
59-
*s.opts.UIDRangeMax)
60-
allErrs = append(allErrs, field.Invalid(securityContextPath.Child("runAsUser"), *container.SecurityContext.RunAsUser, detail))
49+
if *runAsUser < *s.opts.UIDRangeMin || *runAsUser > *s.opts.UIDRangeMax {
50+
detail := fmt.Sprintf("must be in the ranges: [%v, %v]", *s.opts.UIDRangeMin, *s.opts.UIDRangeMax)
51+
allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsUser"), *runAsUser, detail))
52+
return allErrs
6153
}
6254

6355
return allErrs

pkg/security/securitycontextconstraints/user/nonroot.go

+9-10
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package user
22

33
import (
4-
"fmt"
5-
64
"k8s.io/apimachinery/pkg/util/validation/field"
75
api "k8s.io/kubernetes/pkg/apis/core"
86

@@ -27,17 +25,18 @@ func (s *nonRoot) Generate(pod *api.Pod, container *api.Container) (*int64, erro
2725
// of this will pass if either the UID is not set, assuming that the image will provided the UID
2826
// or if the UID is set it is not root. In order to work properly this assumes that the kubelet
2927
// will populate an
30-
func (s *nonRoot) Validate(pod *api.Pod, container *api.Container) field.ErrorList {
28+
func (s *nonRoot) Validate(fldPath *field.Path, _ *api.Pod, _ *api.Container, runAsNonRoot *bool, runAsUser *int64) field.ErrorList {
3129
allErrs := field.ErrorList{}
32-
securityContextPath := field.NewPath("securityContext")
33-
if container.SecurityContext == nil {
34-
detail := fmt.Sprintf("unable to validate nil security context for container %s", container.Name)
35-
allErrs = append(allErrs, field.Invalid(securityContextPath, container.SecurityContext, detail))
30+
if runAsNonRoot == nil && runAsUser == nil {
31+
allErrs = append(allErrs, field.Required(fldPath.Child("runAsNonRoot"), "must be true"))
32+
return allErrs
33+
}
34+
if runAsNonRoot != nil && *runAsNonRoot == false {
35+
allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsNonRoot"), *runAsNonRoot, "must be true"))
3636
return allErrs
3737
}
38-
if container.SecurityContext.RunAsUser != nil && *container.SecurityContext.RunAsUser == 0 {
39-
detail := fmt.Sprintf("running with the root UID is forbidden by the security context constraints %s", container.Name)
40-
allErrs = append(allErrs, field.Invalid(securityContextPath.Child("runAsUser"), *container.SecurityContext.RunAsUser, detail))
38+
if runAsUser != nil && *runAsUser == 0 {
39+
allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsUser"), *runAsUser, "running with the root UID is forbidden"))
4140
return allErrs
4241
}
4342
return allErrs

pkg/security/securitycontextconstraints/user/nonroot_test.go

+26-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package user
22

33
import (
4+
"strings"
45
"testing"
56

67
api "k8s.io/kubernetes/pkg/apis/core"
@@ -38,27 +39,41 @@ func TestNonRootValidate(t *testing.T) {
3839
var badUID int64 = 0
3940
s, err := NewRunAsNonRoot(&securityapi.RunAsUserStrategyOptions{})
4041
if err != nil {
41-
t.Fatalf("unexpected error initializing NewMustRunAs %v", err)
42-
}
43-
container := &api.Container{
44-
SecurityContext: &api.SecurityContext{
45-
RunAsUser: &badUID,
46-
},
42+
t.Fatalf("unexpected error initializing NewRunAsNonRoot %v", err)
4743
}
4844

49-
errs := s.Validate(nil, container)
45+
errs := s.Validate(nil, nil, nil, nil, &badUID)
46+
expectedMessage := "runAsUser: Invalid value: 0: running with the root UID is forbidden"
5047
if len(errs) == 0 {
5148
t.Errorf("expected errors from root uid but got none")
49+
} else if !strings.Contains(errs[0].Error(), expectedMessage) {
50+
t.Errorf("expected error to contain %q but it did not: %v", expectedMessage, errs)
51+
}
52+
53+
errs = s.Validate(nil, nil, nil, nil, nil)
54+
expectedMessage = "runAsNonRoot: Required value: must be true"
55+
if len(errs) == 0 {
56+
t.Errorf("expected error when neither runAsUser nor runAsNonRoot are specified but got none")
57+
} else if !strings.Contains(errs[0].Error(), expectedMessage) {
58+
t.Errorf("expected error to contain %q but it did not: %v", expectedMessage, errs)
59+
}
60+
61+
no := false
62+
errs = s.Validate(nil, nil, nil, &no, nil)
63+
expectedMessage = "runAsNonRoot: Invalid value: false: must be true"
64+
if len(errs) == 0 {
65+
t.Errorf("expected error when runAsNonRoot is false but got none")
66+
} else if !strings.Contains(errs[0].Error(), expectedMessage) {
67+
t.Errorf("expected error to contain %q but it did not: %v", expectedMessage, errs)
5268
}
5369

54-
container.SecurityContext.RunAsUser = &uid
55-
errs = s.Validate(nil, container)
70+
errs = s.Validate(nil, nil, nil, nil, &uid)
5671
if len(errs) != 0 {
5772
t.Errorf("expected no errors from non-root uid but got %v", errs)
5873
}
5974

60-
container.SecurityContext.RunAsUser = nil
61-
errs = s.Validate(nil, container)
75+
yes := true
76+
errs = s.Validate(nil, nil, nil, &yes, nil)
6277
if len(errs) != 0 {
6378
t.Errorf("expected no errors from nil uid but got %v", errs)
6479
}

pkg/security/securitycontextconstraints/user/runasany.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ func (s *runAsAny) Generate(pod *api.Pod, container *api.Container) (*int64, err
2323
}
2424

2525
// Validate ensures that the specified values fall within the range of the strategy.
26-
func (s *runAsAny) Validate(pod *api.Pod, container *api.Container) field.ErrorList {
26+
func (s *runAsAny) Validate(fldPath *field.Path, _ *api.Pod, _ *api.Container, runAsNonRoot *bool, runAsUser *int64) field.ErrorList {
2727
return field.ErrorList{}
2828
}

pkg/security/securitycontextconstraints/user/runasany_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func TestRunAsAnyValidate(t *testing.T) {
3636
if err != nil {
3737
t.Fatalf("unexpected error initializing NewRunAsAny %v", err)
3838
}
39-
errs := s.Validate(nil, nil)
39+
errs := s.Validate(nil, nil, nil, nil, nil)
4040
if len(errs) != 0 {
4141
t.Errorf("unexpected errors validating with ")
4242
}

pkg/security/securitycontextconstraints/user/types.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ type RunAsUserSecurityContextConstraintsStrategy interface {
1010
// Generate creates the uid based on policy rules.
1111
Generate(pod *api.Pod, container *api.Container) (*int64, error)
1212
// Validate ensures that the specified values fall within the range of the strategy.
13-
Validate(pod *api.Pod, container *api.Container) field.ErrorList
13+
Validate(fldPath *field.Path, pod *api.Pod, container *api.Container, runAsNonRoot *bool, runAsUser *int64) field.ErrorList
1414
}

0 commit comments

Comments
 (0)