Skip to content

Commit ccde63b

Browse files
committed
fix windows container root validate
1 parent 607c5da commit ccde63b

File tree

6 files changed

+258
-29
lines changed

6 files changed

+258
-29
lines changed

pkg/kubelet/kuberuntime/BUILD

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ go_library(
2828
"labels.go",
2929
"legacy.go",
3030
"security_context.go",
31+
"security_context_others.go",
32+
"security_context_windows.go",
3133
],
3234
importpath = "k8s.io/kubernetes/pkg/kubelet/kuberuntime",
3335
deps = [
@@ -105,7 +107,8 @@ go_test(
105107
"kuberuntime_sandbox_test.go",
106108
"labels_test.go",
107109
"legacy_test.go",
108-
"security_context_test.go",
110+
"security_context_others_test.go",
111+
"security_context_windows_test.go",
109112
],
110113
embed = [":go_default_library"],
111114
deps = [

pkg/kubelet/kuberuntime/security_context.go

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ limitations under the License.
1717
package kuberuntime
1818

1919
import (
20-
"fmt"
21-
2220
v1 "k8s.io/api/core/v1"
2321
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
2422
"k8s.io/kubernetes/pkg/security/apparmor"
@@ -76,31 +74,6 @@ func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *v1.Po
7674
return synthesized
7775
}
7876

79-
// verifyRunAsNonRoot verifies RunAsNonRoot.
80-
func verifyRunAsNonRoot(pod *v1.Pod, container *v1.Container, uid *int64, username string) error {
81-
effectiveSc := securitycontext.DetermineEffectiveSecurityContext(pod, container)
82-
// If the option is not set, or if running as root is allowed, return nil.
83-
if effectiveSc == nil || effectiveSc.RunAsNonRoot == nil || !*effectiveSc.RunAsNonRoot {
84-
return nil
85-
}
86-
87-
if effectiveSc.RunAsUser != nil {
88-
if *effectiveSc.RunAsUser == 0 {
89-
return fmt.Errorf("container's runAsUser breaks non-root policy")
90-
}
91-
return nil
92-
}
93-
94-
switch {
95-
case uid != nil && *uid == 0:
96-
return fmt.Errorf("container has runAsNonRoot and image will run as root")
97-
case uid == nil && len(username) > 0:
98-
return fmt.Errorf("container has runAsNonRoot and image has non-numeric user (%s), cannot verify user is non-root", username)
99-
default:
100-
return nil
101-
}
102-
}
103-
10477
// convertToRuntimeSecurityContext converts v1.SecurityContext to runtimeapi.SecurityContext.
10578
func convertToRuntimeSecurityContext(securityContext *v1.SecurityContext) *runtimeapi.LinuxContainerSecurityContext {
10679
if securityContext == nil {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// +build !windows
2+
3+
/*
4+
Copyright 2020 The Kubernetes Authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
package kuberuntime
20+
21+
import (
22+
"fmt"
23+
24+
"k8s.io/api/core/v1"
25+
"k8s.io/kubernetes/pkg/securitycontext"
26+
)
27+
28+
// verifyRunAsNonRoot verifies RunAsNonRoot.
29+
func verifyRunAsNonRoot(pod *v1.Pod, container *v1.Container, uid *int64, username string) error {
30+
effectiveSc := securitycontext.DetermineEffectiveSecurityContext(pod, container)
31+
// If the option is not set, or if running as root is allowed, return nil.
32+
if effectiveSc == nil || effectiveSc.RunAsNonRoot == nil || !*effectiveSc.RunAsNonRoot {
33+
return nil
34+
}
35+
36+
if effectiveSc.RunAsUser != nil {
37+
if *effectiveSc.RunAsUser == 0 {
38+
return fmt.Errorf("container's runAsUser breaks non-root policy")
39+
}
40+
return nil
41+
}
42+
43+
switch {
44+
case uid != nil && *uid == 0:
45+
return fmt.Errorf("container has runAsNonRoot and image will run as root")
46+
case uid == nil && len(username) > 0:
47+
return fmt.Errorf("container has runAsNonRoot and image has non-numeric user (%s), cannot verify user is non-root", username)
48+
default:
49+
return nil
50+
}
51+
}

pkg/kubelet/kuberuntime/security_context_test.go renamed to pkg/kubelet/kuberuntime/security_context_others_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
// +build !windows
2+
13
/*
2-
Copyright 2016 The Kubernetes Authors.
4+
Copyright 2020 The Kubernetes Authors.
35
46
Licensed under the Apache License, Version 2.0 (the "License");
57
you may not use this file except in compliance with the License.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// +build windows
2+
3+
/*
4+
Copyright 2020 The Kubernetes Authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
package kuberuntime
20+
21+
import (
22+
"fmt"
23+
"k8s.io/api/core/v1"
24+
"k8s.io/klog/v2"
25+
"k8s.io/kubernetes/pkg/securitycontext"
26+
)
27+
28+
var (
29+
windowsRootUserName = "ContainerAdministrator"
30+
)
31+
32+
// verifyRunAsNonRoot verifies RunAsNonRoot on windows.
33+
// https://github.com/kubernetes/enhancements/blob/master/keps/sig-windows/20190103-windows-node-support.md#v1container
34+
// Windows does not have a root user, we cannot judge the root identity of the windows container by the way to judge the root(uid=0) of the linux container.
35+
// According to the discussion of sig-windows, at present, we assume that ContainerAdministrator is the windows container root user,
36+
// and then optimize this logic according to the best time.
37+
// https://docs.google.com/document/d/1Tjxzjjuy4SQsFSUVXZbvqVb64hjNAG5CQX8bK7Yda9w
38+
func verifyRunAsNonRoot(pod *v1.Pod, container *v1.Container, uid *int64, username string) error {
39+
effectiveSc := securitycontext.DetermineEffectiveSecurityContext(pod, container)
40+
// If the option is not set, or if running as root is allowed, return nil.
41+
if effectiveSc == nil || effectiveSc.RunAsNonRoot == nil || !*effectiveSc.RunAsNonRoot {
42+
return nil
43+
}
44+
if effectiveSc.RunAsUser != nil {
45+
klog.Warningf("Windows container does not support SecurityContext.RunAsUser, please use SecurityContext.WindowsOptions")
46+
}
47+
if effectiveSc.SELinuxOptions != nil {
48+
klog.Warningf("Windows container does not support SecurityContext.SELinuxOptions, please use SecurityContext.WindowsOptions")
49+
}
50+
if effectiveSc.RunAsGroup != nil {
51+
klog.Warningf("Windows container does not support SecurityContext.RunAsGroup")
52+
}
53+
if effectiveSc.WindowsOptions != nil {
54+
if effectiveSc.WindowsOptions.RunAsUserName != nil {
55+
if *effectiveSc.WindowsOptions.RunAsUserName == windowsRootUserName {
56+
return fmt.Errorf("container's runAsUser (%s) which will be regarded as root identity and will break non-root policy", username)
57+
}
58+
return nil
59+
}
60+
}
61+
if len(username) > 0 && username == windowsRootUserName {
62+
return fmt.Errorf("container's runAsUser (%s) which will be regarded as root identity and will break non-root policy", username)
63+
}
64+
return nil
65+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// +build windows
2+
3+
/*
4+
Copyright 2020 The Kubernetes Authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
package kuberuntime
20+
21+
import (
22+
"k8s.io/api/core/v1"
23+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
25+
"github.com/stretchr/testify/assert"
26+
"testing"
27+
)
28+
29+
func TestVerifyRunAsNonRoot(t *testing.T) {
30+
pod := &v1.Pod{
31+
ObjectMeta: metav1.ObjectMeta{
32+
UID: "12345678",
33+
Name: "bar",
34+
Namespace: "new",
35+
},
36+
Spec: v1.PodSpec{
37+
Containers: []v1.Container{
38+
{
39+
Name: "foo",
40+
Image: "windows",
41+
ImagePullPolicy: v1.PullIfNotPresent,
42+
Command: []string{"testCommand"},
43+
WorkingDir: "testWorkingDir",
44+
},
45+
},
46+
},
47+
}
48+
rootUser := "ContainerAdministrator"
49+
anyUser := "anyone"
50+
runAsNonRootTrue := true
51+
runAsNonRootFalse := false
52+
for _, test := range []struct {
53+
desc string
54+
sc *v1.SecurityContext
55+
uid *int64
56+
username string
57+
fail bool
58+
}{
59+
{
60+
desc: "Pass if SecurityContext is not set",
61+
sc: nil,
62+
username: rootUser,
63+
fail: false,
64+
},
65+
{
66+
desc: "Pass if RunAsNonRoot is not set",
67+
sc: &v1.SecurityContext{
68+
RunAsNonRoot: nil,
69+
},
70+
username: rootUser,
71+
fail: false,
72+
},
73+
{
74+
desc: "Pass if RunAsNonRoot is false (image user is root)",
75+
sc: &v1.SecurityContext{
76+
RunAsNonRoot: &runAsNonRootFalse,
77+
},
78+
username: rootUser,
79+
fail: false,
80+
},
81+
{
82+
desc: "Pass if RunAsNonRoot is false (WindowsOptions RunAsUserName is root)",
83+
sc: &v1.SecurityContext{
84+
RunAsNonRoot: &runAsNonRootFalse,
85+
WindowsOptions: &v1.WindowsSecurityContextOptions{
86+
RunAsUserName: &rootUser,
87+
},
88+
},
89+
username: rootUser,
90+
fail: false,
91+
},
92+
{
93+
desc: "Fail if container's RunAsUser is root and RunAsNonRoot is true",
94+
sc: &v1.SecurityContext{
95+
RunAsNonRoot: &runAsNonRootTrue,
96+
WindowsOptions: &v1.WindowsSecurityContextOptions{
97+
RunAsUserName: &rootUser,
98+
},
99+
},
100+
username: rootUser,
101+
fail: true,
102+
},
103+
{
104+
desc: "Fail if image's user is root and RunAsNonRoot is true",
105+
sc: &v1.SecurityContext{
106+
RunAsNonRoot: &runAsNonRootTrue,
107+
},
108+
username: rootUser,
109+
fail: true,
110+
},
111+
{
112+
desc: "Pass if image's user is non-root and RunAsNonRoot is true",
113+
sc: &v1.SecurityContext{
114+
RunAsNonRoot: &runAsNonRootTrue,
115+
},
116+
username: anyUser,
117+
fail: false,
118+
},
119+
{
120+
desc: "Pass if container's user and image's user aren't set and RunAsNonRoot is true",
121+
sc: &v1.SecurityContext{
122+
RunAsNonRoot: &runAsNonRootTrue,
123+
},
124+
fail: false,
125+
},
126+
} {
127+
pod.Spec.Containers[0].SecurityContext = test.sc
128+
err := verifyRunAsNonRoot(pod, &pod.Spec.Containers[0], test.uid, test.username)
129+
if test.fail {
130+
assert.Error(t, err, test.desc)
131+
} else {
132+
assert.NoError(t, err, test.desc)
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)