Add Support for supplemental groups

pull/6/head
Sami Wagiaalla 2015-10-15 13:45:16 -04:00
parent a123b15fd1
commit 030f882f06
18 changed files with 20896 additions and 20526 deletions

View File

@ -13189,6 +13189,18 @@
"v1.PodSecurityContext": {
"id": "v1.PodSecurityContext",
"description": "PodSecurityContext holds pod-level security attributes and common container settings.",
"properties": {
"supplementalGroups": {
"type": "array",
"items": {
"$ref": "integer"
},
"description": "SupplementalGroups can be used to specify a list of additional groups which the main container process will run as. This will be applied to all containers in the pod in addition to the primary group of the container."
}
}
},
"integer": {
"id": "integer",
"properties": {}
},
"v1.PodStatus": {

View File

@ -3837,6 +3837,18 @@
"v1.PodSecurityContext": {
"id": "v1.PodSecurityContext",
"description": "PodSecurityContext holds pod-level security attributes and common container settings.",
"properties": {
"supplementalGroups": {
"type": "array",
"items": {
"$ref": "integer"
},
"description": "SupplementalGroups can be used to specify a list of additional groups which the main container process will run as. This will be applied to all containers in the pod in addition to the primary group of the container."
}
}
},
"integer": {
"id": "integer",
"properties": {}
},
"v1beta1.JobStatus": {

View File

@ -1486,6 +1486,14 @@ func deepCopy_api_PodSecurityContext(in PodSecurityContext, out *PodSecurityCont
out.HostNetwork = in.HostNetwork
out.HostPID = in.HostPID
out.HostIPC = in.HostIPC
if in.SupplementalGroups != nil {
out.SupplementalGroups = make([]int64, len(in.SupplementalGroups))
for i := range in.SupplementalGroups {
out.SupplementalGroups[i] = in.SupplementalGroups[i]
}
} else {
out.SupplementalGroups = nil
}
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -1008,6 +1008,12 @@ type PodSecurityContext struct {
// Use the host's ipc namespace.
// Optional: Default to false.
HostIPC bool `json:"hostIPC,omitempty"`
// SupplementalGroups can be used to specify a list of
// additional groups which the main container process will run
// as. This will be applied to all containers in the pod in
// addition to the primary group of the container.
SupplementalGroups []int64 `json:"supplementalGroups,omitempty"`
}
// PodStatus represents information about the status of a pod. Status may trail the actual

View File

@ -413,6 +413,8 @@ func convert_api_PodSecurityContext_To_v1_PodSecurityContext(in *api.PodSecurity
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*api.PodSecurityContext))(in)
}
out.SupplementalGroups = in.SupplementalGroups
return nil
}
@ -420,5 +422,7 @@ func convert_v1_PodSecurityContext_To_api_PodSecurityContext(in *PodSecurityCont
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*PodSecurityContext))(in)
}
out.SupplementalGroups = in.SupplementalGroups
return nil
}

View File

@ -1501,6 +1501,14 @@ func deepCopy_v1_PodProxyOptions(in PodProxyOptions, out *PodProxyOptions, c *co
}
func deepCopy_v1_PodSecurityContext(in PodSecurityContext, out *PodSecurityContext, c *conversion.Cloner) error {
if in.SupplementalGroups != nil {
out.SupplementalGroups = make([]int64, len(in.SupplementalGroups))
for i := range in.SupplementalGroups {
out.SupplementalGroups[i] = in.SupplementalGroups[i]
}
} else {
out.SupplementalGroups = nil
}
return nil
}

File diff suppressed because it is too large Load Diff

View File

