forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsupplemental_groups.go
184 lines (156 loc) · 6.11 KB
/
supplemental_groups.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
package security
import (
"fmt"
"strconv"
"strings"
"github.com/fsouza/go-dockerclient"
g "github.com/onsi/ginkgo"
o "github.com/onsi/gomega"
kapi "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/test/e2e"
testutil "github.com/openshift/origin/test/util"
)
var _ = g.Describe("[security] supplemental groups", func() {
defer g.GinkgoRecover()
var (
f = e2e.NewDefaultFramework("security-supgroups")
)
g.Describe("Ensure supplemental groups propagate to docker", func() {
g.It("should propagate requested groups to the docker host config", func() {
// Before running any of this test we need to first check that
// the docker version being used supports the supplemental groups feature
g.By("ensuring the feature is supported")
dockerCli, err := testutil.NewDockerClient()
o.Expect(err).NotTo(o.HaveOccurred())
env, err := dockerCli.Version()
o.Expect(err).NotTo(o.HaveOccurred(), "error getting docker environment")
version := env.Get("Version")
supports, err, requiredVersion := supportsSupplementalGroups(version)
if !supports || err != nil {
msg := fmt.Sprintf("skipping supplemental groups test, docker version %s does not meet required version %s", version, requiredVersion)
if err != nil {
msg = fmt.Sprintf("%s - encountered error: %v", msg, err)
}
g.Skip(msg)
}
// on to the real test
fsGroup := int64(1111)
supGroup := int64(2222)
// create a pod that is requesting supplemental groups. We request specific sup groups
// so that we can check for the exact values later and not rely on SCC allocation.
g.By("creating a pod that requests supplemental groups")
submittedPod := supGroupPod(fsGroup, supGroup)
_, err = f.Client.Pods(f.Namespace.Name).Create(submittedPod)
o.Expect(err).NotTo(o.HaveOccurred())
defer f.Client.Pods(f.Namespace.Name).Delete(submittedPod.Name, nil)
// we should have been admitted with the groups that we requested but if for any
// reason they are different we will fail.
g.By("retrieving the pod and ensuring groups are set")
retrievedPod, err := f.Client.Pods(f.Namespace.Name).Get(submittedPod.Name)
o.Expect(err).NotTo(o.HaveOccurred())
o.Expect(*retrievedPod.Spec.SecurityContext.FSGroup).To(o.Equal(*submittedPod.Spec.SecurityContext.FSGroup))
o.Expect(retrievedPod.Spec.SecurityContext.SupplementalGroups).To(o.Equal(submittedPod.Spec.SecurityContext.SupplementalGroups))
// wait for the pod to run so we can inspect it.
g.By("waiting for the pod to become running")
err = f.WaitForPodRunning(submittedPod.Name)
o.Expect(err).NotTo(o.HaveOccurred())
// find the docker id of our running container.
g.By("finding the docker container id on the pod")
retrievedPod, err = f.Client.Pods(f.Namespace.Name).Get(submittedPod.Name)
o.Expect(err).NotTo(o.HaveOccurred())
containerID, err := getContainerID(retrievedPod)
o.Expect(err).NotTo(o.HaveOccurred())
// now check the host config of the container which should have been updated by the
// kubelet. If that is good then ensure we have the groups we expected.
g.By("inspecting the container")
dockerContainer, err := dockerCli.InspectContainer(containerID)
o.Expect(err).NotTo(o.HaveOccurred())
g.By("ensuring the host config has GroupAdd")
groupAdd := dockerContainer.HostConfig.GroupAdd
o.Expect(groupAdd).ToNot(o.BeEmpty(), fmt.Sprintf("groupAdd on host config was %v", groupAdd))
g.By("ensuring the groups are set")
o.Expect(configHasGroup(fsGroup, dockerContainer.HostConfig)).To(o.Equal(true), fmt.Sprintf("fsGroup should exist on host config: %v", groupAdd))
o.Expect(configHasGroup(supGroup, dockerContainer.HostConfig)).To(o.Equal(true), fmt.Sprintf("supGroup should exist on host config: %v", groupAdd))
})
})
})
// supportsSupplementalGroups does a check on the docker version to ensure it is at least
// 1.8.2. This could still fail if the version does not have the /etc/groups patch
// but it will fail when launching the pod so this is as safe as we can get.
func supportsSupplementalGroups(dockerVersion string) (bool, error, string) {
parts := strings.Split(dockerVersion, ".")
var (
requiredMajor = 1
requiredMinor = 8
requiredPatch = 2
requiredVersion = fmt.Sprintf("%d.%d.%d", requiredMajor, requiredMinor, requiredPatch)
major = 0
minor = 0
patch = 0
err error = nil
)
if len(parts) > 0 {
major, err = strconv.Atoi(parts[0])
if err != nil {
return false, err, requiredVersion
}
}
if len(parts) > 1 {
minor, err = strconv.Atoi(parts[1])
if err != nil {
return false, err, requiredVersion
}
}
if len(parts) > 2 {
patch, err = strconv.Atoi(parts[2])
if err != nil {
return false, err, requiredVersion
}
}
// requires at least 1.8.2
if major > requiredMajor || (major == requiredMajor && minor > requiredMinor) ||
(major == requiredMajor && minor == requiredMinor && patch >= requiredPatch) {
return true, nil, requiredVersion
}
return false, nil, requiredVersion
}
// configHasGroup is a helper to ensure that a group is in the host config's addGroups field.
func configHasGroup(group int64, config *docker.HostConfig) bool {
strGroup := strconv.FormatInt(group, 10)
for _, g := range config.GroupAdd {
if g == strGroup {
return true
}
}
return false
}
// getContainerID is a helper to parse the docker container id from a status.
func getContainerID(p *kapi.Pod) (string, error) {
for _, status := range p.Status.ContainerStatuses {
if len(status.ContainerID) > 0 {
containerID := strings.Replace(status.ContainerID, "docker://", "", -1)
return containerID, nil
}
}
return "", fmt.Errorf("unable to find container id on pod")
}
// supGroupPod generates the pod requesting supplemental groups.
func supGroupPod(fsGroup int64, supGroup int64) *kapi.Pod {
return &kapi.Pod{
ObjectMeta: kapi.ObjectMeta{
Name: "supplemental-groups",
},
Spec: kapi.PodSpec{
SecurityContext: &kapi.PodSecurityContext{
FSGroup: &fsGroup,
SupplementalGroups: []int64{supGroup},
},
Containers: []kapi.Container{
{
Name: "supplemental-groups",
Image: "openshift/origin-pod",
},
},
},
}
}