@@ -58,18 +58,34 @@ var (
58
58
apiVersion: {version}
59
59
kind: Policy
60
60
rules:
61
- - level: {level}
61
+ - level: RequestResponse
62
+ namespaces: ["no-webhook-namespace"]
63
+ resources:
64
+ - group: "" # core
65
+ resources: ["configmaps"]
66
+ - level: Metadata
67
+ namespaces: ["webhook-audit-metadata"]
68
+ resources:
69
+ - group: "" # core
70
+ resources: ["configmaps"]
71
+ - level: Request
72
+ namespaces: ["webhook-audit-request"]
73
+ resources:
74
+ - group: "" # core
75
+ resources: ["configmaps"]
76
+ - level: RequestResponse
77
+ namespaces: ["webhook-audit-response"]
62
78
resources:
63
79
- group: "" # core
64
80
resources: ["configmaps"]
65
81
66
82
`
67
- namespace = "default "
68
- watchTestTimeout int64 = 1
69
- watchOptions = metav1.ListOptions {TimeoutSeconds : & watchTestTimeout }
70
- patch , _ = json .Marshal (jsonpatch.Patch {})
71
- auditTestUser = "system:apiserver"
72
- versions = map [string ]schema.GroupVersion {
83
+ nonAdmissionWebhookNamespace = "no-webhook-namespace "
84
+ watchTestTimeout int64 = 1
85
+ watchOptions = metav1.ListOptions {TimeoutSeconds : & watchTestTimeout }
86
+ patch , _ = json .Marshal (jsonpatch.Patch {})
87
+ auditTestUser = "system:apiserver"
88
+ versions = map [string ]schema.GroupVersion {
73
89
"audit.k8s.io/v1" : auditv1 .SchemeGroupVersion ,
74
90
"audit.k8s.io/v1beta1" : auditv1beta1 .SchemeGroupVersion ,
75
91
}
@@ -78,96 +94,96 @@ rules:
78
94
{
79
95
Level : auditinternal .LevelRequestResponse ,
80
96
Stage : auditinternal .StageResponseComplete ,
81
- RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps" , namespace ),
97
+ RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps" , nonAdmissionWebhookNamespace ),
82
98
Verb : "create" ,
83
99
Code : 201 ,
84
100
User : auditTestUser ,
85
101
Resource : "configmaps" ,
86
- Namespace : namespace ,
102
+ Namespace : nonAdmissionWebhookNamespace ,
87
103
RequestObject : true ,
88
104
ResponseObject : true ,
89
105
AuthorizeDecision : "allow" ,
90
106
}, {
91
107
Level : auditinternal .LevelRequestResponse ,
92
108
Stage : auditinternal .StageResponseComplete ,
93
- RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps/audit-configmap" , namespace ),
109
+ RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps/audit-configmap" , nonAdmissionWebhookNamespace ),
94
110
Verb : "get" ,
95
111
Code : 200 ,
96
112
User : auditTestUser ,
97
113
Resource : "configmaps" ,
98
- Namespace : namespace ,
114
+ Namespace : nonAdmissionWebhookNamespace ,
99
115
RequestObject : false ,
100
116
ResponseObject : true ,
101
117
AuthorizeDecision : "allow" ,
102
118
}, {
103
119
Level : auditinternal .LevelRequestResponse ,
104
120
Stage : auditinternal .StageResponseComplete ,
105
- RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps" , namespace ),
121
+ RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps" , nonAdmissionWebhookNamespace ),
106
122
Verb : "list" ,
107
123
Code : 200 ,
108
124
User : auditTestUser ,
109
125
Resource : "configmaps" ,
110
- Namespace : namespace ,
126
+ Namespace : nonAdmissionWebhookNamespace ,
111
127
RequestObject : false ,
112
128
ResponseObject : true ,
113
129
AuthorizeDecision : "allow" ,
114
130
}, {
115
131
Level : auditinternal .LevelRequestResponse ,
116
132
Stage : auditinternal .StageResponseStarted ,
117
- RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps?timeout=%ds&timeoutSeconds=%d&watch=true" , namespace , watchTestTimeout , watchTestTimeout ),
133
+ RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps?timeout=%ds&timeoutSeconds=%d&watch=true" , nonAdmissionWebhookNamespace , watchTestTimeout , watchTestTimeout ),
118
134
Verb : "watch" ,
119
135
Code : 200 ,
120
136
User : auditTestUser ,
121
137
Resource : "configmaps" ,
122
- Namespace : namespace ,
138
+ Namespace : nonAdmissionWebhookNamespace ,
123
139
RequestObject : false ,
124
140
ResponseObject : false ,
125
141
AuthorizeDecision : "allow" ,
126
142
}, {
127
143
Level : auditinternal .LevelRequestResponse ,
128
144
Stage : auditinternal .StageResponseComplete ,
129
- RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps?timeout=%ds&timeoutSeconds=%d&watch=true" , namespace , watchTestTimeout , watchTestTimeout ),
145
+ RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps?timeout=%ds&timeoutSeconds=%d&watch=true" , nonAdmissionWebhookNamespace , watchTestTimeout , watchTestTimeout ),
130
146
Verb : "watch" ,
131
147
Code : 200 ,
132
148
User : auditTestUser ,
133
149
Resource : "configmaps" ,
134
- Namespace : namespace ,
150
+ Namespace : nonAdmissionWebhookNamespace ,
135
151
RequestObject : false ,
136
152
ResponseObject : false ,
137
153
AuthorizeDecision : "allow" ,
138
154
}, {
139
155
Level : auditinternal .LevelRequestResponse ,
140
156
Stage : auditinternal .StageResponseComplete ,
141
- RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps/audit-configmap" , namespace ),
157
+ RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps/audit-configmap" , nonAdmissionWebhookNamespace ),
142
158
Verb : "update" ,
143
159
Code : 200 ,
144
160
User : auditTestUser ,
145
161
Resource : "configmaps" ,
146
- Namespace : namespace ,
162
+ Namespace : nonAdmissionWebhookNamespace ,
147
163
RequestObject : true ,
148
164
ResponseObject : true ,
149
165
AuthorizeDecision : "allow" ,
150
166
}, {
151
167
Level : auditinternal .LevelRequestResponse ,
152
168
Stage : auditinternal .StageResponseComplete ,
153
- RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps/audit-configmap" , namespace ),
169
+ RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps/audit-configmap" , nonAdmissionWebhookNamespace ),
154
170
Verb : "patch" ,
155
171
Code : 200 ,
156
172
User : auditTestUser ,
157
173
Resource : "configmaps" ,
158
- Namespace : namespace ,
174
+ Namespace : nonAdmissionWebhookNamespace ,
159
175
RequestObject : true ,
160
176
ResponseObject : true ,
161
177
AuthorizeDecision : "allow" ,
162
178
}, {
163
179
Level : auditinternal .LevelRequestResponse ,
164
180
Stage : auditinternal .StageResponseComplete ,
165
- RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps/audit-configmap" , namespace ),
181
+ RequestURI : fmt .Sprintf ("/api/v1/namespaces/%s/configmaps/audit-configmap" , nonAdmissionWebhookNamespace ),
166
182
Verb : "delete" ,
167
183
Code : 200 ,
168
184
User : auditTestUser ,
169
185
Resource : "configmaps" ,
170
- Namespace : namespace ,
186
+ Namespace : nonAdmissionWebhookNamespace ,
171
187
RequestObject : true ,
172
188
ResponseObject : true ,
173
189
AuthorizeDecision : "allow" ,
@@ -177,53 +193,22 @@ rules:
177
193
178
194
// TestAudit ensures that both v1beta1 and v1 version audit api could work.
179
195
func TestAudit (t * testing.T ) {
180
- tcs := []struct {
181
- auditLevel auditinternal.Level
182
- enableMutatingWebhook bool
183
- }{
184
- {
185
- auditLevel : auditinternal .LevelRequestResponse ,
186
- enableMutatingWebhook : false ,
187
- },
188
- {
189
- auditLevel : auditinternal .LevelMetadata ,
190
- enableMutatingWebhook : true ,
191
- },
192
- {
193
- auditLevel : auditinternal .LevelRequest ,
194
- enableMutatingWebhook : true ,
195
- },
196
- {
197
- auditLevel : auditinternal .LevelRequestResponse ,
198
- enableMutatingWebhook : true ,
199
- },
200
- }
201
196
for version := range versions {
202
- for _ , tc := range tcs {
203
- t .Run (fmt .Sprintf ("%s.%s.%t" , version , tc .auditLevel , tc .enableMutatingWebhook ), func (t * testing.T ) {
204
- testAudit (t , version , tc .auditLevel , tc .enableMutatingWebhook )
205
- })
206
- }
197
+ runTestWithVersion (t , version )
207
198
}
208
199
}
209
200
210
- func testAudit (t * testing.T , version string , level auditinternal.Level , enableMutatingWebhook bool ) {
211
- var url string
212
- var err error
213
- closeFunc := func () {}
214
- if enableMutatingWebhook {
215
- webhookMux := http .NewServeMux ()
216
- webhookMux .Handle ("/mutation" , utils .AdmissionWebhookHandler (t , admitFunc ))
217
- url , closeFunc , err = utils .NewAdmissionWebhookServer (webhookMux )
218
- }
201
+ func runTestWithVersion (t * testing.T , version string ) {
202
+ webhookMux := http .NewServeMux ()
203
+ webhookMux .Handle ("/mutation" , utils .AdmissionWebhookHandler (t , admitFunc ))
204
+ url , closeFunc , err := utils .NewAdmissionWebhookServer (webhookMux )
219
205
defer closeFunc ()
220
206
if err != nil {
221
207
t .Fatalf ("%v" , err )
222
208
}
223
209
224
210
// prepare audit policy file
225
211
auditPolicy := strings .Replace (auditPolicyPattern , "{version}" , version , 1 )
226
- auditPolicy = strings .Replace (auditPolicy , "{level}" , string (level ), 1 )
227
212
policyFile , err := ioutil .TempFile ("" , "audit-policy.yaml" )
228
213
if err != nil {
229
214
t .Fatalf ("Failed to create audit policy file: %v" , err )
@@ -258,24 +243,59 @@ func testAudit(t *testing.T, version string, level auditinternal.Level, enableMu
258
243
t .Fatalf ("Unexpected error: %v" , err )
259
244
}
260
245
261
- if enableMutatingWebhook {
262
- if err := createV1beta1MutationWebhook (kubeclient , url + "/mutation" ); err != nil {
263
- t .Fatal (err )
264
- }
246
+ if err := createV1beta1MutationWebhook (kubeclient , url + "/mutation" ); err != nil {
247
+ t .Fatal (err )
265
248
}
266
249
250
+ tcs := []struct {
251
+ auditLevel auditinternal.Level
252
+ enableMutatingWebhook bool
253
+ namespace string
254
+ }{
255
+ {
256
+ auditLevel : auditinternal .LevelRequestResponse ,
257
+ enableMutatingWebhook : false ,
258
+ namespace : nonAdmissionWebhookNamespace ,
259
+ },
260
+ {
261
+ auditLevel : auditinternal .LevelMetadata ,
262
+ enableMutatingWebhook : true ,
263
+ namespace : "webhook-audit-metadata" ,
264
+ },
265
+ {
266
+ auditLevel : auditinternal .LevelRequest ,
267
+ enableMutatingWebhook : true ,
268
+ namespace : "webhook-audit-request" ,
269
+ },
270
+ {
271
+ auditLevel : auditinternal .LevelRequestResponse ,
272
+ enableMutatingWebhook : true ,
273
+ namespace : "webhook-audit-response" ,
274
+ },
275
+ }
276
+
277
+ for _ , tc := range tcs {
278
+ t .Run (fmt .Sprintf ("%s.%s.%t" , version , tc .auditLevel , tc .enableMutatingWebhook ), func (t * testing.T ) {
279
+ testAudit (t , version , tc .auditLevel , tc .enableMutatingWebhook , tc .namespace , kubeclient , logFile )
280
+ })
281
+ }
282
+ }
283
+
284
+ func testAudit (t * testing.T , version string , level auditinternal.Level , enableMutatingWebhook bool , namespace string , kubeclient kubernetes.Interface , logFile * os.File ) {
267
285
var lastMissingReport string
286
+ createNamespace (t , kubeclient , namespace )
287
+
268
288
if err := wait .Poll (500 * time .Millisecond , wait .ForeverTestTimeout , func () (bool , error ) {
269
289
// perform configmap operations
270
- configMapOperations (t , kubeclient )
290
+ configMapOperations (t , kubeclient , namespace )
271
291
272
292
// check for corresponding audit logs
273
293
stream , err := os .Open (logFile .Name ())
274
294
if err != nil {
275
295
return false , fmt .Errorf ("unexpected error: %v" , err )
276
296
}
277
297
defer stream .Close ()
278
- missingReport , err := utils .CheckAuditLines (stream , getExpectedEvents (level , enableMutatingWebhook ), versions [version ])
298
+ missingReport , err := utils .CheckAuditLines (stream , getExpectedEvents (level , enableMutatingWebhook , namespace ), versions [version ])
279
299
if err != nil {
280
300
return false , fmt .Errorf ("unexpected error: %v" , err )
281
301
}
@@ -289,7 +309,7 @@ func testAudit(t *testing.T, version string, level auditinternal.Level, enableMu
289
309
}
290
310
}
291
311
292
- func getExpectedEvents (level auditinternal.Level , enableMutatingWebhook bool ) []utils.AuditEvent {
312
+ func getExpectedEvents (level auditinternal.Level , enableMutatingWebhook bool , namespace string ) []utils.AuditEvent {
293
313
if ! enableMutatingWebhook {
294
314
return expectedEvents
295
315
}
@@ -350,16 +370,23 @@ func getExpectedEvents(level auditinternal.Level, enableMutatingWebhook bool) []
350
370
// configMapOperations is a set of known operations performed on the configmap type
351
371
// which correspond to the expected events.
352
372
// This is shared by the dynamic test
353
- func configMapOperations (t * testing.T , kubeclient kubernetes.Interface ) {
373
+ func configMapOperations (t * testing.T , kubeclient kubernetes.Interface , namespace string ) {
354
374
// create, get, watch, update, patch, list and delete configmap.
355
375
configMap := & apiv1.ConfigMap {
356
376
ObjectMeta : metav1.ObjectMeta {
357
- Name : "audit-configmap" ,
377
+ Name : "audit-configmap" ,
378
+ Namespace : namespace ,
358
379
},
359
380
Data : map [string ]string {
360
381
"map-key" : "map-value" ,
361
382
},
362
383
}
384
+ // add admission label to config maps that are to be sent to webhook
385
+ if namespace != nonAdmissionWebhookNamespace {
386
+ configMap .Labels = map [string ]string {
387
+ "admission" : "true" ,
388
+ }
389
+ }
363
390
364
391
_ , err := kubeclient .CoreV1 ().ConfigMaps (namespace ).Create (context .TODO (), configMap , metav1.CreateOptions {})
365
392
expectNoError (t , err , "failed to create audit-configmap" )
@@ -440,9 +467,20 @@ func createV1beta1MutationWebhook(client clientset.Interface, endpoint string) e
440
467
Operations : []admissionv1beta1.OperationType {admissionv1beta1 .Create , admissionv1beta1 .Update },
441
468
Rule : admissionv1beta1.Rule {APIGroups : []string {"*" }, APIVersions : []string {"*" }, Resources : []string {"*/*" }},
442
469
}},
470
+ ObjectSelector : & metav1.LabelSelector {MatchLabels : map [string ]string {"admission" : "true" }},
443
471
FailurePolicy : & fail ,
444
472
AdmissionReviewVersions : []string {"v1beta1" },
445
473
}},
446
474
}, metav1.CreateOptions {})
447
475
return err
448
476
}
477
+
478
+ func createNamespace (t * testing.T , kubeclient clientset.Interface , namespace string ) {
479
+ ns := & apiv1.Namespace {
480
+ ObjectMeta : metav1.ObjectMeta {
481
+ Name : namespace ,
482
+ },
483
+ }
484
+ _ , err := kubeclient .CoreV1 ().Namespaces ().Create (context .TODO (), ns , metav1.CreateOptions {})
485
+ expectNoError (t , err , fmt .Sprintf ("failed to create namespace ns %s" , namespace ))
486
+ }
0 commit comments