Merge pull request #14705 from pmorie/pod-sc-inline

PodSecurityContext with inline fields
This commit is contained in:
Paul Morie 2015-10-22 03:12:16 -04:00
commit e3642f1b3f
22 changed files with 22183 additions and 21218 deletions

View File

@ -12625,7 +12625,7 @@
}, },
"securityContext": { "securityContext": {
"$ref": "v1.PodSecurityContext", "$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": { "imagePullSecrets": {
"type": "array", "type": "array",
@ -13115,28 +13115,28 @@
}, },
"v1.SecurityContext": { "v1.SecurityContext": {
"id": "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": { "properties": {
"capabilities": { "capabilities": {
"$ref": "v1.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": { "privileged": {
"type": "boolean", "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": { "seLinuxOptions": {
"$ref": "v1.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": { "runAsUser": {
"type": "integer", "type": "integer",
"format": "int64", "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": { "runAsNonRoot": {
"type": "boolean", "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": { "properties": {
"user": { "user": {
"type": "string", "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": { "role": {
"type": "string", "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": {
"type": "string", "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": { "level": {
"type": "string", "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" "$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." "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."
} }
} }
}, },

View File

@ -2959,7 +2959,7 @@
}, },
"securityContext": { "securityContext": {
"$ref": "v1.PodSecurityContext", "$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": { "imagePullSecrets": {
"type": "array", "type": "array",
@ -3763,28 +3763,28 @@
}, },
"v1.SecurityContext": { "v1.SecurityContext": {
"id": "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": { "properties": {
"capabilities": { "capabilities": {
"$ref": "v1.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": { "privileged": {
"type": "boolean", "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": { "seLinuxOptions": {
"$ref": "v1.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": { "runAsUser": {
"type": "integer", "type": "integer",
"format": "int64", "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": { "runAsNonRoot": {
"type": "boolean", "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": { "properties": {
"user": { "user": {
"type": "string", "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": { "role": {
"type": "string", "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": {
"type": "string", "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": { "level": {
"type": "string", "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" "$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." "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."
} }
} }
}, },

View File

@ -1494,6 +1494,26 @@ func deepCopy_api_PodSecurityContext(in PodSecurityContext, out *PodSecurityCont
} else { } else {
out.SupplementalGroups = nil 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 return nil
} }
@ -1966,7 +1986,12 @@ func deepCopy_api_SecurityContext(in SecurityContext, out *SecurityContext, c *c
} else { } else {
out.RunAsUser = nil 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 return nil
} }

View File

@ -101,8 +101,10 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
} }
s.TerminationGracePeriodSeconds = &ttl s.TerminationGracePeriodSeconds = &ttl
c.Fuzz(s.SecurityContext)
if s.SecurityContext == nil { if s.SecurityContext == nil {
s.SecurityContext = &api.PodSecurityContext{} s.SecurityContext = new(api.PodSecurityContext)
} }
}, },
func(j *api.PodPhase, c fuzz.Continue) { 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) { func(sc *api.SecurityContext, c fuzz.Continue) {
c.FuzzNoCustom(sc) // fuzz self without calling this function again c.FuzzNoCustom(sc) // fuzz self without calling this function again
priv := c.RandBool() if c.RandBool() {
sc.Privileged = &priv priv := c.RandBool()
sc.Capabilities = &api.Capabilities{ sc.Privileged = &priv
Add: make([]api.Capability, 0), }
Drop: make([]api.Capability, 0),
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) { func(e *api.Event, c fuzz.Continue) {
c.FuzzNoCustom(e) // fuzz self without calling this function again c.FuzzNoCustom(e) // fuzz self without calling this function again

File diff suppressed because it is too large Load Diff

View File

@ -789,7 +789,8 @@ type Container struct {
TerminationMessagePath string `json:"terminationMessagePath,omitempty"` TerminationMessagePath string `json:"terminationMessagePath,omitempty"`
// Required: Policy for pulling images for this container // Required: Policy for pulling images for this container
ImagePullPolicy PullPolicy `json:"imagePullPolicy"` 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"` SecurityContext *SecurityContext `json:"securityContext,omitempty"`
// Variables for interactive containers, these have very specialized use-cases (e.g. debugging) // 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 // the scheduler simply schedules this pod onto that node, assuming that it fits resource
// requirements. // requirements.
NodeName string `json:"nodeName,omitempty"` 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"` 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. // 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, // 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. // 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 { type PodSecurityContext struct {
// Use the host's network namespace. If this option is set, the ports that will be // Use the host's network namespace. If this option is set, the ports that will be
// used must be specified. // used must be specified.
// Optional: Default to false // Optional: Default to false
HostNetwork bool `json:"hostNetwork,omitempty"` HostNetwork bool `json:"hostNetwork,omitempty"`
// Use the host's pid namespace. // Use the host's pid namespace.
// Optional: Default to false. // Optional: Default to false.
HostPID bool `json:"hostPID,omitempty"` HostPID bool `json:"hostPID,omitempty"`
@ -1014,6 +1017,25 @@ type PodSecurityContext struct {
// as. This will be applied to all containers in the pod in // as. This will be applied to all containers in the pod in
// addition to the primary group of the container. // addition to the primary group of the container.
SupplementalGroups []int64 `json:"supplementalGroups,omitempty"` 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 // 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"` Items []ComponentStatus `json:"items"`
} }
// SecurityContext holds security configuration that will be applied to a container. SecurityContext // SecurityContext holds security configuration that will be applied to a container.
// contains duplication of some existing fields from the Container resource. These duplicate fields // Some fields are present in both SecurityContext and PodSecurityContext. When both
// will be populated based on the Container configuration if they are not set. Defining them on // are set, the values in SecurityContext take precedence.
// both the Container AND the SecurityContext will result in an error.
type SecurityContext struct { 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"` Capabilities *Capabilities `json:"capabilities,omitempty"`
// Run container in privileged mode.
// Run the container in privileged mode // Processes in privileged containers are essentially equivalent to root on the host.
// Defaults to false.
Privileged *bool `json:"privileged,omitempty"` Privileged *bool `json:"privileged,omitempty"`
// The SELinux context to be applied to the container.
// SELinuxOptions are the labels to be applied to the container // If unspecified, the container runtime will allocate a random SELinux context for each
// and volumes // 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"` SELinuxOptions *SELinuxOptions `json:"seLinuxOptions,omitempty"`
// The UID to run the entrypoint of the container process.
// RunAsUser is 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"` RunAsUser *int64 `json:"runAsUser,omitempty"`
// Indicates that the container must run as a non-root user.
// RunAsNonRoot indicates that the container should be run as a non-root user. If the RunAsUser // If true, the Kubelet will validate the image at runtime to ensure that it
// field is not explicitly set then the kubelet may check the image for a specified user or // does not run as UID 0 (root) and fail to start the container if it does.
// perform defaulting to specify a user. // If unset or false, no such validation will be performed.
RunAsNonRoot bool // 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. // SELinuxOptions are the labels to be applied to the container.
type SELinuxOptions struct { type SELinuxOptions struct {
// SELinux user label // SELinux user label
User string `json:"user,omitempty"` User string `json:"user,omitempty"`
// SELinux role label // SELinux role label
Role string `json:"role,omitempty"` Role string `json:"role,omitempty"`
// SELinux type label // SELinux type label
Type string `json:"type,omitempty"` Type string `json:"type,omitempty"`
// SELinux level label. // SELinux level label.
Level string `json:"level,omitempty"` Level string `json:"level,omitempty"`
} }

View File

@ -289,6 +289,8 @@ func convert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *PodSpec, s conversi
return err return err
} }
// the host namespace fields have to be handled here for backward compatibilty
// with v1.0.0
out.HostPID = in.SecurityContext.HostPID out.HostPID = in.SecurityContext.HostPID
out.HostNetwork = in.SecurityContext.HostNetwork out.HostNetwork = in.SecurityContext.HostNetwork
out.HostIPC = in.SecurityContext.HostIPC 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 return err
} }
} }
// the host namespace fields have to be handled specially for backward compatibility
// with v1.0.0
if out.SecurityContext == nil { if out.SecurityContext == nil {
out.SecurityContext = new(api.PodSecurityContext) out.SecurityContext = new(api.PodSecurityContext)
} }
@ -415,6 +420,26 @@ func convert_api_PodSecurityContext_To_v1_PodSecurityContext(in *api.PodSecurity
} }
out.SupplementalGroups = in.SupplementalGroups 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 return nil
} }
@ -424,5 +449,25 @@ func convert_v1_PodSecurityContext_To_api_PodSecurityContext(in *PodSecurityCont
} }
out.SupplementalGroups = in.SupplementalGroups 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 return nil
} }

View File

@ -2650,7 +2650,12 @@ func autoconvert_api_SecurityContext_To_v1_SecurityContext(in *api.SecurityConte
} else { } else {
out.RunAsUser = nil 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 return nil
} }
@ -5666,7 +5671,12 @@ func autoconvert_v1_SecurityContext_To_api_SecurityContext(in *SecurityContext,
} else { } else {
out.RunAsUser = nil 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 return nil
} }

View File

@ -1509,6 +1509,26 @@ func deepCopy_v1_PodSecurityContext(in PodSecurityContext, out *PodSecurityConte
} else { } else {
out.SupplementalGroups = nil 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 return nil
} }
@ -1990,7 +2010,12 @@ func deepCopy_v1_SecurityContext(in SecurityContext, out *SecurityContext, c *co
} else { } else {
out.RunAsUser = nil 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 return nil
} }

File diff suppressed because it is too large Load Diff

View File

@ -1247,7 +1247,8 @@ type PodSpec struct {
// Use the host's ipc namespace. // Use the host's ipc namespace.
// Optional: Default to false. // Optional: Default to false.
HostIPC bool `json:"hostIPC,omitempty"` 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"` 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. // 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, // 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 // as. This will be applied to all containers in the pod in
// addition to the primary group of the container. // addition to the primary group of the container.
SupplementalGroups []int64 `json:"supplementalGroups,omitempty"` 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 // 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. // 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 { type SecurityContext struct {
// The linux kernel capabilites that should be added or removed. // The capabilities to add/drop when running containers.
// Default to Container.Capabilities if left unset. // Defaults to the default set of capabilities granted by the container runtime.
// More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context
Capabilities *Capabilities `json:"capabilities,omitempty"` Capabilities *Capabilities `json:"capabilities,omitempty"`
// Run container in privileged mode.
// Run the container in privileged mode. // Processes in privileged containers are essentially equivalent to root on the host.
// Default to Container.Privileged if left unset. // Defaults to false.
// More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context
Privileged *bool `json:"privileged,omitempty"` Privileged *bool `json:"privileged,omitempty"`
// The SELinux context to be applied to the container.
// SELinuxOptions are the labels to be applied to the container // If unspecified, the container runtime will allocate a random SELinux context for each
// and volumes. // container. May also be set in PodSecurityContext. If set in both SecurityContext and
// Options that control the SELinux labels applied. // PodSecurityContext, the value specified in SecurityContext takes precedence.
// More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context
SELinuxOptions *SELinuxOptions `json:"seLinuxOptions,omitempty"` SELinuxOptions *SELinuxOptions `json:"seLinuxOptions,omitempty"`
// The UID to run the entrypoint of the container process.
// RunAsUser is the UID to run the entrypoint of the container process. // Defaults to user specified in image metadata if unspecified.
// The user id that runs the first process in the container. // May also be set in PodSecurityContext. If set in both SecurityContext and
// More info: http://releases.k8s.io/HEAD/docs/design/security_context.md#security-context // PodSecurityContext, the value specified in SecurityContext takes precedence.
RunAsUser *int64 `json:"runAsUser,omitempty"` RunAsUser *int64 `json:"runAsUser,omitempty"`
// Indicates that the container must run as a non-root user.
// RunAsNonRoot indicates that the container should be run as a non-root user. If the RunAsUser // If true, the Kubelet will validate the image at runtime to ensure that it
// field is not explicitly set then the kubelet may check the image for a specified user or // does not run as UID 0 (root) and fail to start the container if it does.
// perform defaulting to specify a user. // If unset or false, no such validation will be performed.
RunAsNonRoot bool `json:"runAsNonRoot,omitempty"` // 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 // SELinuxOptions are the labels to be applied to the container
type SELinuxOptions struct { type SELinuxOptions struct {
// User is a SELinux user label that applies to the container. // 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"` User string `json:"user,omitempty"`
// Role is a SELinux role label that applies to the container. // 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"` Role string `json:"role,omitempty"`
// Type is a SELinux type label that applies to the container. // 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"` Type string `json:"type,omitempty"`
// Level is SELinux level label that applies to the container. // 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"` Level string `json:"level,omitempty"`
} }

View File

@ -977,6 +977,9 @@ func (PodProxyOptions) SwaggerDoc() map[string]string {
var map_PodSecurityContext = 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.", "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 { 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.", "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.", "hostPID": "Use the host's pid namespace. Optional: Default to false.",
"hostIPC": "Use the host's ipc 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", "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{ var map_SELinuxOptions = map[string]string{
"": "SELinuxOptions are the labels to be applied to the container", "": "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", "user": "User is a SELinux user label that applies to the container.",
"role": "Role is a SELinux role 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.",
"type": "Type is a SELinux type 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.",
"level": "Level is SELinux level 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.",
} }
func (SELinuxOptions) SwaggerDoc() map[string]string { func (SELinuxOptions) SwaggerDoc() map[string]string {
@ -1234,12 +1237,12 @@ func (SecretVolumeSource) SwaggerDoc() map[string]string {
} }
var map_SecurityContext = map[string]string{ var map_SecurityContext = map[string]string{
"": "SecurityContext holds security configuration that will be applied to a container.", "": "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 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", "capabilities": "The capabilities to add/drop when running containers. Defaults to the default set of capabilities granted by the container runtime.",
"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", "privileged": "Run container in privileged mode. Processes in privileged containers are essentially equivalent to root on the host. Defaults to false.",
"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", "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": "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", "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": "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": "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 { func (SecurityContext) SwaggerDoc() map[string]string {

View File

@ -472,6 +472,26 @@ func deepCopy_api_PodSecurityContext(in api.PodSecurityContext, out *api.PodSecu
} else { } else {
out.SupplementalGroups = nil 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 return nil
} }
@ -656,7 +676,12 @@ func deepCopy_api_SecurityContext(in api.SecurityContext, out *api.SecurityConte
} else { } else {
out.RunAsUser = nil 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 return nil
} }

View File

@ -333,6 +333,26 @@ func convert_api_PodSecurityContext_To_v1_PodSecurityContext(in *api.PodSecurity
} }
out.SupplementalGroups = in.SupplementalGroups 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 return nil
} }
@ -342,5 +362,25 @@ func convert_v1_PodSecurityContext_To_api_PodSecurityContext(in *v1.PodSecurityC
} }
out.SupplementalGroups = in.SupplementalGroups 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 return nil
} }

View File

@ -889,7 +889,12 @@ func autoconvert_api_SecurityContext_To_v1_SecurityContext(in *api.SecurityConte
} else { } else {
out.RunAsUser = nil 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 return nil
} }
@ -1944,7 +1949,12 @@ func autoconvert_v1_SecurityContext_To_api_SecurityContext(in *v1.SecurityContex
} else { } else {
out.RunAsUser = nil 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 return nil
} }

View File

@ -505,6 +505,26 @@ func deepCopy_v1_PodSecurityContext(in v1.PodSecurityContext, out *v1.PodSecurit
} else { } else {
out.SupplementalGroups = nil 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 return nil
} }
@ -693,7 +713,12 @@ func deepCopy_v1_SecurityContext(in v1.SecurityContext, out *v1.SecurityContext,
} else { } else {
out.RunAsUser = nil 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 return nil
} }

View File

@ -1911,7 +1911,7 @@ func (dm *DockerManager) SyncPod(pod *api.Pod, runningPod kubecontainer.Pod, pod
continue continue
} }
if container.SecurityContext != nil && container.SecurityContext.RunAsNonRoot { if container.SecurityContext != nil && container.SecurityContext.RunAsNonRoot != nil && *container.SecurityContext.RunAsNonRoot {
err := dm.verifyNonRoot(container) err := dm.verifyNonRoot(container)
dm.updateReasonCache(pod, container, "VerifyNonRootError", err) dm.updateReasonCache(pod, container, "VerifyNonRootError", err)
if err != nil { if err != nil {

View File

@ -38,11 +38,12 @@ type SimpleSecurityContextProvider struct{}
// The security context provider can make changes to the Config with which // The security context provider can make changes to the Config with which
// the container is created. // the container is created.
func (p SimpleSecurityContextProvider) ModifyContainerConfig(pod *api.Pod, container *api.Container, config *docker.Config) { 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 return
} }
if container.SecurityContext.RunAsUser != nil { if effectiveSC.RunAsUser != nil {
config.User = strconv.FormatInt(*container.SecurityContext.RunAsUser, 10) 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 { if pod.Spec.SecurityContext.SupplementalGroups != nil && container.Name != leaky.PodInfraContainerName {
hostConfig.GroupAdd = make([]string, len(pod.Spec.SecurityContext.SupplementalGroups)) hostConfig.GroupAdd = make([]string, len(pod.Spec.SecurityContext.SupplementalGroups))
for i, group := range 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 // Apply effective security context for container
if container.SecurityContext == nil { effectiveSC := determineEffectiveSecurityContext(pod, container)
if effectiveSC == nil {
return return
} }
if container.SecurityContext.Privileged != nil {
hostConfig.Privileged = *container.SecurityContext.Privileged if effectiveSC.Privileged != nil {
hostConfig.Privileged = *effectiveSC.Privileged
} }
if container.SecurityContext.Capabilities != nil { if effectiveSC.Capabilities != nil {
add, drop := makeCapabilites(container.SecurityContext.Capabilities.Add, container.SecurityContext.Capabilities.Drop) add, drop := makeCapabilites(effectiveSC.Capabilities.Add, effectiveSC.Capabilities.Drop)
hostConfig.CapAdd = add hostConfig.CapAdd = add
hostConfig.CapDrop = drop hostConfig.CapDrop = drop
} }
if container.SecurityContext.SELinuxOptions != nil { if effectiveSC.SELinuxOptions != nil {
hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelUser, container.SecurityContext.SELinuxOptions.User) hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelUser, effectiveSC.SELinuxOptions.User)
hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelRole, container.SecurityContext.SELinuxOptions.Role) hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelRole, effectiveSC.SELinuxOptions.Role)
hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelType, container.SecurityContext.SELinuxOptions.Type) hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelType, effectiveSC.SELinuxOptions.Type)
hostConfig.SecurityOpt = modifySecurityOption(hostConfig.SecurityOpt, dockerLabelLevel, container.SecurityContext.SELinuxOptions.Level) 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 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
}

View File

@ -28,94 +28,148 @@ import (
) )
func TestModifyContainerConfig(t *testing.T) { func TestModifyContainerConfig(t *testing.T) {
var uid int64 = 1 var uid int64 = 123
testCases := map[string]struct { var overrideUid int64 = 321
securityContext *api.SecurityContext
expected *docker.Config 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, RunAsUser: &uid,
}, },
expected: &docker.Config{ expected: &docker.Config{
User: strconv.FormatInt(uid, 10), User: strconv.FormatInt(uid, 10),
}, },
}, },
"modify config, nil user value": { {
securityContext: &api.SecurityContext{}, name: "no RunAsUser value set",
expected: &docker.Config{}, 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() provider := NewSimpleSecurityContextProvider()
dummyContainer := &api.Container{} dummyContainer := &api.Container{}
for k, v := range testCases { for _, tc := range cases {
dummyContainer.SecurityContext = v.securityContext pod := &api.Pod{Spec: api.PodSpec{SecurityContext: tc.podSc}}
dummyContainer.SecurityContext = tc.sc
dockerCfg := &docker.Config{} dockerCfg := &docker.Config{}
provider.ModifyContainerConfig(nil, dummyContainer, dockerCfg)
if !reflect.DeepEqual(v.expected, dockerCfg) { provider.ModifyContainerConfig(pod, dummyContainer, dockerCfg)
t.Errorf("unexpected modification of docker config for %s. Expected: %#v Got: %#v", k, v.expected, 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) { func TestModifyHostConfig(t *testing.T) {
nilPrivSC := fullValidSecurityContext() priv := true
nilPrivSC.Privileged = nil setPrivSC := &api.SecurityContext{}
nilPrivHC := fullValidHostConfig() setPrivSC.Privileged = &priv
nilPrivHC.Privileged = false setPrivHC := &docker.HostConfig{
Privileged: true,
}
nilCapsSC := fullValidSecurityContext() setCapsHC := &docker.HostConfig{
nilCapsSC.Capabilities = nil CapAdd: []string{"addCapA", "addCapB"},
nilCapsHC := fullValidHostConfig() CapDrop: []string{"dropCapA", "dropCapB"},
nilCapsHC.CapAdd = *new([]string) }
nilCapsHC.CapDrop = *new([]string)
nilSELinuxSC := fullValidSecurityContext() setSELinuxHC := &docker.HostConfig{}
nilSELinuxSC.SELinuxOptions = nil setSELinuxHC.SecurityOpt = []string{
nilSELinuxHC := fullValidHostConfig() fmt.Sprintf("%s:%s", dockerLabelUser, "user"),
nilSELinuxHC.SecurityOpt = *new([]string) fmt.Sprintf("%s:%s", dockerLabelRole, "role"),
fmt.Sprintf("%s:%s", dockerLabelType, "type"),
fmt.Sprintf("%s:%s", dockerLabelLevel, "level"),
}
seLinuxLabelsSC := fullValidSecurityContext() // seLinuxLabelsSC := fullValidSecurityContext()
seLinuxLabelsHC := fullValidHostConfig() // seLinuxLabelsHC := fullValidHostConfig()
testCases := map[string]struct { cases := []struct {
securityContext *api.SecurityContext name string
expected *docker.HostConfig podSc *api.PodSecurityContext
sc *api.SecurityContext
expected *docker.HostConfig
}{ }{
"full settings": { {
securityContext: fullValidSecurityContext(), name: "fully set container.SecurityContext",
expected: fullValidHostConfig(), sc: fullValidSecurityContext(),
expected: fullValidHostConfig(),
}, },
"nil privileged": { {
securityContext: nilPrivSC, name: "container.SecurityContext.Privileged",
expected: nilPrivHC, sc: setPrivSC,
expected: setPrivHC,
}, },
"nil capabilities": { {
securityContext: nilCapsSC, name: "container.SecurityContext.Capabilities",
expected: nilCapsHC, sc: &api.SecurityContext{
Capabilities: inputCapabilities(),
},
expected: setCapsHC,
}, },
"nil selinux options": { {
securityContext: nilSELinuxSC, name: "container.SecurityContext.SELinuxOptions",
expected: nilSELinuxHC, sc: &api.SecurityContext{
SELinuxOptions: inputSELinuxOptions(),
},
expected: setSELinuxHC,
}, },
"selinux labels": { {
securityContext: seLinuxLabelsSC, name: "pod.Spec.SecurityContext.SELinuxOptions",
expected: seLinuxLabelsHC, podSc: &api.PodSecurityContext{
SELinuxOptions: inputSELinuxOptions(),
},
expected: setSELinuxHC,
},
{
name: "container.SecurityContext overrides pod.Spec.SecurityContext",
podSc: overridePodSecurityContext(),
sc: fullValidSecurityContext(),
expected: fullValidHostConfig(),
}, },
} }
provider := NewSimpleSecurityContextProvider() provider := NewSimpleSecurityContextProvider()
dummyContainer := &api.Container{} dummyContainer := &api.Container{}
dummyPod := &api.Pod{
Spec: apitesting.DeepEqualSafePodSpec(), for _, tc := range cases {
} pod := &api.Pod{Spec: api.PodSpec{SecurityContext: tc.podSc}}
for k, v := range testCases { dummyContainer.SecurityContext = tc.sc
dummyContainer.SecurityContext = v.securityContext
dockerCfg := &docker.HostConfig{} dockerCfg := &docker.HostConfig{}
provider.ModifyHostConfig(dummyPod, dummyContainer, dockerCfg)
if !reflect.DeepEqual(v.expected, dockerCfg) { provider.ModifyHostConfig(pod, dummyContainer, dockerCfg)
t.Errorf("unexpected modification of host config for %s. Expected: %#v Got: %#v", k, v.expected, 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 { func fullValidSecurityContext() *api.SecurityContext {
priv := true priv := true
return &api.SecurityContext{ return &api.SecurityContext{
Privileged: &priv, Privileged: &priv,
Capabilities: &api.Capabilities{ Capabilities: inputCapabilities(),
Add: []api.Capability{"addCapA", "addCapB"}, SELinuxOptions: inputSELinuxOptions(),
Drop: []api.Capability{"dropCapA", "dropCapB"}, }
}, }
SELinuxOptions: &api.SELinuxOptions{
User: "user", func inputCapabilities() *api.Capabilities {
Role: "role", return &api.Capabilities{
Type: "type", Add: []api.Capability{"addCapA", "addCapB"},
Level: "level", Drop: []api.Capability{"dropCapA", "dropCapB"},
}, }
}
func inputSELinuxOptions() *api.SELinuxOptions {
return &api.SELinuxOptions{
User: "user",
Role: "role",
Type: "type",
Level: "level",
} }
} }

View File

@ -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 // Admit will deny any pod that defines SELinuxOptions or RunAsUser.
// struct (Capabilities and Privileged)
func (p *plugin) Admit(a admission.Attributes) (err error) { func (p *plugin) Admit(a admission.Attributes) (err error) {
if a.GetResource() != string(api.ResourcePods) { if a.GetResource() != string(api.ResourcePods) {
return nil 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 { if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.SupplementalGroups != nil {
return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("SecurityContext.SupplementalGroups is forbidden")) 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 { for _, v := range pod.Spec.Containers {
if v.SecurityContext != nil { if v.SecurityContext != nil {

View File

@ -29,37 +29,64 @@ func TestAdmission(t *testing.T) {
var runAsUser int64 = 1 var runAsUser int64 = 1
priv := true priv := true
successCases := map[string]*api.SecurityContext{
"no sc": nil,
"empty sc": {},
"valid sc": {Privileged: &priv, Capabilities: &api.Capabilities{}},
}
pod := api.Pod{ cases := []struct {
Spec: api.PodSpec{ name string
Containers: []api.Container{ 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{ for _, tc := range cases {
"run as user": {RunAsUser: &runAsUser}, pod := pod()
"se linux optons": {SELinuxOptions: &api.SELinuxOptions{}}, pod.Spec.SecurityContext = tc.podSc
"mixed settings": {Privileged: &priv, RunAsUser: &runAsUser, SELinuxOptions: &api.SELinuxOptions{}}, pod.Spec.Containers[0].SecurityContext = tc.sc
}
for k, v := range errorCases { err := handler.Admit(admission.NewAttributesRecord(pod, "Pod", "foo", "name", string(api.ResourcePods), "", "ignored", nil))
pod.Spec.Containers[0].SecurityContext = v if err != nil && !tc.expectError {
err := handler.Admit(admission.NewAttributesRecord(&pod, "Pod", "foo", "name", string(api.ResourcePods), "", "ignored", nil)) t.Errorf("%v: unexpected error: %v", tc.name, err)
if err == nil { } else if err == nil && tc.expectError {
t.Errorf("Expected error returned from admission handler for case %s", k) 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{
{},
},
},
}
}

View File

@ -23,13 +23,15 @@ limitations under the License.
package e2e package e2e
import ( import (
"fmt"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/util" "k8s.io/kubernetes/pkg/util"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
) )
func getSecurityContextTestPod() *api.Pod { func scTestPod() *api.Pod {
podName := "security-context-" + string(util.NewUUID()) podName := "security-context-" + string(util.NewUUID())
pod := &api.Pod{ pod := &api.Pod{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
@ -55,11 +57,35 @@ var _ = Describe("[Skipped] Security Context", func() {
framework := NewFramework("security-context") framework := NewFramework("security-context")
It("should support pod.Spec.SecurityContext.SupplementalGroups", func() { It("should support pod.Spec.SecurityContext.SupplementalGroups", func() {
pod := getSecurityContextTestPod() pod := scTestPod()
pod.Spec.Containers[0].Command = []string{"id", "-G"} pod.Spec.Containers[0].Command = []string{"id", "-G"}
pod.Spec.SecurityContext.SupplementalGroups = []int64{1234, 5678} pod.Spec.SecurityContext.SupplementalGroups = []int64{1234, 5678}
groups := []string{"1234", "5678"} groups := []string{"1234", "5678"}
framework.TestContainerOutput("pod.Spec.SecurityContext.SupplementalGroups", pod, 0, groups) 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),
})
})
}) })