mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +00:00
allowPrivilegeEscalation: modify api types & add functionality
Signed-off-by: Jess Frazelle <acidburn@google.com>
This commit is contained in:
parent
d2791d46e3
commit
0f349cc61f
@ -4030,6 +4030,11 @@ type SecurityContext struct {
|
|||||||
// files to, ensuring the persistent data can only be written to mounts.
|
// files to, ensuring the persistent data can only be written to mounts.
|
||||||
// +optional
|
// +optional
|
||||||
ReadOnlyRootFilesystem *bool
|
ReadOnlyRootFilesystem *bool
|
||||||
|
// AllowPrivilegeEscalation controls whether a process can gain more
|
||||||
|
// privileges than it's parent process. This bool directly controls if
|
||||||
|
// the no_new_privs flag will be set on the container process.
|
||||||
|
// +optional
|
||||||
|
AllowPrivilegeEscalation *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// SELinuxOptions are the labels to be applied to the container.
|
// SELinuxOptions are the labels to be applied to the container.
|
||||||
|
@ -675,6 +675,30 @@ func Convert_v1_Secret_To_api_Secret(in *v1.Secret, out *api.Secret, s conversio
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func Convert_api_SecurityContext_To_v1_SecurityContext(in *api.SecurityContext, out *v1.SecurityContext, s conversion.Scope) error {
|
||||||
|
if in.Capabilities != nil {
|
||||||
|
out.Capabilities = new(v1.Capabilities)
|
||||||
|
if err := Convert_api_Capabilities_To_v1_Capabilities(in.Capabilities, out.Capabilities, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.Capabilities = nil
|
||||||
|
}
|
||||||
|
out.Privileged = in.Privileged
|
||||||
|
if in.SELinuxOptions != nil {
|
||||||
|
out.SELinuxOptions = new(v1.SELinuxOptions)
|
||||||
|
if err := Convert_api_SELinuxOptions_To_v1_SELinuxOptions(in.SELinuxOptions, out.SELinuxOptions, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
out.SELinuxOptions = nil
|
||||||
|
}
|
||||||
|
out.RunAsUser = in.RunAsUser
|
||||||
|
out.RunAsNonRoot = in.RunAsNonRoot
|
||||||
|
out.ReadOnlyRootFilesystem = in.ReadOnlyRootFilesystem
|
||||||
|
out.AllowPrivilegeEscalation = in.AllowPrivilegeEscalation
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func Convert_api_PodSecurityContext_To_v1_PodSecurityContext(in *api.PodSecurityContext, out *v1.PodSecurityContext, s conversion.Scope) error {
|
func Convert_api_PodSecurityContext_To_v1_PodSecurityContext(in *api.PodSecurityContext, out *v1.PodSecurityContext, s conversion.Scope) error {
|
||||||
out.SupplementalGroups = in.SupplementalGroups
|
out.SupplementalGroups = in.SupplementalGroups
|
||||||
|
@ -922,6 +922,14 @@ type PodSecurityPolicySpec struct {
|
|||||||
// will not be forced to.
|
// will not be forced to.
|
||||||
// +optional
|
// +optional
|
||||||
ReadOnlyRootFilesystem bool
|
ReadOnlyRootFilesystem bool
|
||||||
|
// DefaultAllowPrivilegeEscalation controls the default setting for whether a
|
||||||
|
// process can gain more privileges than its parent process.
|
||||||
|
// +optional
|
||||||
|
DefaultAllowPrivilegeEscalation *bool
|
||||||
|
// AllowPrivilegeEscalation determines if a pod can request to allow
|
||||||
|
// privilege escalation.
|
||||||
|
// +optional
|
||||||
|
AllowPrivilegeEscalation bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// HostPortRange defines a range of host ports that will be enabled by a policy
|
// HostPortRange defines a range of host ports that will be enabled by a policy
|
||||||
|
@ -59,6 +59,7 @@ func addConversionFuncs(scheme *runtime.Scheme) error {
|
|||||||
Convert_networking_NetworkPolicyPort_To_v1beta1_NetworkPolicyPort,
|
Convert_networking_NetworkPolicyPort_To_v1beta1_NetworkPolicyPort,
|
||||||
Convert_v1beta1_NetworkPolicySpec_To_networking_NetworkPolicySpec,
|
Convert_v1beta1_NetworkPolicySpec_To_networking_NetworkPolicySpec,
|
||||||
Convert_networking_NetworkPolicySpec_To_v1beta1_NetworkPolicySpec,
|
Convert_networking_NetworkPolicySpec_To_v1beta1_NetworkPolicySpec,
|
||||||
|
Convert_extensions_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySpec,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -429,3 +430,7 @@ func Convert_networking_NetworkPolicyList_To_v1beta1_NetworkPolicyList(in *netwo
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Convert_extensions_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySpec(in *extensions.PodSecurityPolicySpec, out *extensionsv1beta1.PodSecurityPolicySpec, s conversion.Scope) error {
|
||||||
|
return autoConvert_extensions_PodSecurityPolicySpec_To_v1beta1_PodSecurityPolicySpec(in, out, s)
|
||||||
|
}
|
||||||
|
@ -661,6 +661,7 @@ func ValidatePodSecurityPolicySpec(spec *extensions.PodSecurityPolicySpec, fldPa
|
|||||||
allErrs = append(allErrs, validatePodSecurityPolicyVolumes(fldPath, spec.Volumes)...)
|
allErrs = append(allErrs, validatePodSecurityPolicyVolumes(fldPath, spec.Volumes)...)
|
||||||
allErrs = append(allErrs, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.DefaultAddCapabilities, field.NewPath("defaultAddCapabilities"))...)
|
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, validatePSPCapsAgainstDrops(spec.RequiredDropCapabilities, spec.AllowedCapabilities, field.NewPath("allowedCapabilities"))...)
|
||||||
|
allErrs = append(allErrs, validatePSPDefaultAllowPrivilegeEscalation(fldPath.Child("defaultAllowPrivilegeEscalation"), spec.DefaultAllowPrivilegeEscalation, spec.AllowPrivilegeEscalation)...)
|
||||||
|
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
@ -786,6 +787,16 @@ func validatePodSecurityPolicyVolumes(fldPath *field.Path, volumes []extensions.
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// validatePSPDefaultAllowPrivilegeEscalation validates the DefaultAllowPrivilegeEscalation field against the AllowPrivilegeEscalation field of a PodSecurityPolicy.
|
||||||
|
func validatePSPDefaultAllowPrivilegeEscalation(fldPath *field.Path, defaultAllowPrivilegeEscalation *bool, allowPrivilegeEscalation bool) field.ErrorList {
|
||||||
|
allErrs := field.ErrorList{}
|
||||||
|
if defaultAllowPrivilegeEscalation != nil && *defaultAllowPrivilegeEscalation && !allowPrivilegeEscalation {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath, defaultAllowPrivilegeEscalation, "Cannot set DefaultAllowPrivilegeEscalation to true without also setting AllowPrivilegeEscalation to true"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
const sysctlPatternSegmentFmt string = "([a-z0-9][-_a-z0-9]*)?[a-z0-9*]"
|
const sysctlPatternSegmentFmt string = "([a-z0-9][-_a-z0-9]*)?[a-z0-9*]"
|
||||||
const SysctlPatternFmt string = "(" + apivalidation.SysctlSegmentFmt + "\\.)*" + sysctlPatternSegmentFmt
|
const SysctlPatternFmt string = "(" + apivalidation.SysctlSegmentFmt + "\\.)*" + sysctlPatternSegmentFmt
|
||||||
|
|
||||||
|
@ -2494,6 +2494,10 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
|
|||||||
seccomp.AllowedProfilesAnnotationKey: "docker/default,not-good",
|
seccomp.AllowedProfilesAnnotationKey: "docker/default,not-good",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invalidDefaultAllowPrivilegeEscalation := validPSP()
|
||||||
|
pe := true
|
||||||
|
invalidDefaultAllowPrivilegeEscalation.Spec.DefaultAllowPrivilegeEscalation = &pe
|
||||||
|
|
||||||
type testCase struct {
|
type testCase struct {
|
||||||
psp *extensions.PodSecurityPolicy
|
psp *extensions.PodSecurityPolicy
|
||||||
errorType field.ErrorType
|
errorType field.ErrorType
|
||||||
@ -2600,6 +2604,11 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
|
|||||||
errorType: field.ErrorTypeInvalid,
|
errorType: field.ErrorTypeInvalid,
|
||||||
errorDetail: "must be a valid seccomp profile",
|
errorDetail: "must be a valid seccomp profile",
|
||||||
},
|
},
|
||||||
|
"invalid defaultAllowPrivilegeEscalation": {
|
||||||
|
psp: invalidDefaultAllowPrivilegeEscalation,
|
||||||
|
errorType: field.ErrorTypeInvalid,
|
||||||
|
errorDetail: "Cannot set DefaultAllowPrivilegeEscalation to true without also setting AllowPrivilegeEscalation to true",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
@ -2674,6 +2683,11 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
|
|||||||
seccomp.AllowedProfilesAnnotationKey: "docker/default,unconfined,localhost/foo",
|
seccomp.AllowedProfilesAnnotationKey: "docker/default,unconfined,localhost/foo",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validDefaultAllowPrivilegeEscalation := validPSP()
|
||||||
|
pe = true
|
||||||
|
validDefaultAllowPrivilegeEscalation.Spec.DefaultAllowPrivilegeEscalation = &pe
|
||||||
|
validDefaultAllowPrivilegeEscalation.Spec.AllowPrivilegeEscalation = true
|
||||||
|
|
||||||
successCases := map[string]struct {
|
successCases := map[string]struct {
|
||||||
psp *extensions.PodSecurityPolicy
|
psp *extensions.PodSecurityPolicy
|
||||||
}{
|
}{
|
||||||
@ -2701,6 +2715,9 @@ func TestValidatePodSecurityPolicy(t *testing.T) {
|
|||||||
"valid seccomp annotations": {
|
"valid seccomp annotations": {
|
||||||
psp: validSeccomp,
|
psp: validSeccomp,
|
||||||
},
|
},
|
||||||
|
"valid defaultAllowPrivilegeEscalation as true": {
|
||||||
|
psp: validDefaultAllowPrivilegeEscalation,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v := range successCases {
|
for k, v := range successCases {
|
||||||
|
@ -514,6 +514,9 @@ message LinuxContainerSecurityContext {
|
|||||||
// * localhost/<full-path-to-profile>: the profile installed on the node.
|
// * localhost/<full-path-to-profile>: the profile installed on the node.
|
||||||
// <full-path-to-profile> is the full path of the profile.
|
// <full-path-to-profile> is the full path of the profile.
|
||||||
string seccomp_profile_path = 10;
|
string seccomp_profile_path = 10;
|
||||||
|
// no_new_privs defines if the flag for no_new_privs should be set on the
|
||||||
|
// container.
|
||||||
|
bool no_new_privs = 11;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LinuxContainerConfig contains platform-specific configuration for
|
// LinuxContainerConfig contains platform-specific configuration for
|
||||||
|
@ -113,6 +113,10 @@ func modifyHostConfig(sc *runtimeapi.LinuxContainerSecurityContext, hostConfig *
|
|||||||
}
|
}
|
||||||
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, apparmorSecurityOpts...)
|
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, apparmorSecurityOpts...)
|
||||||
|
|
||||||
|
if sc.NoNewPrivs {
|
||||||
|
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, "no-new-privileges")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,6 +66,8 @@ func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *v1.Po
|
|||||||
synthesized.SupplementalGroups = append(synthesized.SupplementalGroups, groups...)
|
synthesized.SupplementalGroups = append(synthesized.SupplementalGroups, groups...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
synthesized.NoNewPrivs = securitycontext.AddNoNewPrivileges(effectiveSc)
|
||||||
|
|
||||||
return synthesized
|
return synthesized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,6 +439,14 @@ func setIsolators(app *appctypes.App, c *v1.Container, ctx *v1.SecurityContext)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ok := securitycontext.AddNoNewPrivileges(ctx); ok {
|
||||||
|
isolator, err := newNoNewPrivilegesIsolator(true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
isolators = append(isolators, *isolator)
|
||||||
|
}
|
||||||
|
|
||||||
mergeIsolators(app, isolators)
|
mergeIsolators(app, isolators)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -2621,3 +2629,16 @@ func convertKubePortMappings(portMappings []kubecontainer.PortMapping) ([]appcty
|
|||||||
|
|
||||||
return containerPorts, hostPorts
|
return containerPorts, hostPorts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newNoNewPrivilegesIsolator(v bool) (*appctypes.Isolator, error) {
|
||||||
|
b := fmt.Sprintf(`{"name": "%s", "value": %t}`, appctypes.LinuxNoNewPrivilegesName, v)
|
||||||
|
|
||||||
|
i := &appctypes.Isolator{
|
||||||
|
Name: appctypes.LinuxNoNewPrivilegesName,
|
||||||
|
}
|
||||||
|
if err := i.UnmarshalJSON([]byte(b)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -48,7 +49,6 @@ import (
|
|||||||
"k8s.io/kubernetes/pkg/kubelet/types"
|
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||||
"k8s.io/utils/exec"
|
"k8s.io/utils/exec"
|
||||||
fakeexec "k8s.io/utils/exec/testing"
|
fakeexec "k8s.io/utils/exec/testing"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func mustMarshalPodManifest(man *appcschema.PodManifest) []byte {
|
func mustMarshalPodManifest(man *appcschema.PodManifest) []byte {
|
||||||
@ -938,6 +938,7 @@ func baseImageManifest(t *testing.T) *appcschema.ImageManifest {
|
|||||||
func baseAppWithRootUserGroup(t *testing.T) *appctypes.App {
|
func baseAppWithRootUserGroup(t *testing.T) *appctypes.App {
|
||||||
app := baseApp(t)
|
app := baseApp(t)
|
||||||
app.User, app.Group = "0", "0"
|
app.User, app.Group = "0", "0"
|
||||||
|
app.Isolators = append(app.Isolators)
|
||||||
return app
|
return app
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,6 +183,17 @@ func (s *simpleProvider) CreateContainerSecurityContext(pod *api.Pod, container
|
|||||||
sc.ReadOnlyRootFilesystem = &readOnlyRootFS
|
sc.ReadOnlyRootFilesystem = &readOnlyRootFS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the PSP sets DefaultAllowPrivilegeEscalation and the container security context
|
||||||
|
// allowPrivilegeEscalation is not set, then default to that set by the PSP.
|
||||||
|
if s.psp.Spec.DefaultAllowPrivilegeEscalation != nil && sc.AllowPrivilegeEscalation == nil {
|
||||||
|
sc.AllowPrivilegeEscalation = s.psp.Spec.DefaultAllowPrivilegeEscalation
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the PSP sets psp.AllowPrivilegeEscalation to false set that as the default
|
||||||
|
if !s.psp.Spec.AllowPrivilegeEscalation && sc.AllowPrivilegeEscalation == nil {
|
||||||
|
sc.AllowPrivilegeEscalation = &s.psp.Spec.AllowPrivilegeEscalation
|
||||||
|
}
|
||||||
|
|
||||||
return sc, annotations, nil
|
return sc, annotations, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -301,6 +312,15 @@ func (s *simpleProvider) ValidateContainerSecurityContext(pod *api.Pod, containe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !s.psp.Spec.AllowPrivilegeEscalation && sc.AllowPrivilegeEscalation == nil {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("allowPrivilegeEscalation"), sc.AllowPrivilegeEscalation, "Allowing privilege escalation for containers is not allowed"))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if !s.psp.Spec.AllowPrivilegeEscalation && sc.AllowPrivilegeEscalation != nil && *sc.AllowPrivilegeEscalation {
|
||||||
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("allowPrivilegeEscalation"), *sc.AllowPrivilegeEscalation, "Allowing privilege escalation for containers is not allowed"))
|
||||||
|
}
|
||||||
|
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -920,6 +920,7 @@ func defaultPSP() *extensions.PodSecurityPolicy {
|
|||||||
SupplementalGroups: extensions.SupplementalGroupsStrategyOptions{
|
SupplementalGroups: extensions.SupplementalGroupsStrategyOptions{
|
||||||
Rule: extensions.SupplementalGroupsStrategyRunAsAny,
|
Rule: extensions.SupplementalGroupsStrategyRunAsAny,
|
||||||
},
|
},
|
||||||
|
AllowPrivilegeEscalation: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1033,3 +1034,111 @@ func TestValidateAllowedVolumes(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestValidateAllowPrivilegeEscalation will test that when the podSecurityPolicy
|
||||||
|
// AllowPrivilegeEscalation is false we cannot set a container's securityContext
|
||||||
|
// to allowPrivilegeEscalation, but when it is true we can.
|
||||||
|
func TestValidateAllowPrivilegeEscalation(t *testing.T) {
|
||||||
|
pod := defaultPod()
|
||||||
|
pe := true
|
||||||
|
pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = &pe
|
||||||
|
|
||||||
|
// create a PSP that does not allow privilege escalation
|
||||||
|
psp := defaultPSP()
|
||||||
|
psp.Spec.AllowPrivilegeEscalation = false
|
||||||
|
|
||||||
|
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error creating provider: %v", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// expect a denial for this PSP and test the error message to ensure it's related to allowPrivilegeEscalation
|
||||||
|
errs := provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
||||||
|
if len(errs) != 1 {
|
||||||
|
t.Errorf("expected exactly 1 error but got %v", errs)
|
||||||
|
} else {
|
||||||
|
if !strings.Contains(errs.ToAggregate().Error(), "Allowing privilege escalation for containers is not allowed") {
|
||||||
|
t.Errorf("did not find the expected error, received: %v", errs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now add allowPrivilegeEscalation to the podSecurityPolicy
|
||||||
|
psp.Spec.AllowPrivilegeEscalation = true
|
||||||
|
errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
||||||
|
if len(errs) != 0 {
|
||||||
|
t.Errorf("directly allowing privilege escalation expected no errors but got %v", errs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestValidateDefaultAllowPrivilegeEscalation will test that when the podSecurityPolicy
|
||||||
|
// DefaultAllowPrivilegeEscalation is false we cannot set a container's
|
||||||
|
// securityContext to allowPrivilegeEscalation but when it is true we can.
|
||||||
|
func TestValidateDefaultAllowPrivilegeEscalation(t *testing.T) {
|
||||||
|
pod := defaultPod()
|
||||||
|
pe := true
|
||||||
|
pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = &pe
|
||||||
|
|
||||||
|
// create a PSP that does not allow privilege escalation
|
||||||
|
psp := defaultPSP()
|
||||||
|
dpe := false
|
||||||
|
psp.Spec.DefaultAllowPrivilegeEscalation = &dpe
|
||||||
|
psp.Spec.AllowPrivilegeEscalation = false
|
||||||
|
|
||||||
|
provider, err := NewSimpleProvider(psp, "namespace", NewSimpleStrategyFactory())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("error creating provider: %v", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// expect a denial for this PSP and test the error message to ensure it's related to allowPrivilegeEscalation
|
||||||
|
errs := provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
||||||
|
if len(errs) != 1 {
|
||||||
|
t.Errorf("expected exactly 1 error but got %v", errs)
|
||||||
|
} else {
|
||||||
|
if !strings.Contains(errs.ToAggregate().Error(), "Allowing privilege escalation for containers is not allowed") {
|
||||||
|
t.Errorf("did not find the expected error, received: %v", errs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now add DefaultAllowPrivilegeEscalation to the podSecurityPolicy
|
||||||
|
dpe = true
|
||||||
|
psp.Spec.DefaultAllowPrivilegeEscalation = &dpe
|
||||||
|
psp.Spec.AllowPrivilegeEscalation = false
|
||||||
|
|
||||||
|
// expect a denial for this PSP because we did not allowPrivilege Escalation via the PodSecurityPolicy
|
||||||
|
// and test the error message to ensure it's related to allowPrivilegeEscalation
|
||||||
|
errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
||||||
|
if len(errs) != 1 {
|
||||||
|
t.Errorf("expected exactly 1 error but got %v", errs)
|
||||||
|
} else {
|
||||||
|
if !strings.Contains(errs.ToAggregate().Error(), "Allowing privilege escalation for containers is not allowed") {
|
||||||
|
t.Errorf("did not find the expected error, received: %v", errs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now set AllowPrivilegeEscalation
|
||||||
|
psp.Spec.AllowPrivilegeEscalation = true
|
||||||
|
errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
||||||
|
if len(errs) != 0 {
|
||||||
|
t.Errorf("directly allowing privilege escalation expected no errors but got %v", errs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now set the psp spec to false and reset AllowPrivilegeEscalation
|
||||||
|
psp.Spec.AllowPrivilegeEscalation = false
|
||||||
|
pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = nil
|
||||||
|
errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
||||||
|
if len(errs) != 1 {
|
||||||
|
t.Errorf("expected exactly 1 error but got %v", errs)
|
||||||
|
} else {
|
||||||
|
if !strings.Contains(errs.ToAggregate().Error(), "Allowing privilege escalation for containers is not allowed") {
|
||||||
|
t.Errorf("did not find the expected error, received: %v", errs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now unset both AllowPrivilegeEscalation
|
||||||
|
psp.Spec.AllowPrivilegeEscalation = true
|
||||||
|
pod.Spec.Containers[0].SecurityContext.AllowPrivilegeEscalation = nil
|
||||||
|
errs = provider.ValidateContainerSecurityContext(pod, &pod.Spec.Containers[0], field.NewPath(""))
|
||||||
|
if len(errs) != 0 {
|
||||||
|
t.Errorf("resetting allowing privilege escalation expected no errors but got %v", errs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -133,6 +133,11 @@ func DetermineEffectiveSecurityContext(pod *v1.Pod, container *v1.Container) *v1
|
|||||||
*effectiveSc.ReadOnlyRootFilesystem = *containerSc.ReadOnlyRootFilesystem
|
*effectiveSc.ReadOnlyRootFilesystem = *containerSc.ReadOnlyRootFilesystem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if containerSc.AllowPrivilegeEscalation != nil {
|
||||||
|
effectiveSc.AllowPrivilegeEscalation = new(bool)
|
||||||
|
*effectiveSc.AllowPrivilegeEscalation = *containerSc.AllowPrivilegeEscalation
|
||||||
|
}
|
||||||
|
|
||||||
return effectiveSc
|
return effectiveSc
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,6 +210,11 @@ func InternalDetermineEffectiveSecurityContext(pod *api.Pod, container *api.Cont
|
|||||||
*effectiveSc.ReadOnlyRootFilesystem = *containerSc.ReadOnlyRootFilesystem
|
*effectiveSc.ReadOnlyRootFilesystem = *containerSc.ReadOnlyRootFilesystem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if containerSc.AllowPrivilegeEscalation != nil {
|
||||||
|
effectiveSc.AllowPrivilegeEscalation = new(bool)
|
||||||
|
*effectiveSc.AllowPrivilegeEscalation = *containerSc.AllowPrivilegeEscalation
|
||||||
|
}
|
||||||
|
|
||||||
return effectiveSc
|
return effectiveSc
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,3 +241,38 @@ func internalSecurityContextFromPodSecurityContext(pod *api.Pod) *api.SecurityCo
|
|||||||
|
|
||||||
return synthesized
|
return synthesized
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddNoNewPrivileges returns if we should add the no_new_privs option. This will return true if:
|
||||||
|
// 1) the container is not privileged
|
||||||
|
// 2) CAP_SYS_ADMIN is not being added
|
||||||
|
// 3) if podSecurityPolicy.DefaultAllowPrivilegeEscalation is:
|
||||||
|
// - nil, then return false
|
||||||
|
// - true, then return false
|
||||||
|
// - false, then return true
|
||||||
|
func AddNoNewPrivileges(sc *v1.SecurityContext) bool {
|
||||||
|
if sc == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle the case where the container is privileged
|
||||||
|
if sc.Privileged != nil && *sc.Privileged {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle the case where we are adding CAP_SYS_ADMIN
|
||||||
|
if sc.Capabilities != nil {
|
||||||
|
for _, cap := range sc.Capabilities.Add {
|
||||||
|
if string(cap) == "CAP_SYS_ADMIN" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle the case where the user did not set the default and did not explicitly set allowPrivilegeEscalation
|
||||||
|
if sc.AllowPrivilegeEscalation == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle the case where defaultAllowPrivilegeEscalation is false or the user explicitly set allowPrivilegeEscalation to true/false
|
||||||
|
return !*sc.AllowPrivilegeEscalation
|
||||||
|
}
|
||||||
|
@ -176,3 +176,100 @@ func TestHasRootRunAsUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAddNoNewPrivileges(t *testing.T) {
|
||||||
|
var nonRoot int64 = 1000
|
||||||
|
var root int64 = 0
|
||||||
|
pfalse := false
|
||||||
|
ptrue := true
|
||||||
|
|
||||||
|
tests := map[string]struct {
|
||||||
|
sc v1.SecurityContext
|
||||||
|
expect bool
|
||||||
|
}{
|
||||||
|
"allowPrivilegeEscalation nil security context nil": {},
|
||||||
|
"allowPrivilegeEscalation nil capAddSysadmin": {
|
||||||
|
sc: v1.SecurityContext{
|
||||||
|
Capabilities: &v1.Capabilities{
|
||||||
|
Add: []v1.Capability{"CAP_SYS_ADMIN"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"allowPrivilegeEscalation nil privileged": {
|
||||||
|
sc: v1.SecurityContext{
|
||||||
|
Privileged: &ptrue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"allowPrivilegeEscalation nil nonRoot": {
|
||||||
|
sc: v1.SecurityContext{
|
||||||
|
RunAsUser: &nonRoot,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"allowPrivilegeEscalation nil root": {
|
||||||
|
sc: v1.SecurityContext{
|
||||||
|
RunAsUser: &root,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"allowPrivilegeEscalation false capAddSysadmin": {
|
||||||
|
sc: v1.SecurityContext{
|
||||||
|
Capabilities: &v1.Capabilities{
|
||||||
|
Add: []v1.Capability{"CAP_SYS_ADMIN"},
|
||||||
|
},
|
||||||
|
AllowPrivilegeEscalation: &pfalse,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"allowPrivilegeEscalation false privileged": {
|
||||||
|
sc: v1.SecurityContext{
|
||||||
|
Privileged: &ptrue,
|
||||||
|
AllowPrivilegeEscalation: &pfalse,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"allowPrivilegeEscalation false nonRoot": {
|
||||||
|
sc: v1.SecurityContext{
|
||||||
|
RunAsUser: &nonRoot,
|
||||||
|
AllowPrivilegeEscalation: &pfalse,
|
||||||
|
},
|
||||||
|
expect: true,
|
||||||
|
},
|
||||||
|
"allowPrivilegeEscalation false root": {
|
||||||
|
sc: v1.SecurityContext{
|
||||||
|
RunAsUser: &root,
|
||||||
|
AllowPrivilegeEscalation: &pfalse,
|
||||||
|
},
|
||||||
|
expect: true,
|
||||||
|
},
|
||||||
|
"allowPrivilegeEscalation true capAddSysadmin": {
|
||||||
|
sc: v1.SecurityContext{
|
||||||
|
Capabilities: &v1.Capabilities{
|
||||||
|
Add: []v1.Capability{"CAP_SYS_ADMIN"},
|
||||||
|
},
|
||||||
|
AllowPrivilegeEscalation: &ptrue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"allowPrivilegeEscalation true privileged": {
|
||||||
|
sc: v1.SecurityContext{
|
||||||
|
Privileged: &ptrue,
|
||||||
|
AllowPrivilegeEscalation: &ptrue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"allowPrivilegeEscalation true nonRoot": {
|
||||||
|
sc: v1.SecurityContext{
|
||||||
|
RunAsUser: &nonRoot,
|
||||||
|
AllowPrivilegeEscalation: &ptrue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"allowPrivilegeEscalation true root": {
|
||||||
|
sc: v1.SecurityContext{
|
||||||
|
RunAsUser: &root,
|
||||||
|
AllowPrivilegeEscalation: &ptrue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range tests {
|
||||||
|
actual := AddNoNewPrivileges(&v.sc)
|
||||||
|
if actual != v.expect {
|
||||||
|
t.Errorf("%s failed, expected %t but received %t", k, v.expect, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user