Merge pull request #13447 from pweil-/pid-mode

Auto commit by PR queue bot
pull/6/head
k8s-merge-robot 2015-09-16 23:34:35 -07:00
commit 445fde3dc5
37 changed files with 366 additions and 145 deletions

View File

@ -12518,7 +12518,11 @@
},
"hostNetwork": {
"type": "boolean",
"description": "Host networking requested for this pod. Uses the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false."
"description": "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false."
},
"hostPID": {
"type": "boolean",
"description": "Use the host's pid namespace. Optional: Default to false."
},
"imagePullSecrets": {
"type": "array",

View File

@ -87,7 +87,7 @@ DNS_REPLICAS=1
ENABLE_CLUSTER_UI="${KUBE_ENABLE_CLUSTER_UI:-true}"
# Admission Controllers to invoke prior to persisting objects in cluster
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,DenyEscalatingExec,ResourceQuota
# Optional: Enable/disable public IP assignment for minions.
# Important Note: disable only if you have setup a NAT instance for internet access and configured appropriate routes!

View File

@ -83,7 +83,7 @@ DNS_REPLICAS=1
ENABLE_CLUSTER_UI="${KUBE_ENABLE_CLUSTER_UI:-true}"
# Admission Controllers to invoke prior to persisting objects in cluster
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,DenyEscalatingExec,ResourceQuota
# Optional: Enable/disable public IP assignment for minions.
# Important Note: disable only if you have setup a NAT instance for internet access and configured appropriate routes!

View File

@ -55,4 +55,4 @@ ENABLE_CLUSTER_MONITORING="${KUBE_ENABLE_CLUSTER_MONITORING:-influxdb}"
ENABLE_CLUSTER_UI="${KUBE_ENABLE_CLUSTER_UI:-true}"
# Admission Controllers to invoke prior to persisting objects in cluster
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,DenyEscalatingExec,ResourceQuota

View File

@ -51,7 +51,7 @@ KUBE_SERVICE_ADDRESSES="--service-cluster-ip-range=${SERVICE_CLUSTER_IP_RANGE}"
# to do admission control of resources into cluster.
# Comma-delimited list of:
# LimitRanger, AlwaysDeny, SecurityContextDeny, NamespaceExists,
# NamespaceLifecycle, NamespaceAutoProvision, DenyExecOnPrivileged,
# NamespaceLifecycle, NamespaceAutoProvision, DenyEscalatingExec,
# AlwaysAdmit, ServiceAccount, ResourceQuota
#KUBE_ADMISSION_CONTROL="--admission-control=\"${ADMISSION_CONTROL}\""

View File

@ -104,7 +104,7 @@ if [[ "${ENABLE_HORIZONTAL_POD_AUTOSCALER}" == "true" ]]; then
fi
# Admission Controllers to invoke prior to persisting objects in cluster
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,DenyEscalatingExec,ResourceQuota
# Optional: if set to true kube-up will automatically check for existing resources and clean them up.
KUBE_UP_AUTOMATIC_CLEANUP=${KUBE_UP_AUTOMATIC_CLEANUP:-false}

View File

@ -109,7 +109,7 @@ if [[ "${ENABLE_HORIZONTAL_POD_AUTOSCALER}" == "true" ]]; then
ENABLE_EXPERIMENTAL_API=true
fi
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,DenyEscalatingExec,ResourceQuota
# Optional: if set to true kube-up will automatically check for existing resources and clean them up.
KUBE_UP_AUTOMATIC_CLEANUP=${KUBE_UP_AUTOMATIC_CLEANUP:-false}

View File

@ -89,7 +89,7 @@ apiserver:
--external-hostname=apiserver
--etcd-servers=http://etcd:4001
--port=8888
--admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
--admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,DenyEscalatingExec,ResourceQuota
--authorization-mode=AlwaysAllow
--token-auth-file=/var/run/kubernetes/auth/token-users
--basic-auth-file=/var/run/kubernetes/auth/basic-users

View File

@ -35,7 +35,7 @@ export SERVICE_CLUSTER_IP_RANGE=${SERVICE_CLUSTER_IP_RANGE:-192.168.3.0/24} # f
export FLANNEL_NET=${FLANNEL_NET:-172.16.0.0/16}
# Admission Controllers to invoke prior to persisting objects in cluster
export ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,SecurityContextDeny
export ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,ServiceAccount,ResourceQuota,DenyEscalatingExec,SecurityContextDeny
SERVICE_NODE_PORT_RANGE=${SERVICE_NODE_PORT_RANGE:-"30000-32767"}

View File

@ -53,7 +53,7 @@ MASTER_USER=vagrant
MASTER_PASSWD=vagrant
# Admission Controllers to invoke prior to persisting objects in cluster
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
ADMISSION_CONTROL=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,DenyEscalatingExec,ResourceQuota
# Optional: Enable node logging.
ENABLE_NODE_LOGGING=false

View File

@ -26,7 +26,7 @@ import (
// Admission policies
_ "k8s.io/kubernetes/plugin/pkg/admission/admit"
_ "k8s.io/kubernetes/plugin/pkg/admission/deny"
_ "k8s.io/kubernetes/plugin/pkg/admission/exec/denyprivileged"
_ "k8s.io/kubernetes/plugin/pkg/admission/exec"
_ "k8s.io/kubernetes/plugin/pkg/admission/initialresources"
_ "k8s.io/kubernetes/plugin/pkg/admission/limitranger"
_ "k8s.io/kubernetes/plugin/pkg/admission/namespace/autoprovision"

View File

@ -292,6 +292,7 @@ func (s *APIServer) Run(_ []string) error {
// TODO(vmarmol): Implement support for HostNetworkSources.
PrivilegedSources: capabilities.PrivilegedSources{
HostNetworkSources: []string{},
HostPIDSources: []string{},
},
PerConnectionBandwidthLimitBytesPerSec: s.MaxConnectionBytesPerSec,
})

View File

@ -93,6 +93,7 @@ type KubeletServer struct {
HealthzPort int
HostnameOverride string
HostNetworkSources string
HostPIDSources string
HTTPCheckFrequency time.Duration
ImageGCHighThresholdPercent int
ImageGCLowThresholdPercent int
@ -170,6 +171,7 @@ func NewKubeletServer() *KubeletServer {
HealthzBindAddress: net.ParseIP("127.0.0.1"),
HealthzPort: 10248,
HostNetworkSources: kubelet.FileSource,
HostPIDSources: kubelet.FileSource,
HTTPCheckFrequency: 20 * time.Second,
ImageGCHighThresholdPercent: 90,
ImageGCLowThresholdPercent: 80,
@ -222,6 +224,7 @@ func (s *KubeletServer) AddFlags(fs *pflag.FlagSet) {
fs.StringVar(&s.RootDirectory, "root-dir", s.RootDirectory, "Directory path for managing kubelet files (volume mounts,etc).")
fs.BoolVar(&s.AllowPrivileged, "allow-privileged", s.AllowPrivileged, "If true, allow containers to request privileged mode. [default=false]")
fs.StringVar(&s.HostNetworkSources, "host-network-sources", s.HostNetworkSources, "Comma-separated list of sources from which the Kubelet allows pods to use of host network. For all sources use \"*\" [default=\"file\"]")
fs.StringVar(&s.HostPIDSources, "host-pid-sources", s.HostPIDSources, "Comma-separated list of sources from which the Kubelet allows pods to use the host pid namespace. For all sources use \"*\" [default=\"file\"]")
fs.Float64Var(&s.RegistryPullQPS, "registry-qps", s.RegistryPullQPS, "If > 0, limit registry pull QPS to this value. If 0, unlimited. [default=0.0]")
fs.IntVar(&s.RegistryBurst, "registry-burst", s.RegistryBurst, "Maximum size of a bursty pulls, temporarily allows pulls to burst to this number, while still not exceeding registry-qps. Only used if --registry-qps > 0")
fs.Float32Var(&s.EventRecordQPS, "event-qps", s.EventRecordQPS, "If > 0, limit event creations per second to this value. If 0, unlimited. [default=0.0]")
@ -278,6 +281,11 @@ func (s *KubeletServer) KubeletConfig() (*KubeletConfig, error) {
return nil, err
}
hostPIDSources, err := kubelet.GetValidatedSources(strings.Split(s.HostPIDSources, ","))
if err != nil {
return nil, err
}
mounter := mount.New()
if s.Containerized {
glog.V(2).Info("Running kubelet in containerized mode (experimental)")
@ -342,6 +350,7 @@ func (s *KubeletServer) KubeletConfig() (*KubeletConfig, error) {
FileCheckFrequency: s.FileCheckFrequency,
HostnameOverride: s.HostnameOverride,
HostNetworkSources: hostNetworkSources,
HostPIDSources: hostPIDSources,
HTTPCheckFrequency: s.HTTPCheckFrequency,
ImageGCPolicy: imageGCPolicy,
KubeClient: nil,
@ -673,6 +682,7 @@ func RunKubelet(kcfg *KubeletConfig, builder KubeletBuilder) error {
privilegedSources := capabilities.PrivilegedSources{
HostNetworkSources: kcfg.HostNetworkSources,
HostPIDSources: kcfg.HostPIDSources,
}
capabilities.Setup(kcfg.AllowPrivileged, privilegedSources, 0)
@ -766,6 +776,7 @@ type KubeletConfig struct {
Hostname string
HostnameOverride string
HostNetworkSources []string
HostPIDSources []string
HTTPCheckFrequency time.Duration
ImageGCPolicy kubelet.ImageGCPolicy
KubeClient *client.Client

View File

@ -43,7 +43,8 @@ Documentation for other releases can be found at
- [What does each plug-in do?](#what-does-each-plug-in-do)
- [AlwaysAdmit](#alwaysadmit)
- [AlwaysDeny](#alwaysdeny)
- [DenyExecOnPrivileged](#denyexeconprivileged)
- [DenyExecOnPrivileged (deprecated)](#denyexeconprivileged-deprecated)
- [DenyEscalatingExec](#denyescalatingexec)
- [ServiceAccount](#serviceaccount)
- [SecurityContextDeny](#securitycontextdeny)
- [ResourceQuota](#resourcequota)
@ -92,13 +93,25 @@ Use this plugin by itself to pass-through all requests.
Rejects all requests. Used for testing.
### DenyExecOnPrivileged
### DenyExecOnPrivileged (deprecated)
This plug-in will intercept all requests to exec a command in a pod if that pod has a privileged container.
If your cluster supports privileged containers, and you want to restrict the ability of end-users to exec
commands in those containers, we strongly encourage enabling this plug-in.
This functionality has been merged into [DenyEscalatingExec](#denyescalatingexec).
### DenyEscalatingExec
This plug-in will deny exec and attach commands to pods that run with escalated privileges that
allow host access. This includes pods that run as privileged, have access to the host IPC namespace, and
have access to the host PID namespace.
If your cluster supports containers that run with escalated privileges, and you want to
restrict the ability of end-users to exec commands in those containers, we strongly encourage
enabling this plug-in.
### ServiceAccount
This plug-in implements automation for [serviceAccounts](../user-guide/service-accounts.md).
@ -159,7 +172,7 @@ Yes.
For Kubernetes 1.0, we strongly recommend running the following set of admission control plug-ins (order matters):
```
--admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
--admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,DenyEscalatingExec,ResourceQuota
```

View File

@ -11,7 +11,7 @@ spec:
- /bin/sh
- -c
- /usr/local/bin/kube-apiserver --address=127.0.0.1 --etcd-servers=http://127.0.0.1:4001
--cloud-provider=gce --admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
--cloud-provider=gce --admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,DenyEscalatingExec,ResourceQuota
--service-cluster-ip-range=10.0.0.0/16 --client-ca-file=/srv/kubernetes/ca.crt
--basic-auth-file=/srv/kubernetes/basic_auth.csv --cluster-name=e2e-test-bburns
--tls-cert-file=/srv/kubernetes/server.cert --tls-private-key-file=/srv/kubernetes/server.key

View File

@ -48,7 +48,7 @@ cluster's shared state through which all other components interact.
```
--address=<nil>: DEPRECATED: see --insecure-bind-address instead
--admission-control="": Ordered list of plug-ins to do admission control of resources into cluster. Comma-delimited list of: AlwaysAdmit, AlwaysDeny, DenyExecOnPrivileged, LimitRanger, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, ResourceQuota, SecurityContextDeny, ServiceAccount
--admission-control="": Ordered list of plug-ins to do admission control of resources into cluster. Comma-delimited list of: AlwaysAdmit, AlwaysDeny, DenyExecOnPrivileged, DenyEscalatingExec, LimitRanger, NamespaceAutoProvision, NamespaceExists, NamespaceLifecycle, ResourceQuota, SecurityContextDeny, ServiceAccount
--admission-control-config-file="": File with admission control configuration.
--advertise-address=<nil>: The IP address on which to advertise the apiserver to members of the cluster. This address must be reachable by the rest of the cluster. If blank, the --bind-address will be used. If --bind-address is unspecified, the host's default interface will be used.
--allow-privileged=false: If true, allow privileged containers.

View File

@ -84,6 +84,7 @@ HTTP server: The kubelet can also listen for HTTP and respond to a simple API
--healthz-port=0: The port of the localhost healthz endpoint
-h, --help=false: help for kubelet
--host-network-sources="": Comma-separated list of sources from which the Kubelet allows pods to use of host network. For all sources use "*" [default="file"]
--host-pid-sources="": Comma-separated list of sources from which the Kubelet allows pods to use the host pid namespace. For all sources use "*" [default="file"]
--hostname-override="": If non-empty, will use this string as identification instead of the actual hostname.
--http-check-frequency=0: Duration between checking http for new data
--image-gc-high-threshold=0: The percent of disk usage after which image garbage collection is always run. Default: 90%%

View File

@ -89,7 +89,7 @@ coreos:
ExecStart=/opt/bin/kube-apiserver \
--service-account-key-file=/opt/bin/kube-serviceaccount.key \
--service-account-lookup=false \
--admission-control=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota \
--admission-control=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,SecurityContextDeny,ServiceAccount,DenyEscalatingExec,ResourceQuota \
--runtime-config=api/v1 \
--allow-privileged=true \
--insecure-bind-address=0.0.0.0 \

View File

@ -201,7 +201,7 @@ function set_service_accounts {
function start_apiserver {
# Admission Controllers to invoke prior to persisting objects in cluster
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota
ADMISSION_CONTROL=NamespaceLifecycle,NamespaceAutoProvision,LimitRanger,SecurityContextDeny,ServiceAccount,DenyEscalatingExec,ResourceQuota
# This is the default dir and filename where the apiserver will generate a self-signed cert
# which should be able to be used as the CA to verify itself

View File

@ -106,6 +106,7 @@ horizontal-pod-autoscaler-sync-period
deployment-controller-sync-period
hostname-override
host-network-sources
host-pid-sources
http-check-frequency
http-port
ignore-not-found

View File

@ -1446,6 +1446,7 @@ func deepCopy_api_PodSpec(in PodSpec, out *PodSpec, c *conversion.Cloner) error
out.ServiceAccountName = in.ServiceAccountName
out.NodeName = in.NodeName
out.HostNetwork = in.HostNetwork
out.HostPID = in.HostPID
if in.ImagePullSecrets != nil {
out.ImagePullSecrets = make([]LocalObjectReference, len(in.ImagePullSecrets))
for i := range in.ImagePullSecrets {

View File

@ -980,10 +980,13 @@ type PodSpec struct {
// the scheduler simply schedules this pod onto that node, assuming that it fits resource
// requirements.
NodeName string `json:"nodeName,omitempty"`
// Uses the host's network namespace. If this option is set, the ports that will be
// Use the host's network namespace. If this option is set, the ports that will be
// used must be specified.
// Optional: Default to false.
HostNetwork bool `json:"hostNetwork,omitempty"`
// Use the host's pid namespace.
// Optional: Default to false.
HostPID bool `json:"hostPID,omitempty"`
// ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec.
// If specified, these secrets will be passed to individual puller implementations for them to use. For example,
// in the case of docker, only DockerConfig type secrets are honored.

View File

@ -282,6 +282,7 @@ func convert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *PodSpec, s conversi
out.DeprecatedServiceAccount = in.ServiceAccountName
out.NodeName = in.NodeName
out.HostNetwork = in.HostNetwork
out.HostPID = in.HostPID
if in.ImagePullSecrets != nil {
out.ImagePullSecrets = make([]LocalObjectReference, len(in.ImagePullSecrets))
for i := range in.ImagePullSecrets {
@ -349,6 +350,7 @@ func convert_v1_PodSpec_To_api_PodSpec(in *PodSpec, out *api.PodSpec, s conversi
}
out.NodeName = in.NodeName
out.HostNetwork = in.HostNetwork
out.HostPID = in.HostPID
if in.ImagePullSecrets != nil {
out.ImagePullSecrets = make([]api.LocalObjectReference, len(in.ImagePullSecrets))
for i := range in.ImagePullSecrets {

View File

@ -1446,6 +1446,7 @@ func deepCopy_v1_PodSpec(in PodSpec, out *PodSpec, c *conversion.Cloner) error {
out.DeprecatedServiceAccount = in.DeprecatedServiceAccount
out.NodeName = in.NodeName
out.HostNetwork = in.HostNetwork
out.HostPID = in.HostPID
if in.ImagePullSecrets != nil {
out.ImagePullSecrets = make([]LocalObjectReference, len(in.ImagePullSecrets))
for i := range in.ImagePullSecrets {

View File

@ -1227,10 +1227,13 @@ type PodSpec struct {
// the scheduler simply schedules this pod onto that node, assuming that it fits resource
// requirements.
NodeName string `json:"nodeName,omitempty"`
// Host networking requested for this pod. Uses the host's network namespace.
// Host networking requested for this pod. Use the host's network namespace.
// If this option is set, the ports that will be used must be specified.
// Default to false.
HostNetwork bool `json:"hostNetwork,omitempty"`
// Use the host's pid namespace.
// Optional: Default to false.
HostPID bool `json:"hostPID,omitempty"`
// ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec.
// If specified, these secrets will be passed to individual puller implementations for them to use. For example,
// in the case of docker, only DockerConfig type secrets are honored.

View File

@ -972,7 +972,8 @@ var map_PodSpec = map[string]string{
"serviceAccountName": "ServiceAccountName is the name of the ServiceAccount to use to run this pod. More info: http://releases.k8s.io/HEAD/docs/design/service_accounts.md",
"serviceAccount": "DeprecatedServiceAccount is a depreciated alias for ServiceAccountName. Deprecated: Use serviceAccountName instead.",
"nodeName": "NodeName is a request to schedule this pod onto a specific node. If it is non-empty, the scheduler simply schedules this pod onto that node, assuming that it fits resource requirements.",
"hostNetwork": "Host networking requested for this pod. Uses the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.",
"hostNetwork": "Host networking requested for this pod. Use the host's network namespace. If this option is set, the ports that will be used must be specified. Default to false.",
"hostPID": "Use the host's pid namespace. Optional: Default to false.",
"imagePullSecrets": "ImagePullSecrets is an optional list of references to secrets in the same namespace to use for pulling any of the images used by this PodSpec. If specified, these secrets will be passed to individual puller implementations for them to use. For example, in the case of docker, only DockerConfig type secrets are honored. More info: http://releases.k8s.io/HEAD/docs/user-guide/images.md#specifying-imagepullsecrets-on-a-pod",
}

View File

@ -465,6 +465,7 @@ func deepCopy_api_PodSpec(in api.PodSpec, out *api.PodSpec, c *conversion.Cloner
out.ServiceAccountName = in.ServiceAccountName
out.NodeName = in.NodeName
out.HostNetwork = in.HostNetwork
out.HostPID = in.HostPID
if in.ImagePullSecrets != nil {
out.ImagePullSecrets = make([]api.LocalObjectReference, len(in.ImagePullSecrets))
for i := range in.ImagePullSecrets {

View File

@ -98,6 +98,7 @@ func convert_api_PodSpec_To_v1_PodSpec(in *api.PodSpec, out *v1.PodSpec, s conve
out.DeprecatedServiceAccount = in.ServiceAccountName
out.NodeName = in.NodeName
out.HostNetwork = in.HostNetwork
out.HostPID = in.HostPID
if in.ImagePullSecrets != nil {
out.ImagePullSecrets = make([]v1.LocalObjectReference, len(in.ImagePullSecrets))
for i := range in.ImagePullSecrets {
@ -165,6 +166,7 @@ func convert_v1_PodSpec_To_api_PodSpec(in *v1.PodSpec, out *api.PodSpec, s conve
}
out.NodeName = in.NodeName
out.HostNetwork = in.HostNetwork
out.HostPID = in.HostPID
if in.ImagePullSecrets != nil {
out.ImagePullSecrets = make([]api.LocalObjectReference, len(in.ImagePullSecrets))
for i := range in.ImagePullSecrets {

View File

@ -483,6 +483,7 @@ func deepCopy_v1_PodSpec(in v1.PodSpec, out *v1.PodSpec, c *conversion.Cloner) e
out.DeprecatedServiceAccount = in.DeprecatedServiceAccount
out.NodeName = in.NodeName
out.HostNetwork = in.HostNetwork
out.HostPID = in.HostPID
if in.ImagePullSecrets != nil {
out.ImagePullSecrets = make([]v1.LocalObjectReference, len(in.ImagePullSecrets))
for i := range in.ImagePullSecrets {

View File

@ -38,6 +38,9 @@ type Capabilities struct {
type PrivilegedSources struct {
// List of pod sources for which using host network is allowed.
HostNetworkSources []string
// List of pod sources for which using host pid namespace is allowed.
HostPIDSources []string
}
// TODO: Clean these up into a singleton
@ -79,6 +82,7 @@ func Get() Capabilities {
AllowPrivileged: false,
PrivilegedSources: PrivilegedSources{
HostNetworkSources: []string{},
HostPIDSources: []string{},
},
})
}

View File

@ -622,7 +622,8 @@ func (dm *DockerManager) runContainer(
ref *api.ObjectReference,
netMode string,
ipcMode string,
utsMode string) (string, error) {
utsMode string,
pidMode string) (string, error) {
dockerName := KubeletContainerName{
PodFullName: kubecontainer.GetPodFullName(pod),
@ -739,6 +740,7 @@ func (dm *DockerManager) runContainer(
NetworkMode: netMode,
IpcMode: ipcMode,
UTSMode: utsMode,
PidMode: pidMode,
// Memory and CPU are set here for newer versions of Docker (1.6+).
Memory: memoryLimit,
MemorySwap: -1,
@ -1387,7 +1389,7 @@ func containerAndPodFromLabels(inspect *docker.Container) (pod *api.Pod, contain
}
// Run a single container from a pod. Returns the docker container ID
func (dm *DockerManager) runContainerInPod(pod *api.Pod, container *api.Container, netMode, ipcMode string) (kubeletTypes.DockerID, error) {
func (dm *DockerManager) runContainerInPod(pod *api.Pod, container *api.Container, netMode, ipcMode string, pidMode string) (kubeletTypes.DockerID, error) {
start := time.Now()
defer func() {
metrics.ContainerManagerLatency.WithLabelValues("runContainerInPod").Observe(metrics.SinceInMicroseconds(start))
@ -1407,7 +1409,7 @@ func (dm *DockerManager) runContainerInPod(pod *api.Pod, container *api.Containe
if pod.Spec.HostNetwork {
utsMode = "host"
}
id, err := dm.runContainer(pod, container, opts, ref, netMode, ipcMode, utsMode)
id, err := dm.runContainer(pod, container, opts, ref, netMode, ipcMode, utsMode, pidMode)
if err != nil {
return "", err
}
@ -1539,7 +1541,7 @@ func (dm *DockerManager) createPodInfraContainer(pod *api.Pod) (kubeletTypes.Doc
return "", err
}
id, err := dm.runContainerInPod(pod, container, netNamespace, "")
id, err := dm.runContainerInPod(pod, container, netNamespace, "", getPidMode(pod))
if err != nil {
return "", err
}
@ -1795,7 +1797,7 @@ func (dm *DockerManager) SyncPod(pod *api.Pod, runningPod kubecontainer.Pod, pod
// TODO(dawnchen): Check RestartPolicy.DelaySeconds before restart a container
namespaceMode := fmt.Sprintf("container:%v", podInfraContainerID)
_, err = dm.runContainerInPod(pod, container, namespaceMode, namespaceMode)
_, err = dm.runContainerInPod(pod, container, namespaceMode, namespaceMode, getPidMode(pod))
dm.updateReasonCache(pod, container, err)
if err != nil {
// TODO(bburns) : Perhaps blacklist a container after N failures?
@ -1908,3 +1910,12 @@ func (dm *DockerManager) doBackOff(pod *api.Pod, container *api.Container, podSt
dm.clearReasonCache(pod, container)
return false
}
// getPidMode returns the pid mode to use on the docker container based on pod.Spec.HostPID.
func getPidMode(pod *api.Pod) string {
pidMode := ""
if pod.Spec.HostPID {
pidMode = "host"
}
return pidMode
}

View File

@ -2335,3 +2335,20 @@ func TestGetUidFromUser(t *testing.T) {
}
}
}
func TestGetPidMode(t *testing.T) {
// test false
pod := &api.Pod{}
pidMode := getPidMode(pod)
if pidMode != "" {
t.Errorf("expected empty pid mode for pod but got %v", pidMode)
}
// test true
pod.Spec.HostPID = true
pidMode = getPidMode(pod)
if pidMode != "host" {
t.Errorf("expected host pid mode for pod but got %v", pidMode)
}
}

View File

@ -593,6 +593,8 @@ func (r *runtime) preparePod(pod *api.Pod, pullSecrets []api.Secret) (string, *k
runPrepared = fmt.Sprintf("%s run-prepared --mds-register=false --private-net %s", r.rktBinAbsPath, uuid)
}
// TODO handle pod.Spec.HostPID
units := []*unit.UnitOption{
newUnitOption(unitKubernetesSection, unitRktID, uuid),
newUnitOption(unitKubernetesSection, unitPodName, string(b)),

View File

@ -50,6 +50,16 @@ func canRunPod(pod *api.Pod) error {
}
}
if pod.Spec.HostPID {
allowed, err := allowHostPID(pod)
if err != nil {
return err
}
if !allowed {
return fmt.Errorf("pod with UID %q specified host PID, but is disallowed", pod.UID)
}
}
if !capabilities.Get().AllowPrivileged {
for _, container := range pod.Spec.Containers {
if securitycontext.HasPrivilegedRequest(&container) {
@ -73,3 +83,17 @@ func allowHostNetwork(pod *api.Pod) (bool, error) {
}
return false, nil
}
// Determined whether the specified pod is allowed to use host networking
func allowHostPID(pod *api.Pod) (bool, error) {
podSource, err := getPodSource(pod)
if err != nil {
return false, err
}
for _, source := range capabilities.Get().PrivilegedSources.HostPIDSources {
if source == podSource {
return true, nil
}
}
return false, nil
}

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package denyprivileged
package exec
import (
"fmt"
@ -28,19 +28,55 @@ import (
)
func init() {
admission.RegisterPlugin("DenyEscalatingExec", func(client client.Interface, config io.Reader) (admission.Interface, error) {
return NewDenyEscalatingExec(client), nil
})
// This is for legacy support of the DenyExecOnPrivileged admission controller. Most
// of the time DenyEscalatingExec should be preferred.
admission.RegisterPlugin("DenyExecOnPrivileged", func(client client.Interface, config io.Reader) (admission.Interface, error) {
return NewDenyExecOnPrivileged(client), nil
})
}
// denyExecOnPrivileged is an implementation of admission.Interface which says no to a pod/exec on
// a privileged pod
type denyExecOnPrivileged struct {
// denyExec is an implementation of admission.Interface which says no to a pod/exec on
// a pod using host based configurations.
type denyExec struct {
*admission.Handler
client client.Interface
// these flags control which items will be checked to deny exec/attach
hostIPC bool
hostPID bool
privileged bool
}
func (d *denyExecOnPrivileged) Admit(a admission.Attributes) (err error) {
// NewDenyEscalatingExec creates a new admission controller that denies an exec operation on a pod
// using host based configurations.
func NewDenyEscalatingExec(client client.Interface) admission.Interface {
return &denyExec{
Handler: admission.NewHandler(admission.Connect),
client: client,
hostIPC: true,
hostPID: true,
privileged: true,
}
}
// NewDenyExecOnPrivileged creates a new admission controller that is only checking the privileged
// option. This is for legacy support of the DenyExecOnPrivileged admission controller. Most
// of the time NewDenyEscalatingExec should be preferred.
func NewDenyExecOnPrivileged(client client.Interface) admission.Interface {
return &denyExec{
Handler: admission.NewHandler(admission.Connect),
client: client,
hostIPC: false,
hostPID: false,
privileged: true,
}
}
func (d *denyExec) Admit(a admission.Attributes) (err error) {
connectRequest, ok := a.GetObject().(*rest.ConnectRequest)
if !ok {
return errors.NewBadRequest("a connect request was received, but could not convert the request object.")
@ -53,9 +89,20 @@ func (d *denyExecOnPrivileged) Admit(a admission.Attributes) (err error) {
if err != nil {
return admission.NewForbidden(a, err)
}
if isPrivileged(pod) {
if d.hostPID && pod.Spec.HostPID {
return admission.NewForbidden(a, fmt.Errorf("Cannot exec into or attach to a container using host pid"))
}
//TODO uncomment when this feature lands https://github.com/kubernetes/kubernetes/pull/12470
// if d.hostIPC && pod.Spec.HostIPC {
// return admission.NewForbidden(a, fmt.Errorf("Cannot exec into or attach to a container using host ipc"))
// }
if d.privileged && isPrivileged(pod) {
return admission.NewForbidden(a, fmt.Errorf("Cannot exec into or attach to a privileged container"))
}
return nil
}
@ -71,11 +118,3 @@ func isPrivileged(pod *api.Pod) bool {
}
return false
}
// NewDenyExecOnPrivileged creates a new admission controller that denies an exec operation on a privileged pod
func NewDenyExecOnPrivileged(client client.Interface) admission.Interface {
return &denyExecOnPrivileged{
Handler: admission.NewHandler(admission.Connect),
client: client,
}
}

View File

@ -0,0 +1,183 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package exec
import (
"testing"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/client/unversioned/testclient"
"k8s.io/kubernetes/pkg/runtime"
)
func TestAdmission(t *testing.T) {
privPod := validPod("privileged")
priv := true
privPod.Spec.Containers[0].SecurityContext = &api.SecurityContext{
Privileged: &priv,
}
hostPIDPod := validPod("hostPID")
hostPIDPod.Spec.HostPID = true
// hostIPCPod := validPod("hostIPC")
// hostIPCPod.Spec.HostIPC = true
testCases := map[string]struct {
pod *api.Pod
shouldAccept bool
}{
"priv": {
shouldAccept: false,
pod: privPod,
},
"hostPID": {
shouldAccept: false,
pod: hostPIDPod,
},
// "hostIPC": {
// shouldAccept: false,
// pod: hostIPCPod,
// },
"non privileged": {
shouldAccept: true,
pod: validPod("nonPrivileged"),
},
}
// use the same code as NewDenyEscalatingExec, using the direct object though to allow testAdmission to
// inject the client
handler := &denyExec{
Handler: admission.NewHandler(admission.Connect),
hostIPC: true,
hostPID: true,
privileged: true,
}
for _, tc := range testCases {
testAdmission(t, tc.pod, handler, tc.shouldAccept)
}
// run with a permissive config and all cases should pass
handler.privileged = false
handler.hostPID = false
handler.hostIPC = false
for _, tc := range testCases {
testAdmission(t, tc.pod, handler, true)
}
}
func testAdmission(t *testing.T, pod *api.Pod, handler *denyExec, shouldAccept bool) {
mockClient := &testclient.Fake{}
mockClient.AddReactor("get", "pods", func(action testclient.Action) (bool, runtime.Object, error) {
if action.(testclient.GetAction).GetName() == pod.Name {
return true, pod, nil
}
t.Errorf("Unexpected API call: %#v", action)
return true, nil, nil
})
handler.client = mockClient
// pods/exec
{
req := &rest.ConnectRequest{Name: pod.Name, ResourcePath: "pods/exec"}
err := handler.Admit(admission.NewAttributesRecord(req, "Pod", "test", "name", "pods", "exec", admission.Connect, nil))
if shouldAccept && err != nil {
t.Errorf("Unexpected error returned from admission handler: %v", err)
}
if !shouldAccept && err == nil {
t.Errorf("An error was expected from the admission handler. Received nil")
}
}
// pods/attach
{
req := &rest.ConnectRequest{Name: pod.Name, ResourcePath: "pods/attach"}
err := handler.Admit(admission.NewAttributesRecord(req, "Pod", "test", "name", "pods", "attach", admission.Connect, nil))
if shouldAccept && err != nil {
t.Errorf("Unexpected error returned from admission handler: %v", err)
}
if !shouldAccept && err == nil {
t.Errorf("An error was expected from the admission handler. Received nil")
}
}
}
// Test to ensure legacy admission controller works as expected.
func TestDenyExecOnPrivileged(t *testing.T) {
privPod := validPod("privileged")
priv := true
privPod.Spec.Containers[0].SecurityContext = &api.SecurityContext{
Privileged: &priv,
}
hostPIDPod := validPod("hostPID")
hostPIDPod.Spec.HostPID = true
// hostIPCPod := validPod("hostIPC")
// hostIPCPod.Spec.HostIPC = true
testCases := map[string]struct {
pod *api.Pod
shouldAccept bool
}{
"priv": {
shouldAccept: false,
pod: privPod,
},
"hostPID": {
shouldAccept: true,
pod: hostPIDPod,
},
// "hostIPC": {
// shouldAccept: true,
// pod: hostIPCPod,
// },
"non privileged": {
shouldAccept: true,
pod: validPod("nonPrivileged"),
},
}
// use the same code as NewDenyExecOnPrivileged, using the direct object though to allow testAdmission to
// inject the client
handler := &denyExec{
Handler: admission.NewHandler(admission.Connect),
hostIPC: false,
hostPID: false,
privileged: true,
}
for _, tc := range testCases {
testAdmission(t, tc.pod, handler, tc.shouldAccept)
}
}
func validPod(name string) *api.Pod {
return &api.Pod{
ObjectMeta: api.ObjectMeta{Name: name, Namespace: "test"},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "ctr1", Image: "image"},
{Name: "ctr2", Image: "image2"},
},
},
}
}

View File

@ -1,105 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package denyprivileged
import (
"testing"
"k8s.io/kubernetes/pkg/admission"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/rest"
"k8s.io/kubernetes/pkg/client/unversioned/testclient"
"k8s.io/kubernetes/pkg/runtime"
)
// TestAdmission verifies a namespace is created on create requests for namespace managed resources
func TestAdmissionAccept(t *testing.T) {
testAdmission(t, acceptPod("podname"), true)
}
func TestAdmissionDeny(t *testing.T) {
testAdmission(t, denyPod("podname"), false)
}
func testAdmission(t *testing.T, pod *api.Pod, shouldAccept bool) {
mockClient := &testclient.Fake{}
mockClient.AddReactor("get", "pods", func(action testclient.Action) (bool, runtime.Object, error) {
if action.(testclient.GetAction).GetName() == pod.Name {
return true, pod, nil
}
t.Errorf("Unexpected API call: %#v", action)
return true, nil, nil
})
handler := &denyExecOnPrivileged{
client: mockClient,
}
// pods/exec
{
req := &rest.ConnectRequest{Name: pod.Name, ResourcePath: "pods/exec"}
err := handler.Admit(admission.NewAttributesRecord(req, "Pod", "test", "name", "pods", "exec", admission.Connect, nil))
if shouldAccept && err != nil {
t.Errorf("Unexpected error returned from admission handler: %v", err)
}
if !shouldAccept && err == nil {
t.Errorf("An error was expected from the admission handler. Received nil")
}
}
// pods/attach
{
req := &rest.ConnectRequest{Name: pod.Name, ResourcePath: "pods/attach"}
err := handler.Admit(admission.NewAttributesRecord(req, "Pod", "test", "name", "pods", "attach", admission.Connect, nil))
if shouldAccept && err != nil {
t.Errorf("Unexpected error returned from admission handler: %v", err)
}
if !shouldAccept && err == nil {
t.Errorf("An error was expected from the admission handler. Received nil")
}
}
}
func acceptPod(name string) *api.Pod {
return &api.Pod{
ObjectMeta: api.ObjectMeta{Name: name, Namespace: "test"},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "ctr1", Image: "image"},
{Name: "ctr2", Image: "image2"},
},
},
}
}
func denyPod(name string) *api.Pod {
privileged := true
return &api.Pod{
ObjectMeta: api.ObjectMeta{Name: name, Namespace: "test"},
Spec: api.PodSpec{
Containers: []api.Container{
{Name: "ctr1", Image: "image"},
{
Name: "ctr2",
Image: "image2",
SecurityContext: &api.SecurityContext{
Privileged: &privileged,
},
},
},
},
}
}