mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 15:05:27 +00:00
Merge pull request #13930 from ArtfulCoder/privileged
allow privileged pods
This commit is contained in:
commit
ff1156d4c1
@ -1,2 +1,2 @@
|
||||
# If true, allow privileged containers to be created by API
|
||||
allow_privileged: false
|
||||
allow_privileged: true
|
||||
|
@ -110,7 +110,6 @@ Pod is exposed as a primitive in order to facilitate:
|
||||
|
||||
The current best practice for pets is to create a replication controller with `replicas` equal to `1` and a corresponding service. If you find this cumbersome, please comment on [issue #260](http://issue.k8s.io/260).
|
||||
|
||||
|
||||
## Termination of Pods
|
||||
|
||||
Because pods represent running processes on nodes in the cluster, it is important to allow those processes to gracefully terminate when they are no longer needed (vs being violently killed with a KILL signal and having no chance to clean up). Users should be able to request deletion and know when processes terminate, but also be able to ensure that deletes eventually complete. When a user requests deletion of a pod the system records the intended grace period before the pod is allowed to be forcefully killed, and a TERM signal is sent to the main process in each container. Once the grace period has expired the KILL signal is sent to those processes and the pod is then deleted from the API server. If the Kubelet or the container manager is restarted while waiting for processes to terminate, the termination will be retried with the full grace period.
|
||||
@ -129,6 +128,18 @@ An example flow:
|
||||
|
||||
By default, all deletes are graceful within 30 seconds. The `kubectl delete` command supports the `--grace-period=<seconds>` option which allows a user to override the default and specify their own value. The value `0` indicates that delete should be immediate, and removes the pod in the API immediately so a new pod can be created with the same name. On the node pods that are set to terminate immediately will still be given a small grace period before being force killed.
|
||||
|
||||
## Privileged mode for pod containers
|
||||
|
||||
From kubernetes v1.1, any container in a pod can enable privileged mode, using the `privileged` flag on the `SecurityContext` of the container spec. This is useful for containers that want to use linux capabilities like manipulating the network stack and accessing devices. Processes within the container get almost the same privileges that are available to processes outside a container. With privileged mode, it should be easier to write network and volume plugins as seperate pods that don't need to be compiled into the kubelet.
|
||||
|
||||
If the master is running kubernetes v1.1 or higher, and the nodes are running a version lower than v1.1, then new privileged pods will be accepted by api-server, but will not be launched. They will be pending state.
|
||||
If user calls `kubectl describe pod FooPodName`, user can see the reason why the pod is in pending state. The events table in the describe command output will say:
|
||||
`Error validating pod "FooPodName"."FooPodNamespace" from api, ignoring: spec.containers[0].securityContext.privileged: forbidden '<*>(0xc2089d3248)true'`
|
||||
|
||||
|
||||
If the master is running a version lower than v1.1, then privileged pods cannot be created. If user attempts to create a pod, that has a privileged container, the user will get the following error:
|
||||
`The Pod "FooPodName" is invalid.
|
||||
spec.containers[0].securityContext.privileged: forbidden '<*>(0xc20b222db0)true'`
|
||||
|
||||
## API Object
|
||||
|
||||
|
180
test/e2e/privileged.go
Normal file
180
test/e2e/privileged.go
Normal file
@ -0,0 +1,180 @@
|
||||
/*
|
||||
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 e2e
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/api/latest"
|
||||
"k8s.io/kubernetes/pkg/api/unversioned"
|
||||
client "k8s.io/kubernetes/pkg/client/unversioned"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
const (
|
||||
privilegedPodName = "privileged-pod"
|
||||
privilegedContainerName = "privileged-container"
|
||||
privilegedHttpPort = 8080
|
||||
privilegedUdpPort = 8081
|
||||
notPrivilegedHttpPort = 9090
|
||||
notPrivilegedUdpPort = 9091
|
||||
notPrivilegedContainerName = "not-privileged-container"
|
||||
privilegedContainerImage = "gcr.io/google_containers/netexec:1.1"
|
||||
privilegedCommand = "ip link add dummy1 type dummy"
|
||||
)
|
||||
|
||||
type PrivilegedPodTestConfig struct {
|
||||
privilegedPod *api.Pod
|
||||
f *Framework
|
||||
nodes []string
|
||||
}
|
||||
|
||||
var _ = Describe("PrivilegedPod", func() {
|
||||
f := NewFramework("e2e-privilegedpod")
|
||||
config := &PrivilegedPodTestConfig{
|
||||
f: f,
|
||||
}
|
||||
It("should test privileged pod", func() {
|
||||
|
||||
By("Getting ssh-able hosts")
|
||||
hosts, err := NodeSSHHosts(config.f.Client)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
if len(hosts) == 0 {
|
||||
Failf("No ssh-able nodes")
|
||||
}
|
||||
config.nodes = hosts
|
||||
|
||||
By("Creating a privileged pod")
|
||||
config.createPrivilegedPod()
|
||||
|
||||
By("Executing privileged command on privileged container")
|
||||
config.runPrivilegedCommandOnPrivilegedContainer()
|
||||
|
||||
By("Executing privileged command on non-privileged container")
|
||||
config.runPrivilegedCommandOnNonPrivilegedContainer()
|
||||
})
|
||||
})
|
||||
|
||||
func (config *PrivilegedPodTestConfig) runPrivilegedCommandOnPrivilegedContainer() {
|
||||
outputMap := config.dialFromContainer(config.privilegedPod.Status.PodIP, privilegedHttpPort)
|
||||
if len(outputMap["error"]) > 0 {
|
||||
Failf("Privileged command failed unexpectedly on privileged container, output:%v", outputMap)
|
||||
}
|
||||
}
|
||||
|
||||
func (config *PrivilegedPodTestConfig) runPrivilegedCommandOnNonPrivilegedContainer() {
|
||||
outputMap := config.dialFromContainer(config.privilegedPod.Status.PodIP, notPrivilegedHttpPort)
|
||||
if len(outputMap["error"]) == 0 {
|
||||
Failf("Privileged command should have failed on non-privileged container, output:%v", outputMap)
|
||||
}
|
||||
}
|
||||
|
||||
func (config *PrivilegedPodTestConfig) dialFromContainer(containerIP string, containerHttpPort int) map[string]string {
|
||||
v := url.Values{}
|
||||
v.Set("shellCommand", "ip link add dummy1 type dummy")
|
||||
cmd := fmt.Sprintf("curl -q 'http://%s:%d/shell?%s'",
|
||||
containerIP,
|
||||
containerHttpPort,
|
||||
v.Encode())
|
||||
|
||||
By(fmt.Sprintf("Exec-ing into container over http. Running command:%s", cmd))
|
||||
stdout := config.ssh(cmd)
|
||||
Logf("Output is %q", stdout)
|
||||
var output map[string]string
|
||||
err := json.Unmarshal([]byte(stdout), &output)
|
||||
Expect(err).NotTo(HaveOccurred(), fmt.Sprintf("Could not unmarshal curl response: %s", stdout))
|
||||
Logf("Deserialized output is %v", stdout)
|
||||
return output
|
||||
}
|
||||
|
||||
func (config *PrivilegedPodTestConfig) createPrivilegedPodSpec() *api.Pod {
|
||||
isPrivileged := true
|
||||
notPrivileged := false
|
||||
pod := &api.Pod{
|
||||
TypeMeta: unversioned.TypeMeta{
|
||||
Kind: "Pod",
|
||||
APIVersion: latest.GroupOrDie("").Version,
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: privilegedPodName,
|
||||
Namespace: config.f.Namespace.Name,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: privilegedContainerName,
|
||||
Image: privilegedContainerImage,
|
||||
ImagePullPolicy: api.PullIfNotPresent,
|
||||
SecurityContext: &api.SecurityContext{Privileged: &isPrivileged},
|
||||
Command: []string{
|
||||
"/netexec",
|
||||
fmt.Sprintf("--http-port=%d", privilegedHttpPort),
|
||||
fmt.Sprintf("--udp-port=%d", privilegedUdpPort),
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: notPrivilegedContainerName,
|
||||
Image: privilegedContainerImage,
|
||||
ImagePullPolicy: api.PullIfNotPresent,
|
||||
SecurityContext: &api.SecurityContext{Privileged: ¬Privileged},
|
||||
Command: []string{
|
||||
"/netexec",
|
||||
fmt.Sprintf("--http-port=%d", notPrivilegedHttpPort),
|
||||
fmt.Sprintf("--udp-port=%d", notPrivilegedUdpPort),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return pod
|
||||
}
|
||||
|
||||
func (config *PrivilegedPodTestConfig) createPrivilegedPod() {
|
||||
podSpec := config.createPrivilegedPodSpec()
|
||||
config.privilegedPod = config.createPod(podSpec)
|
||||
}
|
||||
|
||||
func (config *PrivilegedPodTestConfig) createPod(pod *api.Pod) *api.Pod {
|
||||
createdPod, err := config.getPodClient().Create(pod)
|
||||
if err != nil {
|
||||
Failf("Failed to create %q pod: %v", pod.Name, err)
|
||||
}
|
||||
expectNoError(config.f.WaitForPodRunning(pod.Name))
|
||||
createdPod, err = config.getPodClient().Get(pod.Name)
|
||||
if err != nil {
|
||||
Failf("Failed to retrieve %q pod: %v", pod.Name, err)
|
||||
}
|
||||
return createdPod
|
||||
}
|
||||
|
||||
func (config *PrivilegedPodTestConfig) getPodClient() client.PodInterface {
|
||||
return config.f.Client.Pods(config.f.Namespace.Name)
|
||||
}
|
||||
|
||||
func (config *PrivilegedPodTestConfig) getNamespaceClient() client.NamespaceInterface {
|
||||
return config.f.Client.Namespaces()
|
||||
}
|
||||
|
||||
func (config *PrivilegedPodTestConfig) ssh(cmd string) string {
|
||||
stdout, _, code, err := SSH(cmd, config.nodes[0], testContext.Provider)
|
||||
Expect(err).NotTo(HaveOccurred(), "error while SSH-ing to node: %v (code %v)", err, code)
|
||||
Expect(code).Should(BeZero(), "command exited with non-zero code %v. cmd:%s", code, cmd)
|
||||
return stdout
|
||||
}
|
Loading…
Reference in New Issue
Block a user