mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 14:37:00 +00:00
PodSecurity: seLinuxOptions: cleanup
rename to seLinuxOptions make message consistent add unit tests for message consolidate integration test fixtures
This commit is contained in:
parent
19c8ab297c
commit
d541970751
@ -17,6 +17,7 @@ limitations under the License.
|
||||
package policy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@ -52,19 +53,19 @@ spec.initContainers[*].securityContext.seLinuxOptions.role
|
||||
*/
|
||||
|
||||
func init() {
|
||||
addCheck(CheckSELinux)
|
||||
addCheck(CheckSELinuxOptions)
|
||||
}
|
||||
|
||||
// CheckSELinux returns a baseline level check
|
||||
// CheckSELinuxOptions returns a baseline level check
|
||||
// that limits seLinuxOptions type, user, and role values in 1.0+
|
||||
func CheckSELinux() Check {
|
||||
func CheckSELinuxOptions() Check {
|
||||
return Check{
|
||||
ID: "selinux",
|
||||
ID: "seLinuxOptions",
|
||||
Level: api.LevelBaseline,
|
||||
Versions: []VersionedCheck{
|
||||
{
|
||||
MinimumVersion: api.MajorMinorVersion(1, 0),
|
||||
CheckPod: checkSelinux_1_0,
|
||||
CheckPod: seLinuxOptions_1_0,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -74,36 +75,85 @@ var (
|
||||
selinux_allowed_types_1_0 = sets.NewString("", "container_t", "container_init_t", "container_kvm_t")
|
||||
)
|
||||
|
||||
func checkSelinux_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var forbiddenDetails []string
|
||||
func seLinuxOptions_1_0(podMetadata *metav1.ObjectMeta, podSpec *corev1.PodSpec) CheckResult {
|
||||
var (
|
||||
// sources that set bad seLinuxOptions
|
||||
badSetters []string
|
||||
|
||||
checkSelinuxOptions := func(path *field.Path, opts *corev1.SELinuxOptions) {
|
||||
// invalid type values set
|
||||
badTypes = sets.NewString()
|
||||
// was user set?
|
||||
setUser = false
|
||||
// was role set?
|
||||
setRole = false
|
||||
)
|
||||
|
||||
validSELinuxOptions := func(opts *corev1.SELinuxOptions) bool {
|
||||
valid := true
|
||||
if !selinux_allowed_types_1_0.Has(opts.Type) {
|
||||
forbiddenDetails = append(forbiddenDetails, path.Child("securityContext", "seLinuxOptions", "type").String())
|
||||
valid = false
|
||||
badTypes.Insert(opts.Type)
|
||||
}
|
||||
if len(opts.User) > 0 {
|
||||
forbiddenDetails = append(forbiddenDetails, path.Child("securityContext", "seLinuxOptions", "user").String())
|
||||
valid = false
|
||||
setUser = true
|
||||
}
|
||||
if len(opts.Role) > 0 {
|
||||
forbiddenDetails = append(forbiddenDetails, path.Child("securityContext", "seLinuxOptions", "role").String())
|
||||
valid = false
|
||||
setRole = true
|
||||
}
|
||||
return valid
|
||||
}
|
||||
|
||||
if podSpec.SecurityContext != nil && podSpec.SecurityContext.SELinuxOptions != nil {
|
||||
checkSelinuxOptions(field.NewPath("spec"), podSpec.SecurityContext.SELinuxOptions)
|
||||
if !validSELinuxOptions(podSpec.SecurityContext.SELinuxOptions) {
|
||||
badSetters = append(badSetters, "pod")
|
||||
}
|
||||
}
|
||||
|
||||
var badContainers []string
|
||||
visitContainersWithPath(podSpec, field.NewPath("spec"), func(container *corev1.Container, path *field.Path) {
|
||||
if container.SecurityContext != nil && container.SecurityContext.SELinuxOptions != nil {
|
||||
checkSelinuxOptions(path, container.SecurityContext.SELinuxOptions)
|
||||
if !validSELinuxOptions(container.SecurityContext.SELinuxOptions) {
|
||||
badContainers = append(badContainers, container.Name)
|
||||
}
|
||||
}
|
||||
})
|
||||
if len(badContainers) > 0 {
|
||||
badSetters = append(
|
||||
badSetters,
|
||||
fmt.Sprintf(
|
||||
"%s %s",
|
||||
pluralize("container", "containers", len(badContainers)),
|
||||
joinQuote(badContainers),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
if len(badSetters) > 0 {
|
||||
var badData []string
|
||||
if len(badTypes) > 0 {
|
||||
badData = append(badData, fmt.Sprintf(
|
||||
"%s %s",
|
||||
pluralize("type", "types", len(badTypes)),
|
||||
joinQuote(badTypes.List()),
|
||||
))
|
||||
if setUser {
|
||||
badData = append(badData, "user may not be set")
|
||||
}
|
||||
if setRole {
|
||||
badData = append(badData, "role may not be set")
|
||||
}
|
||||
}
|
||||
|
||||
if len(forbiddenDetails) > 0 {
|
||||
return CheckResult{
|
||||
Allowed: false,
|
||||
ForbiddenReason: "forbidden seLinuxOptions",
|
||||
ForbiddenDetail: strings.Join(forbiddenDetails, ", "),
|
||||
ForbiddenReason: "seLinuxOptions",
|
||||
ForbiddenDetail: fmt.Sprintf(
|
||||
`%s set forbidden securityContext.seLinuxOptions: %s`,
|
||||
strings.Join(badSetters, " and "),
|
||||
strings.Join(badData, "; "),
|
||||
),
|
||||
}
|
||||
}
|
||||
return CheckResult{Allowed: true}
|
@ -0,0 +1,137 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package policy
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestSELinuxOptions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *corev1.Pod
|
||||
expectReason string
|
||||
expectDetail string
|
||||
}{
|
||||
{
|
||||
name: "invalid pod and containers",
|
||||
pod: &corev1.Pod{Spec: corev1.PodSpec{
|
||||
SecurityContext: &corev1.PodSecurityContext{
|
||||
SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Type: "foo",
|
||||
User: "bar",
|
||||
Role: "baz",
|
||||
},
|
||||
},
|
||||
Containers: []corev1.Container{
|
||||
{Name: "a", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Type: "container_t",
|
||||
}}},
|
||||
{Name: "b", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Type: "container_init_t",
|
||||
}}},
|
||||
{Name: "c", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Type: "container_kvm_t",
|
||||
}}},
|
||||
{Name: "d", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Type: "bar",
|
||||
}}},
|
||||
{Name: "e", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
User: "bar",
|
||||
}}},
|
||||
{Name: "f", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Role: "baz",
|
||||
}}},
|
||||
},
|
||||
}},
|
||||
expectReason: `seLinuxOptions`,
|
||||
expectDetail: `pod and containers "d", "e", "f" set forbidden securityContext.seLinuxOptions: types "bar", "foo"; user may not be set; role may not be set`,
|
||||
},
|
||||
{
|
||||
name: "invalid pod",
|
||||
pod: &corev1.Pod{Spec: corev1.PodSpec{
|
||||
SecurityContext: &corev1.PodSecurityContext{
|
||||
SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Type: "foo",
|
||||
User: "bar",
|
||||
Role: "baz",
|
||||
},
|
||||
},
|
||||
Containers: []corev1.Container{
|
||||
{Name: "a", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Type: "container_t",
|
||||
}}},
|
||||
{Name: "b", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Type: "container_init_t",
|
||||
}}},
|
||||
{Name: "c", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Type: "container_kvm_t",
|
||||
}}},
|
||||
},
|
||||
}},
|
||||
expectReason: `seLinuxOptions`,
|
||||
expectDetail: `pod set forbidden securityContext.seLinuxOptions: type "foo"; user may not be set; role may not be set`,
|
||||
},
|
||||
{
|
||||
name: "invalid containers",
|
||||
pod: &corev1.Pod{Spec: corev1.PodSpec{
|
||||
SecurityContext: &corev1.PodSecurityContext{
|
||||
SELinuxOptions: &corev1.SELinuxOptions{},
|
||||
},
|
||||
Containers: []corev1.Container{
|
||||
{Name: "a", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Type: "container_t",
|
||||
}}},
|
||||
{Name: "b", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Type: "container_init_t",
|
||||
}}},
|
||||
{Name: "c", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Type: "container_kvm_t",
|
||||
}}},
|
||||
{Name: "d", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Type: "bar",
|
||||
}}},
|
||||
{Name: "e", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
User: "bar",
|
||||
}}},
|
||||
{Name: "f", SecurityContext: &corev1.SecurityContext{SELinuxOptions: &corev1.SELinuxOptions{
|
||||
Role: "baz",
|
||||
}}},
|
||||
},
|
||||
}},
|
||||
expectReason: `seLinuxOptions`,
|
||||
expectDetail: `containers "d", "e", "f" set forbidden securityContext.seLinuxOptions: type "bar"; user may not be set; role may not be set`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
result := seLinuxOptions_1_0(&tc.pod.ObjectMeta, &tc.pod.Spec)
|
||||
if result.Allowed {
|
||||
t.Fatal("expected disallowed")
|
||||
}
|
||||
if e, a := tc.expectReason, result.ForbiddenReason; e != a {
|
||||
t.Errorf("expected\n%s\ngot\n%s", e, a)
|
||||
}
|
||||
if e, a := tc.expectDetail, result.ForbiddenDetail; e != a {
|
||||
t.Errorf("expected\n%s\ngot\n%s", e, a)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
TODO: include field paths in reflect-based unit test
|
||||
|
||||
podFields: []string{
|
||||
`spec.securityContext.seLinuxOptions.type`,
|
||||
`spec.securityContext.seLinuxOptions.user`,
|
||||
`spec.securityContext.seLinuxOptions.role`,
|
||||
},
|
||||
containerFields: []string{
|
||||
`securityContext.seLinuxOptions.type`,
|
||||
`securityContext.seLinuxOptions.user`,
|
||||
`securityContext.seLinuxOptions.role`,
|
||||
},
|
||||
*/
|
||||
|
||||
func init() {
|
||||
fixtureData_1_0 := fixtureGenerator{
|
||||
expectErrorSubstring: "seLinuxOptions",
|
||||
generatePass: func(p *corev1.Pod) []*corev1.Pod {
|
||||
p = ensureSELinuxOptions(p)
|
||||
return []*corev1.Pod{
|
||||
// security context with no seLinuxOptions
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
// no pod-level
|
||||
p.Spec.SecurityContext.SELinuxOptions = nil
|
||||
// no container-level
|
||||
p.Spec.Containers[0].SecurityContext.SELinuxOptions = nil
|
||||
// empty container-level
|
||||
p.Spec.InitContainers[0].SecurityContext.SELinuxOptions = &corev1.SELinuxOptions{}
|
||||
}),
|
||||
tweak(p, func(p *corev1.Pod) {
|
||||
// seLinuxOptions with all valid types
|
||||
p.Spec.SecurityContext.SELinuxOptions.Type = "container_t"
|
||||
p.Spec.Containers[0].SecurityContext.SELinuxOptions.Type = "container_init_t"
|
||||
p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Type = "container_kvm_t"
|
||||
// with empty level and arbitrary level
|
||||
p.Spec.SecurityContext.SELinuxOptions.Level = ""
|
||||
p.Spec.Containers[0].SecurityContext.SELinuxOptions.Level = "somevalue"
|
||||
}),
|
||||
}
|
||||
},
|
||||
generateFail: func(p *corev1.Pod) []*corev1.Pod {
|
||||
p = ensureSELinuxOptions(p)
|
||||
return []*corev1.Pod{
|
||||
// seLinuxOptions with out of bounds type
|
||||
// pod-level
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Type = "somevalue" }),
|
||||
// container
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Type = "somevalue" }),
|
||||
// initContainer
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Type = "somevalue" }),
|
||||
// seLinuxOptions with out of bounds user
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.User = "somevalue" }),
|
||||
// seLinuxOptions with out of bounds role
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Role = "somevalue" }),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
registerFixtureGenerator(
|
||||
fixtureKey{level: api.LevelBaseline, version: api.MajorMinorVersion(1, 0), check: "seLinuxOptions"},
|
||||
fixtureData_1_0,
|
||||
)
|
||||
}
|
@ -1,98 +0,0 @@
|
||||
/*
|
||||
Copyright 2021 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package test
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/pod-security-admission/api"
|
||||
)
|
||||
|
||||
/*
|
||||
TODO: include field paths in reflect-based unit test
|
||||
|
||||
podFields: []string{
|
||||
`spec.securityContext.seLinuxOptions.type`,
|
||||
`spec.securityContext.seLinuxOptions.user`,
|
||||
`spec.securityContext.seLinuxOptions.role`,
|
||||
},
|
||||
containerFields: []string{
|
||||
`securityContext.seLinuxOptions.type`,
|
||||
`securityContext.seLinuxOptions.user`,
|
||||
`securityContext.seLinuxOptions.role`,
|
||||
},
|
||||
*/
|
||||
|
||||
func init() {
|
||||
fixtureData_1_0 := fixtureGenerator{
|
||||
expectErrorSubstring: "seLinuxOptions",
|
||||
generatePass: func(p *corev1.Pod) []*corev1.Pod {
|
||||
p = ensureSELinuxOptions(p)
|
||||
return []*corev1.Pod{
|
||||
// security context with no seLinuxOptions
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions = nil }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions = nil }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions = nil }),
|
||||
// seLinuxOptions with type=""
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Type = "" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Type = "" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Type = "" }),
|
||||
// seLinuxOptions with type="container_t"
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Type = "container_t" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Type = "container_t" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Type = "container_t" }),
|
||||
// seLinuxOptions with type="container_init_t"
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Type = "container_init_t" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Type = "container_init_t" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Type = "container_init_t" }),
|
||||
// seLinuxOptions with type="container_kvm_t"
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Type = "container_kvm_t" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Type = "container_kvm_t" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Type = "container_kvm_t" }),
|
||||
// seLinuxOptions with level=""
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Level = "" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Level = "" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Level = "" }),
|
||||
// seLinuxOptions with arbitrary level=""
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Level = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Level = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Level = "somevalue" }),
|
||||
}
|
||||
},
|
||||
generateFail: func(p *corev1.Pod) []*corev1.Pod {
|
||||
p = ensureSELinuxOptions(p)
|
||||
return []*corev1.Pod{
|
||||
// seLinuxOptions with out of bounds type
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Type = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Type = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Type = "somevalue" }),
|
||||
// seLinuxOptions with out of bounds user
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.User = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.User = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.User = "somevalue" }),
|
||||
// seLinuxOptions with out of bounds role
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.SecurityContext.SELinuxOptions.Role = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.Containers[0].SecurityContext.SELinuxOptions.Role = "somevalue" }),
|
||||
tweak(p, func(p *corev1.Pod) { p.Spec.InitContainers[0].SecurityContext.SELinuxOptions.Role = "somevalue" }),
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
registerFixtureGenerator(
|
||||
fixtureKey{level: api.LevelBaseline, version: api.MajorMinorVersion(1, 0), check: "selinux"},
|
||||
fixtureData_1_0,
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user