@ -1258,6 +1258,11 @@ type PodSpec struct {
// PodSecurityContext holds pod-level security attributes and common container settings.
type PodSecurityContext struct {
// SupplementalGroups can be used to specify a list of
// additional groups which the main container process will run
// as. This will be applied to all containers in the pod in
// addition to the primary group of the container.
SupplementalGroups []int64 `json:"supplementalGroups,omitempty"`
}
// PodStatus represents information about the status of a pod. Status may trail the actual

View File

@ -975,7 +975,8 @@ func (PodProxyOptions) SwaggerDoc() map[string]string {
}
var map_PodSecurityContext = map[string]string{
"": "PodSecurityContext holds pod-level security attributes and common container settings.",
"": "PodSecurityContext holds pod-level security attributes and common container settings.",
"supplementalGroups": "SupplementalGroups can be used to specify a list of additional groups which the main container process will run as. This will be applied to all containers in the pod in addition to the primary group of the container.",
}
func (PodSecurityContext) SwaggerDoc() map[string]string {

View File

@ -464,6 +464,14 @@ func deepCopy_api_PodSecurityContext(in api.PodSecurityContext, out *api.PodSecu
out.HostNetwork = in.HostNetwork
out.HostPID = in.HostPID
out.HostIPC = in.HostIPC
if in.SupplementalGroups != nil {
out.SupplementalGroups = make([]int64, len(in.SupplementalGroups))
for i := range in.SupplementalGroups {
out.SupplementalGroups[i] = in.SupplementalGroups[i]
}
} else {
out.SupplementalGroups = nil
}
return nil
}

View File

@ -331,6 +331,8 @@ func convert_api_PodSecurityContext_To_v1_PodSecurityContext(in *api.PodSecurity
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*api.PodSecurityContext))(in)
}
out.SupplementalGroups = in.SupplementalGroups
return nil
}
@ -338,5 +340,7 @@ func convert_v1_PodSecurityContext_To_api_PodSecurityContext(in *v1.PodSecurityC
if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
defaulting.(func(*v1.PodSecurityContext))(in)
}
out.SupplementalGroups = in.SupplementalGroups
return nil
}

View File

@ -497,6 +497,14 @@ func deepCopy_v1_PersistentVolumeClaimVolumeSource(in v1.PersistentVolumeClaimVo
}
func deepCopy_v1_PodSecurityContext(in v1.PodSecurityContext, out *v1.PodSecurityContext, c *conversion.Cloner) error {
if in.SupplementalGroups != nil {
out.SupplementalGroups = make([]int64, len(in.SupplementalGroups))
for i := range in.SupplementalGroups {
out.SupplementalGroups[i] = in.SupplementalGroups[i]
}
} else {
out.SupplementalGroups = nil
}
return nil
}

View File

