Skip to content

Commit

Permalink
Merge pull request #60319 from php-coder/psp_e2e_tests_and_policy_group
Browse files Browse the repository at this point in the history
Automatic merge from submit-queue (batch tested with PRs 60475, 60514, 60506, 59903, 60319). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

E2E: add tests for PSP in the policy API Group

**What this PR does / why we need it**:
E2E tests were added for testing PSP from the "policy" API Group. They are similar to the tests for PSP from the "extensions" API Group.

**Which issue(s) this PR fixes**:
Addressed to: kubernetes/enhancements#5
Follow-up to: #54933 and #60145
  • Loading branch information
Kubernetes Submit Queue committed Feb 28, 2018
2 parents 6daff5a + 4e273a6 commit 24adcd5
Show file tree
Hide file tree
Showing 2 changed files with 235 additions and 62 deletions.
2 changes: 2 additions & 0 deletions test/e2e/auth/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ go_library(
"//pkg/security/apparmor:go_default_library",
"//pkg/security/podsecuritypolicy/seccomp:go_default_library",
"//pkg/security/podsecuritypolicy/util:go_default_library",
"//pkg/util/pointer:go_default_library",
"//plugin/pkg/admission/serviceaccount:go_default_library",
"//test/e2e/common:go_default_library",
"//test/e2e/framework:go_default_library",
Expand All @@ -32,6 +33,7 @@ go_library(
"//vendor/k8s.io/api/certificates/v1beta1:go_default_library",
"//vendor/k8s.io/api/core/v1:go_default_library",
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
"//vendor/k8s.io/api/policy/v1beta1:go_default_library",
"//vendor/k8s.io/api/rbac/v1beta1:go_default_library",
"//vendor/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1:go_default_library",
"//vendor/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset:go_default_library",
Expand Down
295 changes: 233 additions & 62 deletions test/e2e/auth/pod_security_policy.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"k8s.io/api/core/v1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
policy "k8s.io/api/policy/v1beta1"
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
apierrs "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand All @@ -31,71 +32,14 @@ import (
"k8s.io/kubernetes/pkg/security/apparmor"
"k8s.io/kubernetes/pkg/security/podsecuritypolicy/seccomp"
psputil "k8s.io/kubernetes/pkg/security/podsecuritypolicy/util"
utilpointer "k8s.io/kubernetes/pkg/util/pointer"
"k8s.io/kubernetes/test/e2e/common"
"k8s.io/kubernetes/test/e2e/framework"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var (
restrictivePSPTemplate = &extensionsv1beta1.PodSecurityPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: "restrictive",
Annotations: map[string]string{
seccomp.AllowedProfilesAnnotationKey: "docker/default",
seccomp.DefaultProfileAnnotationKey: "docker/default",
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault,
apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
},
Labels: map[string]string{
"kubernetes.io/cluster-service": "true",
"addonmanager.kubernetes.io/mode": "Reconcile",
},
},
Spec: extensionsv1beta1.PodSecurityPolicySpec{
Privileged: false,
AllowPrivilegeEscalation: boolPtr(false),
RequiredDropCapabilities: []v1.Capability{
"AUDIT_WRITE",
"CHOWN",
"DAC_OVERRIDE",
"FOWNER",
"FSETID",
"KILL",
"MKNOD",
"NET_RAW",
"SETGID",
"SETUID",
"SYS_CHROOT",
},
Volumes: []extensionsv1beta1.FSType{
extensionsv1beta1.ConfigMap,
extensionsv1beta1.EmptyDir,
extensionsv1beta1.PersistentVolumeClaim,
"projected",
extensionsv1beta1.Secret,
},
HostNetwork: false,
HostIPC: false,
HostPID: false,
RunAsUser: extensionsv1beta1.RunAsUserStrategyOptions{
Rule: extensionsv1beta1.RunAsUserStrategyMustRunAsNonRoot,
},
SELinux: extensionsv1beta1.SELinuxStrategyOptions{
Rule: extensionsv1beta1.SELinuxStrategyRunAsAny,
},
SupplementalGroups: extensionsv1beta1.SupplementalGroupsStrategyOptions{
Rule: extensionsv1beta1.SupplementalGroupsStrategyRunAsAny,
},
FSGroup: extensionsv1beta1.FSGroupStrategyOptions{
Rule: extensionsv1beta1.FSGroupStrategyRunAsAny,
},
ReadOnlyRootFilesystem: false,
},
}
)

var _ = SIGDescribe("PodSecurityPolicy", func() {
f := framework.NewDefaultFramework("podsecuritypolicy")
f.SkipPrivilegedPSPBinding = true
Expand Down Expand Up @@ -134,9 +78,27 @@ var _ = SIGDescribe("PodSecurityPolicy", func() {
expectForbidden(err)
})

It("should enforce the restricted PodSecurityPolicy", func() {
// TODO: merge tests for extensions/policy API groups when PSP will be completely moved out of the extensions

It("should enforce the restricted extensions.PodSecurityPolicy", func() {
By("Creating & Binding a restricted policy for the test service account")
_, cleanup := createAndBindPSP(f, restrictedPSP("restrictive"))
defer cleanup()

By("Running a restricted pod")
pod, err := c.CoreV1().Pods(ns).Create(restrictedPod(f, "allowed"))
framework.ExpectNoError(err)
framework.ExpectNoError(framework.WaitForPodNameRunningInNamespace(c, pod.Name, pod.Namespace))

testPrivilegedPods(f, func(pod *v1.Pod) {
_, err := c.CoreV1().Pods(ns).Create(pod)
expectForbidden(err)
})
})

It("should enforce the restricted policy.PodSecurityPolicy", func() {
By("Creating & Binding a restricted policy for the test service account")
_, cleanup := createAndBindPSP(f, restrictivePSPTemplate)
_, cleanup := createAndBindPSPInPolicy(f, restrictedPSPInPolicy("restrictive"))
defer cleanup()

By("Running a restricted pod")
Expand All @@ -150,10 +112,10 @@ var _ = SIGDescribe("PodSecurityPolicy", func() {
})
})

It("should allow pods under the privileged PodSecurityPolicy", func() {
It("should allow pods under the privileged extensions.PodSecurityPolicy", func() {
By("Creating & Binding a privileged policy for the test service account")
// Ensure that the permissive policy is used even in the presence of the restricted policy.
_, cleanup := createAndBindPSP(f, restrictivePSPTemplate)
_, cleanup := createAndBindPSP(f, restrictedPSP("restrictive"))
defer cleanup()
expectedPSP, cleanup := createAndBindPSP(f, framework.PrivilegedPSP("permissive"))
defer cleanup()
Expand All @@ -171,6 +133,28 @@ var _ = SIGDescribe("PodSecurityPolicy", func() {
Expect(validated).To(Equal(expectedPSP.Name), "Unexpected validated PSP")
})
})

It("should allow pods under the privileged policy.PodSecurityPolicy", func() {
By("Creating & Binding a privileged policy for the test service account")
// Ensure that the permissive policy is used even in the presence of the restricted policy.
_, cleanup := createAndBindPSPInPolicy(f, restrictedPSPInPolicy("restrictive"))
defer cleanup()
expectedPSP, cleanup := createAndBindPSPInPolicy(f, privilegedPSPInPolicy("permissive"))
defer cleanup()

testPrivilegedPods(f, func(pod *v1.Pod) {
p, err := c.CoreV1().Pods(ns).Create(pod)
framework.ExpectNoError(err)
framework.ExpectNoError(framework.WaitForPodNameRunningInNamespace(c, p.Name, p.Namespace))

// Verify expected PSP was used.
p, err = c.CoreV1().Pods(ns).Get(p.Name, metav1.GetOptions{})
framework.ExpectNoError(err)
validated, found := p.Annotations[psputil.ValidatedPSPAnnotation]
Expect(found).To(BeTrue(), "PSP annotation not found")
Expect(validated).To(Equal(expectedPSP.Name), "Unexpected validated PSP")
})
})
})

func expectForbidden(err error) {
Expand Down Expand Up @@ -284,6 +268,48 @@ func createAndBindPSP(f *framework.Framework, pspTemplate *extensionsv1beta1.Pod
}
}

// createAndBindPSPInPolicy creates a PSP in the policy API group (unlike createAndBindPSP()).
// TODO: merge these functions when PSP will be completely moved out of the extensions
func createAndBindPSPInPolicy(f *framework.Framework, pspTemplate *policy.PodSecurityPolicy) (psp *policy.PodSecurityPolicy, cleanup func()) {
// Create the PodSecurityPolicy object.
psp = pspTemplate.DeepCopy()
// Add the namespace to the name to ensure uniqueness and tie it to the namespace.
ns := f.Namespace.Name
name := fmt.Sprintf("%s-%s", ns, psp.Name)
psp.Name = name
psp, err := f.ClientSet.PolicyV1beta1().PodSecurityPolicies().Create(psp)
framework.ExpectNoError(err, "Failed to create PSP")

// Create the Role to bind it to the namespace.
_, err = f.ClientSet.RbacV1beta1().Roles(ns).Create(&rbacv1beta1.Role{
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Rules: []rbacv1beta1.PolicyRule{{
APIGroups: []string{"policy"},
Resources: []string{"podsecuritypolicies"},
ResourceNames: []string{name},
Verbs: []string{"use"},
}},
})
framework.ExpectNoError(err, "Failed to create PSP role")

// Bind the role to the namespace.
framework.BindRoleInNamespace(f.ClientSet.RbacV1beta1(), name, ns, rbacv1beta1.Subject{
Kind: rbacv1beta1.ServiceAccountKind,
Namespace: ns,
Name: "default",
})
framework.ExpectNoError(framework.WaitForNamedAuthorizationUpdate(f.ClientSet.AuthorizationV1beta1(),
serviceaccount.MakeUsername(ns, "default"), ns, "use", name,
schema.GroupResource{Group: "policy", Resource: "podsecuritypolicies"}, true))

return psp, func() {
// Cleanup non-namespaced PSP object.
f.ClientSet.PolicyV1beta1().PodSecurityPolicies().Delete(name, &metav1.DeleteOptions{})
}
}

func restrictedPod(f *framework.Framework, name string) *v1.Pod {
return &v1.Pod{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -306,6 +332,151 @@ func restrictedPod(f *framework.Framework, name string) *v1.Pod {
}
}

// privilegedPSPInPolicy creates a PodSecurityPolicy (in the "policy" API Group) that allows everything.
// TODO: replace by PrivilegedPSP when PSP will be completely moved out of the extensions
func privilegedPSPInPolicy(name string) *policy.PodSecurityPolicy {
return &policy.PodSecurityPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Annotations: map[string]string{seccomp.AllowedProfilesAnnotationKey: seccomp.AllowAny},
},
Spec: policy.PodSecurityPolicySpec{
Privileged: true,
AllowPrivilegeEscalation: utilpointer.BoolPtr(true),
AllowedCapabilities: []v1.Capability{"*"},
Volumes: []policy.FSType{policy.All},
HostNetwork: true,
HostPorts: []policy.HostPortRange{{Min: 0, Max: 65535}},
HostIPC: true,
HostPID: true,
RunAsUser: policy.RunAsUserStrategyOptions{
Rule: policy.RunAsUserStrategyRunAsAny,
},
SELinux: policy.SELinuxStrategyOptions{
Rule: policy.SELinuxStrategyRunAsAny,
},
SupplementalGroups: policy.SupplementalGroupsStrategyOptions{
Rule: policy.SupplementalGroupsStrategyRunAsAny,
},
FSGroup: policy.FSGroupStrategyOptions{
Rule: policy.FSGroupStrategyRunAsAny,
},
ReadOnlyRootFilesystem: false,
},
}
}

// restrictedPSPInPolicy creates a PodSecurityPolicy (in the "policy" API Group) that is most strict.
// TODO: replace by restrictedPSP when PSP will be completely moved out of the extensions
func restrictedPSPInPolicy(name string) *policy.PodSecurityPolicy {
return &policy.PodSecurityPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Annotations: map[string]string{
seccomp.AllowedProfilesAnnotationKey: "docker/default",
seccomp.DefaultProfileAnnotationKey: "docker/default",
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault,
apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
},
},
Spec: policy.PodSecurityPolicySpec{
Privileged: false,
AllowPrivilegeEscalation: utilpointer.BoolPtr(false),
RequiredDropCapabilities: []v1.Capability{
"AUDIT_WRITE",
"CHOWN",
"DAC_OVERRIDE",
"FOWNER",
"FSETID",
"KILL",
"MKNOD",
"NET_RAW",
"SETGID",
"SETUID",
"SYS_CHROOT",
},
Volumes: []policy.FSType{
policy.ConfigMap,
policy.EmptyDir,
policy.PersistentVolumeClaim,
"projected",
policy.Secret,
},
HostNetwork: false,
HostIPC: false,
HostPID: false,
RunAsUser: policy.RunAsUserStrategyOptions{
Rule: policy.RunAsUserStrategyMustRunAsNonRoot,
},
SELinux: policy.SELinuxStrategyOptions{
Rule: policy.SELinuxStrategyRunAsAny,
},
SupplementalGroups: policy.SupplementalGroupsStrategyOptions{
Rule: policy.SupplementalGroupsStrategyRunAsAny,
},
FSGroup: policy.FSGroupStrategyOptions{
Rule: policy.FSGroupStrategyRunAsAny,
},
ReadOnlyRootFilesystem: false,
},
}
}

// restrictedPSP creates a PodSecurityPolicy that is most strict.
func restrictedPSP(name string) *extensionsv1beta1.PodSecurityPolicy {
return &extensionsv1beta1.PodSecurityPolicy{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Annotations: map[string]string{
seccomp.AllowedProfilesAnnotationKey: "docker/default",
seccomp.DefaultProfileAnnotationKey: "docker/default",
apparmor.AllowedProfilesAnnotationKey: apparmor.ProfileRuntimeDefault,
apparmor.DefaultProfileAnnotationKey: apparmor.ProfileRuntimeDefault,
},
},
Spec: extensionsv1beta1.PodSecurityPolicySpec{
Privileged: false,
AllowPrivilegeEscalation: utilpointer.BoolPtr(false),
RequiredDropCapabilities: []v1.Capability{
"AUDIT_WRITE",
"CHOWN",
"DAC_OVERRIDE",
"FOWNER",
"FSETID",
"KILL",
"MKNOD",
"NET_RAW",
"SETGID",
"SETUID",
"SYS_CHROOT",
},
Volumes: []extensionsv1beta1.FSType{
extensionsv1beta1.ConfigMap,
extensionsv1beta1.EmptyDir,
extensionsv1beta1.PersistentVolumeClaim,
"projected",
extensionsv1beta1.Secret,
},
HostNetwork: false,
HostIPC: false,
HostPID: false,
RunAsUser: extensionsv1beta1.RunAsUserStrategyOptions{
Rule: extensionsv1beta1.RunAsUserStrategyMustRunAsNonRoot,
},
SELinux: extensionsv1beta1.SELinuxStrategyOptions{
Rule: extensionsv1beta1.SELinuxStrategyRunAsAny,
},
SupplementalGroups: extensionsv1beta1.SupplementalGroupsStrategyOptions{
Rule: extensionsv1beta1.SupplementalGroupsStrategyRunAsAny,
},
FSGroup: extensionsv1beta1.FSGroupStrategyOptions{
Rule: extensionsv1beta1.FSGroupStrategyRunAsAny,
},
ReadOnlyRootFilesystem: false,
},
}
}

func boolPtr(b bool) *bool {
return &b
}
Expand Down

0 comments on commit 24adcd5

Please sign in to comment.