Skip to content

Commit e326fa1

Browse files
committed
Ignore updates related to Scheduling Gates
to allow the installation of external operators that manage pod scheduling. Scheduling Gates don't affect pod privileges, so there's no need to block them through SCC admission. Signed-off-by: bmordeha <[email protected]>
1 parent 8d341e9 commit e326fa1

File tree

2 files changed

+39
-13
lines changed

2 files changed

+39
-13
lines changed

pkg/securitycontextconstraints/sccadmission/admission.go

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func NewConstraint() *constraint {
8181
// any change that claims the pod is no longer privileged will be removed. That should hold until
8282
// we get a true old/new set of objects in.
8383
func (c *constraint) Admit(ctx context.Context, a admission.Attributes, _ admission.ObjectInterfaces) error {
84-
if ignore, err := shouldIgnore(a); err != nil {
84+
if ignore, err := shouldSkipSCCEvaluation(a); err != nil {
8585
return err
8686
} else if ignore {
8787
return nil
@@ -131,7 +131,7 @@ func (c *constraint) Admit(ctx context.Context, a admission.Attributes, _ admiss
131131
}
132132

133133
func (c *constraint) Validate(ctx context.Context, a admission.Attributes, _ admission.ObjectInterfaces) error {
134-
if ignore, err := shouldIgnore(a); err != nil {
134+
if ignore, err := shouldSkipSCCEvaluation(a); err != nil {
135135
return err
136136
} else if ignore {
137137
return nil
@@ -420,7 +420,14 @@ var ignoredAnnotations = sets.NewString(
420420
"k8s.ovn.org/pod-networks",
421421
)
422422

423-
func shouldIgnore(a admission.Attributes) (bool, error) {
423+
// shouldSkipSCCEvaluation skips evaluation for:
424+
// - non-Pod resources,
425+
// - specific subresources that don't affect Pod security context (e.g., exec, attach, log),
426+
// - Windows Pods (SCC is not applied),
427+
// - update operations that only change fields like SchedulingGates or non-critical metadata.
428+
// If the request is malformed (e.g., object can't be cast to a Pod), it fails closed to avoid
429+
// bypassing security enforcement unintentionally.
430+
func shouldSkipSCCEvaluation(a admission.Attributes) (bool, error) {
424431
if a.GetResource().GroupResource() != coreapi.Resource("pods") {
425432
return true, nil
426433
}
@@ -448,24 +455,27 @@ func shouldIgnore(a admission.Attributes) (bool, error) {
448455
return false, admission.NewForbidden(a, fmt.Errorf("object was marked as kind pod but was unable to be converted: %v", a.GetOldObject()))
449456
}
450457

451-
// never ignore any spec changes
452-
if !kapihelper.Semantic.DeepEqual(pod.Spec, oldPod.Spec) {
458+
// Create deep copies to avoid mutating the original objects
459+
podWithoutSchedulingGates := pod.DeepCopy()
460+
// Skip SchedulingGates when comparing specs
461+
podWithoutSchedulingGates.Spec.SchedulingGates = oldPod.Spec.SchedulingGates
462+
if !kapihelper.Semantic.DeepEqual(podWithoutSchedulingGates.Spec, oldPod.Spec) {
453463
return false, nil
454464
}
455465

456466
// see if we are only doing meta changes that should be ignored during admission
457467
// for example, the OVN controller adds informative networking annotations that shouldn't cause the pod to go through admission again
458-
if shouldIgnoreMetaChanges(pod, oldPod) {
468+
if shouldIgnoreMetaChanges(podWithoutSchedulingGates, oldPod) {
459469
return true, nil
460470
}
461471
}
462472

463473
return false, nil
464474
}
465475

466-
func shouldIgnoreMetaChanges(newPod, oldPod *coreapi.Pod) bool {
476+
func shouldIgnoreMetaChanges(newPodCopy, oldPod *coreapi.Pod) bool {
467477
// check if we're adding or changing only annotations from the ignore list
468-
for key, newVal := range newPod.ObjectMeta.Annotations {
478+
for key, newVal := range newPodCopy.ObjectMeta.Annotations {
469479
if oldVal, ok := oldPod.ObjectMeta.Annotations[key]; ok && newVal == oldVal {
470480
continue
471481
}
@@ -477,7 +487,7 @@ func shouldIgnoreMetaChanges(newPod, oldPod *coreapi.Pod) bool {
477487

478488
// check if we're removing only annotations from the ignore list
479489
for key := range oldPod.ObjectMeta.Annotations {
480-
if _, ok := newPod.ObjectMeta.Annotations[key]; ok {
490+
if _, ok := newPodCopy.ObjectMeta.Annotations[key]; ok {
481491
continue
482492
}
483493

@@ -486,12 +496,11 @@ func shouldIgnoreMetaChanges(newPod, oldPod *coreapi.Pod) bool {
486496
}
487497
}
488498

489-
newPodCopy := newPod.DeepCopyObject()
490-
newPodCopyMeta, err := meta.Accessor(newPodCopy)
499+
newPodCopyWithoutSchedulingGatesCopyMeta, err := meta.Accessor(newPodCopy)
491500
if err != nil {
492501
return false
493502
}
494-
newPodCopyMeta.SetAnnotations(oldPod.ObjectMeta.Annotations)
503+
newPodCopyWithoutSchedulingGatesCopyMeta.SetAnnotations(oldPod.ObjectMeta.Annotations)
495504

496505
// see if we are only updating the ownerRef. Garbage collection does this
497506
// and we should allow it in general, since you had the power to update and the power to delete.

pkg/securitycontextconstraints/sccadmission/admission_test.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,15 @@ func TestShouldIgnore(t *testing.T) {
268268
shouldIgnore: true,
269269
admissionAttributes: withStatusUpdate(goodPod()),
270270
},
271+
{
272+
description: "schedulingGates updates should be ignored",
273+
shouldIgnore: true,
274+
admissionAttributes: withUpdate(schedulingGatePod(), "",
275+
func(p *coreapi.Pod) *coreapi.Pod {
276+
p.Spec.SchedulingGates = []coreapi.PodSchedulingGate{}
277+
return p
278+
}),
279+
},
271280
{
272281
description: "don't ignore normal updates",
273282
shouldIgnore: false,
@@ -305,7 +314,7 @@ func TestShouldIgnore(t *testing.T) {
305314

306315
for _, test := range tests {
307316
t.Run(test.description, func(t *testing.T) {
308-
ignored, err := shouldIgnore(test.admissionAttributes)
317+
ignored, err := shouldSkipSCCEvaluation(test.admissionAttributes)
309318
if err != nil {
310319
t.Errorf("expected the test to not error but it errored with %v", err)
311320
}
@@ -2008,6 +2017,14 @@ func goodPod() *coreapi.Pod {
20082017
}
20092018
}
20102019

2020+
// schedulingGatePod is empty pod with scheduling gate. schedulingGates modifications
2021+
// should be safely ignored.
2022+
func schedulingGatePod() *coreapi.Pod {
2023+
p := goodPod()
2024+
p.Spec.SchedulingGates = []coreapi.PodSchedulingGate{{Name: "testGate"}}
2025+
return p
2026+
}
2027+
20112028
// windowsPod returns windows pod without any SCCs which are specific to Linux. The admission of Windows pod
20122029
// should be safely ignored.
20132030
func windowsPod() *coreapi.Pod {

0 commit comments

Comments
 (0)