mirror of https://github.com/k3s-io/k3s
Revert "Security context - types, kubelet, admission"
parent
3931ee9f51
commit
f48904fd5e
|
@ -7501,7 +7501,7 @@
|
|||
"properties": {
|
||||
"capabilities": {
|
||||
"$ref": "v1beta1.Capabilities",
|
||||
"description": "capabilities for container; cannot be updated; deprecated; See SecurityContext"
|
||||
"description": "capabilities for container; cannot be updated"
|
||||
},
|
||||
"command": {
|
||||
"type": "array",
|
||||
|
@ -7563,7 +7563,7 @@
|
|||
},
|
||||
"privileged": {
|
||||
"type": "boolean",
|
||||
"description": "whether or not the container is granted privileged status; defaults to false; cannot be updated; deprecated; See SecurityContext"
|
||||
"description": "whether or not the container is granted privileged status; defaults to false; cannot be updated"
|
||||
},
|
||||
"readinessProbe": {
|
||||
"$ref": "v1beta1.LivenessProbe",
|
||||
|
@ -7573,10 +7573,6 @@
|
|||
"$ref": "v1beta1.ResourceRequirements",
|
||||
"description": "Compute Resources required by this container; cannot be updated"
|
||||
},
|
||||
"securityContext": {
|
||||
"$ref": "v1beta1.SecurityContext",
|
||||
"description": "security options the pod should run with"
|
||||
},
|
||||
"terminationMessagePath": {
|
||||
"type": "string",
|
||||
"description": "path at which the file to which the container's termination message will be written is mounted into the container's filesystem; message written is intended to be brief final status, such as an assertion failure message; defaults to /dev/termination-log; cannot be updated"
|
||||
|
@ -7627,8 +7623,7 @@
|
|||
"description": "restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever"
|
||||
},
|
||||
"terminationGracePeriodSeconds": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"$ref": "int64",
|
||||
"description": "optional duration in seconds the pod needs to terminate gracefully; may be decreased in delete request; value must be non-negative integer; the value zero indicates delete immediately; if this value is not set, the default grace period will be used instead; the grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal; set this value longer than the expected cleanup time for your process"
|
||||
},
|
||||
"uuid": {
|
||||
|
@ -7705,8 +7700,7 @@
|
|||
"description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified"
|
||||
},
|
||||
"gracePeriodSeconds": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"$ref": "int64",
|
||||
"description": "the duration in seconds to wait before deleting this object; defaults to a per object value if not specified; zero means delete immediately"
|
||||
},
|
||||
"id": {
|
||||
|
@ -9823,27 +9817,6 @@
|
|||
"id": "v1beta1.RestartPolicyOnFailure",
|
||||
"properties": {}
|
||||
},
|
||||
"v1beta1.SELinuxOptions": {
|
||||
"id": "v1beta1.SELinuxOptions",
|
||||
"properties": {
|
||||
"level": {
|
||||
"type": "string",
|
||||
"description": "the level label to apply to the container"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "the role label to apply to the container"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "the type label to apply to the container"
|
||||
},
|
||||
"user": {
|
||||
"type": "string",
|
||||
"description": "the user label to apply to the container"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1beta1.Secret": {
|
||||
"id": "v1beta1.Secret",
|
||||
"properties": {
|
||||
|
@ -9972,28 +9945,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1beta1.SecurityContext": {
|
||||
"id": "v1beta1.SecurityContext",
|
||||
"properties": {
|
||||
"capabilities": {
|
||||
"$ref": "v1beta1.Capabilities",
|
||||
"description": "the linux capabilites that should be added or removed"
|
||||
},
|
||||
"privileged": {
|
||||
"type": "boolean",
|
||||
"description": "run the container in privileged mode"
|
||||
},
|
||||
"runAsUser": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "the user id that runs the first process in the container"
|
||||
},
|
||||
"seLinuxOptions": {
|
||||
"$ref": "v1beta1.SELinuxOptions",
|
||||
"description": "options that control the SELinux labels applied"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1beta1.Service": {
|
||||
"id": "v1beta1.Service",
|
||||
"required": [
|
||||
|
|
|
@ -7501,7 +7501,7 @@
|
|||
"properties": {
|
||||
"capabilities": {
|
||||
"$ref": "v1beta2.Capabilities",
|
||||
"description": "capabilities for container; cannot be updated; deprecated; See SecurityContext"
|
||||
"description": "capabilities for container; cannot be updated"
|
||||
},
|
||||
"command": {
|
||||
"type": "array",
|
||||
|
@ -7563,7 +7563,7 @@
|
|||
},
|
||||
"privileged": {
|
||||
"type": "boolean",
|
||||
"description": "whether or not the container is granted privileged status; defaults to false; cannot be updated; deprecated; See SecurityContext"
|
||||
"description": "whether or not the container is granted privileged status; defaults to false; cannot be updated"
|
||||
},
|
||||
"readinessProbe": {
|
||||
"$ref": "v1beta2.LivenessProbe",
|
||||
|
@ -7573,10 +7573,6 @@
|
|||
"$ref": "v1beta2.ResourceRequirements",
|
||||
"description": "Compute Resources required by this container; cannot be updated"
|
||||
},
|
||||
"securityContext": {
|
||||
"$ref": "v1beta2.SecurityContext",
|
||||
"description": "security options the pod should run with"
|
||||
},
|
||||
"terminationMessagePath": {
|
||||
"type": "string",
|
||||
"description": "path at which the file to which the container's termination message will be written is mounted into the container's filesystem; message written is intended to be brief final status, such as an assertion failure message; defaults to /dev/termination-log; cannot be updated"
|
||||
|
@ -7627,8 +7623,7 @@
|
|||
"description": "restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever"
|
||||
},
|
||||
"terminationGracePeriodSeconds": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"$ref": "int64",
|
||||
"description": "optional duration in seconds the pod needs to terminate gracefully; may be decreased in delete request; value must be non-negative integer; the value zero indicates delete immediately; if this value is not set, the default grace period will be used instead; the grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal; set this value longer than the expected cleanup time for your process"
|
||||
},
|
||||
"uuid": {
|
||||
|
@ -7705,8 +7700,7 @@
|
|||
"description": "an optional prefix to use to generate a unique name; has the same validation rules as name; optional, and is applied only name if is not specified"
|
||||
},
|
||||
"gracePeriodSeconds": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"$ref": "int64",
|
||||
"description": "the duration in seconds to wait before deleting this object; defaults to a per object value if not specified; zero means delete immediately"
|
||||
},
|
||||
"id": {
|
||||
|
@ -9237,10 +9231,10 @@
|
|||
"v1beta2.PersistentVolumeSpec": {
|
||||
"id": "v1beta2.PersistentVolumeSpec",
|
||||
"required": [
|
||||
"glusterfs",
|
||||
"persistentDisk",
|
||||
"awsElasticBlockStore",
|
||||
"hostPath",
|
||||
"glusterfs"
|
||||
"hostPath"
|
||||
],
|
||||
"properties": {
|
||||
"accessModes": {
|
||||
|
@ -9812,27 +9806,6 @@
|
|||
"id": "v1beta2.RestartPolicyOnFailure",
|
||||
"properties": {}
|
||||
},
|
||||
"v1beta2.SELinuxOptions": {
|
||||
"id": "v1beta2.SELinuxOptions",
|
||||
"properties": {
|
||||
"level": {
|
||||
"type": "string",
|
||||
"description": "the level label to apply to the container"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "the role label to apply to the container"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "the type label to apply to the container"
|
||||
},
|
||||
"user": {
|
||||
"type": "string",
|
||||
"description": "the user label to apply to the container"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1beta2.Secret": {
|
||||
"id": "v1beta2.Secret",
|
||||
"properties": {
|
||||
|
@ -9961,28 +9934,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1beta2.SecurityContext": {
|
||||
"id": "v1beta2.SecurityContext",
|
||||
"properties": {
|
||||
"capabilities": {
|
||||
"$ref": "v1beta2.Capabilities",
|
||||
"description": "the linux capabilites that should be added or removed"
|
||||
},
|
||||
"privileged": {
|
||||
"type": "boolean",
|
||||
"description": "run the container in privileged mode"
|
||||
},
|
||||
"runAsUser": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "the user id that runs the first process in the container"
|
||||
},
|
||||
"seLinuxOptions": {
|
||||
"$ref": "v1beta2.SELinuxOptions",
|
||||
"description": "options that control the SELinux labels applied"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1beta2.Service": {
|
||||
"id": "v1beta2.Service",
|
||||
"required": [
|
||||
|
|
|
@ -8458,7 +8458,7 @@
|
|||
},
|
||||
"capabilities": {
|
||||
"$ref": "v1beta3.Capabilities",
|
||||
"description": "capabilities for container; cannot be updated; deprecated; See SecurityContext."
|
||||
"description": "capabilities for container; cannot be updated"
|
||||
},
|
||||
"command": {
|
||||
"type": "array",
|
||||
|
@ -8503,7 +8503,7 @@
|
|||
},
|
||||
"privileged": {
|
||||
"type": "boolean",
|
||||
"description": "whether or not the container is granted privileged status; defaults to false; cannot be updated; deprecated; See SecurityContext."
|
||||
"description": "whether or not the container is granted privileged status; defaults to false; cannot be updated"
|
||||
},
|
||||
"readinessProbe": {
|
||||
"$ref": "v1beta3.Probe",
|
||||
|
@ -8513,10 +8513,6 @@
|
|||
"$ref": "v1beta3.ResourceRequirements",
|
||||
"description": "Compute Resources required by this container; cannot be updated"
|
||||
},
|
||||
"securityContext": {
|
||||
"$ref": "v1beta3.SecurityContext",
|
||||
"description": "security options the pod should run with"
|
||||
},
|
||||
"terminationMessagePath": {
|
||||
"type": "string",
|
||||
"description": "path at which the file to which the container's termination message will be written is mounted into the container's filesystem; message written is intended to be brief final status, such as an assertion failure message; defaults to /dev/termination-log; cannot be updated"
|
||||
|
@ -8693,8 +8689,7 @@
|
|||
"description": "version of the schema the object should have"
|
||||
},
|
||||
"gracePeriodSeconds": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"$ref": "int64",
|
||||
"description": "the duration in seconds to wait before deleting this object; defaults to a per object value if not specified; zero means delete immediately"
|
||||
},
|
||||
"kind": {
|
||||
|
@ -9893,8 +9888,7 @@
|
|||
"description": "restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever"
|
||||
},
|
||||
"terminationGracePeriodSeconds": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"$ref": "int64",
|
||||
"description": "optional duration in seconds the pod needs to terminate gracefully; may be decreased in delete request; value must be non-negative integer; the value zero indicates delete immediately; if this value is not set, the default grace period will be used instead; the grace period is the duration in seconds after the processes running in the pod are sent a termination signal and the time when the processes are forcibly halted with a kill signal; set this value longer than the expected cleanup time for your process"
|
||||
},
|
||||
"volumes": {
|
||||
|
@ -10206,27 +10200,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1beta3.SELinuxOptions": {
|
||||
"id": "v1beta3.SELinuxOptions",
|
||||
"properties": {
|
||||
"level": {
|
||||
"type": "string",
|
||||
"description": "the level label to apply to the container"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "the role label to apply to the container"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "the type label to apply to the container"
|
||||
},
|
||||
"user": {
|
||||
"type": "string",
|
||||
"description": "the user label to apply to the container"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1beta3.Secret": {
|
||||
"id": "v1beta3.Secret",
|
||||
"properties": {
|
||||
|
@ -10291,28 +10264,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"v1beta3.SecurityContext": {
|
||||
"id": "v1beta3.SecurityContext",
|
||||
"properties": {
|
||||
"capabilities": {
|
||||
"$ref": "v1beta3.Capabilities",
|
||||
"description": "the linux capabilites that should be added or removed"
|
||||
},
|
||||
"privileged": {
|
||||
"type": "boolean",
|
||||
"description": "run the container in privileged mode"
|
||||
},
|
||||
"runAsUser": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "the user id that runs the first process in the container"
|
||||
},
|
||||
"seLinuxOptions": {
|
||||
"$ref": "v1beta3.SELinuxOptions",
|
||||
"description": "options that control the SELinux labels applied"
|
||||
}
|
||||
}
|
||||
},
|
||||
"v1beta3.Service": {
|
||||
"id": "v1beta3.Service",
|
||||
"properties": {
|
||||
|
@ -10529,15 +10480,15 @@
|
|||
"id": "v1beta3.Volume",
|
||||
"required": [
|
||||
"name",
|
||||
"gcePersistentDisk",
|
||||
"awsElasticBlockStore",
|
||||
"gitRepo",
|
||||
"nfs",
|
||||
"iscsi",
|
||||
"hostPath",
|
||||
"secret",
|
||||
"nfs",
|
||||
"glusterfs",
|
||||
"emptyDir"
|
||||
"hostPath",
|
||||
"emptyDir",
|
||||
"gcePersistentDisk",
|
||||
"iscsi"
|
||||
],
|
||||
"properties": {
|
||||
"awsElasticBlockStore": {
|
||||
|
|
|
@ -72,4 +72,4 @@ DNS_DOMAIN="kubernetes.local"
|
|||
DNS_REPLICAS=1
|
||||
|
||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,SecurityContextDeny,ResourceQuota
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,ResourceQuota
|
||||
|
|
|
@ -49,4 +49,4 @@ ELASTICSEARCH_LOGGING_REPLICAS=1
|
|||
ENABLE_CLUSTER_MONITORING="${KUBE_ENABLE_CLUSTER_MONITORING:-true}"
|
||||
|
||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,SecurityContextDeny,ResourceQuota
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,ResourceQuota
|
||||
|
|
|
@ -111,4 +111,4 @@ DNS_DOMAIN="kubernetes.local"
|
|||
DNS_REPLICAS=1
|
||||
|
||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,SecurityContextDeny,ResourceQuota,
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,ResourceQuota
|
||||
|
|
|
@ -70,4 +70,4 @@ DNS_SERVER_IP="10.0.0.10"
|
|||
DNS_DOMAIN="kubernetes.local"
|
||||
DNS_REPLICAS=1
|
||||
|
||||
ADMISSION_CONTROL=NamespaceAutoProvision,LimitRanger,SecurityContextDeny,ResourceQuota
|
||||
ADMISSION_CONTROL=NamespaceAutoProvision,LimitRanger,ResourceQuota
|
||||
|
|
|
@ -50,7 +50,7 @@ MASTER_USER=vagrant
|
|||
MASTER_PASSWD=vagrant
|
||||
|
||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,SecurityContextDeny,ResourceQuota
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,ResourceQuota
|
||||
|
||||
# Optional: Install node monitoring.
|
||||
ENABLE_NODE_MONITORING=true
|
||||
|
|
|
@ -36,5 +36,4 @@ import (
|
|||
_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/namespace/exists"
|
||||
_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/namespace/lifecycle"
|
||||
_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/resourcequota"
|
||||
_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/securitycontext/scdeny"
|
||||
)
|
||||
|
|
|
@ -117,7 +117,7 @@ echo "Starting etcd"
|
|||
kube::etcd::start
|
||||
|
||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,SecurityContextDeny,ResourceQuota
|
||||
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,ResourceQuota
|
||||
|
||||
APISERVER_LOG=/tmp/kube-apiserver.log
|
||||
sudo -E "${GO_OUT}/kube-apiserver" \
|
||||
|
|
|
@ -204,17 +204,6 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
|
|||
ev.ValueFrom.FieldRef.FieldPath = c.RandString()
|
||||
}
|
||||
},
|
||||
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.CapabilityType, 0),
|
||||
Drop: make([]api.CapabilityType, 0),
|
||||
}
|
||||
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
|
||||
// Fix event count to 1, otherwise, if a v1beta1 or v1beta2 event has a count set arbitrarily, it's count is ignored
|
||||
|
|
|
@ -623,10 +623,12 @@ type Container struct {
|
|||
Lifecycle *Lifecycle `json:"lifecycle,omitempty"`
|
||||
// Required.
|
||||
TerminationMessagePath string `json:"terminationMessagePath,omitempty"`
|
||||
// Optional: Default to false.
|
||||
Privileged bool `json:"privileged,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
|
||||
SecurityContext *SecurityContext `json:"securityContext,omitempty" description:"security options the pod should run with"`
|
||||
// Optional: Capabilities for container.
|
||||
Capabilities Capabilities `json:"capabilities,omitempty"`
|
||||
}
|
||||
|
||||
// Handler defines a specific action that should be taken
|
||||
|
@ -1874,37 +1876,3 @@ 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.
|
||||
type SecurityContext struct {
|
||||
// Capabilities are the capabilities to add/drop when running the container
|
||||
Capabilities *Capabilities `json:"capabilities,omitempty" description:"the linux capabilites that should be added or removed"`
|
||||
|
||||
// Run the container in privileged mode
|
||||
Privileged *bool `json:"privileged,omitempty" description:"run the container in privileged mode"`
|
||||
|
||||
// SELinuxOptions are the labels to be applied to the container
|
||||
// and volumes
|
||||
SELinuxOptions *SELinuxOptions `json:"seLinuxOptions,omitempty" description:"options that control the SELinux labels applied"`
|
||||
|
||||
// RunAsUser is the UID to run the entrypoint of the container process.
|
||||
RunAsUser *int64 `json:"runAsUser,omitempty" description:"the user id that runs the first process in the container"`
|
||||
}
|
||||
|
||||
// SELinuxOptions are the labels to be applied to the container.
|
||||
type SELinuxOptions struct {
|
||||
// SELinux user label
|
||||
User string `json:"user,omitempty" description:"the user label to apply to the container"`
|
||||
|
||||
// SELinux role label
|
||||
Role string `json:"role,omitempty" description:"the role label to apply to the container"`
|
||||
|
||||
// SELinux type label
|
||||
Type string `json:"type,omitempty" description:"the type label to apply to the container"`
|
||||
|
||||
// SELinux level label.
|
||||
Level string `json:"level,omitempty" description:"the level label to apply to the container"`
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package v1
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
||||
|
@ -238,22 +237,9 @@ func init() {
|
|||
return err
|
||||
}
|
||||
out.TerminationMessagePath = in.TerminationMessagePath
|
||||
out.Privileged = in.Privileged
|
||||
out.ImagePullPolicy = newer.PullPolicy(in.ImagePullPolicy)
|
||||
|
||||
if in.SecurityContext != nil {
|
||||
if in.SecurityContext.Capabilities != nil {
|
||||
if !reflect.DeepEqual(in.SecurityContext.Capabilities.Add, in.Capabilities.Add) ||
|
||||
!reflect.DeepEqual(in.SecurityContext.Capabilities.Drop, in.Capabilities.Drop) {
|
||||
return fmt.Errorf("container capability settings do not match security context settings, cannot convert")
|
||||
}
|
||||
}
|
||||
if in.SecurityContext.Privileged != nil {
|
||||
if in.Privileged != *in.SecurityContext.Privileged {
|
||||
return fmt.Errorf("container privileged settings do not match security context settings, cannot convert")
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := s.Convert(&in.SecurityContext, &out.SecurityContext, 0); err != nil {
|
||||
if err := s.Convert(&in.Capabilities, &out.Capabilities, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -311,19 +297,11 @@ func init() {
|
|||
return err
|
||||
}
|
||||
out.TerminationMessagePath = in.TerminationMessagePath
|
||||
out.Privileged = in.Privileged
|
||||
out.ImagePullPolicy = PullPolicy(in.ImagePullPolicy)
|
||||
if err := s.Convert(&in.SecurityContext, &out.SecurityContext, 0); err != nil {
|
||||
if err := s.Convert(&in.Capabilities, &out.Capabilities, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// now that we've converted set the container field from security context
|
||||
if out.SecurityContext != nil && out.SecurityContext.Privileged != nil {
|
||||
out.Privileged = *out.SecurityContext.Privileged
|
||||
}
|
||||
// now that we've converted set the container field from security context
|
||||
if out.SecurityContext != nil && out.SecurityContext.Capabilities != nil {
|
||||
out.Capabilities = *out.SecurityContext.Capabilities
|
||||
}
|
||||
return nil
|
||||
},
|
||||
func(in *ContainerPort, out *newer.ContainerPort, s conversion.Scope) error {
|
||||
|
|
|
@ -45,62 +45,3 @@ func TestNodeConversion(t *testing.T) {
|
|||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadSecurityContextConversion(t *testing.T) {
|
||||
priv := false
|
||||
testCases := map[string]struct {
|
||||
c *current.Container
|
||||
err string
|
||||
}{
|
||||
// this use case must use true for the container and false for the sc. Otherwise the defaulter
|
||||
// will assume privileged was left undefined (since it is the default value) and copy the
|
||||
// sc setting upwards
|
||||
"mismatched privileged": {
|
||||
c: ¤t.Container{
|
||||
Privileged: true,
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &priv,
|
||||
},
|
||||
},
|
||||
err: "container privileged settings do not match security context settings, cannot convert",
|
||||
},
|
||||
"mismatched caps add": {
|
||||
c: ¤t.Container{
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: "container capability settings do not match security context settings, cannot convert",
|
||||
},
|
||||
"mismatched caps drop": {
|
||||
c: ¤t.Container{
|
||||
Capabilities: current.Capabilities{
|
||||
Drop: []current.CapabilityType{"foo"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: "container capability settings do not match security context settings, cannot convert",
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range testCases {
|
||||
got := newer.Container{}
|
||||
err := newer.Scheme.Convert(v.c, &got)
|
||||
if err == nil {
|
||||
t.Errorf("expected error for case %s but got none", k)
|
||||
} else {
|
||||
if err.Error() != v.err {
|
||||
t.Errorf("unexpected error for case %s. Expected: %s but got: %s", k, v.err, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,6 @@ package v1
|
|||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
@ -68,7 +66,6 @@ func init() {
|
|||
if obj.TerminationMessagePath == "" {
|
||||
obj.TerminationMessagePath = TerminationMessagePathDefault
|
||||
}
|
||||
defaultSecurityContext(obj)
|
||||
},
|
||||
func(obj *ServiceSpec) {
|
||||
if obj.SessionAffinity == "" {
|
||||
|
@ -159,44 +156,3 @@ func defaultHostNetworkPorts(containers *[]Container) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// defaultSecurityContext performs the downward and upward merges of a pod definition
|
||||
func defaultSecurityContext(container *Container) {
|
||||
if container.SecurityContext == nil {
|
||||
glog.V(4).Infof("creating security context for container %s", container.Name)
|
||||
container.SecurityContext = &SecurityContext{}
|
||||
}
|
||||
// if there are no capabilities defined on the SecurityContext then copy the container settings
|
||||
if container.SecurityContext.Capabilities == nil {
|
||||
glog.V(4).Infof("downward merge of container.Capabilities for container %s", container.Name)
|
||||
container.SecurityContext.Capabilities = &container.Capabilities
|
||||
} else {
|
||||
// if there are capabilities defined on the security context and the container setting is
|
||||
// empty then assume that it was left off the pod definition and ensure that the container
|
||||
// settings match the security context settings (checked by the convert functions). If
|
||||
// there are settings in both then don't touch it, the converter will error if they don't
|
||||
// match
|
||||
if len(container.Capabilities.Add) == 0 {
|
||||
glog.V(4).Infof("upward merge of container.Capabilities.Add for container %s", container.Name)
|
||||
container.Capabilities.Add = container.SecurityContext.Capabilities.Add
|
||||
}
|
||||
if len(container.Capabilities.Drop) == 0 {
|
||||
glog.V(4).Infof("upward merge of container.Capabilities.Drop for container %s", container.Name)
|
||||
container.Capabilities.Drop = container.SecurityContext.Capabilities.Drop
|
||||
}
|
||||
}
|
||||
// if there are no privileged settings on the security context then copy the container settings
|
||||
if container.SecurityContext.Privileged == nil {
|
||||
glog.V(4).Infof("downward merge of container.Privileged for container %s", container.Name)
|
||||
container.SecurityContext.Privileged = &container.Privileged
|
||||
} else {
|
||||
// we don't have a good way to know if container.Privileged was set or just defaulted to false
|
||||
// so the best we can do here is check if the securityContext is set to true and the
|
||||
// container is set to false and assume that the Privileged field was left off the container
|
||||
// definition and not an intentional mismatch
|
||||
if *container.SecurityContext.Privileged && !container.Privileged {
|
||||
glog.V(4).Infof("upward merge of container.Privileged for container %s", container.Name)
|
||||
container.Privileged = *container.SecurityContext.Privileged
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -349,104 +349,3 @@ func TestSetDefaultObjectFieldSelectorAPIVersion(t *testing.T) {
|
|||
t.Errorf("Expected default APIVersion v1, got: %v", apiVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDefaultSecurityContext(t *testing.T) {
|
||||
priv := false
|
||||
privTrue := true
|
||||
testCases := map[string]struct {
|
||||
c current.Container
|
||||
}{
|
||||
"downward defaulting caps": {
|
||||
c: current.Container{
|
||||
Privileged: false,
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &priv,
|
||||
},
|
||||
},
|
||||
},
|
||||
"downward defaulting priv": {
|
||||
c: current.Container{
|
||||
Privileged: false,
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"upward defaulting caps": {
|
||||
c: current.Container{
|
||||
Privileged: false,
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &priv,
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"biz"},
|
||||
Drop: []current.CapabilityType{"baz"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"upward defaulting priv": {
|
||||
c: current.Container{
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &privTrue,
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pod := ¤t.Pod{
|
||||
Spec: current.PodSpec{},
|
||||
}
|
||||
|
||||
for k, v := range testCases {
|
||||
pod.Spec.Containers = []current.Container{v.c}
|
||||
obj := roundTrip(t, runtime.Object(pod))
|
||||
defaultedPod := obj.(*current.Pod)
|
||||
c := defaultedPod.Spec.Containers[0]
|
||||
if isEqual, issues := areSecurityContextAndContainerEqual(&c); !isEqual {
|
||||
t.Errorf("test case %s expected the security context to have the same values as the container but found %#v", k, issues)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func areSecurityContextAndContainerEqual(c *current.Container) (bool, []string) {
|
||||
issues := make([]string, 0)
|
||||
equal := true
|
||||
|
||||
if c.SecurityContext == nil || c.SecurityContext.Privileged == nil || c.SecurityContext.Capabilities == nil {
|
||||
equal = false
|
||||
issues = append(issues, "Expected non nil settings for SecurityContext")
|
||||
return equal, issues
|
||||
}
|
||||
if *c.SecurityContext.Privileged != c.Privileged {
|
||||
equal = false
|
||||
issues = append(issues, "The defaulted SecurityContext.Privileged value did not match the container value")
|
||||
}
|
||||
if !reflect.DeepEqual(c.Capabilities.Add, c.Capabilities.Add) {
|
||||
equal = false
|
||||
issues = append(issues, "The defaulted SecurityContext.Capabilities.Add did not match the container settings")
|
||||
}
|
||||
if !reflect.DeepEqual(c.Capabilities.Drop, c.Capabilities.Drop) {
|
||||
equal = false
|
||||
issues = append(issues, "The defaulted SecurityContext.Capabilities.Drop did not match the container settings")
|
||||
}
|
||||
return equal, issues
|
||||
}
|
||||
|
|
|
@ -636,14 +636,12 @@ type Container struct {
|
|||
Lifecycle *Lifecycle `json:"lifecycle,omitempty" description:"actions that the management system should take in response to container lifecycle events; cannot be updated"`
|
||||
// Optional: Defaults to /dev/termination-log
|
||||
TerminationMessagePath string `json:"terminationMessagePath,omitempty" description:"path at which the file to which the container's termination message will be written is mounted into the container's filesystem; message written is intended to be brief final status, such as an assertion failure message; defaults to /dev/termination-log; cannot be updated"`
|
||||
// Deprecated - see SecurityContext. Optional: Default to false.
|
||||
Privileged bool `json:"privileged,omitempty" description:"hether or not the container is granted privileged status; defaults to false; cannot be updated; deprecated; See SecurityContext"`
|
||||
// Optional: Default to false.
|
||||
Privileged bool `json:"privileged,omitempty" description:"whether or not the container is granted privileged status; defaults to false; cannot be updated"`
|
||||
// Optional: Policy for pulling images for this container
|
||||
ImagePullPolicy PullPolicy `json:"imagePullPolicy" description:"image pull policy; one of PullAlways, PullNever, PullIfNotPresent; defaults to PullAlways if :latest tag is specified, or PullIfNotPresent otherwise; cannot be updated"`
|
||||
// Deprecated - see SecurityContext. Optional: Capabilities for container.
|
||||
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated; deprecated; See SecurityContext"`
|
||||
// Optional: SecurityContext defines the security options the pod should be run with
|
||||
SecurityContext *SecurityContext `json:"securityContext,omitempty" description:"security options the pod should run with"`
|
||||
// Optional: Capabilities for container.
|
||||
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated"`
|
||||
}
|
||||
|
||||
// Handler defines a specific action that should be taken
|
||||
|
@ -1737,39 +1735,3 @@ type ComponentStatusList struct {
|
|||
|
||||
Items []ComponentStatus `json:"items" description:"list of component status objects"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
type SecurityContext struct {
|
||||
// Capabilities are the capabilities to add/drop when running the container
|
||||
// Must match Container.Capabilities or be unset. Will be defaulted to Container.Capabilities if left unset
|
||||
Capabilities *Capabilities `json:"capabilities,omitempty" description:"the linux capabilites that should be added or removed"`
|
||||
|
||||
// Run the container in privileged mode
|
||||
// Must match Container.Privileged or be unset. Will be defaulted to Container.Privileged if left unset
|
||||
Privileged *bool `json:"privileged,omitempty" description:"run the container in privileged mode"`
|
||||
|
||||
// SELinuxOptions are the labels to be applied to the container
|
||||
// and volumes
|
||||
SELinuxOptions *SELinuxOptions `json:"seLinuxOptions,omitempty" description:"options that control the SELinux labels applied"`
|
||||
|
||||
// RunAsUser is the UID to run the entrypoint of the container process.
|
||||
RunAsUser *int64 `json:"runAsUser,omitempty" description:"the user id that runs the first process in the container"`
|
||||
}
|
||||
|
||||
// SELinuxOptions are the labels to be applied to the container
|
||||
type SELinuxOptions struct {
|
||||
// SELinux user label
|
||||
User string `json:"user,omitempty" description:"the user label to apply to the container"`
|
||||
|
||||
// SELinux role label
|
||||
Role string `json:"role,omitempty" description:"the role label to apply to the container"`
|
||||
|
||||
// SELinux type label
|
||||
Type string `json:"type,omitempty" description:"the type label to apply to the container"`
|
||||
|
||||
// SELinux level label.
|
||||
Level string `json:"level,omitempty" description:"the level label to apply to the container"`
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package v1beta1
|
|||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
|
@ -580,20 +579,15 @@ func init() {
|
|||
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.Privileged, &out.Privileged, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.ImagePullPolicy, &out.ImagePullPolicy, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.SecurityContext, &out.SecurityContext, 0); err != nil {
|
||||
if err := s.Convert(&in.Capabilities, &out.Capabilities, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
// now that we've converted set the container field from security context
|
||||
if out.SecurityContext != nil && out.SecurityContext.Privileged != nil {
|
||||
out.Privileged = *out.SecurityContext.Privileged
|
||||
}
|
||||
// now that we've converted set the container field from security context
|
||||
if out.SecurityContext != nil && out.SecurityContext.Capabilities != nil {
|
||||
out.Capabilities = *out.SecurityContext.Capabilities
|
||||
}
|
||||
return nil
|
||||
},
|
||||
// Internal API does not support CPU to be specified via an explicit field.
|
||||
|
@ -671,23 +665,13 @@ func init() {
|
|||
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.Privileged, &out.Privileged, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.ImagePullPolicy, &out.ImagePullPolicy, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if in.SecurityContext != nil {
|
||||
if in.SecurityContext.Capabilities != nil {
|
||||
if !reflect.DeepEqual(in.SecurityContext.Capabilities.Add, in.Capabilities.Add) ||
|
||||
!reflect.DeepEqual(in.SecurityContext.Capabilities.Drop, in.Capabilities.Drop) {
|
||||
return fmt.Errorf("container capability settings do not match security context settings, cannot convert")
|
||||
}
|
||||
}
|
||||
if in.SecurityContext.Privileged != nil {
|
||||
if in.Privileged != *in.SecurityContext.Privileged {
|
||||
return fmt.Errorf("container privileged settings do not match security context settings, cannot convert")
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := s.Convert(&in.SecurityContext, &out.SecurityContext, 0); err != nil {
|
||||
if err := s.Convert(&in.Capabilities, &out.Capabilities, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -749,63 +749,3 @@ func TestSecretVolumeSourceConversion(t *testing.T) {
|
|||
t.Errorf("Expected %v; got %v", given, got2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadSecurityContextConversion(t *testing.T) {
|
||||
priv := false
|
||||
testCases := map[string]struct {
|
||||
c *current.Container
|
||||
err string
|
||||
}{
|
||||
// this use case must use true for the container and false for the sc. Otherwise the defaulter
|
||||
// will assume privileged was left undefined (since it is the default value) and copy the
|
||||
// sc setting upwards
|
||||
"mismatched privileged": {
|
||||
c: ¤t.Container{
|
||||
Privileged: true,
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &priv,
|
||||
},
|
||||
},
|
||||
err: "container privileged settings do not match security context settings, cannot convert",
|
||||
},
|
||||
"mismatched caps add": {
|
||||
c: ¤t.Container{
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: "container capability settings do not match security context settings, cannot convert",
|
||||
},
|
||||
"mismatched caps drop": {
|
||||
c: ¤t.Container{
|
||||
Capabilities: current.Capabilities{
|
||||
Drop: []current.CapabilityType{"foo"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: "container capability settings do not match security context settings, cannot convert",
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range testCases {
|
||||
got := newer.Container{}
|
||||
err := Convert(v.c, &got)
|
||||
if err == nil {
|
||||
t.Errorf("expected error for case %s but got none", k)
|
||||
} else {
|
||||
if err.Error() != v.err {
|
||||
t.Errorf("unexpected error for case %s. Expected: %s but got: %s", k, v.err, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -62,7 +62,6 @@ func init() {
|
|||
if obj.TerminationMessagePath == "" {
|
||||
obj.TerminationMessagePath = TerminationMessagePathDefault
|
||||
}
|
||||
defaultSecurityContext(obj)
|
||||
},
|
||||
func(obj *RestartPolicy) {
|
||||
if util.AllPtrFieldsNil(obj) {
|
||||
|
@ -195,44 +194,3 @@ func defaultHostNetworkPorts(containers *[]Container) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// defaultSecurityContext performs the downward and upward merges of a pod definition
|
||||
func defaultSecurityContext(container *Container) {
|
||||
if container.SecurityContext == nil {
|
||||
glog.V(4).Infof("creating security context for container %s", container.Name)
|
||||
container.SecurityContext = &SecurityContext{}
|
||||
}
|
||||
// if there are no capabilities defined on the SecurityContext then copy the container settings
|
||||
if container.SecurityContext.Capabilities == nil {
|
||||
glog.V(4).Infof("downward merge of container.Capabilities for container %s", container.Name)
|
||||
container.SecurityContext.Capabilities = &container.Capabilities
|
||||
} else {
|
||||
// if there are capabilities defined on the security context and the container setting is
|
||||
// empty then assume that it was left off the pod definition and ensure that the container
|
||||
// settings match the security context settings (checked by the convert functions). If
|
||||
// there are settings in both then don't touch it, the converter will error if they don't
|
||||
// match
|
||||
if len(container.Capabilities.Add) == 0 {
|
||||
glog.V(4).Infof("upward merge of container.Capabilities.Add for container %s", container.Name)
|
||||
container.Capabilities.Add = container.SecurityContext.Capabilities.Add
|
||||
}
|
||||
if len(container.Capabilities.Drop) == 0 {
|
||||
glog.V(4).Infof("upward merge of container.Capabilities.Drop for container %s", container.Name)
|
||||
container.Capabilities.Drop = container.SecurityContext.Capabilities.Drop
|
||||
}
|
||||
}
|
||||
// if there are no privileged settings on the security context then copy the container settings
|
||||
if container.SecurityContext.Privileged == nil {
|
||||
glog.V(4).Infof("downward merge of container.Privileged for container %s", container.Name)
|
||||
container.SecurityContext.Privileged = &container.Privileged
|
||||
} else {
|
||||
// we don't have a good way to know if container.Privileged was set or just defaulted to false
|
||||
// so the best we can do here is check if the securityContext is set to true and the
|
||||
// container is set to false and assume that the Privileged field was left off the container
|
||||
// definition and not an intentional mismatch
|
||||
if *container.SecurityContext.Privileged && !container.Privileged {
|
||||
glog.V(4).Infof("upward merge of container.Privileged for container %s", container.Name)
|
||||
container.Privileged = *container.SecurityContext.Privileged
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -340,106 +340,3 @@ func TestSetDefaultObjectFieldSelectorAPIVersion(t *testing.T) {
|
|||
t.Errorf("Expected default APIVersion v1beta1, got: %v", apiVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDefaultSecurityContext(t *testing.T) {
|
||||
priv := false
|
||||
privTrue := true
|
||||
testCases := map[string]struct {
|
||||
c current.Container
|
||||
}{
|
||||
"downward defaulting caps": {
|
||||
c: current.Container{
|
||||
Privileged: false,
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &priv,
|
||||
},
|
||||
},
|
||||
},
|
||||
"downward defaulting priv": {
|
||||
c: current.Container{
|
||||
Privileged: false,
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"upward defaulting caps": {
|
||||
c: current.Container{
|
||||
Privileged: false,
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &priv,
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"biz"},
|
||||
Drop: []current.CapabilityType{"baz"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"upward defaulting priv": {
|
||||
c: current.Container{
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &privTrue,
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pod := ¤t.Pod{
|
||||
DesiredState: current.PodState{
|
||||
Manifest: current.ContainerManifest{},
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range testCases {
|
||||
pod.DesiredState.Manifest.Containers = []current.Container{v.c}
|
||||
obj := roundTrip(t, runtime.Object(pod))
|
||||
defaultedPod := obj.(*current.Pod)
|
||||
c := defaultedPod.DesiredState.Manifest.Containers[0]
|
||||
if isEqual, issues := areSecurityContextAndContainerEqual(&c); !isEqual {
|
||||
t.Errorf("test case %s expected the security context to have the same values as the container but found %#v", k, issues)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func areSecurityContextAndContainerEqual(c *current.Container) (bool, []string) {
|
||||
issues := make([]string, 0)
|
||||
equal := true
|
||||
|
||||
if c.SecurityContext == nil || c.SecurityContext.Privileged == nil || c.SecurityContext.Capabilities == nil {
|
||||
equal = false
|
||||
issues = append(issues, "Expected non nil settings for SecurityContext")
|
||||
return equal, issues
|
||||
}
|
||||
if *c.SecurityContext.Privileged != c.Privileged {
|
||||
equal = false
|
||||
issues = append(issues, "The defaulted SecurityContext.Privileged value did not match the container value")
|
||||
}
|
||||
if !reflect.DeepEqual(c.Capabilities.Add, c.Capabilities.Add) {
|
||||
equal = false
|
||||
issues = append(issues, "The defaulted SecurityContext.Capabilities.Add did not match the container settings")
|
||||
}
|
||||
if !reflect.DeepEqual(c.Capabilities.Drop, c.Capabilities.Drop) {
|
||||
equal = false
|
||||
issues = append(issues, "The defaulted SecurityContext.Capabilities.Drop did not match the container settings")
|
||||
}
|
||||
return equal, issues
|
||||
}
|
||||
|
|
|
@ -525,14 +525,12 @@ type Container struct {
|
|||
Lifecycle *Lifecycle `json:"lifecycle,omitempty" description:"actions that the management system should take in response to container lifecycle events; cannot be updated"`
|
||||
// Optional: Defaults to /dev/termination-log
|
||||
TerminationMessagePath string `json:"terminationMessagePath,omitempty" description:"path at which the file to which the container's termination message will be written is mounted into the container's filesystem; message written is intended to be brief final status, such as an assertion failure message; defaults to /dev/termination-log; cannot be updated"`
|
||||
// Deprecated - see SecurityContext. Optional: Default to false.
|
||||
Privileged bool `json:"privileged,omitempty" description:"whether or not the container is granted privileged status; defaults to false; cannot be updated; deprecated; See SecurityContext"`
|
||||
// Optional: Default to false.
|
||||
Privileged bool `json:"privileged,omitempty" description:"whether or not the container is granted privileged status; defaults to false; cannot be updated"`
|
||||
// Optional: Policy for pulling images for this container
|
||||
ImagePullPolicy PullPolicy `json:"imagePullPolicy" description:"image pull policy; one of PullAlways, PullNever, PullIfNotPresent; defaults to PullAlways if :latest tag is specified, or PullIfNotPresent otherwise; cannot be updated"`
|
||||
// Deprecated - see SecurityContext. Optional: Capabilities for container.
|
||||
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated; deprecated; See SecurityContext"`
|
||||
// Optional: SecurityContext defines the security options the pod should be run with
|
||||
SecurityContext *SecurityContext `json:"securityContext,omitempty" description:"security options the pod should run with"`
|
||||
// Optional: Capabilities for container.
|
||||
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated"`
|
||||
}
|
||||
|
||||
// Handler defines a specific action that should be taken
|
||||
|
@ -1657,39 +1655,3 @@ type ComponentStatusList struct {
|
|||
|
||||
Items []ComponentStatus `json:"items" description:"list of component status objects"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
type SecurityContext struct {
|
||||
// Capabilities are the capabilities to add/drop when running the container
|
||||
// Must match Container.Capabilities or be unset. Will be defaulted to Container.Capabilities if left unset
|
||||
Capabilities *Capabilities `json:"capabilities,omitempty" description:"the linux capabilites that should be added or removed"`
|
||||
|
||||
// Run the container in privileged mode
|
||||
// Must match Container.Privileged or be unset. Will be defaulted to Container.Privileged if left unset
|
||||
Privileged *bool `json:"privileged,omitempty" description:"run the container in privileged mode"`
|
||||
|
||||
// SELinuxOptions are the labels to be applied to the container
|
||||
// and volumes
|
||||
SELinuxOptions *SELinuxOptions `json:"seLinuxOptions,omitempty" description:"options that control the SELinux labels applied"`
|
||||
|
||||
// RunAsUser is the UID to run the entrypoint of the container process.
|
||||
RunAsUser *int64 `json:"runAsUser,omitempty" description:"the user id that runs the first process in the container"`
|
||||
}
|
||||
|
||||
// SELinuxOptions are the labels to be applied to the container.
|
||||
type SELinuxOptions struct {
|
||||
// SELinux user label
|
||||
User string `json:"user,omitempty" description:"the user label to apply to the container"`
|
||||
|
||||
// SELinux role label
|
||||
Role string `json:"role,omitempty" description:"the role label to apply to the container"`
|
||||
|
||||
// SELinux type label
|
||||
Type string `json:"type,omitempty" description:"the type label to apply to the container"`
|
||||
|
||||
// SELinux level label.
|
||||
Level string `json:"level,omitempty" description:"the level label to apply to the container"`
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package v1beta2
|
|||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
|
@ -358,20 +357,15 @@ func init() {
|
|||
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.Privileged, &out.Privileged, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.ImagePullPolicy, &out.ImagePullPolicy, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.SecurityContext, &out.SecurityContext, 0); err != nil {
|
||||
if err := s.Convert(&in.Capabilities, &out.Capabilities, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
// now that we've converted set the container field from security context
|
||||
if out.SecurityContext != nil && out.SecurityContext.Privileged != nil {
|
||||
out.Privileged = *out.SecurityContext.Privileged
|
||||
}
|
||||
// now that we've converted set the container field from security context
|
||||
if out.SecurityContext != nil && out.SecurityContext.Capabilities != nil {
|
||||
out.Capabilities = *out.SecurityContext.Capabilities
|
||||
}
|
||||
return nil
|
||||
},
|
||||
// Internal API does not support CPU to be specified via an explicit field.
|
||||
|
@ -451,23 +445,13 @@ func init() {
|
|||
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.Privileged, &out.Privileged, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.Convert(&in.ImagePullPolicy, &out.ImagePullPolicy, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
if in.SecurityContext != nil {
|
||||
if in.SecurityContext.Capabilities != nil {
|
||||
if !reflect.DeepEqual(in.SecurityContext.Capabilities.Add, in.Capabilities.Add) ||
|
||||
!reflect.DeepEqual(in.SecurityContext.Capabilities.Drop, in.Capabilities.Drop) {
|
||||
return fmt.Errorf("container capability settings do not match security context settings, cannot convert")
|
||||
}
|
||||
}
|
||||
if in.SecurityContext.Privileged != nil {
|
||||
if in.Privileged != *in.SecurityContext.Privileged {
|
||||
return fmt.Errorf("container privileged settings do not match security context settings, cannot convert")
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := s.Convert(&in.SecurityContext, &out.SecurityContext, 0); err != nil {
|
||||
if err := s.Convert(&in.Capabilities, &out.Capabilities, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -564,63 +564,3 @@ func TestSecretVolumeSourceConversion(t *testing.T) {
|
|||
t.Errorf("Expected %v; got %v", given, got2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadSecurityContextConversion(t *testing.T) {
|
||||
priv := false
|
||||
testCases := map[string]struct {
|
||||
c *current.Container
|
||||
err string
|
||||
}{
|
||||
// this use case must use true for the container and false for the sc. Otherwise the defaulter
|
||||
// will assume privileged was left undefined (since it is the default value) and copy the
|
||||
// sc setting upwards
|
||||
"mismatched privileged": {
|
||||
c: ¤t.Container{
|
||||
Privileged: true,
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &priv,
|
||||
},
|
||||
},
|
||||
err: "container privileged settings do not match security context settings, cannot convert",
|
||||
},
|
||||
"mismatched caps add": {
|
||||
c: ¤t.Container{
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: "container capability settings do not match security context settings, cannot convert",
|
||||
},
|
||||
"mismatched caps drop": {
|
||||
c: ¤t.Container{
|
||||
Capabilities: current.Capabilities{
|
||||
Drop: []current.CapabilityType{"foo"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: "container capability settings do not match security context settings, cannot convert",
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range testCases {
|
||||
got := newer.Container{}
|
||||
err := newer.Scheme.Convert(v.c, &got)
|
||||
if err == nil {
|
||||
t.Errorf("expected error for case %s but got none", k)
|
||||
} else {
|
||||
if err.Error() != v.err {
|
||||
t.Errorf("unexpected error for case %s. Expected: %s but got: %s", k, v.err, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -63,7 +63,6 @@ func init() {
|
|||
if obj.TerminationMessagePath == "" {
|
||||
obj.TerminationMessagePath = TerminationMessagePathDefault
|
||||
}
|
||||
defaultSecurityContext(obj)
|
||||
},
|
||||
func(obj *RestartPolicy) {
|
||||
if util.AllPtrFieldsNil(obj) {
|
||||
|
@ -196,44 +195,3 @@ func defaultHostNetworkPorts(containers *[]Container) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// defaultSecurityContext performs the downward and upward merges of a pod definition
|
||||
func defaultSecurityContext(container *Container) {
|
||||
if container.SecurityContext == nil {
|
||||
glog.V(4).Infof("creating security context for container %s", container.Name)
|
||||
container.SecurityContext = &SecurityContext{}
|
||||
}
|
||||
// if there are no capabilities defined on the SecurityContext then copy the container settings
|
||||
if container.SecurityContext.Capabilities == nil {
|
||||
glog.V(4).Infof("downward merge of container.Capabilities for container %s", container.Name)
|
||||
container.SecurityContext.Capabilities = &container.Capabilities
|
||||
} else {
|
||||
// if there are capabilities defined on the security context and the container setting is
|
||||
// empty then assume that it was left off the pod definition and ensure that the container
|
||||
// settings match the security context settings (checked by the convert functions). If
|
||||
// there are settings in both then don't touch it, the converter will error if they don't
|
||||
// match
|
||||
if len(container.Capabilities.Add) == 0 {
|
||||
glog.V(4).Infof("upward merge of container.Capabilities.Add for container %s", container.Name)
|
||||
container.Capabilities.Add = container.SecurityContext.Capabilities.Add
|
||||
}
|
||||
if len(container.Capabilities.Drop) == 0 {
|
||||
glog.V(4).Infof("upward merge of container.Capabilities.Drop for container %s", container.Name)
|
||||
container.Capabilities.Drop = container.SecurityContext.Capabilities.Drop
|
||||
}
|
||||
}
|
||||
// if there are no privileged settings on the security context then copy the container settings
|
||||
if container.SecurityContext.Privileged == nil {
|
||||
glog.V(4).Infof("downward merge of container.Privileged for container %s", container.Name)
|
||||
container.SecurityContext.Privileged = &container.Privileged
|
||||
} else {
|
||||
// we don't have a good way to know if container.Privileged was set or just defaulted to false
|
||||
// so the best we can do here is check if the securityContext is set to true and the
|
||||
// container is set to false and assume that the Privileged field was left off the container
|
||||
// definition and not an intentional mismatch
|
||||
if *container.SecurityContext.Privileged && !container.Privileged {
|
||||
glog.V(4).Infof("upward merge of container.Privileged for container %s", container.Name)
|
||||
container.Privileged = *container.SecurityContext.Privileged
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -339,106 +339,3 @@ func TestSetDefaultObjectFieldSelectorAPIVersion(t *testing.T) {
|
|||
t.Errorf("Expected default APIVersion v1beta2, got: %v", apiVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDefaultSecurityContext(t *testing.T) {
|
||||
priv := false
|
||||
privTrue := true
|
||||
testCases := map[string]struct {
|
||||
c current.Container
|
||||
}{
|
||||
"downward defaulting caps": {
|
||||
c: current.Container{
|
||||
Privileged: false,
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &priv,
|
||||
},
|
||||
},
|
||||
},
|
||||
"downward defaulting priv": {
|
||||
c: current.Container{
|
||||
Privileged: false,
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"upward defaulting caps": {
|
||||
c: current.Container{
|
||||
Privileged: false,
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &priv,
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"biz"},
|
||||
Drop: []current.CapabilityType{"baz"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"upward defaulting priv": {
|
||||
c: current.Container{
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &privTrue,
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pod := ¤t.Pod{
|
||||
DesiredState: current.PodState{
|
||||
Manifest: current.ContainerManifest{},
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range testCases {
|
||||
pod.DesiredState.Manifest.Containers = []current.Container{v.c}
|
||||
obj := roundTrip(t, runtime.Object(pod))
|
||||
defaultedPod := obj.(*current.Pod)
|
||||
c := defaultedPod.DesiredState.Manifest.Containers[0]
|
||||
if isEqual, issues := areSecurityContextAndContainerEqual(&c); !isEqual {
|
||||
t.Errorf("test case %s expected the security context to have the same values as the container but found %#v", k, issues)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func areSecurityContextAndContainerEqual(c *current.Container) (bool, []string) {
|
||||
issues := make([]string, 0)
|
||||
equal := true
|
||||
|
||||
if c.SecurityContext == nil || c.SecurityContext.Privileged == nil || c.SecurityContext.Capabilities == nil {
|
||||
equal = false
|
||||
issues = append(issues, "Expected non nil settings for SecurityContext")
|
||||
return equal, issues
|
||||
}
|
||||
if *c.SecurityContext.Privileged != c.Privileged {
|
||||
equal = false
|
||||
issues = append(issues, "The defaulted SecurityContext.Privileged value did not match the container value")
|
||||
}
|
||||
if !reflect.DeepEqual(c.Capabilities.Add, c.Capabilities.Add) {
|
||||
equal = false
|
||||
issues = append(issues, "The defaulted SecurityContext.Capabilities.Add did not match the container settings")
|
||||
}
|
||||
if !reflect.DeepEqual(c.Capabilities.Drop, c.Capabilities.Drop) {
|
||||
equal = false
|
||||
issues = append(issues, "The defaulted SecurityContext.Capabilities.Drop did not match the container settings")
|
||||
}
|
||||
return equal, issues
|
||||
}
|
||||
|
|
|
@ -513,14 +513,12 @@ type Container struct {
|
|||
Lifecycle *Lifecycle `json:"lifecycle,omitempty" description:"actions that the management system should take in response to container lifecycle events; cannot be updated"`
|
||||
// Optional: Defaults to /dev/termination-log
|
||||
TerminationMessagePath string `json:"terminationMessagePath,omitempty" description:"path at which the file to which the container's termination message will be written is mounted into the container's filesystem; message written is intended to be brief final status, such as an assertion failure message; defaults to /dev/termination-log; cannot be updated"`
|
||||
// Deprecated - see SecurityContext. Optional: Default to false.
|
||||
Privileged bool `json:"privileged,omitempty" description:"whether or not the container is granted privileged status; defaults to false; cannot be updated; deprecated; See SecurityContext"`
|
||||
// Optional: Default to false.
|
||||
Privileged bool `json:"privileged,omitempty" description:"whether or not the container is granted privileged status; defaults to false; cannot be updated"`
|
||||
// Optional: Policy for pulling images for this container
|
||||
ImagePullPolicy PullPolicy `json:"imagePullPolicy" description:"image pull policy; one of PullAlways, PullNever, PullIfNotPresent; defaults to PullAlways if :latest tag is specified, or PullIfNotPresent otherwise; cannot be updated"`
|
||||
// Deprecated - see SecurityContext. Optional: Capabilities for container.
|
||||
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated; deprecated; See SecurityContext"`
|
||||
// Optional: SecurityContext defines the security options the pod should be run with
|
||||
SecurityContext *SecurityContext `json:"securityContext,omitempty" description:"security options the pod should run with"`
|
||||
// Optional: Capabilities for container.
|
||||
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated"`
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -1719,39 +1717,3 @@ type ComponentStatusList struct {
|
|||
|
||||
Items []ComponentStatus `json:"items" description:"list of component status objects"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
type SecurityContext struct {
|
||||
// Capabilities are the capabilities to add/drop when running the container
|
||||
// Must match Container.Capabilities or be unset. Will be defaulted to Container.Capabilities if left unset
|
||||
Capabilities *Capabilities `json:"capabilities,omitempty" description:"the linux capabilites that should be added or removed"`
|
||||
|
||||
// Run the container in privileged mode
|
||||
// Must match Container.Privileged or be unset. Will be defaulted to Container.Privileged if left unset
|
||||
Privileged *bool `json:"privileged,omitempty" description:"run the container in privileged mode"`
|
||||
|
||||
// SELinuxOptions are the labels to be applied to the container
|
||||
// and volumes
|
||||
SELinuxOptions *SELinuxOptions `json:"seLinuxOptions,omitempty" description:"options that control the SELinux labels applied"`
|
||||
|
||||
// RunAsUser is the UID to run the entrypoint of the container process.
|
||||
RunAsUser *int64 `json:"runAsUser,omitempty" description:"the user id that runs the first process in the container"`
|
||||
}
|
||||
|
||||
// SELinuxOptions are the labels to be applied to the container.
|
||||
type SELinuxOptions struct {
|
||||
// SELinux user label
|
||||
User string `json:"user,omitempty" description:"the user label to apply to the container"`
|
||||
|
||||
// SELinux role label
|
||||
Role string `json:"role,omitempty" description:"the role label to apply to the container"`
|
||||
|
||||
// SELinux type label
|
||||
Type string `json:"type,omitempty" description:"the type label to apply to the container"`
|
||||
|
||||
// SELinux level label.
|
||||
Level string `json:"level,omitempty" description:"the level label to apply to the container"`
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -45,63 +45,3 @@ func TestNodeConversion(t *testing.T) {
|
|||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBadSecurityContextConversion(t *testing.T) {
|
||||
priv := false
|
||||
testCases := map[string]struct {
|
||||
c *current.Container
|
||||
err string
|
||||
}{
|
||||
// this use case must use true for the container and false for the sc. Otherwise the defaulter
|
||||
// will assume privileged was left undefined (since it is the default value) and copy the
|
||||
// sc setting upwards
|
||||
"mismatched privileged": {
|
||||
c: ¤t.Container{
|
||||
Privileged: true,
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &priv,
|
||||
},
|
||||
},
|
||||
err: "container privileged settings do not match security context settings, cannot convert",
|
||||
},
|
||||
"mismatched caps add": {
|
||||
c: ¤t.Container{
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: "container capability settings do not match security context settings, cannot convert",
|
||||
},
|
||||
"mismatched caps drop": {
|
||||
c: ¤t.Container{
|
||||
Capabilities: current.Capabilities{
|
||||
Drop: []current.CapabilityType{"foo"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
err: "container capability settings do not match security context settings, cannot convert",
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range testCases {
|
||||
got := newer.Container{}
|
||||
err := newer.Scheme.Convert(v.c, &got)
|
||||
if err == nil {
|
||||
t.Errorf("expected error for case %s but got none", k)
|
||||
} else {
|
||||
if err.Error() != v.err {
|
||||
t.Errorf("unexpected error for case %s. Expected: %s but got: %s", k, v.err, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -67,7 +66,6 @@ func init() {
|
|||
if obj.TerminationMessagePath == "" {
|
||||
obj.TerminationMessagePath = TerminationMessagePathDefault
|
||||
}
|
||||
defaultSecurityContext(obj)
|
||||
},
|
||||
func(obj *ServiceSpec) {
|
||||
if obj.SessionAffinity == "" {
|
||||
|
@ -158,44 +156,3 @@ func defaultHostNetworkPorts(containers *[]Container) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// defaultSecurityContext performs the downward and upward merges of a pod definition
|
||||
func defaultSecurityContext(container *Container) {
|
||||
if container.SecurityContext == nil {
|
||||
glog.V(4).Infof("creating security context for container %s", container.Name)
|
||||
container.SecurityContext = &SecurityContext{}
|
||||
}
|
||||
// if there are no capabilities defined on the SecurityContext then copy the container settings
|
||||
if container.SecurityContext.Capabilities == nil {
|
||||
glog.V(4).Infof("downward merge of container.Capabilities for container %s", container.Name)
|
||||
container.SecurityContext.Capabilities = &container.Capabilities
|
||||
} else {
|
||||
// if there are capabilities defined on the security context and the container setting is
|
||||
// empty then assume that it was left off the pod definition and ensure that the container
|
||||
// settings match the security context settings (checked by the convert functions). If
|
||||
// there are settings in both then don't touch it, the converter will error if they don't
|
||||
// match
|
||||
if len(container.Capabilities.Add) == 0 {
|
||||
glog.V(4).Infof("upward merge of container.Capabilities.Add for container %s", container.Name)
|
||||
container.Capabilities.Add = container.SecurityContext.Capabilities.Add
|
||||
}
|
||||
if len(container.Capabilities.Drop) == 0 {
|
||||
glog.V(4).Infof("upward merge of container.Capabilities.Drop for container %s", container.Name)
|
||||
container.Capabilities.Drop = container.SecurityContext.Capabilities.Drop
|
||||
}
|
||||
}
|
||||
// if there are no privileged settings on the security context then copy the container settings
|
||||
if container.SecurityContext.Privileged == nil {
|
||||
glog.V(4).Infof("downward merge of container.Privileged for container %s", container.Name)
|
||||
container.SecurityContext.Privileged = &container.Privileged
|
||||
} else {
|
||||
// we don't have a good way to know if container.Privileged was set or just defaulted to false
|
||||
// so the best we can do here is check if the securityContext is set to true and the
|
||||
// container is set to false and assume that the Privileged field was left off the container
|
||||
// definition and not an intentional mismatch
|
||||
if *container.SecurityContext.Privileged && !container.Privileged {
|
||||
glog.V(4).Infof("upward merge of container.Privileged for container %s", container.Name)
|
||||
container.Privileged = *container.SecurityContext.Privileged
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -349,104 +349,3 @@ func TestSetDefaultObjectFieldSelectorAPIVersion(t *testing.T) {
|
|||
t.Errorf("Expected default APIVersion v1beta3, got: %v", apiVersion)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDefaultSecurityContext(t *testing.T) {
|
||||
priv := false
|
||||
privTrue := true
|
||||
testCases := map[string]struct {
|
||||
c current.Container
|
||||
}{
|
||||
"downward defaulting caps": {
|
||||
c: current.Container{
|
||||
Privileged: false,
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &priv,
|
||||
},
|
||||
},
|
||||
},
|
||||
"downward defaulting priv": {
|
||||
c: current.Container{
|
||||
Privileged: false,
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"upward defaulting caps": {
|
||||
c: current.Container{
|
||||
Privileged: false,
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &priv,
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"biz"},
|
||||
Drop: []current.CapabilityType{"baz"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"upward defaulting priv": {
|
||||
c: current.Container{
|
||||
Capabilities: current.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
SecurityContext: ¤t.SecurityContext{
|
||||
Privileged: &privTrue,
|
||||
Capabilities: ¤t.Capabilities{
|
||||
Add: []current.CapabilityType{"foo"},
|
||||
Drop: []current.CapabilityType{"bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
pod := ¤t.Pod{
|
||||
Spec: current.PodSpec{},
|
||||
}
|
||||
|
||||
for k, v := range testCases {
|
||||
pod.Spec.Containers = []current.Container{v.c}
|
||||
obj := roundTrip(t, runtime.Object(pod))
|
||||
defaultedPod := obj.(*current.Pod)
|
||||
c := defaultedPod.Spec.Containers[0]
|
||||
if isEqual, issues := areSecurityContextAndContainerEqual(&c); !isEqual {
|
||||
t.Errorf("test case %s expected the security context to have the same values as the container but found %#v", k, issues)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func areSecurityContextAndContainerEqual(c *current.Container) (bool, []string) {
|
||||
issues := make([]string, 0)
|
||||
equal := true
|
||||
|
||||
if c.SecurityContext == nil || c.SecurityContext.Privileged == nil || c.SecurityContext.Capabilities == nil {
|
||||
equal = false
|
||||
issues = append(issues, "Expected non nil settings for SecurityContext")
|
||||
return equal, issues
|
||||
}
|
||||
if *c.SecurityContext.Privileged != c.Privileged {
|
||||
equal = false
|
||||
issues = append(issues, "The defaulted SecurityContext.Privileged value did not match the container value")
|
||||
}
|
||||
if !reflect.DeepEqual(c.Capabilities.Add, c.Capabilities.Add) {
|
||||
equal = false
|
||||
issues = append(issues, "The defaulted SecurityContext.Capabilities.Add did not match the container settings")
|
||||
}
|
||||
if !reflect.DeepEqual(c.Capabilities.Drop, c.Capabilities.Drop) {
|
||||
equal = false
|
||||
issues = append(issues, "The defaulted SecurityContext.Capabilities.Drop did not match the container settings")
|
||||
}
|
||||
return equal, issues
|
||||
}
|
||||
|
|
|
@ -636,14 +636,12 @@ type Container struct {
|
|||
Lifecycle *Lifecycle `json:"lifecycle,omitempty" description:"actions that the management system should take in response to container lifecycle events; cannot be updated"`
|
||||
// Optional: Defaults to /dev/termination-log
|
||||
TerminationMessagePath string `json:"terminationMessagePath,omitempty" description:"path at which the file to which the container's termination message will be written is mounted into the container's filesystem; message written is intended to be brief final status, such as an assertion failure message; defaults to /dev/termination-log; cannot be updated"`
|
||||
// Deprecated - see SecurityContext. Optional: Default to false.
|
||||
Privileged bool `json:"privileged,omitempty" description:"whether or not the container is granted privileged status; defaults to false; cannot be updated; deprecated; See SecurityContext."`
|
||||
// Optional: Default to false.
|
||||
Privileged bool `json:"privileged,omitempty" description:"whether or not the container is granted privileged status; defaults to false; cannot be updated"`
|
||||
// Optional: Policy for pulling images for this container
|
||||
ImagePullPolicy PullPolicy `json:"imagePullPolicy" description:"image pull policy; one of PullAlways, PullNever, PullIfNotPresent; defaults to PullAlways if :latest tag is specified, or PullIfNotPresent otherwise; cannot be updated"`
|
||||
// Deprecated - see SecurityContext. Optional: Capabilities for container.
|
||||
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated; deprecated; See SecurityContext."`
|
||||
// Optional: SecurityContext defines the security options the pod should be run with
|
||||
SecurityContext *SecurityContext `json:"securityContext,omitempty" description:"security options the pod should run with"`
|
||||
// Optional: Capabilities for container.
|
||||
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated"`
|
||||
}
|
||||
|
||||
// Handler defines a specific action that should be taken
|
||||
|
@ -1737,39 +1735,3 @@ type ComponentStatusList struct {
|
|||
|
||||
Items []ComponentStatus `json:"items" description:"list of component status objects"`
|
||||
}
|
||||
|
||||
// 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.
|
||||
type SecurityContext struct {
|
||||
// Capabilities are the capabilities to add/drop when running the container
|
||||
// Must match Container.Capabilities or be unset. Will be defaulted to Container.Capabilities if left unset
|
||||
Capabilities *Capabilities `json:"capabilities,omitempty" description:"the linux capabilites that should be added or removed"`
|
||||
|
||||
// Run the container in privileged mode
|
||||
// Must match Container.Privileged or be unset. Will be defaulted to Container.Privileged if left unset
|
||||
Privileged *bool `json:"privileged,omitempty" description:"run the container in privileged mode"`
|
||||
|
||||
// SELinuxOptions are the labels to be applied to the container
|
||||
// and volumes
|
||||
SELinuxOptions *SELinuxOptions `json:"seLinuxOptions,omitempty" description:"options that control the SELinux labels applied"`
|
||||
|
||||
// RunAsUser is the UID to run the entrypoint of the container process.
|
||||
RunAsUser *int64 `json:"runAsUser,omitempty" description:"the user id that runs the first process in the container"`
|
||||
}
|
||||
|
||||
// SELinuxOptions are the labels to be applied to the container.
|
||||
type SELinuxOptions struct {
|
||||
// SELinux user label
|
||||
User string `json:"user,omitempty" description:"the user label to apply to the container"`
|
||||
|
||||
// SELinux role label
|
||||
Role string `json:"role,omitempty" description:"the role label to apply to the container"`
|
||||
|
||||
// SELinux type label
|
||||
Type string `json:"type,omitempty" description:"the type label to apply to the container"`
|
||||
|
||||
// SELinux level label.
|
||||
Level string `json:"level,omitempty" description:"the level label to apply to the container"`
|
||||
}
|
||||
|
|
|
@ -776,12 +776,15 @@ func validateContainers(containers []api.Container, volumes util.StringSet) errs
|
|||
allNames := util.StringSet{}
|
||||
for i, ctr := range containers {
|
||||
cErrs := errs.ValidationErrorList{}
|
||||
capabilities := capabilities.Get()
|
||||
if len(ctr.Name) == 0 {
|
||||
cErrs = append(cErrs, errs.NewFieldRequired("name"))
|
||||
} else if !util.IsDNS1123Label(ctr.Name) {
|
||||
cErrs = append(cErrs, errs.NewFieldInvalid("name", ctr.Name, dns1123LabelErrorMsg))
|
||||
} else if allNames.Has(ctr.Name) {
|
||||
cErrs = append(cErrs, errs.NewFieldDuplicate("name", ctr.Name))
|
||||
} else if ctr.Privileged && !capabilities.AllowPrivileged {
|
||||
cErrs = append(cErrs, errs.NewFieldForbidden("privileged", ctr.Privileged))
|
||||
} else {
|
||||
allNames.Insert(ctr.Name)
|
||||
}
|
||||
|
@ -798,7 +801,6 @@ func validateContainers(containers []api.Container, volumes util.StringSet) errs
|
|||
cErrs = append(cErrs, validateVolumeMounts(ctr.VolumeMounts, volumes).Prefix("volumeMounts")...)
|
||||
cErrs = append(cErrs, validatePullPolicy(&ctr).Prefix("pullPolicy")...)
|
||||
cErrs = append(cErrs, ValidateResourceRequirements(&ctr.Resources).Prefix("resources")...)
|
||||
cErrs = append(cErrs, ValidateSecurityContext(ctr.SecurityContext).Prefix("securityContext")...)
|
||||
allErrs = append(allErrs, cErrs.PrefixIndex(i)...)
|
||||
}
|
||||
// Check for colliding ports across all containers.
|
||||
|
@ -1479,25 +1481,3 @@ func ValidateEndpointsUpdate(oldEndpoints, newEndpoints *api.Endpoints) errs.Val
|
|||
allErrs = append(allErrs, validateEndpointSubsets(newEndpoints.Subsets).Prefix("subsets")...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
// ValidateSecurityContext ensure the security context contains valid settings
|
||||
func ValidateSecurityContext(sc *api.SecurityContext) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
//this should only be true for testing since SecurityContext is defaulted by the api
|
||||
if sc == nil {
|
||||
return allErrs
|
||||
}
|
||||
|
||||
if sc.Privileged != nil {
|
||||
if *sc.Privileged && !capabilities.Get().AllowPrivileged {
|
||||
allErrs = append(allErrs, errs.NewFieldForbidden("privileged", sc.Privileged))
|
||||
}
|
||||
}
|
||||
|
||||
if sc.RunAsUser != nil {
|
||||
if *sc.RunAsUser < 0 {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("runAsUser", *sc.RunAsUser, "runAsUser cannot be negative"))
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
}
|
||||
|
|
|
@ -901,7 +901,7 @@ func TestValidateContainers(t *testing.T) {
|
|||
},
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
{Name: "abc-1234", Image: "image", ImagePullPolicy: "IfNotPresent", SecurityContext: fakeValidSecurityContext(true)},
|
||||
{Name: "abc-1234", Image: "image", Privileged: true, ImagePullPolicy: "IfNotPresent"},
|
||||
}
|
||||
if errs := validateContainers(successCase, volumes); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
|
@ -1015,7 +1015,7 @@ func TestValidateContainers(t *testing.T) {
|
|||
},
|
||||
},
|
||||
"privilege disabled": {
|
||||
{Name: "abc", Image: "image", SecurityContext: fakeValidSecurityContext(true)},
|
||||
{Name: "abc", Image: "image", Privileged: true},
|
||||
},
|
||||
"invalid compute resource": {
|
||||
{
|
||||
|
@ -3180,89 +3180,3 @@ func TestValidateEndpoints(t *testing.T) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateSecurityContext(t *testing.T) {
|
||||
priv := false
|
||||
var runAsUser int64 = 1
|
||||
fullValidSC := func() *api.SecurityContext {
|
||||
return &api.SecurityContext{
|
||||
Privileged: &priv,
|
||||
Capabilities: &api.Capabilities{
|
||||
Add: []api.CapabilityType{"foo"},
|
||||
Drop: []api.CapabilityType{"bar"},
|
||||
},
|
||||
SELinuxOptions: &api.SELinuxOptions{
|
||||
User: "user",
|
||||
Role: "role",
|
||||
Type: "type",
|
||||
Level: "level",
|
||||
},
|
||||
RunAsUser: &runAsUser,
|
||||
}
|
||||
}
|
||||
|
||||
//setup data
|
||||
allSettings := fullValidSC()
|
||||
noCaps := fullValidSC()
|
||||
noCaps.Capabilities = nil
|
||||
|
||||
noSELinux := fullValidSC()
|
||||
noSELinux.SELinuxOptions = nil
|
||||
|
||||
noPrivRequest := fullValidSC()
|
||||
noPrivRequest.Privileged = nil
|
||||
|
||||
noRunAsUser := fullValidSC()
|
||||
noRunAsUser.RunAsUser = nil
|
||||
|
||||
successCases := map[string]struct {
|
||||
sc *api.SecurityContext
|
||||
}{
|
||||
"all settings": {allSettings},
|
||||
"no capabilities": {noCaps},
|
||||
"no selinux": {noSELinux},
|
||||
"no priv request": {noPrivRequest},
|
||||
"no run as user": {noRunAsUser},
|
||||
}
|
||||
for k, v := range successCases {
|
||||
if errs := ValidateSecurityContext(v.sc); len(errs) != 0 {
|
||||
t.Errorf("Expected success for %s, got %v", k, errs)
|
||||
}
|
||||
}
|
||||
|
||||
privRequestWithGlobalDeny := fullValidSC()
|
||||
requestPrivileged := true
|
||||
privRequestWithGlobalDeny.Privileged = &requestPrivileged
|
||||
|
||||
negativeRunAsUser := fullValidSC()
|
||||
var negativeUser int64 = -1
|
||||
negativeRunAsUser.RunAsUser = &negativeUser
|
||||
|
||||
errorCases := map[string]struct {
|
||||
sc *api.SecurityContext
|
||||
errorType fielderrors.ValidationErrorType
|
||||
errorDetail string
|
||||
}{
|
||||
"request privileged when capabilities forbids": {
|
||||
sc: privRequestWithGlobalDeny,
|
||||
errorType: "FieldValueForbidden",
|
||||
errorDetail: "",
|
||||
},
|
||||
"negative RunAsUser": {
|
||||
sc: negativeRunAsUser,
|
||||
errorType: "FieldValueInvalid",
|
||||
errorDetail: "runAsUser cannot be negative",
|
||||
},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
if errs := ValidateSecurityContext(v.sc); len(errs) == 0 || errs[0].(*errors.ValidationError).Type != v.errorType || errs[0].(*errors.ValidationError).Detail != v.errorDetail {
|
||||
t.Errorf("Expected error type %s with detail %s for %s, got %v", v.errorType, v.errorDetail, k, errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fakeValidSecurityContext(priv bool) *api.SecurityContext {
|
||||
return &api.SecurityContext{
|
||||
Privileged: &priv,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,6 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/testclient"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||
|
@ -113,7 +112,6 @@ func newReplicationController(replicas int) *api.ReplicationController {
|
|||
Image: "foo/bar",
|
||||
TerminationMessagePath: api.TerminationMessagePathDefault,
|
||||
ImagePullPolicy: api.PullIfNotPresent,
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
||||
},
|
||||
},
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
|
|
|
@ -22,7 +22,6 @@ import (
|
|||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
||||
|
||||
"github.com/ghodss/yaml"
|
||||
)
|
||||
|
@ -47,7 +46,6 @@ func TestDecodeSinglePod(t *testing.T) {
|
|||
Image: "test/image",
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
@ -110,7 +108,6 @@ func TestDecodePodList(t *testing.T) {
|
|||
Image: "test/image",
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -63,14 +62,7 @@ func CreateValidPod(name, namespace, source string) *api.Pod {
|
|||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: "ctr",
|
||||
Image: "image",
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
||||
},
|
||||
},
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
||||
)
|
||||
|
||||
|
@ -106,8 +105,7 @@ func TestReadContainerManifestFromFile(t *testing.T) {
|
|||
Name: "image",
|
||||
Image: "test/image",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "Always",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
ImagePullPolicy: "Always"}},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
@ -133,8 +131,7 @@ func TestReadContainerManifestFromFile(t *testing.T) {
|
|||
Name: "image",
|
||||
Image: "test/image",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "Always",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
ImagePullPolicy: "Always"}},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
@ -185,7 +182,7 @@ func TestReadPodsFromFile(t *testing.T) {
|
|||
Namespace: "mynamespace",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Name: "image", Image: "test/image", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
Containers: []api.Container{{Name: "image", Image: "test/image"}},
|
||||
},
|
||||
},
|
||||
expected: CreatePodUpdate(kubelet.SET, kubelet.FileSource, &api.Pod{
|
||||
|
@ -203,8 +200,7 @@ func TestReadPodsFromFile(t *testing.T) {
|
|||
Name: "image",
|
||||
Image: "test/image",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
@ -220,7 +216,7 @@ func TestReadPodsFromFile(t *testing.T) {
|
|||
UID: "12345",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{{Name: "image", Image: "test/image", SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
Containers: []api.Container{{Name: "image", Image: "test/image"}},
|
||||
},
|
||||
},
|
||||
expected: CreatePodUpdate(kubelet.SET, kubelet.FileSource, &api.Pod{
|
||||
|
@ -238,8 +234,7 @@ func TestReadPodsFromFile(t *testing.T) {
|
|||
Name: "image",
|
||||
Image: "test/image",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
|
|
@ -28,7 +28,6 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
|
||||
)
|
||||
|
@ -152,8 +151,7 @@ func TestExtractManifestFromHTTP(t *testing.T) {
|
|||
Name: "1",
|
||||
Image: "foo",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "Always",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
ImagePullPolicy: "Always"}},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
@ -179,8 +177,7 @@ func TestExtractManifestFromHTTP(t *testing.T) {
|
|||
Name: "ctr",
|
||||
Image: "image",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
@ -206,8 +203,7 @@ func TestExtractManifestFromHTTP(t *testing.T) {
|
|||
Name: "1",
|
||||
Image: "foo",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "Always",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
ImagePullPolicy: "Always"}},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
@ -237,8 +233,7 @@ func TestExtractManifestFromHTTP(t *testing.T) {
|
|||
Name: "1",
|
||||
Image: "foo",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "Always",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
ImagePullPolicy: "Always"}},
|
||||
},
|
||||
},
|
||||
&api.Pod{
|
||||
|
@ -257,8 +252,7 @@ func TestExtractManifestFromHTTP(t *testing.T) {
|
|||
Name: "1",
|
||||
Image: "foo",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
@ -350,8 +344,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
|
|||
Name: "1",
|
||||
Image: "foo",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "Always",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
ImagePullPolicy: "Always"}},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
@ -403,8 +396,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
|
|||
Name: "1",
|
||||
Image: "foo",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "Always",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
ImagePullPolicy: "Always"}},
|
||||
},
|
||||
},
|
||||
&api.Pod{
|
||||
|
@ -423,8 +415,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
|
|||
Name: "2",
|
||||
Image: "bar",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
||||
ImagePullPolicy: "IfNotPresent"}},
|
||||
},
|
||||
}),
|
||||
},
|
||||
|
|
|
@ -38,7 +38,6 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/prober"
|
||||
kubeletTypes "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/types"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
|
@ -525,8 +524,6 @@ func (dm *DockerManager) runContainer(pod *api.Pod, container *api.Container, op
|
|||
|
||||
glog.V(3).Infof("Container %v/%v/%v: setting entrypoint \"%v\" and command \"%v\"", pod.Namespace, pod.Name, container.Name, dockerOpts.Config.Entrypoint, dockerOpts.Config.Cmd)
|
||||
|
||||
securityContextProvider := securitycontext.NewSimpleSecurityContextProvider()
|
||||
securityContextProvider.ModifyContainerConfig(pod, container, dockerOpts.Config)
|
||||
dockerContainer, err := dm.client.CreateContainer(dockerOpts)
|
||||
if err != nil {
|
||||
if ref != nil {
|
||||
|
@ -557,15 +554,22 @@ func (dm *DockerManager) runContainer(pod *api.Pod, container *api.Container, op
|
|||
}
|
||||
}
|
||||
|
||||
if !capabilities.Get().AllowPrivileged && securitycontext.HasPrivilegedRequest(container) {
|
||||
privileged := false
|
||||
if capabilities.Get().AllowPrivileged {
|
||||
privileged = container.Privileged
|
||||
} else if container.Privileged {
|
||||
return "", fmt.Errorf("container requested privileged mode, but it is disallowed globally.")
|
||||
}
|
||||
|
||||
capAdd, capDrop := makeCapabilites(container.Capabilities.Add, container.Capabilities.Drop)
|
||||
hc := &docker.HostConfig{
|
||||
PortBindings: portBindings,
|
||||
Binds: opts.Binds,
|
||||
NetworkMode: opts.NetMode,
|
||||
IpcMode: opts.IpcMode,
|
||||
Privileged: privileged,
|
||||
CapAdd: capAdd,
|
||||
CapDrop: capDrop,
|
||||
}
|
||||
if len(opts.DNS) > 0 {
|
||||
hc.DNS = opts.DNS
|
||||
|
@ -576,7 +580,6 @@ func (dm *DockerManager) runContainer(pod *api.Pod, container *api.Container, op
|
|||
if len(opts.CgroupParent) > 0 {
|
||||
hc.CgroupParent = opts.CgroupParent
|
||||
}
|
||||
securityContextProvider.ModifyHostConfig(pod, container, hc)
|
||||
|
||||
if err = dm.client.StartContainer(dockerContainer.ID, hc); err != nil {
|
||||
if ref != nil {
|
||||
|
@ -634,6 +637,20 @@ func makePortsAndBindings(container *api.Container) (map[docker.Port]struct{}, m
|
|||
return exposedPorts, portBindings
|
||||
}
|
||||
|
||||
func makeCapabilites(capAdd []api.CapabilityType, capDrop []api.CapabilityType) ([]string, []string) {
|
||||
var (
|
||||
addCaps []string
|
||||
dropCaps []string
|
||||
)
|
||||
for _, cap := range capAdd {
|
||||
addCaps = append(addCaps, string(cap))
|
||||
}
|
||||
for _, cap := range capDrop {
|
||||
dropCaps = append(dropCaps, string(cap))
|
||||
}
|
||||
return addCaps, dropCaps
|
||||
}
|
||||
|
||||
// A helper function to get the KubeletContainerName and hash from a docker
|
||||
// container.
|
||||
func getDockerContainerNameInfo(c *docker.APIContainers) (*KubeletContainerName, uint64, error) {
|
||||
|
|
|
@ -38,7 +38,6 @@ import (
|
|||
kubecontainer "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/container"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/prober"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
|
||||
|
@ -188,29 +187,23 @@ func rawValue(value string) *json.RawMessage {
|
|||
}
|
||||
|
||||
// setIsolators overrides the isolators of the pod manifest if necessary.
|
||||
// TODO need an apply config in security context for rkt
|
||||
func setIsolators(app *appctypes.App, c *api.Container) error {
|
||||
hasCapRequests := securitycontext.HasCapabilitiesRequest(c)
|
||||
if hasCapRequests || len(c.Resources.Limits) > 0 || len(c.Resources.Requests) > 0 {
|
||||
if len(c.Capabilities.Add) > 0 || len(c.Capabilities.Drop) > 0 || len(c.Resources.Limits) > 0 || len(c.Resources.Requests) > 0 {
|
||||
app.Isolators = []appctypes.Isolator{}
|
||||
}
|
||||
|
||||
// Retained capabilities/privileged.
|
||||
privileged := false
|
||||
if !capabilities.Get().AllowPrivileged && securitycontext.HasPrivilegedRequest(c) {
|
||||
return fmt.Errorf("container requested privileged mode, but it is disallowed globally.")
|
||||
} else {
|
||||
if c.SecurityContext != nil && c.SecurityContext.Privileged != nil {
|
||||
privileged = *c.SecurityContext.Privileged
|
||||
}
|
||||
if capabilities.Get().AllowPrivileged {
|
||||
privileged = c.Privileged
|
||||
} else if c.Privileged {
|
||||
return fmt.Errorf("privileged is disallowed globally")
|
||||
}
|
||||
var addCaps string
|
||||
if privileged {
|
||||
addCaps = getAllCapabilities()
|
||||
} else {
|
||||
if hasCapRequests {
|
||||
addCaps = getCapabilities(c.SecurityContext.Capabilities.Add)
|
||||
}
|
||||
addCaps = getCapabilities(c.Capabilities.Add)
|
||||
}
|
||||
if len(addCaps) > 0 {
|
||||
// TODO(yifan): Replace with constructor, see:
|
||||
|
@ -223,10 +216,7 @@ func setIsolators(app *appctypes.App, c *api.Container) error {
|
|||
}
|
||||
|
||||
// Removed capabilities.
|
||||
var dropCaps string
|
||||
if hasCapRequests {
|
||||
dropCaps = getCapabilities(c.SecurityContext.Capabilities.Drop)
|
||||
}
|
||||
dropCaps := getCapabilities(c.Capabilities.Drop)
|
||||
if len(dropCaps) > 0 {
|
||||
// TODO(yifan): Replace with constructor, see:
|
||||
// https://github.com/appc/spec/issues/268
|
||||
|
|
|
@ -32,7 +32,6 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/pod"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools/etcdtest"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
|
@ -69,7 +68,6 @@ func validNewPod() *api.Pod {
|
|||
ImagePullPolicy: api.PullAlways,
|
||||
|
||||
TerminationMessagePath: api.TerminationMessagePathDefault,
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1110,9 +1108,8 @@ func TestEtcdUpdateScheduled(t *testing.T) {
|
|||
Host: "machine",
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: "foobar",
|
||||
Image: "foo:v1",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
||||
Name: "foobar",
|
||||
Image: "foo:v1",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1134,7 +1131,6 @@ func TestEtcdUpdateScheduled(t *testing.T) {
|
|||
Image: "foo:v2",
|
||||
ImagePullPolicy: api.PullIfNotPresent,
|
||||
TerminationMessagePath: api.TerminationMessagePathDefault,
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
||||
},
|
||||
},
|
||||
RestartPolicy: api.RestartPolicyAlways,
|
||||
|
@ -1173,8 +1169,7 @@ func TestEtcdUpdateStatus(t *testing.T) {
|
|||
Host: "machine",
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Image: "foo:v1",
|
||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
||||
Image: "foo:v1",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package securitycontext contains security context api implementations
|
||||
package securitycontext
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package securitycontext
|
||||
|
||||
import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
// ValidSecurityContextWithContainerDefaults creates a valid security context provider based on
|
||||
// empty container defaults. Used for testing.
|
||||
func ValidSecurityContextWithContainerDefaults() *api.SecurityContext {
|
||||
priv := false
|
||||
return &api.SecurityContext{
|
||||
Capabilities: &api.Capabilities{},
|
||||
Privileged: &priv,
|
||||
}
|
||||
}
|
||||
|
||||
// NewFakeSecurityContextProvider creates a new, no-op security context provider.
|
||||
func NewFakeSecurityContextProvider() SecurityContextProvider {
|
||||
return FakeSecurityContextProvider{}
|
||||
}
|
||||
|
||||
type FakeSecurityContextProvider struct{}
|
||||
|
||||
func (p FakeSecurityContextProvider) ModifyContainerConfig(pod *api.Pod, container *api.Container, config *docker.Config) {
|
||||
}
|
||||
func (p FakeSecurityContextProvider) ModifyHostConfig(pod *api.Pod, container *api.Container, hostConfig *docker.HostConfig) {
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package securitycontext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
// NewSimpleSecurityContextProvider creates a new SimpleSecurityContextProvider.
|
||||
func NewSimpleSecurityContextProvider() SecurityContextProvider {
|
||||
return SimpleSecurityContextProvider{}
|
||||
}
|
||||
|
||||
// SimpleSecurityContextProvider is the default implementation of a SecurityContextProvider.
|
||||
type SimpleSecurityContextProvider struct{}
|
||||
|
||||
// ModifyContainerConfig is called before the Docker createContainer call.
|
||||
// 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 {
|
||||
return
|
||||
}
|
||||
if container.SecurityContext.RunAsUser != nil {
|
||||
config.User = strconv.FormatInt(*container.SecurityContext.RunAsUser, 10)
|
||||
}
|
||||
}
|
||||
|
||||
// ModifyHostConfig is called before the Docker runContainer call.
|
||||
// The security context provider can make changes to the HostConfig, affecting
|
||||
// security options, whether the container is privileged, volume binds, etc.
|
||||
// An error is returned if it's not possible to secure the container as requested
|
||||
// with a security context.
|
||||
func (p SimpleSecurityContextProvider) ModifyHostConfig(pod *api.Pod, container *api.Container, hostConfig *docker.HostConfig) {
|
||||
if container.SecurityContext == nil {
|
||||
return
|
||||
}
|
||||
if container.SecurityContext.Privileged != nil {
|
||||
hostConfig.Privileged = *container.SecurityContext.Privileged
|
||||
}
|
||||
|
||||
if container.SecurityContext.Capabilities != nil {
|
||||
add, drop := makeCapabilites(container.SecurityContext.Capabilities.Add, container.SecurityContext.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)
|
||||
}
|
||||
}
|
||||
|
||||
// modifySecurityOption adds the security option of name to the config array with value in the form
|
||||
// of name:value
|
||||
func modifySecurityOption(config []string, name, value string) []string {
|
||||
if len(value) > 0 {
|
||||
config = append(config, fmt.Sprintf("%s:%s", name, value))
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
// makeCapabilites creates string slices from CapabilityType slices
|
||||
func makeCapabilites(capAdd []api.CapabilityType, capDrop []api.CapabilityType) ([]string, []string) {
|
||||
var (
|
||||
addCaps []string
|
||||
dropCaps []string
|
||||
)
|
||||
for _, cap := range capAdd {
|
||||
addCaps = append(addCaps, string(cap))
|
||||
}
|
||||
for _, cap := range capDrop {
|
||||
dropCaps = append(dropCaps, string(cap))
|
||||
}
|
||||
return addCaps, dropCaps
|
||||
}
|
|
@ -1,181 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package securitycontext
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
func TestModifyContainerConfig(t *testing.T) {
|
||||
var uid int64 = 1
|
||||
testCases := map[string]struct {
|
||||
securityContext *api.SecurityContext
|
||||
expected *docker.Config
|
||||
}{
|
||||
"modify config, value set for user": {
|
||||
securityContext: &api.SecurityContext{
|
||||
RunAsUser: &uid,
|
||||
},
|
||||
expected: &docker.Config{
|
||||
User: strconv.FormatInt(uid, 10),
|
||||
},
|
||||
},
|
||||
"modify config, nil user value": {
|
||||
securityContext: &api.SecurityContext{},
|
||||
expected: &docker.Config{},
|
||||
},
|
||||
}
|
||||
|
||||
provider := NewSimpleSecurityContextProvider()
|
||||
dummyContainer := &api.Container{}
|
||||
for k, v := range testCases {
|
||||
dummyContainer.SecurityContext = v.securityContext
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestModifyHostConfig(t *testing.T) {
|
||||
nilPrivSC := fullValidSecurityContext()
|
||||
nilPrivSC.Privileged = nil
|
||||
nilPrivHC := fullValidHostConfig()
|
||||
nilPrivHC.Privileged = false
|
||||
|
||||
nilCapsSC := fullValidSecurityContext()
|
||||
nilCapsSC.Capabilities = nil
|
||||
nilCapsHC := fullValidHostConfig()
|
||||
nilCapsHC.CapAdd = *new([]string)
|
||||
nilCapsHC.CapDrop = *new([]string)
|
||||
|
||||
nilSELinuxSC := fullValidSecurityContext()
|
||||
nilSELinuxSC.SELinuxOptions = nil
|
||||
nilSELinuxHC := fullValidHostConfig()
|
||||
nilSELinuxHC.SecurityOpt = *new([]string)
|
||||
|
||||
seLinuxLabelsSC := fullValidSecurityContext()
|
||||
seLinuxLabelsHC := fullValidHostConfig()
|
||||
|
||||
testCases := map[string]struct {
|
||||
securityContext *api.SecurityContext
|
||||
expected *docker.HostConfig
|
||||
}{
|
||||
"full settings": {
|
||||
securityContext: fullValidSecurityContext(),
|
||||
expected: fullValidHostConfig(),
|
||||
},
|
||||
"nil privileged": {
|
||||
securityContext: nilPrivSC,
|
||||
expected: nilPrivHC,
|
||||
},
|
||||
"nil capabilities": {
|
||||
securityContext: nilCapsSC,
|
||||
expected: nilCapsHC,
|
||||
},
|
||||
"nil selinux options": {
|
||||
securityContext: nilSELinuxSC,
|
||||
expected: nilSELinuxHC,
|
||||
},
|
||||
"selinux labels": {
|
||||
securityContext: seLinuxLabelsSC,
|
||||
expected: seLinuxLabelsHC,
|
||||
},
|
||||
}
|
||||
|
||||
provider := NewSimpleSecurityContextProvider()
|
||||
dummyContainer := &api.Container{}
|
||||
for k, v := range testCases {
|
||||
dummyContainer.SecurityContext = v.securityContext
|
||||
dockerCfg := &docker.HostConfig{}
|
||||
provider.ModifyHostConfig(nil, dummyContainer, dockerCfg)
|
||||
if !reflect.DeepEqual(v.expected, dockerCfg) {
|
||||
t.Errorf("unexpected modification of host config for %s. Expected: %#v Got: %#v", k, v.expected, dockerCfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestModifySecurityOption(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
config []string
|
||||
optName string
|
||||
optVal string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "Empty val",
|
||||
config: []string{"a:b", "c:d"},
|
||||
optName: "optA",
|
||||
optVal: "",
|
||||
expected: []string{"a:b", "c:d"},
|
||||
},
|
||||
{
|
||||
name: "Valid",
|
||||
config: []string{"a:b", "c:d"},
|
||||
optName: "e",
|
||||
optVal: "f",
|
||||
expected: []string{"a:b", "c:d", "e:f"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
actual := modifySecurityOption(tc.config, tc.optName, tc.optVal)
|
||||
if !reflect.DeepEqual(tc.expected, actual) {
|
||||
t.Errorf("Failed to apply options correctly for tc: %S. Expected: %v but got %v", tc.name, tc.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fullValidSecurityContext() *api.SecurityContext {
|
||||
priv := true
|
||||
return &api.SecurityContext{
|
||||
Privileged: &priv,
|
||||
Capabilities: &api.Capabilities{
|
||||
Add: []api.CapabilityType{"addCapA", "addCapB"},
|
||||
Drop: []api.CapabilityType{"dropCapA", "dropCapB"},
|
||||
},
|
||||
SELinuxOptions: &api.SELinuxOptions{
|
||||
User: "user",
|
||||
Role: "role",
|
||||
Type: "type",
|
||||
Level: "level",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func fullValidHostConfig() *docker.HostConfig {
|
||||
return &docker.HostConfig{
|
||||
Privileged: true,
|
||||
CapAdd: []string{"addCapA", "addCapB"},
|
||||
CapDrop: []string{"dropCapA", "dropCapB"},
|
||||
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"),
|
||||
},
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package securitycontext
|
||||
|
||||
import (
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
|
||||
docker "github.com/fsouza/go-dockerclient"
|
||||
)
|
||||
|
||||
type SecurityContextProvider interface {
|
||||
// ModifyContainerConfig is called before the Docker createContainer call.
|
||||
// The security context provider can make changes to the Config with which
|
||||
// the container is created.
|
||||
ModifyContainerConfig(pod *api.Pod, container *api.Container, config *docker.Config)
|
||||
|
||||
// ModifyHostConfig is called before the Docker runContainer call.
|
||||
// The security context provider can make changes to the HostConfig, affecting
|
||||
// security options, whether the container is privileged, volume binds, etc.
|
||||
// An error is returned if it's not possible to secure the container as requested
|
||||
// with a security context.
|
||||
ModifyHostConfig(pod *api.Pod, container *api.Container, hostConfig *docker.HostConfig)
|
||||
}
|
||||
|
||||
const (
|
||||
dockerLabelUser string = "label:user"
|
||||
dockerLabelRole string = "label:role"
|
||||
dockerLabelType string = "label:type"
|
||||
dockerLabelLevel string = "label:level"
|
||||
dockerLabelDisable string = "label:disable"
|
||||
)
|
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package securitycontext
|
||||
|
||||
import "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
|
||||
// HasPrivilegedRequest returns the value of SecurityContext.Privileged, taking into account
|
||||
// the possibility of nils
|
||||
func HasPrivilegedRequest(container *api.Container) bool {
|
||||
if container.SecurityContext == nil {
|
||||
return false
|
||||
}
|
||||
if container.SecurityContext.Privileged == nil {
|
||||
return false
|
||||
}
|
||||
return *container.SecurityContext.Privileged
|
||||
}
|
||||
|
||||
// HasCapabilitiesRequest returns true if Adds or Drops are defined in the security context
|
||||
// capabilities, taking into account nils
|
||||
func HasCapabilitiesRequest(container *api.Container) bool {
|
||||
if container.SecurityContext == nil {
|
||||
return false
|
||||
}
|
||||
if container.SecurityContext.Capabilities == nil {
|
||||
return false
|
||||
}
|
||||
return len(container.SecurityContext.Capabilities.Add) > 0 || len(container.SecurityContext.Capabilities.Drop) > 0
|
||||
}
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package scdeny
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
apierrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||
)
|
||||
|
||||
func init() {
|
||||
admission.RegisterPlugin("SecurityContextDeny", func(client client.Interface, config io.Reader) (admission.Interface, error) {
|
||||
return NewSecurityContextDeny(client), nil
|
||||
})
|
||||
}
|
||||
|
||||
// plugin contains the client used by the SecurityContextDeny admission controller
|
||||
type plugin struct {
|
||||
client client.Interface
|
||||
}
|
||||
|
||||
// NewSecurityContextDeny creates a new instance of the SecurityContextDeny admission controller
|
||||
func NewSecurityContextDeny(client client.Interface) admission.Interface {
|
||||
return &plugin{client}
|
||||
}
|
||||
|
||||
// Admit will deny any SecurityContext that defines options that were not previously available in the api.Container
|
||||
// struct (Capabilities and Privileged)
|
||||
func (p *plugin) Admit(a admission.Attributes) (err error) {
|
||||
if a.GetOperation() == "DELETE" {
|
||||
return nil
|
||||
}
|
||||
if a.GetResource() != string(api.ResourcePods) {
|
||||
return nil
|
||||
}
|
||||
|
||||
pod, ok := a.GetObject().(*api.Pod)
|
||||
if !ok {
|
||||
return apierrors.NewBadRequest("Resource was marked with kind Pod but was unable to be converted")
|
||||
}
|
||||
for _, v := range pod.Spec.Containers {
|
||||
if v.SecurityContext != nil {
|
||||
if v.SecurityContext.SELinuxOptions != nil {
|
||||
return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("SecurityContext.SELinuxOptions is forbidden"))
|
||||
}
|
||||
if v.SecurityContext.RunAsUser != nil {
|
||||
return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("SecurityContext.RunAsUser is forbidden"))
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package scdeny
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
)
|
||||
|
||||
// ensures the SecurityContext is denied if it defines anything more than Caps or Privileged
|
||||
func TestAdmission(t *testing.T) {
|
||||
handler := NewSecurityContextDeny(nil)
|
||||
|
||||
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{
|
||||
{},
|
||||
},
|
||||
},
|
||||
}
|
||||
for k, v := range successCases {
|
||||
pod.Spec.Containers[0].SecurityContext = v
|
||||
err := handler.Admit(admission.NewAttributesRecord(&pod, "Pod", "foo", string(api.ResourcePods), "ignored"))
|
||||
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", string(api.ResourcePods), "ignored"))
|
||||
if err == nil {
|
||||
t.Errorf("Expected error returned from admission handler for case %s", k)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue