Revert "Security context - types, kubelet, admission"
This commit is contained in:
parent
3931ee9f51
commit
f48904fd5e
@ -7501,7 +7501,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"$ref": "v1beta1.Capabilities",
|
"$ref": "v1beta1.Capabilities",
|
||||||
"description": "capabilities for container; cannot be updated; deprecated; See SecurityContext"
|
"description": "capabilities for container; cannot be updated"
|
||||||
},
|
},
|
||||||
"command": {
|
"command": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@ -7563,7 +7563,7 @@
|
|||||||
},
|
},
|
||||||
"privileged": {
|
"privileged": {
|
||||||
"type": "boolean",
|
"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": {
|
"readinessProbe": {
|
||||||
"$ref": "v1beta1.LivenessProbe",
|
"$ref": "v1beta1.LivenessProbe",
|
||||||
@ -7573,10 +7573,6 @@
|
|||||||
"$ref": "v1beta1.ResourceRequirements",
|
"$ref": "v1beta1.ResourceRequirements",
|
||||||
"description": "Compute Resources required by this container; cannot be updated"
|
"description": "Compute Resources required by this container; cannot be updated"
|
||||||
},
|
},
|
||||||
"securityContext": {
|
|
||||||
"$ref": "v1beta1.SecurityContext",
|
|
||||||
"description": "security options the pod should run with"
|
|
||||||
},
|
|
||||||
"terminationMessagePath": {
|
"terminationMessagePath": {
|
||||||
"type": "string",
|
"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"
|
"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"
|
"description": "restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever"
|
||||||
},
|
},
|
||||||
"terminationGracePeriodSeconds": {
|
"terminationGracePeriodSeconds": {
|
||||||
"type": "integer",
|
"$ref": "int64",
|
||||||
"format": "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"
|
"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": {
|
"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"
|
"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": {
|
"gracePeriodSeconds": {
|
||||||
"type": "integer",
|
"$ref": "int64",
|
||||||
"format": "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"
|
"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": {
|
"id": {
|
||||||
@ -9823,27 +9817,6 @@
|
|||||||
"id": "v1beta1.RestartPolicyOnFailure",
|
"id": "v1beta1.RestartPolicyOnFailure",
|
||||||
"properties": {}
|
"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": {
|
"v1beta1.Secret": {
|
||||||
"id": "v1beta1.Secret",
|
"id": "v1beta1.Secret",
|
||||||
"properties": {
|
"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": {
|
"v1beta1.Service": {
|
||||||
"id": "v1beta1.Service",
|
"id": "v1beta1.Service",
|
||||||
"required": [
|
"required": [
|
||||||
|
@ -7501,7 +7501,7 @@
|
|||||||
"properties": {
|
"properties": {
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"$ref": "v1beta2.Capabilities",
|
"$ref": "v1beta2.Capabilities",
|
||||||
"description": "capabilities for container; cannot be updated; deprecated; See SecurityContext"
|
"description": "capabilities for container; cannot be updated"
|
||||||
},
|
},
|
||||||
"command": {
|
"command": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@ -7563,7 +7563,7 @@
|
|||||||
},
|
},
|
||||||
"privileged": {
|
"privileged": {
|
||||||
"type": "boolean",
|
"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": {
|
"readinessProbe": {
|
||||||
"$ref": "v1beta2.LivenessProbe",
|
"$ref": "v1beta2.LivenessProbe",
|
||||||
@ -7573,10 +7573,6 @@
|
|||||||
"$ref": "v1beta2.ResourceRequirements",
|
"$ref": "v1beta2.ResourceRequirements",
|
||||||
"description": "Compute Resources required by this container; cannot be updated"
|
"description": "Compute Resources required by this container; cannot be updated"
|
||||||
},
|
},
|
||||||
"securityContext": {
|
|
||||||
"$ref": "v1beta2.SecurityContext",
|
|
||||||
"description": "security options the pod should run with"
|
|
||||||
},
|
|
||||||
"terminationMessagePath": {
|
"terminationMessagePath": {
|
||||||
"type": "string",
|
"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"
|
"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"
|
"description": "restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever"
|
||||||
},
|
},
|
||||||
"terminationGracePeriodSeconds": {
|
"terminationGracePeriodSeconds": {
|
||||||
"type": "integer",
|
"$ref": "int64",
|
||||||
"format": "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"
|
"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": {
|
"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"
|
"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": {
|
"gracePeriodSeconds": {
|
||||||
"type": "integer",
|
"$ref": "int64",
|
||||||
"format": "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"
|
"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": {
|
"id": {
|
||||||
@ -9237,10 +9231,10 @@
|
|||||||
"v1beta2.PersistentVolumeSpec": {
|
"v1beta2.PersistentVolumeSpec": {
|
||||||
"id": "v1beta2.PersistentVolumeSpec",
|
"id": "v1beta2.PersistentVolumeSpec",
|
||||||
"required": [
|
"required": [
|
||||||
|
"glusterfs",
|
||||||
"persistentDisk",
|
"persistentDisk",
|
||||||
"awsElasticBlockStore",
|
"awsElasticBlockStore",
|
||||||
"hostPath",
|
"hostPath"
|
||||||
"glusterfs"
|
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"accessModes": {
|
"accessModes": {
|
||||||
@ -9812,27 +9806,6 @@
|
|||||||
"id": "v1beta2.RestartPolicyOnFailure",
|
"id": "v1beta2.RestartPolicyOnFailure",
|
||||||
"properties": {}
|
"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": {
|
"v1beta2.Secret": {
|
||||||
"id": "v1beta2.Secret",
|
"id": "v1beta2.Secret",
|
||||||
"properties": {
|
"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": {
|
"v1beta2.Service": {
|
||||||
"id": "v1beta2.Service",
|
"id": "v1beta2.Service",
|
||||||
"required": [
|
"required": [
|
||||||
|
@ -8458,7 +8458,7 @@
|
|||||||
},
|
},
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"$ref": "v1beta3.Capabilities",
|
"$ref": "v1beta3.Capabilities",
|
||||||
"description": "capabilities for container; cannot be updated; deprecated; See SecurityContext."
|
"description": "capabilities for container; cannot be updated"
|
||||||
},
|
},
|
||||||
"command": {
|
"command": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@ -8503,7 +8503,7 @@
|
|||||||
},
|
},
|
||||||
"privileged": {
|
"privileged": {
|
||||||
"type": "boolean",
|
"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": {
|
"readinessProbe": {
|
||||||
"$ref": "v1beta3.Probe",
|
"$ref": "v1beta3.Probe",
|
||||||
@ -8513,10 +8513,6 @@
|
|||||||
"$ref": "v1beta3.ResourceRequirements",
|
"$ref": "v1beta3.ResourceRequirements",
|
||||||
"description": "Compute Resources required by this container; cannot be updated"
|
"description": "Compute Resources required by this container; cannot be updated"
|
||||||
},
|
},
|
||||||
"securityContext": {
|
|
||||||
"$ref": "v1beta3.SecurityContext",
|
|
||||||
"description": "security options the pod should run with"
|
|
||||||
},
|
|
||||||
"terminationMessagePath": {
|
"terminationMessagePath": {
|
||||||
"type": "string",
|
"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"
|
"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"
|
"description": "version of the schema the object should have"
|
||||||
},
|
},
|
||||||
"gracePeriodSeconds": {
|
"gracePeriodSeconds": {
|
||||||
"type": "integer",
|
"$ref": "int64",
|
||||||
"format": "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"
|
"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": {
|
"kind": {
|
||||||
@ -9893,8 +9888,7 @@
|
|||||||
"description": "restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever"
|
"description": "restart policy for all containers within the pod; one of RestartPolicyAlways, RestartPolicyOnFailure, RestartPolicyNever"
|
||||||
},
|
},
|
||||||
"terminationGracePeriodSeconds": {
|
"terminationGracePeriodSeconds": {
|
||||||
"type": "integer",
|
"$ref": "int64",
|
||||||
"format": "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"
|
"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": {
|
"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": {
|
"v1beta3.Secret": {
|
||||||
"id": "v1beta3.Secret",
|
"id": "v1beta3.Secret",
|
||||||
"properties": {
|
"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": {
|
"v1beta3.Service": {
|
||||||
"id": "v1beta3.Service",
|
"id": "v1beta3.Service",
|
||||||
"properties": {
|
"properties": {
|
||||||
@ -10529,15 +10480,15 @@
|
|||||||
"id": "v1beta3.Volume",
|
"id": "v1beta3.Volume",
|
||||||
"required": [
|
"required": [
|
||||||
"name",
|
"name",
|
||||||
"gcePersistentDisk",
|
|
||||||
"awsElasticBlockStore",
|
"awsElasticBlockStore",
|
||||||
"gitRepo",
|
"gitRepo",
|
||||||
"nfs",
|
|
||||||
"iscsi",
|
|
||||||
"hostPath",
|
|
||||||
"secret",
|
"secret",
|
||||||
|
"nfs",
|
||||||
"glusterfs",
|
"glusterfs",
|
||||||
"emptyDir"
|
"hostPath",
|
||||||
|
"emptyDir",
|
||||||
|
"gcePersistentDisk",
|
||||||
|
"iscsi"
|
||||||
],
|
],
|
||||||
"properties": {
|
"properties": {
|
||||||
"awsElasticBlockStore": {
|
"awsElasticBlockStore": {
|
||||||
|
@ -72,4 +72,4 @@ DNS_DOMAIN="kubernetes.local"
|
|||||||
DNS_REPLICAS=1
|
DNS_REPLICAS=1
|
||||||
|
|
||||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
# 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}"
|
ENABLE_CLUSTER_MONITORING="${KUBE_ENABLE_CLUSTER_MONITORING:-true}"
|
||||||
|
|
||||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
# 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
|
DNS_REPLICAS=1
|
||||||
|
|
||||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
# 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_DOMAIN="kubernetes.local"
|
||||||
DNS_REPLICAS=1
|
DNS_REPLICAS=1
|
||||||
|
|
||||||
ADMISSION_CONTROL=NamespaceAutoProvision,LimitRanger,SecurityContextDeny,ResourceQuota
|
ADMISSION_CONTROL=NamespaceAutoProvision,LimitRanger,ResourceQuota
|
||||||
|
@ -50,7 +50,7 @@ MASTER_USER=vagrant
|
|||||||
MASTER_PASSWD=vagrant
|
MASTER_PASSWD=vagrant
|
||||||
|
|
||||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
# 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.
|
# Optional: Install node monitoring.
|
||||||
ENABLE_NODE_MONITORING=true
|
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/exists"
|
||||||
_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/namespace/lifecycle"
|
_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/namespace/lifecycle"
|
||||||
_ "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/admission/resourcequota"
|
_ "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
|
kube::etcd::start
|
||||||
|
|
||||||
# Admission Controllers to invoke prior to persisting objects in cluster
|
# 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
|
APISERVER_LOG=/tmp/kube-apiserver.log
|
||||||
sudo -E "${GO_OUT}/kube-apiserver" \
|
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()
|
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) {
|
func(e *api.Event, c fuzz.Continue) {
|
||||||
c.FuzzNoCustom(e) // fuzz self without calling this function again
|
c.FuzzNoCustom(e) // fuzz self without calling this function again
|
||||||
// Fix event count to 1, otherwise, if a v1beta1 or v1beta2 event has a count set arbitrarily, it's count is ignored
|
// 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"`
|
Lifecycle *Lifecycle `json:"lifecycle,omitempty"`
|
||||||
// Required.
|
// Required.
|
||||||
TerminationMessagePath string `json:"terminationMessagePath,omitempty"`
|
TerminationMessagePath string `json:"terminationMessagePath,omitempty"`
|
||||||
|
// Optional: Default to false.
|
||||||
|
Privileged bool `json:"privileged,omitempty"`
|
||||||
// Required: Policy for pulling images for this container
|
// Required: Policy for pulling images for this container
|
||||||
ImagePullPolicy PullPolicy `json:"imagePullPolicy"`
|
ImagePullPolicy PullPolicy `json:"imagePullPolicy"`
|
||||||
// Optional: SecurityContext defines the security options the pod should be run with
|
// Optional: Capabilities for container.
|
||||||
SecurityContext *SecurityContext `json:"securityContext,omitempty" description:"security options the pod should run with"`
|
Capabilities Capabilities `json:"capabilities,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler defines a specific action that should be taken
|
// Handler defines a specific action that should be taken
|
||||||
@ -1874,37 +1876,3 @@ type ComponentStatusList struct {
|
|||||||
|
|
||||||
Items []ComponentStatus `json:"items"`
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
|
|
||||||
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
||||||
@ -238,22 +237,9 @@ func init() {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
out.TerminationMessagePath = in.TerminationMessagePath
|
out.TerminationMessagePath = in.TerminationMessagePath
|
||||||
|
out.Privileged = in.Privileged
|
||||||
out.ImagePullPolicy = newer.PullPolicy(in.ImagePullPolicy)
|
out.ImagePullPolicy = newer.PullPolicy(in.ImagePullPolicy)
|
||||||
|
if err := s.Convert(&in.Capabilities, &out.Capabilities, 0); err != nil {
|
||||||
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 {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -311,19 +297,11 @@ func init() {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
out.TerminationMessagePath = in.TerminationMessagePath
|
out.TerminationMessagePath = in.TerminationMessagePath
|
||||||
|
out.Privileged = in.Privileged
|
||||||
out.ImagePullPolicy = PullPolicy(in.ImagePullPolicy)
|
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
|
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
|
return nil
|
||||||
},
|
},
|
||||||
func(in *ContainerPort, out *newer.ContainerPort, s conversion.Scope) error {
|
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)
|
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 (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
@ -68,7 +66,6 @@ func init() {
|
|||||||
if obj.TerminationMessagePath == "" {
|
if obj.TerminationMessagePath == "" {
|
||||||
obj.TerminationMessagePath = TerminationMessagePathDefault
|
obj.TerminationMessagePath = TerminationMessagePathDefault
|
||||||
}
|
}
|
||||||
defaultSecurityContext(obj)
|
|
||||||
},
|
},
|
||||||
func(obj *ServiceSpec) {
|
func(obj *ServiceSpec) {
|
||||||
if obj.SessionAffinity == "" {
|
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)
|
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"`
|
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
|
// 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"`
|
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.
|
// 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"`
|
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
|
// 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"`
|
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.
|
// Optional: Capabilities for container.
|
||||||
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated; deprecated; See SecurityContext"`
|
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated"`
|
||||||
// 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"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler defines a specific action that should be taken
|
// 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"`
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
@ -580,20 +579,15 @@ func init() {
|
|||||||
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
||||||
return err
|
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 {
|
if err := s.Convert(&in.ImagePullPolicy, &out.ImagePullPolicy, 0); err != nil {
|
||||||
return err
|
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
|
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
|
return nil
|
||||||
},
|
},
|
||||||
// Internal API does not support CPU to be specified via an explicit field.
|
// 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 {
|
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
||||||
return err
|
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 {
|
if err := s.Convert(&in.ImagePullPolicy, &out.ImagePullPolicy, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if in.SecurityContext != nil {
|
if err := s.Convert(&in.Capabilities, &out.Capabilities, 0); err != 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 {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -749,63 +749,3 @@ func TestSecretVolumeSourceConversion(t *testing.T) {
|
|||||||
t.Errorf("Expected %v; got %v", given, got2)
|
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 == "" {
|
if obj.TerminationMessagePath == "" {
|
||||||
obj.TerminationMessagePath = TerminationMessagePathDefault
|
obj.TerminationMessagePath = TerminationMessagePathDefault
|
||||||
}
|
}
|
||||||
defaultSecurityContext(obj)
|
|
||||||
},
|
},
|
||||||
func(obj *RestartPolicy) {
|
func(obj *RestartPolicy) {
|
||||||
if util.AllPtrFieldsNil(obj) {
|
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)
|
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"`
|
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
|
// 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"`
|
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.
|
// 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"`
|
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
|
// 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"`
|
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.
|
// Optional: Capabilities for container.
|
||||||
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated; deprecated; See SecurityContext"`
|
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated"`
|
||||||
// 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"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler defines a specific action that should be taken
|
// 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"`
|
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 (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
@ -358,20 +357,15 @@ func init() {
|
|||||||
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
||||||
return err
|
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 {
|
if err := s.Convert(&in.ImagePullPolicy, &out.ImagePullPolicy, 0); err != nil {
|
||||||
return err
|
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
|
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
|
return nil
|
||||||
},
|
},
|
||||||
// Internal API does not support CPU to be specified via an explicit field.
|
// 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 {
|
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
||||||
return err
|
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 {
|
if err := s.Convert(&in.ImagePullPolicy, &out.ImagePullPolicy, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if in.SecurityContext != nil {
|
if err := s.Convert(&in.Capabilities, &out.Capabilities, 0); err != 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 {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -564,63 +564,3 @@ func TestSecretVolumeSourceConversion(t *testing.T) {
|
|||||||
t.Errorf("Expected %v; got %v", given, got2)
|
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 == "" {
|
if obj.TerminationMessagePath == "" {
|
||||||
obj.TerminationMessagePath = TerminationMessagePathDefault
|
obj.TerminationMessagePath = TerminationMessagePathDefault
|
||||||
}
|
}
|
||||||
defaultSecurityContext(obj)
|
|
||||||
},
|
},
|
||||||
func(obj *RestartPolicy) {
|
func(obj *RestartPolicy) {
|
||||||
if util.AllPtrFieldsNil(obj) {
|
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)
|
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"`
|
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
|
// 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"`
|
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.
|
// 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"`
|
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
|
// 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"`
|
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.
|
// Optional: Capabilities for container.
|
||||||
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated; deprecated; See SecurityContext"`
|
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated"`
|
||||||
// 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"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -1719,39 +1717,3 @@ type ComponentStatusList struct {
|
|||||||
|
|
||||||
Items []ComponentStatus `json:"items" description:"list of component status objects"`
|
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)
|
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/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
"github.com/golang/glog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -67,7 +66,6 @@ func init() {
|
|||||||
if obj.TerminationMessagePath == "" {
|
if obj.TerminationMessagePath == "" {
|
||||||
obj.TerminationMessagePath = TerminationMessagePathDefault
|
obj.TerminationMessagePath = TerminationMessagePathDefault
|
||||||
}
|
}
|
||||||
defaultSecurityContext(obj)
|
|
||||||
},
|
},
|
||||||
func(obj *ServiceSpec) {
|
func(obj *ServiceSpec) {
|
||||||
if obj.SessionAffinity == "" {
|
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)
|
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"`
|
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
|
// 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"`
|
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.
|
// 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."`
|
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
|
// 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"`
|
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.
|
// Optional: Capabilities for container.
|
||||||
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated; deprecated; See SecurityContext."`
|
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container; cannot be updated"`
|
||||||
// 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"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handler defines a specific action that should be taken
|
// 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"`
|
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{}
|
allNames := util.StringSet{}
|
||||||
for i, ctr := range containers {
|
for i, ctr := range containers {
|
||||||
cErrs := errs.ValidationErrorList{}
|
cErrs := errs.ValidationErrorList{}
|
||||||
|
capabilities := capabilities.Get()
|
||||||
if len(ctr.Name) == 0 {
|
if len(ctr.Name) == 0 {
|
||||||
cErrs = append(cErrs, errs.NewFieldRequired("name"))
|
cErrs = append(cErrs, errs.NewFieldRequired("name"))
|
||||||
} else if !util.IsDNS1123Label(ctr.Name) {
|
} else if !util.IsDNS1123Label(ctr.Name) {
|
||||||
cErrs = append(cErrs, errs.NewFieldInvalid("name", ctr.Name, dns1123LabelErrorMsg))
|
cErrs = append(cErrs, errs.NewFieldInvalid("name", ctr.Name, dns1123LabelErrorMsg))
|
||||||
} else if allNames.Has(ctr.Name) {
|
} else if allNames.Has(ctr.Name) {
|
||||||
cErrs = append(cErrs, errs.NewFieldDuplicate("name", 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 {
|
} else {
|
||||||
allNames.Insert(ctr.Name)
|
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, validateVolumeMounts(ctr.VolumeMounts, volumes).Prefix("volumeMounts")...)
|
||||||
cErrs = append(cErrs, validatePullPolicy(&ctr).Prefix("pullPolicy")...)
|
cErrs = append(cErrs, validatePullPolicy(&ctr).Prefix("pullPolicy")...)
|
||||||
cErrs = append(cErrs, ValidateResourceRequirements(&ctr.Resources).Prefix("resources")...)
|
cErrs = append(cErrs, ValidateResourceRequirements(&ctr.Resources).Prefix("resources")...)
|
||||||
cErrs = append(cErrs, ValidateSecurityContext(ctr.SecurityContext).Prefix("securityContext")...)
|
|
||||||
allErrs = append(allErrs, cErrs.PrefixIndex(i)...)
|
allErrs = append(allErrs, cErrs.PrefixIndex(i)...)
|
||||||
}
|
}
|
||||||
// Check for colliding ports across all containers.
|
// 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")...)
|
allErrs = append(allErrs, validateEndpointSubsets(newEndpoints.Subsets).Prefix("subsets")...)
|
||||||
return allErrs
|
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",
|
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 {
|
if errs := validateContainers(successCase, volumes); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
@ -1015,7 +1015,7 @@ func TestValidateContainers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
"privilege disabled": {
|
"privilege disabled": {
|
||||||
{Name: "abc", Image: "image", SecurityContext: fakeValidSecurityContext(true)},
|
{Name: "abc", Image: "image", Privileged: true},
|
||||||
},
|
},
|
||||||
"invalid compute resource": {
|
"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/client/testclient"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
||||||
@ -113,7 +112,6 @@ func newReplicationController(replicas int) *api.ReplicationController {
|
|||||||
Image: "foo/bar",
|
Image: "foo/bar",
|
||||||
TerminationMessagePath: api.TerminationMessagePathDefault,
|
TerminationMessagePath: api.TerminationMessagePathDefault,
|
||||||
ImagePullPolicy: api.PullIfNotPresent,
|
ImagePullPolicy: api.PullIfNotPresent,
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RestartPolicy: api.RestartPolicyAlways,
|
RestartPolicy: api.RestartPolicyAlways,
|
||||||
|
@ -22,7 +22,6 @@ import (
|
|||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
|
||||||
|
|
||||||
"github.com/ghodss/yaml"
|
"github.com/ghodss/yaml"
|
||||||
)
|
)
|
||||||
@ -47,7 +46,6 @@ func TestDecodeSinglePod(t *testing.T) {
|
|||||||
Image: "test/image",
|
Image: "test/image",
|
||||||
ImagePullPolicy: "IfNotPresent",
|
ImagePullPolicy: "IfNotPresent",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -110,7 +108,6 @@ func TestDecodePodList(t *testing.T) {
|
|||||||
Image: "test/image",
|
Image: "test/image",
|
||||||
ImagePullPolicy: "IfNotPresent",
|
ImagePullPolicy: "IfNotPresent",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,6 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/record"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -63,14 +62,7 @@ func CreateValidPod(name, namespace, source string) *api.Pod {
|
|||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
RestartPolicy: api.RestartPolicyAlways,
|
RestartPolicy: api.RestartPolicyAlways,
|
||||||
DNSPolicy: api.DNSClusterFirst,
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||||
{
|
|
||||||
Name: "ctr",
|
|
||||||
Image: "image",
|
|
||||||
ImagePullPolicy: "IfNotPresent",
|
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,6 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -106,8 +105,7 @@ func TestReadContainerManifestFromFile(t *testing.T) {
|
|||||||
Name: "image",
|
Name: "image",
|
||||||
Image: "test/image",
|
Image: "test/image",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
ImagePullPolicy: "Always",
|
ImagePullPolicy: "Always"}},
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -133,8 +131,7 @@ func TestReadContainerManifestFromFile(t *testing.T) {
|
|||||||
Name: "image",
|
Name: "image",
|
||||||
Image: "test/image",
|
Image: "test/image",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
ImagePullPolicy: "Always",
|
ImagePullPolicy: "Always"}},
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -185,7 +182,7 @@ func TestReadPodsFromFile(t *testing.T) {
|
|||||||
Namespace: "mynamespace",
|
Namespace: "mynamespace",
|
||||||
},
|
},
|
||||||
Spec: api.PodSpec{
|
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{
|
expected: CreatePodUpdate(kubelet.SET, kubelet.FileSource, &api.Pod{
|
||||||
@ -203,8 +200,7 @@ func TestReadPodsFromFile(t *testing.T) {
|
|||||||
Name: "image",
|
Name: "image",
|
||||||
Image: "test/image",
|
Image: "test/image",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
ImagePullPolicy: "IfNotPresent",
|
ImagePullPolicy: "IfNotPresent"}},
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -220,7 +216,7 @@ func TestReadPodsFromFile(t *testing.T) {
|
|||||||
UID: "12345",
|
UID: "12345",
|
||||||
},
|
},
|
||||||
Spec: api.PodSpec{
|
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{
|
expected: CreatePodUpdate(kubelet.SET, kubelet.FileSource, &api.Pod{
|
||||||
@ -238,8 +234,7 @@ func TestReadPodsFromFile(t *testing.T) {
|
|||||||
Name: "image",
|
Name: "image",
|
||||||
Image: "test/image",
|
Image: "test/image",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
ImagePullPolicy: "IfNotPresent",
|
ImagePullPolicy: "IfNotPresent"}},
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
@ -28,7 +28,6 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
|
||||||
)
|
)
|
||||||
@ -152,8 +151,7 @@ func TestExtractManifestFromHTTP(t *testing.T) {
|
|||||||
Name: "1",
|
Name: "1",
|
||||||
Image: "foo",
|
Image: "foo",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
ImagePullPolicy: "Always",
|
ImagePullPolicy: "Always"}},
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -179,8 +177,7 @@ func TestExtractManifestFromHTTP(t *testing.T) {
|
|||||||
Name: "ctr",
|
Name: "ctr",
|
||||||
Image: "image",
|
Image: "image",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
ImagePullPolicy: "IfNotPresent",
|
ImagePullPolicy: "IfNotPresent"}},
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -206,8 +203,7 @@ func TestExtractManifestFromHTTP(t *testing.T) {
|
|||||||
Name: "1",
|
Name: "1",
|
||||||
Image: "foo",
|
Image: "foo",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
ImagePullPolicy: "Always",
|
ImagePullPolicy: "Always"}},
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -237,8 +233,7 @@ func TestExtractManifestFromHTTP(t *testing.T) {
|
|||||||
Name: "1",
|
Name: "1",
|
||||||
Image: "foo",
|
Image: "foo",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
ImagePullPolicy: "Always",
|
ImagePullPolicy: "Always"}},
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&api.Pod{
|
&api.Pod{
|
||||||
@ -257,8 +252,7 @@ func TestExtractManifestFromHTTP(t *testing.T) {
|
|||||||
Name: "1",
|
Name: "1",
|
||||||
Image: "foo",
|
Image: "foo",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
ImagePullPolicy: "IfNotPresent",
|
ImagePullPolicy: "IfNotPresent"}},
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -350,8 +344,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
|
|||||||
Name: "1",
|
Name: "1",
|
||||||
Image: "foo",
|
Image: "foo",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
ImagePullPolicy: "Always",
|
ImagePullPolicy: "Always"}},
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
@ -403,8 +396,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
|
|||||||
Name: "1",
|
Name: "1",
|
||||||
Image: "foo",
|
Image: "foo",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
ImagePullPolicy: "Always",
|
ImagePullPolicy: "Always"}},
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
&api.Pod{
|
&api.Pod{
|
||||||
@ -423,8 +415,7 @@ func TestExtractPodsFromHTTP(t *testing.T) {
|
|||||||
Name: "2",
|
Name: "2",
|
||||||
Image: "bar",
|
Image: "bar",
|
||||||
TerminationMessagePath: "/dev/termination-log",
|
TerminationMessagePath: "/dev/termination-log",
|
||||||
ImagePullPolicy: "IfNotPresent",
|
ImagePullPolicy: "IfNotPresent"}},
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults()}},
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
@ -38,7 +38,6 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/prober"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/prober"
|
||||||
kubeletTypes "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/types"
|
kubeletTypes "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/types"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
docker "github.com/fsouza/go-dockerclient"
|
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)
|
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)
|
dockerContainer, err := dm.client.CreateContainer(dockerOpts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ref != 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.")
|
return "", fmt.Errorf("container requested privileged mode, but it is disallowed globally.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
capAdd, capDrop := makeCapabilites(container.Capabilities.Add, container.Capabilities.Drop)
|
||||||
hc := &docker.HostConfig{
|
hc := &docker.HostConfig{
|
||||||
PortBindings: portBindings,
|
PortBindings: portBindings,
|
||||||
Binds: opts.Binds,
|
Binds: opts.Binds,
|
||||||
NetworkMode: opts.NetMode,
|
NetworkMode: opts.NetMode,
|
||||||
IpcMode: opts.IpcMode,
|
IpcMode: opts.IpcMode,
|
||||||
|
Privileged: privileged,
|
||||||
|
CapAdd: capAdd,
|
||||||
|
CapDrop: capDrop,
|
||||||
}
|
}
|
||||||
if len(opts.DNS) > 0 {
|
if len(opts.DNS) > 0 {
|
||||||
hc.DNS = opts.DNS
|
hc.DNS = opts.DNS
|
||||||
@ -576,7 +580,6 @@ func (dm *DockerManager) runContainer(pod *api.Pod, container *api.Container, op
|
|||||||
if len(opts.CgroupParent) > 0 {
|
if len(opts.CgroupParent) > 0 {
|
||||||
hc.CgroupParent = opts.CgroupParent
|
hc.CgroupParent = opts.CgroupParent
|
||||||
}
|
}
|
||||||
securityContextProvider.ModifyHostConfig(pod, container, hc)
|
|
||||||
|
|
||||||
if err = dm.client.StartContainer(dockerContainer.ID, hc); err != nil {
|
if err = dm.client.StartContainer(dockerContainer.ID, hc); err != nil {
|
||||||
if ref != nil {
|
if ref != nil {
|
||||||
@ -634,6 +637,20 @@ func makePortsAndBindings(container *api.Container) (map[docker.Port]struct{}, m
|
|||||||
return exposedPorts, portBindings
|
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
|
// A helper function to get the KubeletContainerName and hash from a docker
|
||||||
// container.
|
// container.
|
||||||
func getDockerContainerNameInfo(c *docker.APIContainers) (*KubeletContainerName, uint64, error) {
|
func getDockerContainerNameInfo(c *docker.APIContainers) (*KubeletContainerName, uint64, error) {
|
||||||
|
@ -38,7 +38,6 @@ import (
|
|||||||
kubecontainer "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/container"
|
kubecontainer "github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/container"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/prober"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/kubelet/prober"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/probe"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/volume"
|
"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.
|
// 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 {
|
func setIsolators(app *appctypes.App, c *api.Container) error {
|
||||||
hasCapRequests := securitycontext.HasCapabilitiesRequest(c)
|
if len(c.Capabilities.Add) > 0 || len(c.Capabilities.Drop) > 0 || len(c.Resources.Limits) > 0 || len(c.Resources.Requests) > 0 {
|
||||||
if hasCapRequests || len(c.Resources.Limits) > 0 || len(c.Resources.Requests) > 0 {
|
|
||||||
app.Isolators = []appctypes.Isolator{}
|
app.Isolators = []appctypes.Isolator{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Retained capabilities/privileged.
|
// Retained capabilities/privileged.
|
||||||
privileged := false
|
privileged := false
|
||||||
if !capabilities.Get().AllowPrivileged && securitycontext.HasPrivilegedRequest(c) {
|
if capabilities.Get().AllowPrivileged {
|
||||||
return fmt.Errorf("container requested privileged mode, but it is disallowed globally.")
|
privileged = c.Privileged
|
||||||
} else {
|
} else if c.Privileged {
|
||||||
if c.SecurityContext != nil && c.SecurityContext.Privileged != nil {
|
return fmt.Errorf("privileged is disallowed globally")
|
||||||
privileged = *c.SecurityContext.Privileged
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var addCaps string
|
var addCaps string
|
||||||
if privileged {
|
if privileged {
|
||||||
addCaps = getAllCapabilities()
|
addCaps = getAllCapabilities()
|
||||||
} else {
|
} else {
|
||||||
if hasCapRequests {
|
addCaps = getCapabilities(c.Capabilities.Add)
|
||||||
addCaps = getCapabilities(c.SecurityContext.Capabilities.Add)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if len(addCaps) > 0 {
|
if len(addCaps) > 0 {
|
||||||
// TODO(yifan): Replace with constructor, see:
|
// TODO(yifan): Replace with constructor, see:
|
||||||
@ -223,10 +216,7 @@ func setIsolators(app *appctypes.App, c *api.Container) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Removed capabilities.
|
// Removed capabilities.
|
||||||
var dropCaps string
|
dropCaps := getCapabilities(c.Capabilities.Drop)
|
||||||
if hasCapRequests {
|
|
||||||
dropCaps = getCapabilities(c.SecurityContext.Capabilities.Drop)
|
|
||||||
}
|
|
||||||
if len(dropCaps) > 0 {
|
if len(dropCaps) > 0 {
|
||||||
// TODO(yifan): Replace with constructor, see:
|
// TODO(yifan): Replace with constructor, see:
|
||||||
// https://github.com/appc/spec/issues/268
|
// https://github.com/appc/spec/issues/268
|
||||||
|
@ -32,7 +32,6 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/pod"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/pod"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/securitycontext"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools/etcdtest"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/tools/etcdtest"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
@ -69,7 +68,6 @@ func validNewPod() *api.Pod {
|
|||||||
ImagePullPolicy: api.PullAlways,
|
ImagePullPolicy: api.PullAlways,
|
||||||
|
|
||||||
TerminationMessagePath: api.TerminationMessagePathDefault,
|
TerminationMessagePath: api.TerminationMessagePathDefault,
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1110,9 +1108,8 @@ func TestEtcdUpdateScheduled(t *testing.T) {
|
|||||||
Host: "machine",
|
Host: "machine",
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Name: "foobar",
|
Name: "foobar",
|
||||||
Image: "foo:v1",
|
Image: "foo:v1",
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1134,7 +1131,6 @@ func TestEtcdUpdateScheduled(t *testing.T) {
|
|||||||
Image: "foo:v2",
|
Image: "foo:v2",
|
||||||
ImagePullPolicy: api.PullIfNotPresent,
|
ImagePullPolicy: api.PullIfNotPresent,
|
||||||
TerminationMessagePath: api.TerminationMessagePathDefault,
|
TerminationMessagePath: api.TerminationMessagePathDefault,
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RestartPolicy: api.RestartPolicyAlways,
|
RestartPolicy: api.RestartPolicyAlways,
|
||||||
@ -1173,8 +1169,7 @@ func TestEtcdUpdateStatus(t *testing.T) {
|
|||||||
Host: "machine",
|
Host: "machine",
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Image: "foo:v1",
|
Image: "foo:v1",
|
||||||
SecurityContext: securitycontext.ValidSecurityContextWithContainerDefaults(),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -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
Block a user