123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251 |
- /*
- Copyright 2017 The Kubernetes Authors.
- 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 common
- import (
- "fmt"
- api "k8s.io/api/core/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/labels"
- "k8s.io/kubernetes/pkg/security/apparmor"
- "k8s.io/kubernetes/test/e2e/framework"
- imageutils "k8s.io/kubernetes/test/utils/image"
- . "github.com/onsi/gomega"
- )
- const (
- appArmorProfilePrefix = "e2e-apparmor-test-"
- appArmorAllowedPath = "/expect_allowed_write"
- appArmorDeniedPath = "/expect_permission_denied"
- loaderLabelKey = "name"
- loaderLabelValue = "e2e-apparmor-loader"
- )
- // AppArmorDistros are distros with AppArmor support
- var AppArmorDistros = []string{"gci", "ubuntu"}
- func IsAppArmorSupported() bool {
- return framework.NodeOSDistroIs(AppArmorDistros...)
- }
- func SkipIfAppArmorNotSupported() {
- framework.SkipUnlessNodeOSDistroIs(AppArmorDistros...)
- }
- func LoadAppArmorProfiles(f *framework.Framework) {
- createAppArmorProfileCM(f)
- createAppArmorProfileLoader(f)
- }
- // CreateAppArmorTestPod creates a pod that tests apparmor profile enforcement. The pod exits with
- // an error code if the profile is incorrectly enforced. If runOnce is true the pod will exit after
- // a single test, otherwise it will repeat the test every 1 second until failure.
- func CreateAppArmorTestPod(f *framework.Framework, unconfined bool, runOnce bool) *api.Pod {
- profile := "localhost/" + appArmorProfilePrefix + f.Namespace.Name
- testCmd := fmt.Sprintf(`
- if touch %[1]s; then
- echo "FAILURE: write to %[1]s should be denied"
- exit 1
- elif ! touch %[2]s; then
- echo "FAILURE: write to %[2]s should be allowed"
- exit 2
- elif [[ $(< /proc/self/attr/current) != "%[3]s" ]]; then
- echo "FAILURE: not running with expected profile %[3]s"
- echo "found: $(cat /proc/self/attr/current)"
- exit 3
- fi`, appArmorDeniedPath, appArmorAllowedPath, appArmorProfilePrefix+f.Namespace.Name)
- if unconfined {
- profile = apparmor.ProfileNameUnconfined
- testCmd = `
- if cat /proc/sysrq-trigger 2>&1 | grep 'Permission denied'; then
- echo 'FAILURE: reading /proc/sysrq-trigger should be allowed'
- exit 1
- elif [[ $(< /proc/self/attr/current) != "unconfined" ]]; then
- echo 'FAILURE: not running with expected profile unconfined'
- exit 2
- fi`
- }
- if !runOnce {
- testCmd = fmt.Sprintf(`while true; do
- %s
- sleep 1
- done`, testCmd)
- }
- loaderAffinity := &api.Affinity{
- PodAffinity: &api.PodAffinity{
- RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{{
- Namespaces: []string{f.Namespace.Name},
- LabelSelector: &metav1.LabelSelector{
- MatchLabels: map[string]string{loaderLabelKey: loaderLabelValue},
- },
- TopologyKey: "kubernetes.io/hostname",
- }},
- },
- }
- pod := &api.Pod{
- ObjectMeta: metav1.ObjectMeta{
- GenerateName: "test-apparmor-",
- Annotations: map[string]string{
- apparmor.ContainerAnnotationKeyPrefix + "test": profile,
- },
- Labels: map[string]string{
- "test": "apparmor",
- },
- },
- Spec: api.PodSpec{
- Affinity: loaderAffinity,
- Containers: []api.Container{{
- Name: "test",
- Image: imageutils.GetE2EImage(imageutils.BusyBox),
- Command: []string{"sh", "-c", testCmd},
- }},
- RestartPolicy: api.RestartPolicyNever,
- },
- }
- if runOnce {
- pod = f.PodClient().Create(pod)
- framework.ExpectNoError(framework.WaitForPodSuccessInNamespace(
- f.ClientSet, pod.Name, f.Namespace.Name))
- var err error
- pod, err = f.PodClient().Get(pod.Name, metav1.GetOptions{})
- framework.ExpectNoError(err)
- } else {
- pod = f.PodClient().CreateSync(pod)
- framework.ExpectNoError(f.WaitForPodReady(pod.Name))
- }
- // Verify Pod affinity colocated the Pods.
- loader := getRunningLoaderPod(f)
- Expect(pod.Spec.NodeName).To(Equal(loader.Spec.NodeName))
- return pod
- }
- func createAppArmorProfileCM(f *framework.Framework) {
- profileName := appArmorProfilePrefix + f.Namespace.Name
- profile := fmt.Sprintf(`#include <tunables/global>
- profile %s flags=(attach_disconnected) {
- #include <abstractions/base>
- file,
- deny %s w,
- audit %s w,
- }
- `, profileName, appArmorDeniedPath, appArmorAllowedPath)
- cm := &api.ConfigMap{
- ObjectMeta: metav1.ObjectMeta{
- Name: "apparmor-profiles",
- Namespace: f.Namespace.Name,
- },
- Data: map[string]string{
- profileName: profile,
- },
- }
- _, err := f.ClientSet.CoreV1().ConfigMaps(f.Namespace.Name).Create(cm)
- framework.ExpectNoError(err, "Failed to create apparmor-profiles ConfigMap")
- }
- func createAppArmorProfileLoader(f *framework.Framework) {
- True := true
- One := int32(1)
- loader := &api.ReplicationController{
- ObjectMeta: metav1.ObjectMeta{
- Name: "apparmor-loader",
- Namespace: f.Namespace.Name,
- },
- Spec: api.ReplicationControllerSpec{
- Replicas: &One,
- Template: &api.PodTemplateSpec{
- ObjectMeta: metav1.ObjectMeta{
- Labels: map[string]string{loaderLabelKey: loaderLabelValue},
- },
- Spec: api.PodSpec{
- Containers: []api.Container{{
- Name: "apparmor-loader",
- Image: imageutils.GetE2EImage(imageutils.AppArmorLoader),
- Args: []string{"-poll", "10s", "/profiles"},
- SecurityContext: &api.SecurityContext{
- Privileged: &True,
- },
- VolumeMounts: []api.VolumeMount{{
- Name: "sys",
- MountPath: "/sys",
- ReadOnly: true,
- }, {
- Name: "apparmor-includes",
- MountPath: "/etc/apparmor.d",
- ReadOnly: true,
- }, {
- Name: "profiles",
- MountPath: "/profiles",
- ReadOnly: true,
- }},
- }},
- Volumes: []api.Volume{{
- Name: "sys",
- VolumeSource: api.VolumeSource{
- HostPath: &api.HostPathVolumeSource{
- Path: "/sys",
- },
- },
- }, {
- Name: "apparmor-includes",
- VolumeSource: api.VolumeSource{
- HostPath: &api.HostPathVolumeSource{
- Path: "/etc/apparmor.d",
- },
- },
- }, {
- Name: "profiles",
- VolumeSource: api.VolumeSource{
- ConfigMap: &api.ConfigMapVolumeSource{
- LocalObjectReference: api.LocalObjectReference{
- Name: "apparmor-profiles",
- },
- },
- },
- }},
- },
- },
- },
- }
- _, err := f.ClientSet.CoreV1().ReplicationControllers(f.Namespace.Name).Create(loader)
- framework.ExpectNoError(err, "Failed to create apparmor-loader ReplicationController")
- // Wait for loader to be ready.
- getRunningLoaderPod(f)
- }
- func getRunningLoaderPod(f *framework.Framework) *api.Pod {
- label := labels.SelectorFromSet(labels.Set(map[string]string{loaderLabelKey: loaderLabelValue}))
- pods, err := framework.WaitForPodsWithLabelScheduled(f.ClientSet, f.Namespace.Name, label)
- framework.ExpectNoError(err, "Failed to schedule apparmor-loader Pod")
- pod := &pods.Items[0]
- framework.ExpectNoError(framework.WaitForPodRunningInNamespace(f.ClientSet, pod), "Failed to run apparmor-loader Pod")
- return pod
- }
|