mirror of https://github.com/k3s-io/k3s
commit
445fde3dc5
|
@ -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",
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}\""
|
||||
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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%%
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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",
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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{},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue