From 9015a826925e0dc1575625527c3dcdced78b6dc5 Mon Sep 17 00:00:00 2001 From: Slava Semushin Date: Fri, 25 Aug 2017 16:30:27 +0200 Subject: [PATCH] PodSecurityPolicy.allowedCapabilities: add support for using * to allow to request any capabilities. Also modify "privileged" PSP to use it and allow privileged users to use any capabilities. --- examples/podsecuritypolicy/rbac/policies.yaml | 2 ++ pkg/apis/extensions/types.go | 5 +++++ pkg/apis/extensions/validation/validation.go | 4 ++++ pkg/apis/extensions/validation/validation_test.go | 9 +++++++++ .../podsecuritypolicy/capabilities/mustrunas.go | 9 ++++++++- .../capabilities/mustrunas_test.go | 7 +++++++ .../security/podsecuritypolicy/admission_test.go | 13 ++++++++++++- 7 files changed, 47 insertions(+), 2 deletions(-) diff --git a/examples/podsecuritypolicy/rbac/policies.yaml b/examples/podsecuritypolicy/rbac/policies.yaml index 6ddd9422fb..670a0e4448 100644 --- a/examples/podsecuritypolicy/rbac/policies.yaml +++ b/examples/podsecuritypolicy/rbac/policies.yaml @@ -14,6 +14,8 @@ spec: rule: RunAsAny volumes: - '*' + allowedCapabilities: + - '*' --- apiVersion: extensions/v1beta1 kind: PodSecurityPolicy diff --git a/pkg/apis/extensions/types.go b/pkg/apis/extensions/types.go index 165bbee667..f684fc4de1 100644 --- a/pkg/apis/extensions/types.go +++ b/pkg/apis/extensions/types.go @@ -898,6 +898,7 @@ type PodSecurityPolicySpec struct { // AllowedCapabilities is a list of capabilities that can be requested to add to the container. // Capabilities in this field may be added at the pod author's discretion. // You must not list a capability in both AllowedCapabilities and RequiredDropCapabilities. + // To allow all capabilities you may use '*'. // +optional AllowedCapabilities []api.Capability // Volumes is a white list of allowed volume plugins. Empty indicates that all plugins @@ -966,6 +967,10 @@ type HostPortRange struct { Max int } +// AllowAllCapabilities can be used as a value for the PodSecurityPolicy.AllowAllCapabilities +// field and means that any capabilities are allowed to be requested. +var AllowAllCapabilities api.Capability = "*" + // FSType gives strong typing to different file systems that are used by volumes. type FSType string diff --git a/pkg/apis/extensions/validation/validation.go b/pkg/apis/extensions/validation/validation.go index 7323b6c989..bb4600262c 100644 --- a/pkg/apis/extensions/validation/validation.go +++ b/pkg/apis/extensions/validation/validation.go @@ -660,6 +660,10 @@ func ValidatePodSecurityPolicySpec(spec *extensions.PodSecurityPolicySpec, fldPa allErrs = append(allErrs, validatePSPSupplementalGroup(fldPath.Child("supplementalGroups"), &spec.SupplementalGroups)...) allErrs = append(allErrs, validatePSPFSGroup(fldPath.Child("fsGroup"), &spec.FSGroup)...) allErrs = append(allErrs, validatePodSecurityPolicyVolumes(fldPath, spec.Volumes)...) + if len(spec.RequiredDropCapabilities) > 0 && hasCap(extensions.AllowAllCapabilities, spec.AllowedCapabilities) { + allErrs = append(allErrs, field.Invalid(field.NewPath("requiredDropCapabilities"), spec.RequiredDropCapabilities, + "must be empty when all capabilities are allowed by a wildcard")) + } allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.DefaultAddCapabilities, field.NewPath("defaultAddCapabilities"))...) allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.AllowedCapabilities, field.NewPath("allowedCapabilities"))...) allErrs = append(allErrs, validatePSPDefaultAllowPrivilegeEscalation(fldPath.Child("defaultAllowPrivilegeEscalation"), spec.DefaultAllowPrivilegeEscalation, spec.AllowPrivilegeEscalation)...) diff --git a/pkg/apis/extensions/validation/validation_test.go b/pkg/apis/extensions/validation/validation_test.go index 2bd0d5a051..7abc901583 100644 --- a/pkg/apis/extensions/validation/validation_test.go +++ b/pkg/apis/extensions/validation/validation_test.go @@ -2472,6 +2472,10 @@ func TestValidatePodSecurityPolicy(t *testing.T) { {Min: 1, Max: -10}, } + wildcardAllowedCapAndRequiredDrop := validPSP() + wildcardAllowedCapAndRequiredDrop.Spec.RequiredDropCapabilities = []api.Capability{"foo"} + wildcardAllowedCapAndRequiredDrop.Spec.AllowedCapabilities = []api.Capability{extensions.AllowAllCapabilities} + requiredCapAddAndDrop := validPSP() requiredCapAddAndDrop.Spec.DefaultAddCapabilities = []api.Capability{"foo"} requiredCapAddAndDrop.Spec.RequiredDropCapabilities = []api.Capability{"foo"} @@ -2586,6 +2590,11 @@ func TestValidatePodSecurityPolicy(t *testing.T) { errorType: field.ErrorTypeInvalid, errorDetail: "max cannot be negative", }, + "non-empty required drops and all caps are allowed by a wildcard": { + psp: wildcardAllowedCapAndRequiredDrop, + errorType: field.ErrorTypeInvalid, + errorDetail: "must be empty when all capabilities are allowed by a wildcard", + }, "invalid required caps": { psp: requiredCapAddAndDrop, errorType: field.ErrorTypeInvalid, diff --git a/pkg/security/podsecuritypolicy/capabilities/mustrunas.go b/pkg/security/podsecuritypolicy/capabilities/mustrunas.go index cc4ce06d01..d8efe066db 100644 --- a/pkg/security/podsecuritypolicy/capabilities/mustrunas.go +++ b/pkg/security/podsecuritypolicy/capabilities/mustrunas.go @@ -22,6 +22,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/extensions" ) // defaultCapabilities implements the Strategy interface @@ -101,9 +102,15 @@ func (s *defaultCapabilities) Validate(pod *api.Pod, container *api.Container) f return allErrs } + allowedAdd := makeCapSet(s.allowedCaps) + allowAllCaps := allowedAdd.Has(string(extensions.AllowAllCapabilities)) + if allowAllCaps { + // skip validation against allowed/defaultAdd/requiredDrop because all capabilities are allowed by a wildcard + return allErrs + } + // validate that anything being added is in the default or allowed sets defaultAdd := makeCapSet(s.defaultAddCapabilities) - allowedAdd := makeCapSet(s.allowedCaps) for _, cap := range container.SecurityContext.Capabilities.Add { sCap := string(cap) diff --git a/pkg/security/podsecuritypolicy/capabilities/mustrunas_test.go b/pkg/security/podsecuritypolicy/capabilities/mustrunas_test.go index aaf2d5a29a..7e9bfa5155 100644 --- a/pkg/security/podsecuritypolicy/capabilities/mustrunas_test.go +++ b/pkg/security/podsecuritypolicy/capabilities/mustrunas_test.go @@ -21,6 +21,7 @@ import ( "testing" "k8s.io/kubernetes/pkg/api" + "k8s.io/kubernetes/pkg/apis/extensions" ) func TestGenerateAdds(t *testing.T) { @@ -250,6 +251,12 @@ func TestValidateAdds(t *testing.T) { Add: []api.Capability{"foo"}, }, }, + "no required, all allowed, container requests valid": { + allowedCaps: []api.Capability{extensions.AllowAllCapabilities}, + containerCaps: &api.Capabilities{ + Add: []api.Capability{"foo"}, + }, + }, "no required, allowed, container requests invalid": { allowedCaps: []api.Capability{"foo"}, containerCaps: &api.Capabilities{ diff --git a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go b/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go index 53a1dcc5c3..b88df3deef 100644 --- a/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go +++ b/plugin/pkg/admission/security/podsecuritypolicy/admission_test.go @@ -272,6 +272,10 @@ func TestAdmitCaps(t *testing.T) { requiresFooToBeDropped.Name = "requireDrop" requiresFooToBeDropped.Spec.RequiredDropCapabilities = []kapi.Capability{"foo"} + allowAllInAllowed := restrictivePSP() + allowAllInAllowed.Name = "allowAllCapsInAllowed" + allowAllInAllowed.Spec.AllowedCapabilities = []kapi.Capability{extensions.AllowAllCapabilities} + tc := map[string]struct { pod *kapi.Pod psps []*extensions.PodSecurityPolicy @@ -337,6 +341,13 @@ func TestAdmitCaps(t *testing.T) { }, expectedPSP: requiresFooToBeDropped.Name, }, + // UC 8: using '*' in allowed caps + "should accept cap add when all caps are allowed": { + pod: createPodWithCaps(&kapi.Capabilities{Add: []kapi.Capability{"foo"}}), + psps: []*extensions.PodSecurityPolicy{restricted, allowAllInAllowed}, + shouldPass: true, + expectedPSP: allowAllInAllowed.Name, + }, } for k, v := range tc { @@ -1360,7 +1371,7 @@ func testPSPAdmit(testCaseName string, psps []*extensions.PodSecurityPolicy, pod if shouldPass && err == nil { if pod.Annotations[psputil.ValidatedPSPAnnotation] != expectedPSP { - t.Errorf("%s: expected to validate under %s but found %s", testCaseName, expectedPSP, pod.Annotations[psputil.ValidatedPSPAnnotation]) + t.Errorf("%s: expected to validate under %q PSP but found %q", testCaseName, expectedPSP, pod.Annotations[psputil.ValidatedPSPAnnotation]) } }