Commit 393e2bc0 authored by Paul Morie's avatar Paul Morie

Inline some SecurityContext fields into PodSecurityContext

parent 236193a2
......@@ -12625,7 +12625,7 @@
},
"securityContext": {
"$ref": "v1.PodSecurityContext",
"description": "SecurityContext holds pod-level security attributes and common container settings"
"description": "SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field."
},
"imagePullSecrets": {
"type": "array",
......@@ -13115,28 +13115,28 @@
},
"v1.SecurityContext": {
"id": "v1.SecurityContext",
"description": "SecurityContext holds security configuration that will be applied to a container.",
"description": "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.",
"properties": {
"capabilities": {
"$ref": "v1.Capabilities",
"description": "The linux kernel capabilites that should be added or removed. Default to Container.Capabilities if left unset. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context"
"description": "The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime."
},
"privileged": {
"type": "boolean",
"description": "Run the container in privileged mode. Default to Container.Privileged if left unset. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context"
"description": "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false."
},
"seLinuxOptions": {
"$ref": "v1.SELinuxOptions",
"description": "SELinuxOptions are the labels to be applied to the container and volumes. Options that control the SELinux labels applied. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context"
"description": "The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence."
},
"runAsUser": {
"type": "integer",
"format": "int64",
"description": "RunAsUser is the UID to run the entrypoint of the container process. The user id that runs the first process in the container. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context"
"description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence."
},
"runAsNonRoot": {
"type": "boolean",
"description": "RunAsNonRoot indicates that the container should be run as a non-root user. If the RunAsUser field is not explicitly set then the kubelet may check the image for a specified user or perform defaulting to specify a user."
"description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence."
}
}
},
......@@ -13170,19 +13170,19 @@
"properties": {
"user": {
"type": "string",
"description": "User is a SELinux user label that applies to the container. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md"
"description": "User is a SELinux user label that applies to the container."
},
"role": {
"type": "string",
"description": "Role is a SELinux role label that applies to the container. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md"
"description": "Role is a SELinux role label that applies to the container."
},
"type": {
"type": "string",
"description": "Type is a SELinux type label that applies to the container. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md"
"description": "Type is a SELinux type label that applies to the container."
},
"level": {
"type": "string",
"description": "Level is SELinux level label that applies to the container. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md"
"description": "Level is SELinux level label that applies to the container."
}
}
},
......@@ -13196,6 +13196,19 @@
"$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."
},
"seLinuxOptions": {
"$ref": "v1.SELinuxOptions",
"description": "SELinuxOptions is the SELinux context to be applied to all containers If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container."
},
"runAsUser": {
"type": "integer",
"format": "int64",
"description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container."
},
"runAsNonRoot": {
"type": "boolean",
"description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence."
}
}
},
......
......@@ -2959,7 +2959,7 @@
},
"securityContext": {
"$ref": "v1.PodSecurityContext",
"description": "SecurityContext holds pod-level security attributes and common container settings"
"description": "SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field."
},
"imagePullSecrets": {
"type": "array",
......@@ -3763,28 +3763,28 @@
},
"v1.SecurityContext": {
"id": "v1.SecurityContext",
"description": "SecurityContext holds security configuration that will be applied to a container.",
"description": "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.",
"properties": {
"capabilities": {
"$ref": "v1.Capabilities",
"description": "The linux kernel capabilites that should be added or removed. Default to Container.Capabilities if left unset. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context"
"description": "The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime."
},
"privileged": {
"type": "boolean",
"description": "Run the container in privileged mode. Default to Container.Privileged if left unset. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context"
"description": "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false."
},
"seLinuxOptions": {
"$ref": "v1.SELinuxOptions",
"description": "SELinuxOptions are the labels to be applied to the container and volumes. Options that control the SELinux labels applied. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context"
"description": "The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence."
},
"runAsUser": {
"type": "integer",
"format": "int64",
"description": "RunAsUser is the UID to run the entrypoint of the container process. The user id that runs the first process in the container. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context"
"description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence."
},
"runAsNonRoot": {
"type": "boolean",
"description": "RunAsNonRoot indicates that the container should be run as a non-root user. If the RunAsUser field is not explicitly set then the kubelet may check the image for a specified user or perform defaulting to specify a user."
"description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence."
}
}
},
......@@ -3818,19 +3818,19 @@
"properties": {
"user": {
"type": "string",
"description": "User is a SELinux user label that applies to the container. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md"
"description": "User is a SELinux user label that applies to the container."
},
"role": {
"type": "string",
"description": "Role is a SELinux role label that applies to the container. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md"
"description": "Role is a SELinux role label that applies to the container."
},
"type": {
"type": "string",
"description": "Type is a SELinux type label that applies to the container. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md"
"description": "Type is a SELinux type label that applies to the container."
},
"level": {
"type": "string",
"description": "Level is SELinux level label that applies to the container. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md"
"description": "Level is SELinux level label that applies to the container."
}
}
},
......@@ -3844,6 +3844,19 @@
"$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."
},
"seLinuxOptions": {
"$ref": "v1.SELinuxOptions",
"description": "SELinuxOptions is the SELinux context to be applied to all containers If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container."
},
"runAsUser": {
"type": "integer",
"format": "int64",
"description": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container."
},
"runAsNonRoot": {
"type": "boolean",
"description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence."
}
}
},
......
......@@ -1494,6 +1494,26 @@ func deepCopy_api_PodSecurityContext(in PodSecurityContext, out *PodSecurityCont
} else {
out.SupplementalGroups = nil
}
if in.SELinuxOptions != nil {
out.SELinuxOptions = new(SELinuxOptions)
if err := deepCopy_api_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil {
return err
}
} else {
out.SELinuxOptions = nil
}
if in.RunAsUser != nil {
out.RunAsUser = new(int64)
*out.RunAsUser = *in.RunAsUser
} else {
out.RunAsUser = nil
}
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......@@ -1966,7 +1986,12 @@ func deepCopy_api_SecurityContext(in SecurityContext, out *SecurityContext, c *c
} else {
out.RunAsUser = nil
}
out.RunAsNonRoot = in.RunAsNonRoot
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......
......@@ -101,8 +101,10 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
}
s.TerminationGracePeriodSeconds = &ttl
c.Fuzz(s.SecurityContext)
if s.SecurityContext == nil {
s.SecurityContext = &api.PodSecurityContext{}
s.SecurityContext = new(api.PodSecurityContext)
}
},
func(j *api.PodPhase, c fuzz.Continue) {
......@@ -297,14 +299,19 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
},
func(sc *api.SecurityContext, c fuzz.Continue) {
c.FuzzNoCustom(sc) // fuzz self without calling this function again
priv := c.RandBool()
sc.Privileged = &priv
sc.Capabilities = &api.Capabilities{
Add: make([]api.Capability, 0),
Drop: make([]api.Capability, 0),
if c.RandBool() {
priv := c.RandBool()
sc.Privileged = &priv
}
if c.RandBool() {
sc.Capabilities = &api.Capabilities{
Add: make([]api.Capability, 0),
Drop: make([]api.Capability, 0),
}
c.Fuzz(&sc.Capabilities.Add)
c.Fuzz(&sc.Capabilities.Drop)
}
c.Fuzz(&sc.Capabilities.Add)
c.Fuzz(&sc.Capabilities.Drop)
},
func(e *api.Event, c fuzz.Continue) {
c.FuzzNoCustom(e) // fuzz self without calling this function again
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -789,7 +789,8 @@ type Container struct {
TerminationMessagePath string `json:"terminationMessagePath,omitempty"`
// Required: Policy for pulling images for this container
ImagePullPolicy PullPolicy `json:"imagePullPolicy"`
// Optional: SecurityContext defines the security options the pod should be run with
// Optional: SecurityContext defines the security options the container should be run with.
// If set, the fields of SecurityContext override the equivalent fields of PodSecurityContext.
SecurityContext *SecurityContext `json:"securityContext,omitempty"`
// Variables for interactive containers, these have very specialized use-cases (e.g. debugging)
......@@ -987,7 +988,8 @@ type PodSpec struct {
// the scheduler simply schedules this pod onto that node, assuming that it fits resource
// requirements.
NodeName string `json:"nodeName,omitempty"`
// SecurityContext holds pod-level security attributes and common container settings
// SecurityContext holds pod-level security attributes and common container settings.
// Optional: Defaults to empty. See type description for default values of each field.
SecurityContext *PodSecurityContext `json:"securityContext,omitempty"`
// ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec.
// If specified, these secrets will be passed to individual puller implementations for them to use. For example,
......@@ -996,12 +998,13 @@ type PodSpec struct {
}
// PodSecurityContext holds pod-level security attributes and common container settings.
// Some fields are also present in SecurityContext. Field values of SecurityContext take
// precedence over field values of PodSecurityContext.
type PodSecurityContext struct {
// Use the host's network namespace. If this option is set, the ports that will be
// used must be specified.
// Optional: Default to false
HostNetwork bool `json:"hostNetwork,omitempty"`
// Use the host's pid namespace.
// Optional: Default to false.
HostPID bool `json:"hostPID,omitempty"`
......@@ -1014,6 +1017,25 @@ type PodSecurityContext struct {
// 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"`
// The SELinux context to be applied to all containers.
// If unspecified, the container runtime will allocate a random SELinux context for each
// container. May also be set in SecurityContext. If set in
// both SecurityContext and PodSecurityContext, the value specified in SecurityContext
// takes precedence for that container.
SELinuxOptions *SELinuxOptions `json:"seLinuxOptions,omitempty"`
// The UID to run the entrypoint of the container process.
// Defaults to user specified in image metadata if unspecified.
// May also be set in SecurityContext. If set in both SecurityContext and
// PodSecurityContext, the value specified in SecurityContext takes precedence
// for that container.
RunAsUser *int64 `json:"runAsUser,omitempty"`
// Indicates that the container must run as a non-root user.
// If true, the Kubelet will validate the image at runtime to ensure that it
// does not run as UID 0 (root) and fail to start the container if it does.
// If unset or false, no such validation will be performed.
// May also be set in SecurityContext. If set in both SecurityContext and
// PodSecurityContext, the value specified in SecurityContext takes precedence.
RunAsNonRoot *bool `json:"runAsNonRoot,omitempty"`
}
// PodStatus represents information about the status of a pod. Status may trail the actual
......@@ -2037,41 +2059,44 @@ type ComponentStatusList struct {
Items []ComponentStatus `json:"items"`
}
// SecurityContext holds security configuration that will be applied to a container. SecurityContext
// contains duplication of some existing fields from the Container resource. These duplicate fields
// will be populated based on the Container configuration if they are not set. Defining them on
// both the Container AND the SecurityContext will result in an error.
// SecurityContext holds security configuration that will be applied to a container.
// Some fields are present in both SecurityContext and PodSecurityContext. When both
// are set, the values in SecurityContext take precedence.
type SecurityContext struct {
// Capabilities are the capabilities to add/drop when running the container
// The capabilities to add/drop when running containers.
// Defaults to the default set of capabilities granted by the container runtime.
Capabilities *Capabilities `json:"capabilities,omitempty"`
// Run the container in privileged mode
// Run container in privileged mode.
// Processes in privileged containers are essentially equivalent to root on the host.
// Defaults to false.
Privileged *bool `json:"privileged,omitempty"`
// SELinuxOptions are the labels to be applied to the container
// and volumes
// The SELinux context to be applied to the container.
// If unspecified, the container runtime will allocate a random SELinux context for each
// container. May also be set in PodSecurityContext. If set in both SecurityContext and
// PodSecurityContext, the value specified in SecurityContext takes precedence.
SELinuxOptions *SELinuxOptions `json:"seLinuxOptions,omitempty"`
// RunAsUser is the UID to run the entrypoint of the container process.
// The UID to run the entrypoint of the container process.
// Defaults to user specified in image metadata if unspecified.
// May also be set in PodSecurityContext. If set in both SecurityContext and
// PodSecurityContext, the value specified in SecurityContext takes precedence.
RunAsUser *int64 `json:"runAsUser,omitempty"`
// RunAsNonRoot indicates that the container should be run as a non-root user. If the RunAsUser
// field is not explicitly set then the kubelet may check the image for a specified user or
// perform defaulting to specify a user.
RunAsNonRoot bool
// Indicates that the container must run as a non-root user.
// If true, the Kubelet will validate the image at runtime to ensure that it
// does not run as UID 0 (root) and fail to start the container if it does.
// If unset or false, no such validation will be performed.
// May also be set in PodSecurityContext. If set in both SecurityContext and
// PodSecurityContext, the value specified in SecurityContext takes precedence.
RunAsNonRoot *bool `json:"runAsNonRoot,omitempty"`
}
// SELinuxOptions are the labels to be applied to the container.
type SELinuxOptions struct {
// SELinux user label
User string `json:"user,omitempty"`
// SELinux role label
Role string `json:"role,omitempty"`
// SELinux type label
Type string `json:"type,omitempty"`
// SELinux level label.
Level string `json:"level,omitempty"`
}
......
......@@ -289,6 +289,8 @@ func convert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *PodSpec, s conversi
return err
}
// the host namespace fields have to be handled here for backward compatibilty
// with v1.0.0
out.HostPID = in.SecurityContext.HostPID
out.HostNetwork = in.SecurityContext.HostNetwork
out.HostIPC = in.SecurityContext.HostIPC
......@@ -365,6 +367,9 @@ func convert_v1_PodSpec_To_api_PodSpec(in *PodSpec, out *api.PodSpec, s conversi
return err
}
}
// the host namespace fields have to be handled specially for backward compatibility
// with v1.0.0
if out.SecurityContext == nil {
out.SecurityContext = new(api.PodSecurityContext)
}
......@@ -415,6 +420,26 @@ func convert_api_PodSecurityContext_To_v1_PodSecurityContext(in *api.PodSecurity
}
out.SupplementalGroups = in.SupplementalGroups
if in.SELinuxOptions != nil {
out.SELinuxOptions = new(SELinuxOptions)
if err := convert_api_SELinuxOptions_To_v1_SELinuxOptions(in.SELinuxOptions, out.SELinuxOptions, s); err != nil {
return err
}
} else {
out.SELinuxOptions = nil
}
if in.RunAsUser != nil {
out.RunAsUser = new(int64)
*out.RunAsUser = *in.RunAsUser
} else {
out.RunAsUser = nil
}
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......@@ -424,5 +449,25 @@ func convert_v1_PodSecurityContext_To_api_PodSecurityContext(in *PodSecurityCont
}
out.SupplementalGroups = in.SupplementalGroups
if in.SELinuxOptions != nil {
out.SELinuxOptions = new(api.SELinuxOptions)
if err := convert_v1_SELinuxOptions_To_api_SELinuxOptions(in.SELinuxOptions, out.SELinuxOptions, s); err != nil {
return err
}
} else {
out.SELinuxOptions = nil
}
if in.RunAsUser != nil {
out.RunAsUser = new(int64)
*out.RunAsUser = *in.RunAsUser
} else {
out.RunAsUser = nil
}
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......@@ -2650,7 +2650,12 @@ func autoconvert_api_SecurityContext_To_v1_SecurityContext(in *api.SecurityConte
} else {
out.RunAsUser = nil
}
out.RunAsNonRoot = in.RunAsNonRoot
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......@@ -5666,7 +5671,12 @@ func autoconvert_v1_SecurityContext_To_api_SecurityContext(in *SecurityContext,
} else {
out.RunAsUser = nil
}
out.RunAsNonRoot = in.RunAsNonRoot
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......
......@@ -1509,6 +1509,26 @@ func deepCopy_v1_PodSecurityContext(in PodSecurityContext, out *PodSecurityConte
} else {
out.SupplementalGroups = nil
}
if in.SELinuxOptions != nil {
out.SELinuxOptions = new(SELinuxOptions)
if err := deepCopy_v1_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil {
return err
}
} else {
out.SELinuxOptions = nil
}
if in.RunAsUser != nil {
out.RunAsUser = new(int64)
*out.RunAsUser = *in.RunAsUser
} else {
out.RunAsUser = nil
}
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......@@ -1990,7 +2010,12 @@ func deepCopy_v1_SecurityContext(in SecurityContext, out *SecurityContext, c *co
} else {
out.RunAsUser = nil
}
out.RunAsNonRoot = in.RunAsNonRoot
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -1247,7 +1247,8 @@ type PodSpec struct {
// Use the host's ipc namespace.
// Optional: Default to false.
HostIPC bool `json:"hostIPC,omitempty"`
// SecurityContext holds pod-level security attributes and common container settings
// SecurityContext holds pod-level security attributes and common container settings.
// Optional: Defaults to empty. See type description for default values of each field.
SecurityContext *PodSecurityContext `json:"securityContext,omitempty"`
// ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec.
// If specified, these secrets will be passed to individual puller implementations for them to use. For example,
......@@ -1263,6 +1264,25 @@ type PodSecurityContext struct {
// 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"`
// SELinuxOptions is the SELinux context to be applied to all containers
// If unspecified, the container runtime will allocate a random SELinux context for each
// container. May also be set in SecurityContext. If set in
// both SecurityContext and PodSecurityContext, the value specified in SecurityContext
// takes precedence for that container.
SELinuxOptions *SELinuxOptions `json:"seLinuxOptions,omitempty"`
// The UID to run the entrypoint of the container process.
// Defaults to user specified in image metadata if unspecified.
// May also be set in SecurityContext. If set in both SecurityContext and
// PodSecurityContext, the value specified in SecurityContext takes precedence
// for that container.
RunAsUser *int64 `json:"runAsUser,omitempty"`
// Indicates that the container must run as a non-root user.
// If true, the Kubelet will validate the image at runtime to ensure that it
// does not run as UID 0 (root) and fail to start the container if it does.
// If unset or false, no such validation will be performed.
// May also be set in SecurityContext. If set in both SecurityContext and
// PodSecurityContext, the value specified in SecurityContext takes precedence.
RunAsNonRoot *bool `json:"runAsNonRoot,omitempty"`
}
// PodStatus represents information about the status of a pod. Status may trail the actual
......@@ -2474,50 +2494,44 @@ type DownwardAPIVolumeFile struct {
}
// SecurityContext holds security configuration that will be applied to a container.
// Some fields are present in both SecurityContext and PodSecurityContext. When both
// are set, the values in SecurityContext take precedence.
type SecurityContext struct {
// The linux kernel capabilites that should be added or removed.
// Default to Container.Capabilities if left unset.
// More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context
// The capabilities to add/drop when running containers.
// Defaults to the default set of capabilities granted by the container runtime.
Capabilities *Capabilities `json:"capabilities,omitempty"`
// Run the container in privileged mode.
// Default to Container.Privileged if left unset.
// More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context
// Run container in privileged mode.
// Processes in privileged containers are essentially equivalent to root on the host.
// Defaults to false.
Privileged *bool `json:"privileged,omitempty"`
// SELinuxOptions are the labels to be applied to the container
// and volumes.
// Options that control the SELinux labels applied.
// More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context
// The SELinux context to be applied to the container.
// If unspecified, the container runtime will allocate a random SELinux context for each
// container. May also be set in PodSecurityContext. If set in both SecurityContext and
// PodSecurityContext, the value specified in SecurityContext takes precedence.
SELinuxOptions *SELinuxOptions `json:"seLinuxOptions,omitempty"`
// RunAsUser is the UID to run the entrypoint of the container process.
// The user id that runs the first process in the container.
// More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context
// The UID to run the entrypoint of the container process.
// Defaults to user specified in image metadata if unspecified.
// May also be set in PodSecurityContext. If set in both SecurityContext and
// PodSecurityContext, the value specified in SecurityContext takes precedence.
RunAsUser *int64 `json:"runAsUser,omitempty"`
// RunAsNonRoot indicates that the container should be run as a non-root user. If the RunAsUser
// field is not explicitly set then the kubelet may check the image for a specified user or
// perform defaulting to specify a user.
RunAsNonRoot bool `json:"runAsNonRoot,omitempty"`
// Indicates that the container must run as a non-root user.
// If true, the Kubelet will validate the image at runtime to ensure that it
// does not run as UID 0 (root) and fail to start the container if it does.
// If unset or false, no such validation will be performed.
// May also be set in PodSecurityContext. If set in both SecurityContext and
// PodSecurityContext, the value specified in SecurityContext takes precedence.
RunAsNonRoot *bool `json:"runAsNonRoot,omitempty"`
}
// SELinuxOptions are the labels to be applied to the container
type SELinuxOptions struct {
// User is a SELinux user label that applies to the container.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md
User string `json:"user,omitempty"`
// Role is a SELinux role label that applies to the container.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md
Role string `json:"role,omitempty"`
// Type is a SELinux type label that applies to the container.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md
Type string `json:"type,omitempty"`
// Level is SELinux level label that applies to the container.
// More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md
Level string `json:"level,omitempty"`
}
......
......@@ -977,6 +977,9 @@ func (PodProxyOptions) SwaggerDoc() map[string]string {
var map_PodSecurityContext = map[string]string{
"": "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.",
"seLinuxOptions": "SELinuxOptions is the SELinux context to be applied to all containers If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.",
"runAsUser": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.",
"runAsNonRoot": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
}
func (PodSecurityContext) SwaggerDoc() map[string]string {
......@@ -998,7 +1001,7 @@ var map_PodSpec = map[string]string{
"hostNetwork": "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.",
"hostPID": "Use the host's pid namespace. Optional: Default to false.",
"hostIPC": "Use the host's ipc namespace. Optional: Default to false.",
"securityContext": "SecurityContext holds pod-level security attributes and common container settings",
"securityContext": "SecurityContext holds pod-level security attributes and common container settings. Optional: Defaults to empty. See type description for default values of each field.",
"imagePullSecrets": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: http://releases.k8s.io/HEAD/docs/user-guide/images.md#specifying-imagepullsecrets-on-a-pod",
}
......@@ -1193,10 +1196,10 @@ func (ResourceRequirements) SwaggerDoc() map[string]string {
var map_SELinuxOptions = map[string]string{
"": "SELinuxOptions are the labels to be applied to the container",
"user": "User is a SELinux user label that applies to the container. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md",
"role": "Role is a SELinux role label that applies to the container. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md",
"type": "Type is a SELinux type label that applies to the container. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md",
"level": "Level is SELinux level label that applies to the container. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md",
"user": "User is a SELinux user label that applies to the container.",
"role": "Role is a SELinux role label that applies to the container.",
"type": "Type is a SELinux type label that applies to the container.",
"level": "Level is SELinux level label that applies to the container.",
}
func (SELinuxOptions) SwaggerDoc() map[string]string {
......@@ -1234,12 +1237,12 @@ func (SecretVolumeSource) SwaggerDoc() map[string]string {
}
var map_SecurityContext = map[string]string{
"": "SecurityContext holds security configuration that will be applied to a container.",
"capabilities": "The linux kernel capabilites that should be added or removed. Default to Container.Capabilities if left unset. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context",
"privileged": "Run the container in privileged mode. Default to Container.Privileged if left unset. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context",
"seLinuxOptions": "SELinuxOptions are the labels to be applied to the container and volumes. Options that control the SELinux labels applied. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context",
"runAsUser": "RunAsUser is the UID to run the entrypoint of the container process. The user id that runs the first process in the container. More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context",
"runAsNonRoot": "RunAsNonRoot indicates that the container should be run as a non-root user. If the RunAsUser field is not explicitly set then the kubelet may check the image for a specified user or perform defaulting to specify a user.",
"": "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.",
"capabilities": "The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime.",
"privileged": "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.",
"seLinuxOptions": "The SELinux context to be applied to the container. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
"runAsUser": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
"runAsNonRoot": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in PodSecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
}
func (SecurityContext) SwaggerDoc() map[string]string {
......
......@@ -472,6 +472,26 @@ func deepCopy_api_PodSecurityContext(in api.PodSecurityContext, out *api.PodSecu
} else {
out.SupplementalGroups = nil
}
if in.SELinuxOptions != nil {
out.SELinuxOptions = new(api.SELinuxOptions)
if err := deepCopy_api_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil {
return err
}
} else {
out.SELinuxOptions = nil
}
if in.RunAsUser != nil {
out.RunAsUser = new(int64)
*out.RunAsUser = *in.RunAsUser
} else {
out.RunAsUser = nil
}
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......@@ -656,7 +676,12 @@ func deepCopy_api_SecurityContext(in api.SecurityContext, out *api.SecurityConte
} else {
out.RunAsUser = nil
}
out.RunAsNonRoot = in.RunAsNonRoot
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......
......@@ -333,6 +333,26 @@ func convert_api_PodSecurityContext_To_v1_PodSecurityContext(in *api.PodSecurity
}
out.SupplementalGroups = in.SupplementalGroups
if in.SELinuxOptions != nil {
out.SELinuxOptions = new(v1.SELinuxOptions)
if err := convert_api_SELinuxOptions_To_v1_SELinuxOptions(in.SELinuxOptions, out.SELinuxOptions, s); err != nil {
return err
}
} else {
out.SELinuxOptions = nil
}
if in.RunAsUser != nil {
out.RunAsUser = new(int64)
*out.RunAsUser = *in.RunAsUser
} else {
out.RunAsUser = nil
}
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......@@ -342,5 +362,25 @@ func convert_v1_PodSecurityContext_To_api_PodSecurityContext(in *v1.PodSecurityC
}
out.SupplementalGroups = in.SupplementalGroups
if in.SELinuxOptions != nil {
out.SELinuxOptions = new(api.SELinuxOptions)
if err := convert_v1_SELinuxOptions_To_api_SELinuxOptions(in.SELinuxOptions, out.SELinuxOptions, s); err != nil {
return err
}
} else {
out.SELinuxOptions = nil
}
if in.RunAsUser != nil {
out.RunAsUser = new(int64)
*out.RunAsUser = *in.RunAsUser
} else {
out.RunAsUser = nil
}
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......@@ -889,7 +889,12 @@ func autoconvert_api_SecurityContext_To_v1_SecurityContext(in *api.SecurityConte
} else {
out.RunAsUser = nil
}
out.RunAsNonRoot = in.RunAsNonRoot
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......@@ -1944,7 +1949,12 @@ func autoconvert_v1_SecurityContext_To_api_SecurityContext(in *v1.SecurityContex
} else {
out.RunAsUser = nil
}
out.RunAsNonRoot = in.RunAsNonRoot
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......
......@@ -505,6 +505,26 @@ func deepCopy_v1_PodSecurityContext(in v1.PodSecurityContext, out *v1.PodSecurit
} else {
out.SupplementalGroups = nil
}
if in.SELinuxOptions != nil {
out.SELinuxOptions = new(v1.SELinuxOptions)
if err := deepCopy_v1_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil {
return err
}
} else {
out.SELinuxOptions = nil
}
if in.RunAsUser != nil {
out.RunAsUser = new(int64)
*out.RunAsUser = *in.RunAsUser
} else {
out.RunAsUser = nil
}
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......@@ -693,7 +713,12 @@ func deepCopy_v1_SecurityContext(in v1.SecurityContext, out *v1.SecurityContext,
} else {
out.RunAsUser = nil
}
out.RunAsNonRoot = in.RunAsNonRoot
if in.RunAsNonRoot != nil {
out.RunAsNonRoot = new(bool)
*out.RunAsNonRoot = *in.RunAsNonRoot
} else {
out.RunAsNonRoot = nil
}
return nil
}
......
......@@ -1911,7 +1911,7 @@ func (dm *DockerManager) SyncPod(pod *api.Pod, runningPod kubecontainer.Pod, pod
continue
}
if container.SecurityContext != nil && container.SecurityContext.RunAsNonRoot {
if container.SecurityContext != nil && container.SecurityContext.RunAsNonRoot != nil && *container.SecurityContext.RunAsNonRoot {
err := dm.verifyNonRoot(container)
dm.updateReasonCache(pod, container, "VerifyNonRootError", err)
if err != nil {
......
......@@ -38,11 +38,12 @@ type SimpleSecurityContextProvider struct{}
// The security context provider can make changes to the Config with which
// the container is created.
func (p SimpleSecurityContextProvider) ModifyContainerConfig(pod *api.Pod, container *api.Container, config *docker.Config) {
if container.SecurityContext == nil {
effectiveSC := determineEffectiveSecurityContext(pod, container)
if effectiveSC == nil {
return
}
if container.SecurityContext.RunAsUser != nil {
config.User = strconv.FormatInt(*container.SecurityContext.RunAsUser, 10)
if effectiveSC.RunAsUser != nil {
config.User = strconv.Itoa(int(*effectiveSC.RunAsUser))
}
}
......@@ -62,30 +63,32 @@ func (p SimpleSecurityContextProvider) ModifyHostConfig(pod *api.Pod, container
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)
hostConfig.GroupAdd[i] = strconv.Itoa(int(group))
}
}
}
// Apply container security context
if container.SecurityContext == nil {
// Apply effective security context for container
effectiveSC := determineEffectiveSecurityContext(pod, container)
if effectiveSC == nil {
return
}
if container.SecurityContext.Privileged != nil {
hostConfig.Privileged = *container.SecurityContext.Privileged
if effectiveSC.Privileged != nil {
hostConfig.Privileged = *effectiveSC.Privileged
}
if container.SecurityContext.Capabilities != nil {
add, drop := makeCapabilites(container.SecurityContext.Capabilities.Add, container.SecurityContext.Capabilities.Drop)
if effectiveSC.Capabilities != nil {
add, drop := makeCapabilites(effectiveSC.Capabilities.Add, effectiveSC.Capabilities.Drop)
hostConfig.CapAdd = add
hostConfig.CapDrop = drop
}
if container.SecurityContext.SELinuxOptions != nil {
hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelUser, container.SecurityContext.SELinuxOptions.User)
hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelRole, container.SecurityContext.SELinuxOptions.Role)
hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelType, container.SecurityContext.SELinuxOptions.Type)
hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelLevel, container.SecurityContext.SELinuxOptions.Level)
if effectiveSC.SELinuxOptions != nil {
hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelUser, effectiveSC.SELinuxOptions.User)
hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelRole, effectiveSC.SELinuxOptions.Role)
hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelType, effectiveSC.SELinuxOptions.Type)
hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelLevel, effectiveSC.SELinuxOptions.Level)
}
}
......@@ -112,3 +115,69 @@ func makeCapabilites(capAdd []api.Capability, capDrop []api.Capability) ([]strin
}
return addCaps, dropCaps
}
func determineEffectiveSecurityContext(pod *api.Pod, container *api.Container) *api.SecurityContext {
effectiveSc := securityContextFromPodSecurityContext(pod)
containerSc := container.SecurityContext
if effectiveSc == nil && containerSc == nil {
return nil
}
if effectiveSc != nil && containerSc == nil {
return effectiveSc
}
if effectiveSc == nil && containerSc != nil {
return containerSc
}
if containerSc.SELinuxOptions != nil {
effectiveSc.SELinuxOptions = new(api.SELinuxOptions)
*effectiveSc.SELinuxOptions = *containerSc.SELinuxOptions
}
if containerSc.Capabilities != nil {
effectiveSc.Capabilities = new(api.Capabilities)
*effectiveSc.Capabilities = *containerSc.Capabilities
}
if containerSc.Privileged != nil {
effectiveSc.Privileged = new(bool)
*effectiveSc.Privileged = *containerSc.Privileged
}
if containerSc.RunAsUser != nil {
effectiveSc.RunAsUser = new(int64)
*effectiveSc.RunAsUser = *containerSc.RunAsUser
}
if containerSc.RunAsNonRoot != nil {
effectiveSc.RunAsNonRoot = new(bool)
*effectiveSc.RunAsNonRoot = *containerSc.RunAsNonRoot
}
return effectiveSc
}
func securityContextFromPodSecurityContext(pod *api.Pod) *api.SecurityContext {
if pod.Spec.SecurityContext == nil {
return nil
}
synthesized := &api.SecurityContext{}
if pod.Spec.SecurityContext.SELinuxOptions != nil {
synthesized.SELinuxOptions = &api.SELinuxOptions{}
*synthesized.SELinuxOptions = *pod.Spec.SecurityContext.SELinuxOptions
}
if pod.Spec.SecurityContext.RunAsUser != nil {
synthesized.RunAsUser = new(int64)
*synthesized.RunAsUser = *pod.Spec.SecurityContext.RunAsUser
}
if pod.Spec.SecurityContext.RunAsNonRoot != nil {
synthesized.RunAsNonRoot = new(bool)
*synthesized.RunAsNonRoot = *pod.Spec.SecurityContext.RunAsNonRoot
}
return synthesized
}
......@@ -28,94 +28,148 @@ import (
)
func TestModifyContainerConfig(t *testing.T) {
var uid int64 = 1
testCases := map[string]struct {
securityContext *api.SecurityContext
expected *docker.Config
var uid int64 = 123
var overrideUid int64 = 321
cases := []struct {
name string
podSc *api.PodSecurityContext
sc *api.SecurityContext
expected *docker.Config
}{
"modify config, value set for user": {
securityContext: &api.SecurityContext{
{
name: "container.SecurityContext.RunAsUser set",
sc: &api.SecurityContext{
RunAsUser: &uid,
},
expected: &docker.Config{
User: strconv.FormatInt(uid, 10),
},
},
"modify config, nil user value": {
securityContext: &api.SecurityContext{},
expected: &docker.Config{},
{
name: "no RunAsUser value set",
sc: &api.SecurityContext{},
expected: &docker.Config{},
},
{
name: "pod.Spec.SecurityContext.RunAsUser set",
podSc: &api.PodSecurityContext{
RunAsUser: &uid,
},
expected: &docker.Config{
User: strconv.FormatInt(uid, 10),
},
},
{
name: "container.SecurityContext.RunAsUser overrides pod.Spec.SecurityContext.RunAsUser",
podSc: &api.PodSecurityContext{
RunAsUser: &uid,
},
sc: &api.SecurityContext{
RunAsUser: &overrideUid,
},
expected: &docker.Config{
User: strconv.FormatInt(overrideUid, 10),
},
},
}
provider := NewSimpleSecurityContextProvider()
dummyContainer := &api.Container{}
for k, v := range testCases {
dummyContainer.SecurityContext = v.securityContext
for _, tc := range cases {
pod := &api.Pod{Spec: api.PodSpec{SecurityContext: tc.podSc}}
dummyContainer.SecurityContext = tc.sc
dockerCfg := &docker.Config{}
provider.ModifyContainerConfig(nil, dummyContainer, dockerCfg)
if !reflect.DeepEqual(v.expected, dockerCfg) {
t.Errorf("unexpected modification of docker config for %s. Expected: %#v Got: %#v", k, v.expected, dockerCfg)
provider.ModifyContainerConfig(pod, dummyContainer, dockerCfg)
if e, a := tc.expected, dockerCfg; !reflect.DeepEqual(e, a) {
t.Errorf("%v: unexpected modification of docker config\nExpected:\n\n%#v\n\nGot:\n\n%#v", tc.name, e, a)
}
}
}
func TestModifyHostConfig(t *testing.T) {
nilPrivSC := fullValidSecurityContext()
nilPrivSC.Privileged = nil
nilPrivHC := fullValidHostConfig()
nilPrivHC.Privileged = false
priv := true
setPrivSC := &api.SecurityContext{}
setPrivSC.Privileged = &priv
setPrivHC := &docker.HostConfig{
Privileged: true,
}
nilCapsSC := fullValidSecurityContext()
nilCapsSC.Capabilities = nil
nilCapsHC := fullValidHostConfig()
nilCapsHC.CapAdd = *new([]string)
nilCapsHC.CapDrop = *new([]string)
setCapsHC := &docker.HostConfig{
CapAdd: []string{"addCapA", "addCapB"},
CapDrop: []string{"dropCapA", "dropCapB"},
}
nilSELinuxSC := fullValidSecurityContext()
nilSELinuxSC.SELinuxOptions = nil
nilSELinuxHC := fullValidHostConfig()
nilSELinuxHC.SecurityOpt = *new([]string)
setSELinuxHC := &docker.HostConfig{}
setSELinuxHC.SecurityOpt = []string{
fmt.Sprintf("%s:%s", dockerLabelUser, "user"),
fmt.Sprintf("%s:%s", dockerLabelRole, "role"),
fmt.Sprintf("%s:%s", dockerLabelType, "type"),
fmt.Sprintf("%s:%s", dockerLabelLevel, "level"),
}
seLinuxLabelsSC := fullValidSecurityContext()
seLinuxLabelsHC := fullValidHostConfig()
// seLinuxLabelsSC := fullValidSecurityContext()
// seLinuxLabelsHC := fullValidHostConfig()
testCases := map[string]struct {
securityContext *api.SecurityContext
expected *docker.HostConfig
cases := []struct {
name string
podSc *api.PodSecurityContext
sc *api.SecurityContext
expected *docker.HostConfig
}{
"full settings": {
securityContext: fullValidSecurityContext(),
expected: fullValidHostConfig(),
{
name: "fully set container.SecurityContext",
sc: fullValidSecurityContext(),
expected: fullValidHostConfig(),
},
"nil privileged": {
securityContext: nilPrivSC,
expected: nilPrivHC,
{
name: "container.SecurityContext.Privileged",
sc: setPrivSC,
expected: setPrivHC,
},
"nil capabilities": {
securityContext: nilCapsSC,
expected: nilCapsHC,
{
name: "container.SecurityContext.Capabilities",
sc: &api.SecurityContext{
Capabilities: inputCapabilities(),
},
expected: setCapsHC,
},
"nil selinux options": {
securityContext: nilSELinuxSC,
expected: nilSELinuxHC,
{
name: "container.SecurityContext.SELinuxOptions",
sc: &api.SecurityContext{
SELinuxOptions: inputSELinuxOptions(),
},
expected: setSELinuxHC,
},
"selinux labels": {
securityContext: seLinuxLabelsSC,
expected: seLinuxLabelsHC,
{
name: "pod.Spec.SecurityContext.SELinuxOptions",
podSc: &api.PodSecurityContext{
SELinuxOptions: inputSELinuxOptions(),
},
expected: setSELinuxHC,
},
{
name: "container.SecurityContext overrides pod.Spec.SecurityContext",
podSc: overridePodSecurityContext(),
sc: fullValidSecurityContext(),
expected: fullValidHostConfig(),
},
}
provider := NewSimpleSecurityContextProvider()
dummyContainer := &api.Container{}
dummyPod := &api.Pod{
Spec: apitesting.DeepEqualSafePodSpec(),
}
for k, v := range testCases {
dummyContainer.SecurityContext = v.securityContext
for _, tc := range cases {
pod := &api.Pod{Spec: api.PodSpec{SecurityContext: tc.podSc}}
dummyContainer.SecurityContext = tc.sc
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)
provider.ModifyHostConfig(pod, dummyContainer, dockerCfg)
if e, a := tc.expected, dockerCfg; !reflect.DeepEqual(e, a) {
t.Errorf("%v: unexpected modification of host config\nExpected:\n\n%#v\n\nGot:\n\n%#v", tc.name, e, a)
}
}
}
......@@ -189,20 +243,45 @@ func TestModifySecurityOption(t *testing.T) {
}
}
func overridePodSecurityContext() *api.PodSecurityContext {
return &api.PodSecurityContext{
SELinuxOptions: &api.SELinuxOptions{
User: "user2",
Role: "role2",
Type: "type2",
Level: "level2",
},
}
}
func fullValidPodSecurityContext() *api.PodSecurityContext {
return &api.PodSecurityContext{
SELinuxOptions: inputSELinuxOptions(),
}
}
func fullValidSecurityContext() *api.SecurityContext {
priv := true
return &api.SecurityContext{
Privileged: &priv,
Capabilities: &api.Capabilities{
Add: []api.Capability{"addCapA", "addCapB"},
Drop: []api.Capability{"dropCapA", "dropCapB"},
},
SELinuxOptions: &api.SELinuxOptions{
User: "user",
Role: "role",
Type: "type",
Level: "level",
},
Privileged: &priv,
Capabilities: inputCapabilities(),
SELinuxOptions: inputSELinuxOptions(),
}
}
func inputCapabilities() *api.Capabilities {
return &api.Capabilities{
Add: []api.Capability{"addCapA", "addCapB"},
Drop: []api.Capability{"dropCapA", "dropCapB"},
}
}
func inputSELinuxOptions() *api.SELinuxOptions {
return &api.SELinuxOptions{
User: "user",
Role: "role",
Type: "type",
Level: "level",
}
}
......
......@@ -46,8 +46,7 @@ func NewSecurityContextDeny(client client.Interface) admission.Interface {
}
}
// Admit will deny any SecurityContext that defines options that were not previously available in the api.Container
// struct (Capabilities and Privileged)
// Admit will deny any pod that defines SELinuxOptions or RunAsUser.
func (p *plugin) Admit(a admission.Attributes) (err error) {
if a.GetResource() != string(api.ResourcePods) {
return nil
......@@ -61,6 +60,14 @@ func (p *plugin) Admit(a admission.Attributes) (err error) {
if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.SupplementalGroups != nil {
return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("SecurityContext.SupplementalGroups is forbidden"))
}
if pod.Spec.SecurityContext != nil {
if pod.Spec.SecurityContext.SELinuxOptions != nil {
return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("pod.Spec.SecurityContext.SELinuxOptions is forbidden"))
}
if pod.Spec.SecurityContext.RunAsUser != nil {
return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("pod.Spec.SecurityContext.RunAsUser is forbidden"))
}
}
for _, v := range pod.Spec.Containers {
if v.SecurityContext != nil {
......
......@@ -29,37 +29,64 @@ func TestAdmission(t *testing.T) {
var runAsUser int64 = 1
priv := true
successCases := map[string]*api.SecurityContext{
"no sc": nil,
"empty sc": {},
"valid sc": {Privileged: &priv, Capabilities: &api.Capabilities{}},
}
pod := api.Pod{
Spec: api.PodSpec{
Containers: []api.Container{
{},
},
cases := []struct {
name string
sc *api.SecurityContext
podSc *api.PodSecurityContext
expectError bool
}{
{
name: "unset",
},
{
name: "empty container.SecurityContext",
sc: &api.SecurityContext{},
},
{
name: "empty pod.Spec.SecurityContext",
podSc: &api.PodSecurityContext{},
},
{
name: "valid container.SecurityContext",
sc: &api.SecurityContext{Privileged: &priv, Capabilities: &api.Capabilities{}},
},
{
name: "valid pod.Spec.SecurityContext",
podSc: &api.PodSecurityContext{},
},
{
name: "container.SecurityContext.RunAsUser",
sc: &api.SecurityContext{RunAsUser: &runAsUser},
expectError: true,
},
{
name: "container.SecurityContext.SELinuxOptions",
sc: &api.SecurityContext{SELinuxOptions: &api.SELinuxOptions{}},
expectError: true,
},
{
name: "pod.Spec.SecurityContext.RunAsUser",
podSc: &api.PodSecurityContext{RunAsUser: &runAsUser},
expectError: true,
},
{
name: "pod.Spec.SecurityContext.SELinuxOptions",
podSc: &api.PodSecurityContext{SELinuxOptions: &api.SELinuxOptions{}},
expectError: true,
},
}
for k, v := range successCases {
pod.Spec.Containers[0].SecurityContext = v
err := handler.Admit(admission.NewAttributesRecord(&pod, "Pod", "foo", "name", string(api.ResourcePods), "", "ignored", nil))
if err != nil {
t.Errorf("Unexpected error returned from admission handler for case %s", k)
}
}
errorCases := map[string]*api.SecurityContext{
"run as user": {RunAsUser: &runAsUser},
"se linux optons": {SELinuxOptions: &api.SELinuxOptions{}},
"mixed settings": {Privileged: &priv, RunAsUser: &runAsUser, SELinuxOptions: &api.SELinuxOptions{}},
}
for k, v := range errorCases {
pod.Spec.Containers[0].SecurityContext = v
err := handler.Admit(admission.NewAttributesRecord(&pod, "Pod", "foo", "name", string(api.ResourcePods), "", "ignored", nil))
if err == nil {
t.Errorf("Expected error returned from admission handler for case %s", k)
for _, tc := range cases {
pod := pod()
pod.Spec.SecurityContext = tc.podSc
pod.Spec.Containers[0].SecurityContext = tc.sc
err := handler.Admit(admission.NewAttributesRecord(pod, "Pod", "foo", "name", string(api.ResourcePods), "", "ignored", nil))
if err != nil && !tc.expectError {
t.Errorf("%v: unexpected error: %v", tc.name, err)
} else if err == nil && tc.expectError {
t.Errorf("%v: expected error", tc.name)
}
}
}
......@@ -118,3 +145,13 @@ func TestHandles(t *testing.T) {
}
}
}
func pod() *api.Pod {
return &api.Pod{
Spec: api.PodSpec{
Containers: []api.Container{
{},
},
},
}
}
......@@ -23,13 +23,15 @@ limitations under the License.
package e2e
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util"
. "github.com/onsi/ginkgo"
)
func getSecurityContextTestPod() *api.Pod {
func scTestPod() *api.Pod {
podName := "security-context-" + string(util.NewUUID())
pod := &api.Pod{
ObjectMeta: api.ObjectMeta{
......@@ -55,11 +57,35 @@ var _ = Describe("[Skipped] Security Context", func() {
framework := NewFramework("security-context")
It("should support pod.Spec.SecurityContext.SupplementalGroups", func() {
pod := getSecurityContextTestPod()
pod := scTestPod()
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)
})
It("should support pod.Spec.SecurityContext.RunAsUser", func() {
pod := scTestPod()
var uid int64 = 1001
pod.Spec.SecurityContext.RunAsUser = &uid
pod.Spec.Containers[0].Command = []string{"sh", "-c", "id -u"}
framework.TestContainerOutput("pod.Spec.SecurityContext.RunAsUser", pod, 0, []string{
fmt.Sprintf("%v", uid),
})
})
It("should support container.SecurityContext.RunAsUser", func() {
pod := scTestPod()
var uid int64 = 1001
var overrideUid int64 = 1002
pod.Spec.SecurityContext.RunAsUser = &uid
pod.Spec.Containers[0].SecurityContext = new(api.SecurityContext)
pod.Spec.Containers[0].SecurityContext.RunAsUser = &overrideUid
pod.Spec.Containers[0].Command = []string{"sh", "-c", "id -u"}
framework.TestContainerOutput("pod.Spec.SecurityContext.RunAsUser", pod, 0, []string{
fmt.Sprintf("%v", overrideUid),
})
})
})
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment