github.com/cilium/cilium@v1.16.2/pkg/k8s/apis/cilium.io/v2/validator/validator_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package validator 5 6 import ( 7 "encoding/json" 8 "testing" 9 10 "github.com/stretchr/testify/require" 11 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 12 "sigs.k8s.io/yaml" 13 ) 14 15 func Test_GH10643(t *testing.T) { 16 cnp := []byte(`apiVersion: cilium.io/v2 17 kind: CiliumNetworkPolicy 18 metadata: 19 name: exampleapp 20 namespace: examplens 21 spec: 22 egress: 23 - toFQDNs: 24 - matchPattern: prefix*.tier.location.example.com 25 toPorts: 26 - ports: 27 - port "8050" 28 - protocol TCP 29 - port "8051" 30 - protocol TCP 31 - port "8052" 32 - protocol TCP 33 - port "8053" 34 - protocol TCP 35 - port "8054" 36 - protocol TCP 37 - port "8055" 38 - protocol TCP 39 - port "8056" 40 - protocol TCP 41 - port "8057" 42 - protocol TCP 43 - port "8058" 44 - protocol TCP 45 - port "8059" 46 - protocol TCP 47 endpointSelector: 48 matchExpressions: 49 - key: app 50 operator: In 51 values: 52 - example-app-0-spark-worker 53 - example-app-0-spark-driver 54 - example-app-0-spark-worker-qe3 55 - example-app-0-spark-driver-qe3 56 - example-app-1-spark-worker 57 - example-app-1-spark-driver 58 - example-app-1-spark-worker-qe3 59 - example-app-1-spark-driver-qe3 60 `) 61 jsnByte, err := yaml.YAMLToJSON(cnp) 62 require.NoError(t, err) 63 64 us := unstructured.Unstructured{} 65 err = json.Unmarshal(jsnByte, &us) 66 require.NoError(t, err) 67 68 validator, err := NewNPValidator() 69 require.NoError(t, err) 70 err = validator.ValidateCNP(&us) 71 // Err can't be nil since validation should detect the policy is not correct. 72 require.Error(t, err) 73 } 74 75 func Test_BadMatchLabels(t *testing.T) { 76 cnp := []byte(`apiVersion: cilium.io/v2 77 kind: CiliumNetworkPolicy 78 metadata: 79 name: cnp-test-1 80 namespace: ns-2 81 spec: 82 egress: 83 - toServices: 84 - k8sService: 85 namespace: default 86 serviceName: kubernetes 87 endpointSelector: 88 matchLabels: 89 key: app 90 operator: In 91 values: 92 - prometheus 93 - kube-state-metrics 94 `) 95 jsnByte, err := yaml.YAMLToJSON(cnp) 96 require.NoError(t, err) 97 98 us := unstructured.Unstructured{} 99 err = json.Unmarshal(jsnByte, &us) 100 require.NoError(t, err) 101 102 validator, err := NewNPValidator() 103 require.NoError(t, err) 104 err = validator.ValidateCNP(&us) 105 // Err can't be nil since validation should detect the policy is not correct. 106 require.Error(t, err) 107 } 108 109 func Test_GoodCNP(t *testing.T) { 110 cnp := []byte(`apiVersion: cilium.io/v2 111 kind: CiliumNetworkPolicy 112 metadata: 113 name: cnp-test-1 114 namespace: ns-2 115 spec: 116 egress: 117 - toServices: 118 - k8sService: 119 namespace: default 120 serviceName: kubernetes 121 endpointSelector: 122 matchLabels: 123 key: app 124 operator: In 125 `) 126 jsnByte, err := yaml.YAMLToJSON(cnp) 127 require.NoError(t, err) 128 129 us := unstructured.Unstructured{} 130 err = json.Unmarshal(jsnByte, &us) 131 require.NoError(t, err) 132 133 validator, err := NewNPValidator() 134 require.NoError(t, err) 135 err = validator.ValidateCNP(&us) 136 require.NoError(t, err) 137 } 138 139 func Test_GoodCCNP(t *testing.T) { 140 ccnp := []byte(`apiVersion: cilium.io/v2 141 kind: CiliumClusterwideNetworkPolicy 142 metadata: 143 name: cnp-test-1 144 spec: 145 egress: 146 - toServices: 147 - k8sService: 148 namespace: default 149 serviceName: kubernetes 150 endpointSelector: 151 matchLabels: 152 key: app 153 operator: In 154 `) 155 jsnByte, err := yaml.YAMLToJSON(ccnp) 156 require.NoError(t, err) 157 158 us := unstructured.Unstructured{} 159 err = json.Unmarshal(jsnByte, &us) 160 require.NoError(t, err) 161 162 validator, err := NewNPValidator() 163 require.NoError(t, err) 164 err = validator.ValidateCCNP(&us) 165 require.NoError(t, err) 166 } 167 168 func Test_BadCCNP(t *testing.T) { 169 // Bad CCNP with endpointSelector and nodeSelector 170 ccnp := []byte(`apiVersion: cilium.io/v2 171 kind: CiliumClusterwideNetworkPolicy 172 metadata: 173 name: cnp-test-1 174 spec: 175 egress: 176 - toServices: 177 - k8sService: 178 namespace: default 179 serviceName: kubernetes 180 endpointSelector: 181 matchLabels: 182 key: app 183 operator: In 184 nodeSelector: 185 matchLabels: 186 key: app 187 operator: In 188 `) 189 jsnByte, err := yaml.YAMLToJSON(ccnp) 190 require.NoError(t, err) 191 192 us := unstructured.Unstructured{} 193 err = json.Unmarshal(jsnByte, &us) 194 require.NoError(t, err) 195 196 validator, err := NewNPValidator() 197 require.NoError(t, err) 198 err = validator.ValidateCCNP(&us) 199 // Err can't be nil since validation should detect the policy is not correct. 200 require.Error(t, err) 201 } 202 203 func Test_UnknownFieldDetection(t *testing.T) { 204 tests := []struct { 205 name string 206 policy []byte 207 clusterwide bool 208 err error 209 }{ 210 { 211 name: "ccnp GH-14526", 212 policy: []byte(` 213 apiVersion: cilium.io/v2 214 kind: CiliumClusterwideNetworkPolicy 215 metadata: 216 annotations: 217 kubectl.kubernetes.io/last-applied-configuration: | 218 {"apiVersion":"cilium.io/v2","kind":"CiliumClusterwideNetworkPolicy","metadata":{"annotations":{},"name":"ccnp-default-deny-egress"},"spec":{"egress":[{}],"endpointSelector":{}}} 219 creationTimestamp: "2021-01-07T00:26:34Z" 220 generation: 1 221 managedFields: 222 - apiVersion: cilium.io/v2 223 fieldsType: FieldsV1 224 fieldsV1: 225 f:metadata: 226 f:annotations: 227 .: {} 228 f:kubectl.kubernetes.io/last-applied-configuration: {} 229 f:spec: 230 .: {} 231 f:egress: {} 232 f:endpointSelector: {} 233 manager: kubectl-client-side-apply 234 operation: Update 235 time: "2021-01-07T00:26:34Z" 236 name: ccnp-default-deny-egress 237 resourceVersion: "7849" 238 selfLink: /apis/cilium.io/v2/ciliumclusterwidenetworkpolicies/ccnp-default-deny-egress 239 uid: f776ca84-86dc-4589-ab91-64fccdec468a 240 spec: 241 egress: 242 - {} 243 endpointSelector: {} 244 `), 245 clusterwide: true, 246 err: nil, 247 }, 248 { 249 name: "cnp GH-14526", 250 policy: []byte(` 251 apiVersion: cilium.io/v2 252 kind: CiliumNetworkPolicy 253 metadata: 254 annotations: 255 kubectl.kubernetes.io/last-applied-configuration: | 256 {"apiVersion":"cilium.io/v2","kind":"CiliumNetworkPolicy","metadata":{"annotations":{},"name":"ccnp-default-deny-egress"},"spec":{"egress":[{}],"endpointSelector":{}}} 257 creationTimestamp: "2021-01-07T00:26:34Z" 258 generation: 1 259 managedFields: 260 - apiVersion: cilium.io/v2 261 fieldsType: FieldsV1 262 fieldsV1: 263 f:metadata: 264 f:annotations: 265 .: {} 266 f:kubectl.kubernetes.io/last-applied-configuration: {} 267 f:spec: 268 .: {} 269 f:egress: {} 270 f:endpointSelector: {} 271 manager: kubectl-client-side-apply 272 operation: Update 273 time: "2021-01-07T00:26:34Z" 274 name: ccnp-default-deny-egress 275 resourceVersion: "7849" 276 selfLink: /apis/cilium.io/v2/ciliumnetworkpolicies/ccnp-default-deny-egress 277 uid: f776ca84-86dc-4589-ab91-64fccdec468a 278 spec: 279 egress: 280 - {} 281 endpointSelector: {} 282 `), 283 clusterwide: false, 284 err: nil, 285 }, 286 { 287 name: "neither a cnp or ccnp", 288 policy: []byte(` 289 kind: ServiceAccount 290 apiVersion: v1 291 metadata: 292 name: app1-account 293 `), 294 clusterwide: false, 295 err: ErrUnknownKind{ 296 kind: "ServiceAccount", 297 }, 298 }, 299 300 { 301 name: "cnp top-level description exists", 302 policy: []byte(` 303 apiVersion: cilium.io/v2 304 kind: CiliumNetworkPolicy 305 description: "default deny policy" 306 metadata: 307 name: cnp-test-1 308 spec: 309 endpointSelector: {} 310 `), 311 clusterwide: false, 312 err: ErrTopLevelDescriptionFound, 313 }, 314 { 315 name: "cnp top-level description does not exist", 316 policy: []byte(` 317 apiVersion: cilium.io/v2 318 kind: CiliumNetworkPolicy 319 metadata: 320 name: cnp-test-1 321 spec: 322 endpointSelector: {} 323 `), 324 clusterwide: false, 325 err: nil, 326 }, 327 { 328 name: "ccnp top-level description exists", 329 policy: []byte(` 330 apiVersion: cilium.io/v2 331 kind: CiliumClusterwideNetworkPolicy 332 description: "default deny policy" 333 metadata: 334 name: ccnp-test-1 335 spec: 336 nodeSelector: {} 337 `), 338 clusterwide: true, 339 err: ErrTopLevelDescriptionFound, 340 }, 341 { 342 name: "ccnp top-level description does not exist", 343 policy: []byte(` 344 apiVersion: cilium.io/v2 345 kind: CiliumClusterwideNetworkPolicy 346 metadata: 347 name: ccnp-test-1 348 spec: 349 nodeSelector: {} 350 `), 351 clusterwide: true, 352 err: nil, 353 }, 354 355 { 356 name: "cnp extra unknown fields", 357 policy: []byte(` 358 apiVersion: cilium.io/v2 359 kind: CiliumNetworkPolicy 360 foo: bar 361 metadata: 362 name: ccnp-test-1 363 spec: 364 endpointSelector: {} 365 bar: baz 366 `), 367 clusterwide: false, 368 err: ErrUnknownFields{ 369 extras: []string{"foo", "spec.bar"}, 370 }, 371 }, 372 { 373 name: "ccnp extra unknown fields", 374 policy: []byte(` 375 apiVersion: cilium.io/v2 376 kind: CiliumClusterwideNetworkPolicy 377 foo: bar 378 metadata: 379 name: ccnp-test-1 380 spec: 381 nodeSelector: {} 382 bar: baz 383 `), 384 clusterwide: true, 385 err: ErrUnknownFields{ 386 extras: []string{"foo", "spec.bar"}, 387 }, 388 }, 389 { 390 name: "cnp specs", 391 policy: []byte(` 392 apiVersion: "cilium.io/v2" 393 kind: CiliumNetworkPolicy 394 metadata: 395 name: "cnp-specs" 396 specs: 397 - description: "Policy to test multiple rules in a single file" 398 endpointSelector: 399 matchLabels: 400 app: ratings 401 version: v1 402 ingress: 403 - fromEndpoints: 404 - matchLabels: 405 app: reviews 406 track: stable 407 version: v1 408 toPorts: 409 - ports: 410 - port: "9080" 411 protocol: TCP 412 rules: 413 http: 414 - method: "GET" 415 path: "/health" 416 - endpointSelector: 417 matchLabels: 418 app: details 419 track: stable 420 version: v1 421 ingress: 422 - fromEndpoints: 423 - matchLabels: 424 app: productpage 425 track: stable 426 version: v1 427 toPorts: 428 - ports: 429 - port: "9080" 430 protocol: TCP 431 rules: 432 http: 433 - method: "GET" 434 path: "/.*" 435 `), 436 clusterwide: false, 437 err: nil, 438 }, 439 } 440 for _, tt := range tests { 441 t.Log(tt.name) 442 443 jsnByte, err := yaml.YAMLToJSON(tt.policy) 444 require.NoError(t, err) 445 446 us := unstructured.Unstructured{} 447 err = json.Unmarshal(jsnByte, &us) 448 require.NoError(t, err) 449 450 validator, err := NewNPValidator() 451 require.NoError(t, err) 452 453 if tt.clusterwide { 454 require.EqualValues(t, tt.err, validator.ValidateCCNP(&us)) 455 } else { 456 require.EqualValues(t, tt.err, validator.ValidateCNP(&us)) 457 } 458 } 459 } 460 461 func Test_GH28007(t *testing.T) { 462 cnp := []byte(`apiVersion: cilium.io/v2 463 kind: CiliumNetworkPolicy 464 metadata: 465 name: exampleapp 466 namespace: examplens 467 spec: 468 egress: 469 - toEntities: 470 - world 471 endpointSelector: 472 matchExpressions: 473 - key: reserved:init 474 operator: DoesNotExist 475 `) 476 jsnByte, err := yaml.YAMLToJSON(cnp) 477 require.NoError(t, err) 478 479 us := unstructured.Unstructured{} 480 err = json.Unmarshal(jsnByte, &us) 481 require.NoError(t, err) 482 483 validator, err := NewNPValidator() 484 require.NoError(t, err) 485 err = validator.ValidateCNP(&us) 486 // Err can't be nil since validation should detect the policy is not correct. 487 require.Equal(t, errInitPolicyCNP, err) 488 }