mirror of https://github.com/k3s-io/k3s
Add Support for supplemental groups
parent
a123b15fd1
commit
030f882f06
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
|
||||
})
|
Loading…
Reference in New Issue