@ -21,6 +21,7 @@ import (
"strconv"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/kubelet/leaky"
docker "github.com/fsouza/go-dockerclient"
)
@ -48,9 +49,25 @@ func (p SimpleSecurityContextProvider) ModifyContainerConfig(pod *api.Pod, conta
// ModifyHostConfig is called before the Docker runContainer call.
// The security context provider can make changes to the HostConfig, affecting
// security options, whether the container is privileged, volume binds, etc.
// An error is returned if it's not possible to secure the container as requested
// with a security context.
func (p SimpleSecurityContextProvider) ModifyHostConfig(pod *api.Pod, container *api.Container, hostConfig *docker.HostConfig) {
// Apply pod security context
if pod.Spec.SecurityContext != nil {
// We skip application of supplemental groups to the
// infra container to work around a runc issue which
// requires containers to have the '/etc/group'. For
// more information see:
// https://github.com/opencontainers/runc/pull/313
// This can be removed once the fix makes it into the
// required version of docker.
if pod.Spec.SecurityContext.SupplementalGroups != nil && container.Name != leaky.PodInfraContainerName {
hostConfig.GroupAdd = make([]string, len(pod.Spec.SecurityContext.SupplementalGroups))
for i, group := range pod.Spec.SecurityContext.SupplementalGroups {
hostConfig.GroupAdd[i] = strconv.FormatInt(group, 10)
}
}
}
// Apply container security context
if container.SecurityContext == nil {
return
}

View File

@ -22,9 +22,9 @@ import (
"strconv"
"testing"
"k8s.io/kubernetes/pkg/api"
docker "github.com/fsouza/go-dockerclient"
"k8s.io/kubernetes/pkg/api"
apitesting "k8s.io/kubernetes/pkg/api/testing"
)
func TestModifyContainerConfig(t *testing.T) {
@ -107,10 +107,50 @@ func TestModifyHostConfig(t *testing.T) {
provider := NewSimpleSecurityContextProvider()
dummyContainer := &api.Container{}
dummyPod := &api.Pod{
Spec: apitesting.DeepEqualSafePodSpec(),
}
for k, v := range testCases {
dummyContainer.SecurityContext = v.securityContext
dockerCfg := &docker.HostConfig{}
provider.ModifyHostConfig(nil, dummyContainer, dockerCfg)
provider.ModifyHostConfig(dummyPod, dummyContainer, dockerCfg)
if !reflect.DeepEqual(v.expected, dockerCfg) {
t.Errorf("unexpected modification of host config for %s. Expected: %#v Got: %#v", k, v.expected, dockerCfg)
}
}
}
func TestModifyHostConfigPodSecurityContext(t *testing.T) {
supplementalGroupsSC := &api.PodSecurityContext{}
supplementalGroupsSC.SupplementalGroups = []int64{2222}
supplementalGroupHC := fullValidHostConfig()
supplementalGroupHC.GroupAdd = []string{"2222"}
testCases := map[string]struct {
securityContext *api.PodSecurityContext
expected *docker.HostConfig
}{
"nil Security Context": {
securityContext: nil,
expected: fullValidHostConfig(),
},
"Security Context with SupplementalGroup": {
securityContext: supplementalGroupsSC,
expected: supplementalGroupHC,
},
}
provider := NewSimpleSecurityContextProvider()
dummyContainer := &api.Container{}
dummyContainer.SecurityContext = fullValidSecurityContext()
dummyPod := &api.Pod{
Spec: apitesting.DeepEqualSafePodSpec(),
}
for k, v := range testCases {
dummyPod.Spec.SecurityContext = v.securityContext
dockerCfg := &docker.HostConfig{}
provider.ModifyHostConfig(dummyPod, dummyContainer, dockerCfg)
if !reflect.DeepEqual(v.expected, dockerCfg) {
t.Errorf("unexpected modification of host config for %s. Expected: %#v Got: %#v", k, v.expected, dockerCfg)
}

View File

@ -57,6 +57,11 @@ func (p *plugin) Admit(a admission.Attributes) (err error) {
if !ok {
return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted")
}
if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.SupplementalGroups != nil {
return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("SecurityContext.SupplementalGroups is forbidden"))
}
for _, v := range pod.Spec.Containers {
if v.SecurityContext != nil {
if v.SecurityContext.SELinuxOptions != nil {

View File

@ -64,6 +64,45 @@ func TestAdmission(t *testing.T) {
}
}
func TestPodSecurityContextAdmission(t *testing.T) {
handler := NewSecurityContextDeny(nil)
pod := api.Pod{
Spec: api.PodSpec{
Containers: []api.Container{
{},
},
},
}
tests := []struct {
securityContext api.PodSecurityContext
errorExpected bool
}{
{
securityContext: api.PodSecurityContext{},
errorExpected: false,
},
{
securityContext: api.PodSecurityContext{
SupplementalGroups: []int64{1234},
},
errorExpected: true,
},
}
for _, test := range tests {
pod.Spec.SecurityContext = &test.securityContext
err := handler.Admit(admission.NewAttributesRecord(&pod, "Pod", "foo", "name", string(api.ResourcePods), "", "ignored", nil))
if test.errorExpected && err == nil {
t.Errorf("Expected error for security context %+v but did not get an error", test.securityContext)
}
if !test.errorExpected && err != nil {
t.Errorf("Unexpected error %v for security context %+v", err, test.securityContext)
}
}
}
func TestHandles(t *testing.T) {
handler := NewSecurityContextDeny(nil)
tests := map[admission.Operation]bool{

View File

@ -0,0 +1,65 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
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.
*/
/* This test check that SecurityContext parameters specified at the
* pod or the container level work as intended. These tests cannot be
* run when the 'SecurityContextDeny' addmissioin controller is not used
* so they are skipped by default.
*/
package e2e
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util"
. "github.com/onsi/ginkgo"
)
func getSecurityContextTestPod() *api.Pod {
podName := "security-context-" + string(util.NewUUID())
pod := &api.Pod{
ObjectMeta: api.ObjectMeta{
Name: podName,
Labels: map[string]string{"name": podName},
},
Spec: api.PodSpec{
SecurityContext: &api.PodSecurityContext{},
Containers: []api.Container{
{
Name: "test-container",
Image: "gcr.io/google_containers/busybox",
},
},
RestartPolicy: api.RestartPolicyNever,
},
}
return pod
}
var _ = Describe("[Skipped] Security Context", func() {
framework := NewFramework("security-context")
It("should support pod.Spec.SecurityContext.SupplementalGroups", func() {
pod := getSecurityContextTestPod()
pod.Spec.Containers[0].Command = []string{"id", "-G"}
pod.Spec.SecurityContext.SupplementalGroups = []int64{1234, 5678}
groups := []string{"1234", "5678"}
framework.TestContainerOutput("pod.Spec.SecurityContext.SupplementalGroups", pod, 0, groups)
})
})