Skip to content

Commit e939d2a

Browse files
committed
Add support for checking new MinimallySufficientPodSecurityStandard
annotation when determining whether a namespace would be violating - falling back to existing check for PSA Audit and Warn labels if it is not set. Add support for tracking namespaces where Pod Security Admission evaluations are inconclusive with a new condition type. Add relevant tests.
1 parent 93fe617 commit e939d2a

File tree

6 files changed

+450
-72
lines changed

6 files changed

+450
-72
lines changed

pkg/operator/podsecurityreadinesscontroller/conditions.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,12 @@ const (
1818
PodSecurityOpenshiftType = "PodSecurityOpenshiftEvaluationConditionsDetected"
1919
PodSecurityRunLevelZeroType = "PodSecurityRunLevelZeroEvaluationConditionsDetected"
2020
PodSecurityDisabledSyncerType = "PodSecurityDisabledSyncerEvaluationConditionsDetected"
21+
PodSecurityInconclusiveType = "PodSecurityInconclusiveEvaluationConditionsDetected"
2122

2223
labelSyncControlLabel = "security.openshift.io/scc.podSecurityLabelSync"
24+
25+
violationReason = "PSViolationsDetected"
26+
inconclusiveReason = "PSViolationDecisionInconclusive"
2327
)
2428

