github.com/imran-kn/cilium-fork@v1.6.9/pkg/policy/api/rule_validation_test.go (about) 1 // Copyright 2018-2019 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // +build !privileged_tests 16 17 package api 18 19 import ( 20 "fmt" 21 22 "github.com/cilium/cilium/pkg/labels" 23 24 . "gopkg.in/check.v1" 25 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 ) 28 29 // This test ensures that only PortRules which have L7Rules associated with them 30 // are invalid if any protocol except TCP is used as a protocol for any port 31 // in the list of PortProtocol supplied to the rule. 32 func (s *PolicyAPITestSuite) TestL7RulesWithNonTCPProtocols(c *C) { 33 34 // Rule is valid because only ProtoTCP is allowed for L7 rules (except with ToFQDNs, below). 35 validPortRule := Rule{ 36 EndpointSelector: WildcardEndpointSelector, 37 Ingress: []IngressRule{ 38 { 39 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 40 ToPorts: []PortRule{{ 41 Ports: []PortProtocol{ 42 {Port: "80", Protocol: ProtoTCP}, 43 {Port: "81", Protocol: ProtoTCP}, 44 }, 45 Rules: &L7Rules{ 46 HTTP: []PortRuleHTTP{ 47 {Method: "GET", Path: "/"}, 48 }, 49 }, 50 }}, 51 }, 52 }, 53 } 54 55 err := validPortRule.Sanitize() 56 c.Assert(err, IsNil) 57 58 // Rule is invalid because no port is specified for DNS proxy rule. 59 validPortRule = Rule{ 60 EndpointSelector: WildcardEndpointSelector, 61 Egress: []EgressRule{ 62 { 63 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 64 ToPorts: []PortRule{{ 65 Rules: &L7Rules{ 66 DNS: []PortRuleDNS{ 67 {MatchName: "domain.com"}, 68 }, 69 }, 70 }}, 71 }, 72 }, 73 } 74 75 err = validPortRule.Sanitize() 76 c.Assert(err, Not(IsNil), Commentf("Port 53 must be specified for DNS rules")) 77 78 // Rule is valid because all protocols are allowed for L7 rules with ToFQDNs. 79 validPortRule = Rule{ 80 EndpointSelector: WildcardEndpointSelector, 81 Egress: []EgressRule{ 82 { 83 ToEndpoints: []EndpointSelector{WildcardEndpointSelector}, 84 ToPorts: []PortRule{{ 85 Ports: []PortProtocol{ 86 {Port: "53", Protocol: ProtoTCP}, 87 {Port: "53", Protocol: ProtoUDP}, 88 }, 89 Rules: &L7Rules{ 90 DNS: []PortRuleDNS{ 91 {MatchName: "domain.com"}, 92 }, 93 }, 94 }}, 95 }, 96 }, 97 } 98 99 err = validPortRule.Sanitize() 100 c.Assert(err, IsNil, Commentf("Saw an error for a L7 rule with DNS rules. This should be allowed.")) 101 102 // Rule is invalid because only ProtoTCP is allowed for L7 rules (except with DNS, below). 103 invalidPortRule := Rule{ 104 EndpointSelector: WildcardEndpointSelector, 105 Ingress: []IngressRule{ 106 { 107 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 108 ToPorts: []PortRule{{ 109 Ports: []PortProtocol{ 110 {Port: "80", Protocol: ProtoUDP}, 111 }, 112 Rules: &L7Rules{ 113 HTTP: []PortRuleHTTP{ 114 {Method: "GET", Path: "/"}, 115 }, 116 }, 117 }}, 118 }, 119 }, 120 } 121 122 err = invalidPortRule.Sanitize() 123 c.Assert(err.Error(), Equals, "L7 rules can only apply to TCP (not UDP) except for DNS rules") 124 125 // Rule is invalid because only ProtoTCP is allowed for L7 rules (except with DNS, below). 126 invalidPortRule = Rule{ 127 EndpointSelector: WildcardEndpointSelector, 128 Ingress: []IngressRule{ 129 { 130 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 131 ToPorts: []PortRule{{ 132 Ports: []PortProtocol{ 133 {Port: "80", Protocol: ProtoAny}, 134 }, 135 Rules: &L7Rules{ 136 HTTP: []PortRuleHTTP{ 137 {Method: "GET", Path: "/"}, 138 }, 139 }, 140 }}, 141 }, 142 }, 143 } 144 145 err = invalidPortRule.Sanitize() 146 c.Assert(err, Not(IsNil)) 147 c.Assert(err.Error(), Equals, "L7 rules can only apply to TCP (not ANY) except for DNS rules") 148 149 // Rule is invalid because only ProtoTCP is allowed for L7 rules (except with DNS, below). 150 invalidPortRule = Rule{ 151 EndpointSelector: WildcardEndpointSelector, 152 Ingress: []IngressRule{ 153 { 154 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 155 ToPorts: []PortRule{{ 156 Ports: []PortProtocol{ 157 {Port: "80", Protocol: ProtoTCP}, 158 {Port: "12345", Protocol: ProtoUDP}, 159 }, 160 Rules: &L7Rules{ 161 HTTP: []PortRuleHTTP{ 162 {Method: "GET", Path: "/"}, 163 }, 164 }, 165 }}, 166 }, 167 }, 168 } 169 170 err = invalidPortRule.Sanitize() 171 c.Assert(err, Not(IsNil)) 172 c.Assert(err.Error(), Equals, "L7 rules can only apply to TCP (not UDP) except for DNS rules") 173 174 // Same as previous rule, but ensure ordering doesn't affect validation. 175 invalidPortRule = Rule{ 176 EndpointSelector: WildcardEndpointSelector, 177 Ingress: []IngressRule{ 178 { 179 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 180 ToPorts: []PortRule{{ 181 Ports: []PortProtocol{ 182 {Port: "80", Protocol: ProtoUDP}, 183 {Port: "12345", Protocol: ProtoTCP}, 184 }, 185 Rules: &L7Rules{ 186 HTTP: []PortRuleHTTP{ 187 {Method: "GET", Path: "/"}, 188 }, 189 }, 190 }}, 191 }, 192 }, 193 } 194 195 err = invalidPortRule.Sanitize() 196 c.Assert(err, Not(IsNil)) 197 c.Assert(err.Error(), Equals, "L7 rules can only apply to TCP (not UDP) except for DNS rules") 198 199 } 200 201 // This test ensures that PortRules using the HTTP protocol have valid regular 202 // expressions for the method and path fields. 203 func (s *PolicyAPITestSuite) TestHTTPRuleRegexes(c *C) { 204 205 invalidHTTPRegexPathRule := Rule{ 206 EndpointSelector: WildcardEndpointSelector, 207 Ingress: []IngressRule{ 208 { 209 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 210 ToPorts: []PortRule{{ 211 Ports: []PortProtocol{ 212 {Port: "80", Protocol: ProtoTCP}, 213 {Port: "81", Protocol: ProtoTCP}, 214 }, 215 Rules: &L7Rules{ 216 HTTP: []PortRuleHTTP{ 217 {Method: "GET", Path: "*"}, 218 }, 219 }, 220 }}, 221 }, 222 }, 223 } 224 225 err := invalidHTTPRegexPathRule.Sanitize() 226 c.Assert(err, Not(IsNil)) 227 228 invalidHTTPRegexMethodRule := Rule{ 229 EndpointSelector: WildcardEndpointSelector, 230 Ingress: []IngressRule{ 231 { 232 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 233 ToPorts: []PortRule{{ 234 Ports: []PortProtocol{ 235 {Port: "80", Protocol: ProtoTCP}, 236 {Port: "81", Protocol: ProtoTCP}, 237 }, 238 Rules: &L7Rules{ 239 HTTP: []PortRuleHTTP{ 240 {Method: "*", Path: "/"}, 241 }, 242 }, 243 }}, 244 }, 245 }, 246 } 247 248 err = invalidHTTPRegexMethodRule.Sanitize() 249 c.Assert(err, Not(IsNil)) 250 } 251 252 // Test the validation of CIDR rule prefix definitions 253 func (s *PolicyAPITestSuite) TestCIDRsanitize(c *C) { 254 // IPv4 255 cidr := CIDRRule{Cidr: "0.0.0.0/0"} 256 length, err := cidr.sanitize() 257 c.Assert(err, IsNil) 258 c.Assert(length, Equals, 0) 259 260 cidr = CIDRRule{Cidr: "10.0.0.0/24"} 261 length, err = cidr.sanitize() 262 c.Assert(err, IsNil) 263 c.Assert(length, Equals, 24) 264 265 cidr = CIDRRule{Cidr: "192.0.2.3/32"} 266 length, err = cidr.sanitize() 267 c.Assert(err, IsNil) 268 c.Assert(length, Equals, 32) 269 270 // IPv6 271 cidr = CIDRRule{Cidr: "::/0"} 272 length, err = cidr.sanitize() 273 c.Assert(err, IsNil) 274 c.Assert(length, Equals, 0) 275 276 cidr = CIDRRule{Cidr: "ff02::/64"} 277 length, err = cidr.sanitize() 278 c.Assert(err, IsNil) 279 c.Assert(length, Equals, 64) 280 281 cidr = CIDRRule{Cidr: "2001:0db8:85a3:0000:0000:8a2e:0370:7334/128"} 282 length, err = cidr.sanitize() 283 c.Assert(err, IsNil) 284 c.Assert(length, Equals, 128) 285 286 // Non-contiguous mask. 287 cidr = CIDRRule{Cidr: "10.0.0.0/254.0.0.255"} 288 _, err = cidr.sanitize() 289 c.Assert(err, NotNil) 290 } 291 292 func (s *PolicyAPITestSuite) TestToServicesSanitize(c *C) { 293 294 svcLabels := map[string]string{ 295 "app": "tested-service", 296 } 297 selector := ServiceSelector(NewESFromMatchRequirements(svcLabels, nil)) 298 toServicesL3L4 := Rule{ 299 EndpointSelector: WildcardEndpointSelector, 300 Egress: []EgressRule{ 301 { 302 ToServices: []Service{ 303 { 304 K8sServiceSelector: &K8sServiceSelectorNamespace{ 305 Selector: selector, 306 Namespace: "", 307 }, 308 }, 309 }, 310 ToPorts: []PortRule{{ 311 Ports: []PortProtocol{ 312 {Port: "80", Protocol: ProtoTCP}, 313 {Port: "81", Protocol: ProtoTCP}, 314 }, 315 }}, 316 }, 317 }, 318 } 319 320 err := toServicesL3L4.Sanitize() 321 c.Assert(err, IsNil) 322 323 } 324 325 // This test ensures that PortRules using key-value pairs do not have empty keys 326 func (s *PolicyAPITestSuite) TestL7Rules(c *C) { 327 328 validL7Rule := Rule{ 329 EndpointSelector: WildcardEndpointSelector, 330 Ingress: []IngressRule{ 331 { 332 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 333 ToPorts: []PortRule{{ 334 Ports: []PortProtocol{ 335 {Port: "80", Protocol: ProtoTCP}, 336 {Port: "81", Protocol: ProtoTCP}, 337 }, 338 Rules: &L7Rules{ 339 L7Proto: "test.lineparser", 340 L7: []PortRuleL7{ 341 {"method": "PUT", "path": "/"}, 342 {"method": "GET", "path": "/"}, 343 }, 344 }, 345 }}, 346 }, 347 }, 348 } 349 350 err := validL7Rule.Sanitize() 351 c.Assert(err, IsNil) 352 353 validL7Rule2 := Rule{ 354 EndpointSelector: WildcardEndpointSelector, 355 Ingress: []IngressRule{ 356 { 357 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 358 ToPorts: []PortRule{{ 359 Ports: []PortProtocol{ 360 {Port: "80", Protocol: ProtoTCP}, 361 {Port: "81", Protocol: ProtoTCP}, 362 }, 363 Rules: &L7Rules{ 364 L7Proto: "test.lineparser", 365 // No L7 rules 366 }, 367 }}, 368 }, 369 }, 370 } 371 372 err = validL7Rule2.Sanitize() 373 c.Assert(err, IsNil) 374 375 invalidL7Rule := Rule{ 376 EndpointSelector: WildcardEndpointSelector, 377 Ingress: []IngressRule{ 378 { 379 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 380 ToPorts: []PortRule{{ 381 Ports: []PortProtocol{ 382 {Port: "80", Protocol: ProtoTCP}, 383 {Port: "81", Protocol: ProtoTCP}, 384 }, 385 Rules: &L7Rules{ 386 L7Proto: "test.lineparser", 387 L7: []PortRuleL7{ 388 map[string]string{ 389 "method": "PUT", 390 "": "Foo"}, 391 }, 392 }, 393 }}, 394 }, 395 }, 396 } 397 398 err = invalidL7Rule.Sanitize() 399 c.Assert(err, Not(IsNil)) 400 } 401 402 func (s *PolicyAPITestSuite) TestInvalidEndpointSelectors(c *C) { 403 404 // Operator in MatchExpressions is invalid, so sanitization should fail. 405 labelSel := &metav1.LabelSelector{ 406 MatchLabels: map[string]string{ 407 "any.foo": "bar", 408 "k8s.baz": "alice", 409 }, 410 MatchExpressions: []metav1.LabelSelectorRequirement{ 411 { 412 Key: "any.foo", 413 Operator: "asdfasdfasdf", 414 Values: []string{"default"}, 415 }, 416 }, 417 } 418 419 invalidSel := NewESFromK8sLabelSelector(labels.LabelSourceK8sKeyPrefix, labelSel) 420 421 invalidEpSelectorRule := Rule{ 422 EndpointSelector: invalidSel, 423 } 424 425 err := invalidEpSelectorRule.Sanitize() 426 c.Assert(err, Not(IsNil)) 427 428 invalidEpSelectorIngress := Rule{ 429 EndpointSelector: WildcardEndpointSelector, 430 Ingress: []IngressRule{ 431 { 432 FromEndpoints: []EndpointSelector{invalidSel}, 433 }, 434 }, 435 } 436 437 err = invalidEpSelectorIngress.Sanitize() 438 c.Assert(err, Not(IsNil)) 439 440 invalidEpSelectorIngressFromReq := Rule{ 441 EndpointSelector: WildcardEndpointSelector, 442 Ingress: []IngressRule{ 443 { 444 FromRequires: []EndpointSelector{invalidSel}, 445 }, 446 }, 447 } 448 449 err = invalidEpSelectorIngressFromReq.Sanitize() 450 c.Assert(err, Not(IsNil)) 451 452 invalidEpSelectorEgress := Rule{ 453 EndpointSelector: WildcardEndpointSelector, 454 Egress: []EgressRule{ 455 { 456 ToEndpoints: []EndpointSelector{invalidSel}, 457 }, 458 }, 459 } 460 461 err = invalidEpSelectorEgress.Sanitize() 462 c.Assert(err, Not(IsNil)) 463 464 invalidEpSelectorEgressToReq := Rule{ 465 EndpointSelector: WildcardEndpointSelector, 466 Egress: []EgressRule{ 467 { 468 ToRequires: []EndpointSelector{invalidSel}, 469 }, 470 }, 471 } 472 473 err = invalidEpSelectorEgressToReq.Sanitize() 474 c.Assert(err, Not(IsNil)) 475 476 } 477 478 func (s *PolicyAPITestSuite) TestTooManyPortsRule(c *C) { 479 480 var portProtocols []PortProtocol 481 482 for i := 80; i <= 80+maxPorts; i++ { 483 portProtocols = append(portProtocols, PortProtocol{ 484 Port: fmt.Sprintf("%d", i), 485 Protocol: ProtoTCP, 486 }) 487 } 488 489 tooManyPortsRule := Rule{ 490 EndpointSelector: WildcardEndpointSelector, 491 Ingress: []IngressRule{ 492 { 493 FromEndpoints: []EndpointSelector{WildcardEndpointSelector}, 494 ToPorts: []PortRule{{ 495 Ports: portProtocols, 496 }}, 497 }, 498 }, 499 } 500 err := tooManyPortsRule.Sanitize() 501 c.Assert(err, NotNil) 502 } 503 504 // This test ensures that PortRules aren't configured in the wrong direction, 505 // which ends up being a no-op with only vague error messages rather than a 506 // clear indication that something is wrong in the policy. 507 func (s *PolicyAPITestSuite) TestL7RuleDirectionalitySupport(c *C) { 508 509 // Kafka egress is not supported. 510 invalidKafkaRule := Rule{ 511 EndpointSelector: WildcardEndpointSelector, 512 Egress: []EgressRule{ 513 { 514 ToPorts: []PortRule{{ 515 Ports: []PortProtocol{ 516 {Port: "80", Protocol: ProtoTCP}, 517 {Port: "81", Protocol: ProtoTCP}, 518 }, 519 Rules: &L7Rules{ 520 Kafka: []PortRuleKafka{{ 521 Role: "consume", 522 Topic: "deathstar-plans", 523 }}, 524 }, 525 }}, 526 }, 527 }, 528 } 529 530 err := invalidKafkaRule.Sanitize() 531 c.Assert(err, Not(IsNil)) 532 533 // DNS ingress is not supported. 534 invalidDNSRule := Rule{ 535 EndpointSelector: WildcardEndpointSelector, 536 Ingress: []IngressRule{ 537 { 538 ToPorts: []PortRule{{ 539 Ports: []PortProtocol{ 540 {Port: "53", Protocol: ProtoTCP}, 541 {Port: "53", Protocol: ProtoUDP}, 542 }, 543 Rules: &L7Rules{ 544 DNS: []PortRuleDNS{{ 545 MatchName: "empire.gov", 546 }}, 547 }, 548 }}, 549 }, 550 }, 551 } 552 553 err = invalidDNSRule.Sanitize() 554 c.Assert(err, Not(IsNil)) 555 556 }