2529
var (
@@ -36,6 +40,7 @@ type podSecurityOperatorConditions struct {
3640
violatingRunLevelZeroNamespaces []string
3741
violatingCustomerNamespaces []string
3842
violatingDisabledSyncerNamespaces []string
43+
inconclusiveNamespaces []string
3944
}
4045

4146
func (c *podSecurityOperatorConditions) addViolation(ns *corev1.Namespace) {
@@ -59,16 +64,31 @@ func (c *podSecurityOperatorConditions) addViolation(ns *corev1.Namespace) {
5964
c.violatingCustomerNamespaces = append(c.violatingCustomerNamespaces, ns.Name)
6065
}
6166

62-
func makeCondition(conditionType string, namespaces []string) operatorv1.OperatorCondition {
67+
func (c *podSecurityOperatorConditions) addInconclusive(ns *corev1.Namespace) {
68+
c.inconclusiveNamespaces = append(c.inconclusiveNamespaces, ns.Name)
69+
}
70+
71+
func makeCondition(conditionType, conditionReason string, namespaces []string) operatorv1.OperatorCondition {
72+
var messageFormatter string
73+
74+
switch conditionReason {
75+
case violationReason:
76+
messageFormatter = "Violations detected in namespaces: %v"
77+
case inconclusiveReason:
78+
messageFormatter = "Could not evaluate violations for namespaces: %v"
79+
default:
80+
messageFormatter = "Unexpected condition for namespace: %v"
81+
}
82+
6383
if len(namespaces) > 0 {
6484
sort.Strings(namespaces)
6585
return operatorv1.OperatorCondition{
6686
Type: conditionType,
6787
Status: operatorv1.ConditionTrue,
6888
LastTransitionTime: metav1.Now(),
69-
Reason: "PSViolationsDetected",
89+
Reason: conditionReason,
7090
Message: fmt.Sprintf(
71-
"Violations detected in namespaces: %v",
91+
messageFormatter,
7292
namespaces,
7393
),
7494
}
@@ -84,9 +104,10 @@ func makeCondition(conditionType string, namespaces []string) operatorv1.Operato
84104

85105
func (c *podSecurityOperatorConditions) toConditionFuncs() []v1helpers.UpdateStatusFunc {
86106
return []v1helpers.UpdateStatusFunc{
87-
v1helpers.UpdateConditionFn(makeCondition(PodSecurityCustomerType, c.violatingCustomerNamespaces)),
88-
v1helpers.UpdateConditionFn(makeCondition(PodSecurityOpenshiftType, c.violatingOpenShiftNamespaces)),
89-
v1helpers.UpdateConditionFn(makeCondition(PodSecurityRunLevelZeroType, c.violatingRunLevelZeroNamespaces)),
90-
v1helpers.UpdateConditionFn(makeCondition(PodSecurityDisabledSyncerType, c.violatingDisabledSyncerNamespaces)),
107+
v1helpers.UpdateConditionFn(makeCondition(PodSecurityCustomerType, violationReason, c.violatingCustomerNamespaces)),
108+
v1helpers.UpdateConditionFn(makeCondition(PodSecurityOpenshiftType, violationReason, c.violatingOpenShiftNamespaces)),
109+
v1helpers.UpdateConditionFn(makeCondition(PodSecurityRunLevelZeroType, violationReason, c.violatingRunLevelZeroNamespaces)),
110+
v1helpers.UpdateConditionFn(makeCondition(PodSecurityDisabledSyncerType, violationReason, c.violatingDisabledSyncerNamespaces)),
111+
v1helpers.UpdateConditionFn(makeCondition(PodSecurityInconclusiveType, inconclusiveReason, c.inconclusiveNamespaces)),
91112
}
92113
}

pkg/operator/podsecurityreadinesscontroller/conditions_test.go

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import (
99
)
1010

1111
func TestCondition(t *testing.T) {
12-
t.Run("with namespaces", func(t *testing.T) {
12+
t.Run("with violating namespaces", func(t *testing.T) {
1313
namespaces := []string{"namespace1", "namespace2"}
1414
expectedCondition := operatorv1.OperatorCondition{
1515
Type: PodSecurityCustomerType,
@@ -18,7 +18,35 @@ func TestCondition(t *testing.T) {
1818
Message: "Violations detected in namespaces: [namespace1 namespace2]",
1919
}
2020

21-
condition := makeCondition(PodSecurityCustomerType, namespaces)
21+
condition := makeCondition(PodSecurityCustomerType, violationReason, namespaces)
22+
23+
if condition.Type != expectedCondition.Type {
24+
t.Errorf("expected condition type %s, got %s", expectedCondition.Type, condition.Type)
25+
}
26+
27+
if condition.Status != expectedCondition.Status {
28+
t.Errorf("expected condition status %s, got %s", expectedCondition.Status, condition.Status)
29+
}
30+
31+
if condition.Reason != expectedCondition.Reason {
32+
t.Errorf("expected condition reason %s, got %s", expectedCondition.Reason, condition.Reason)
33+
}
34+
35+
if condition.Message != expectedCondition.Message {
36+
t.Errorf("expected condition message %s, got %s", expectedCondition.Message, condition.Message)
37+
}
38+
})
39+
40+
t.Run("with inconclusive namespaces", func(t *testing.T) {
41+
namespaces := []string{"namespace1", "namespace2"}
42+
expectedCondition := operatorv1.OperatorCondition{
43+
Type: PodSecurityCustomerType,
44+
Status: operatorv1.ConditionTrue,
45+
Reason: "PSViolationDecisionInconclusive",
46+
Message: "Could not evaluate violations for namespaces: [namespace1 namespace2]",
47+
}
48+
49+
condition := makeCondition(PodSecurityCustomerType, inconclusiveReason, namespaces)
2250

2351
if condition.Type != expectedCondition.Type {
2452
t.Errorf("expected condition type %s, got %s", expectedCondition.Type, condition.Type)
@@ -45,7 +73,7 @@ func TestCondition(t *testing.T) {
4573
Reason: "ExpectedReason",
4674
}
4775

48-
condition := makeCondition(PodSecurityCustomerType, namespaces)
76+
condition := makeCondition(PodSecurityCustomerType, violationReason, namespaces)
4977

5078
if condition.Type != expectedCondition.Type {
5179
t.Errorf("expected condition type %s, got %s", expectedCondition.Type, condition.Type)
@@ -68,28 +96,32 @@ func TestCondition(t *testing.T) {
6896

6997
func TestOperatorStatus(t *testing.T) {
7098
for _, tt := range []struct {
71-
name string
72-
namespace []*corev1.Namespace
73-
expected map[string]operatorv1.ConditionStatus
99+
name string
100+
namespace []*corev1.Namespace
101+
expected map[string]operatorv1.ConditionStatus
102+
addViolation, addInconclusive bool
74103
}{
75104
{
76-
name: "with default namespace",
105+
name: "with violating default namespace",
77106
namespace: []*corev1.Namespace{
78107
{
79108
ObjectMeta: metav1.ObjectMeta{
80109
Name: "syncer-by-default",
81110
},
82111
},
83112
},
113+
addViolation: true,
114+
addInconclusive: false,
84115
expected: map[string]operatorv1.ConditionStatus{
85116
"PodSecurityCustomerEvaluationConditionsDetected": operatorv1.ConditionTrue,
86117
"PodSecurityOpenshiftEvaluationConditionsDetected": operatorv1.ConditionFalse,
87118
"PodSecurityRunLevelZeroEvaluationConditionsDetected": operatorv1.ConditionFalse,
88119
"PodSecurityDisabledSyncerEvaluationConditionsDetected": operatorv1.ConditionFalse,
120+
"PodSecurityInconclusiveEvaluationConditionsDetected": operatorv1.ConditionFalse,
89121
},
90122
},
91123
{
92-
name: "with customer disabled syncer",
124+
name: "with violating customer disabled syncer",
93125
namespace: []*corev1.Namespace{
94126
{
95127
ObjectMeta: metav1.ObjectMeta{
@@ -100,15 +132,17 @@ func TestOperatorStatus(t *testing.T) {
100132
},
101133
},
102134
},
135+
addViolation: true,
103136
expected: map[string]operatorv1.ConditionStatus{
104137
"PodSecurityCustomerEvaluationConditionsDetected": operatorv1.ConditionFalse,
105138
"PodSecurityOpenshiftEvaluationConditionsDetected": operatorv1.ConditionFalse,
106139
"PodSecurityRunLevelZeroEvaluationConditionsDetected": operatorv1.ConditionFalse,
107140
"PodSecurityDisabledSyncerEvaluationConditionsDetected": operatorv1.ConditionTrue,
141+
"PodSecurityInconclusiveEvaluationConditionsDetected": operatorv1.ConditionFalse,
108142
},
109143
},
110144
{
111-
name: "with customer re-enabled syncer",
145+
name: "with violating customer re-enabled syncer",
112146
namespace: []*corev1.Namespace{
113147
{
114148
ObjectMeta: metav1.ObjectMeta{
@@ -119,47 +153,53 @@ func TestOperatorStatus(t *testing.T) {
119153
},
120154
},
121155
},
156+
addViolation: true,
122157
expected: map[string]operatorv1.ConditionStatus{
123158
"PodSecurityCustomerEvaluationConditionsDetected": operatorv1.ConditionTrue,
124159
"PodSecurityOpenshiftEvaluationConditionsDetected": operatorv1.ConditionFalse,
125160
"PodSecurityRunLevelZeroEvaluationConditionsDetected": operatorv1.ConditionFalse,
126161
"PodSecurityDisabledSyncerEvaluationConditionsDetected": operatorv1.ConditionFalse,
162+
"PodSecurityInconclusiveEvaluationConditionsDetected": operatorv1.ConditionFalse,
127163
},
128164
},
129165
{
130-
name: "with openshift namespace",
166+
name: "with violating openshift namespace",
131167
namespace: []*corev1.Namespace{
132168
{
133169
ObjectMeta: metav1.ObjectMeta{
134170
Name: "openshift-fail",
135171
},
136172
},
137173
},
174+
addViolation: true,
138175
expected: map[string]operatorv1.ConditionStatus{
139176
"PodSecurityCustomerEvaluationConditionsDetected": operatorv1.ConditionFalse,
140177
"PodSecurityOpenshiftEvaluationConditionsDetected": operatorv1.ConditionTrue,
141178
"PodSecurityRunLevelZeroEvaluationConditionsDetected": operatorv1.ConditionFalse,
142179
"PodSecurityDisabledSyncerEvaluationConditionsDetected": operatorv1.ConditionFalse,
180+
"PodSecurityInconclusiveEvaluationConditionsDetected": operatorv1.ConditionFalse,
143181
},
144182
},
145183
{
146-
name: "with run-level 0 namespace",
184+
name: "with violating run-level 0 namespace",
147185
namespace: []*corev1.Namespace{
148186
{
149187
ObjectMeta: metav1.ObjectMeta{
150188
Name: "kube-system",
151189
},
152190
},
153191
},
192+
addViolation: true,
154193
expected: map[string]operatorv1.ConditionStatus{
155194
"PodSecurityCustomerEvaluationConditionsDetected": operatorv1.ConditionFalse,
156195
"PodSecurityOpenshiftEvaluationConditionsDetected": operatorv1.ConditionFalse,
157196
"PodSecurityRunLevelZeroEvaluationConditionsDetected": operatorv1.ConditionTrue,
158197
"PodSecurityDisabledSyncerEvaluationConditionsDetected": operatorv1.ConditionFalse,
198+
"PodSecurityInconclusiveEvaluationConditionsDetected": operatorv1.ConditionFalse,
159199
},
160200
},
161201
{
162-
name: "with other customer types in combination",
202+
name: "with other violating customer types in combination",
163203
namespace: []*corev1.Namespace{
164204
{
165205
ObjectMeta: metav1.ObjectMeta{
@@ -175,15 +215,17 @@ func TestOperatorStatus(t *testing.T) {
175215
},
176216
},
177217
},
218+
addViolation: true,
178219
expected: map[string]operatorv1.ConditionStatus{
179220
"PodSecurityCustomerEvaluationConditionsDetected": operatorv1.ConditionTrue,
180221
"PodSecurityOpenshiftEvaluationConditionsDetected": operatorv1.ConditionFalse,
181222
"PodSecurityRunLevelZeroEvaluationConditionsDetected": operatorv1.ConditionFalse,
182223
"PodSecurityDisabledSyncerEvaluationConditionsDetected": operatorv1.ConditionTrue,
224+
"PodSecurityInconclusiveEvaluationConditionsDetected": operatorv1.ConditionFalse,
183225
},
184226
},
185227
{
186-
name: "with other system types in combination",
228+
name: "with other violating system types in combination",
187229
namespace: []*corev1.Namespace{
188230
{
189231
ObjectMeta: metav1.ObjectMeta{
@@ -203,11 +245,32 @@ func TestOperatorStatus(t *testing.T) {
203245
},
204246
},
205247
},
248+
addViolation: true,
206249
expected: map[string]operatorv1.ConditionStatus{
207250
"PodSecurityCustomerEvaluationConditionsDetected": operatorv1.ConditionFalse,
208251
"PodSecurityOpenshiftEvaluationConditionsDetected": operatorv1.ConditionTrue,
209252
"PodSecurityRunLevelZeroEvaluationConditionsDetected": operatorv1.ConditionTrue,
210253
"PodSecurityDisabledSyncerEvaluationConditionsDetected": operatorv1.ConditionFalse,
254+
"PodSecurityInconclusiveEvaluationConditionsDetected": operatorv1.ConditionFalse,
255+
},
256+
},
257+
{
258+
name: "with inconclusive namespace",
259+
namespace: []*corev1.Namespace{
260+
{
261+
ObjectMeta: metav1.ObjectMeta{
262+
Name: "syncer-by-default",
263+
},
264+
},
265+
},
266+
addViolation: false,
267+
addInconclusive: true,
268+
expected: map[string]operatorv1.ConditionStatus{
269+
"PodSecurityCustomerEvaluationConditionsDetected": operatorv1.ConditionFalse,
270+
"PodSecurityOpenshiftEvaluationConditionsDetected": operatorv1.ConditionFalse,
271+
"PodSecurityRunLevelZeroEvaluationConditionsDetected": operatorv1.ConditionFalse,
272+
"PodSecurityDisabledSyncerEvaluationConditionsDetected": operatorv1.ConditionFalse,
273+
"PodSecurityInconclusiveEvaluationConditionsDetected": operatorv1.ConditionTrue,
211274
},
212275
},
213276
} {
@@ -216,7 +279,12 @@ func TestOperatorStatus(t *testing.T) {
216279
cond := podSecurityOperatorConditions{}
217280

218281
for _, ns := range tt.namespace {
219-
cond.addViolation(ns)
282+
if tt.addViolation {
283+
cond.addViolation(ns)
284+
}
285+
if tt.addInconclusive {
286+
cond.addInconclusive(ns)
287+
}
220288
}
221289

222290
status := &operatorv1.OperatorStatus{}

pkg/operator/podsecurityreadinesscontroller/podsecurityreadinesscontroller.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,7 @@ func (c *PodSecurityReadinessController) sync(ctx context.Context, syncCtx facto
8787
if err != nil {
8888
klog.V(2).ErrorS(err, "namespace:", ns.Name)
8989

90-
// We don't want to sync more often than the resync interval.
91-
return nil
92-
90+
conditions.addInconclusive(&ns)
9391
}
9492
}
9593

0 commit comments

Comments
 (0)