github.com/imran-kn/cilium-fork@v1.6.9/pkg/policy/api/rule_validation.go (about) 1 // Copyright 2016-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 package api 16 17 import ( 18 "errors" 19 "fmt" 20 "net" 21 "strconv" 22 "strings" 23 24 "github.com/cilium/cilium/pkg/labels" 25 "github.com/cilium/cilium/pkg/option" 26 ) 27 28 const ( 29 maxPorts = 40 30 // MaxCIDRPrefixLengths is used to prevent compile failures at runtime. 31 MaxCIDRPrefixLengths = 40 32 ) 33 34 type exists struct{} 35 36 // Sanitize validates and sanitizes a policy rule. Minor edits such as 37 // capitalization of the protocol name are automatically fixed up. More 38 // fundamental violations will cause an error to be returned. 39 func (r Rule) Sanitize() error { 40 41 // reject cilium-generated labels on insert. 42 // This isn't a proper function because r.Labels is a labels.LabelArray and 43 // not a labels.Labels, where we could add a function similar to GetReserved 44 for _, lbl := range r.Labels { 45 if lbl.Source == labels.LabelSourceCiliumGenerated { 46 return fmt.Errorf("rule labels cannot have cilium-generated source") 47 } 48 } 49 50 if r.EndpointSelector.LabelSelector == nil { 51 return fmt.Errorf("rule cannot have nil EndpointSelector") 52 } 53 54 if err := r.EndpointSelector.sanitize(); err != nil { 55 return err 56 } 57 58 for i := range r.Ingress { 59 if err := r.Ingress[i].sanitize(); err != nil { 60 return err 61 } 62 } 63 64 for i := range r.Egress { 65 if err := r.Egress[i].sanitize(); err != nil { 66 return err 67 } 68 } 69 70 return nil 71 } 72 73 func countL7Rules(ports []PortRule) map[string]int { 74 result := make(map[string]int) 75 for _, port := range ports { 76 if !port.Rules.IsEmpty() { 77 result["DNS"] += len(port.Rules.DNS) 78 result["HTTP"] += len(port.Rules.HTTP) 79 result["Kafka"] += len(port.Rules.Kafka) 80 } 81 } 82 return result 83 } 84 85 func (i *IngressRule) sanitize() error { 86 l3Members := map[string]int{ 87 "FromEndpoints": len(i.FromEndpoints), 88 "FromCIDR": len(i.FromCIDR), 89 "FromCIDRSet": len(i.FromCIDRSet), 90 "FromEntities": len(i.FromEntities), 91 } 92 l3DependentL4Support := map[interface{}]bool{ 93 "FromEndpoints": true, 94 "FromCIDR": false, 95 "FromCIDRSet": false, 96 "FromEntities": true, 97 } 98 l7Members := countL7Rules(i.ToPorts) 99 l7IngressSupport := map[string]bool{ 100 "DNS": false, 101 "Kafka": true, 102 "HTTP": true, 103 } 104 105 for m1 := range l3Members { 106 for m2 := range l3Members { 107 if m2 != m1 && l3Members[m1] > 0 && l3Members[m2] > 0 { 108 return fmt.Errorf("Combining %s and %s is not supported yet", m1, m2) 109 } 110 } 111 } 112 for member := range l3Members { 113 if l3Members[member] > 0 && len(i.ToPorts) > 0 && !l3DependentL4Support[member] { 114 return fmt.Errorf("Combining %s and ToPorts is not supported yet", member) 115 } 116 } 117 118 if len(l7Members) > 0 && !option.Config.EnableL7Proxy { 119 return errors.New("L7 policy is not supported since L7 proxy is not enabled") 120 } 121 for member := range l7Members { 122 if l7Members[member] > 0 && !l7IngressSupport[member] { 123 return fmt.Errorf("L7 protocol %s is not supported on ingress yet", member) 124 } 125 } 126 127 for _, es := range i.FromEndpoints { 128 if err := es.sanitize(); err != nil { 129 return err 130 } 131 } 132 133 for _, es := range i.FromRequires { 134 if err := es.sanitize(); err != nil { 135 return err 136 } 137 } 138 139 for n := range i.ToPorts { 140 if err := i.ToPorts[n].sanitize(); err != nil { 141 return err 142 } 143 } 144 145 prefixLengths := map[int]exists{} 146 for n := range i.FromCIDR { 147 prefixLength, err := i.FromCIDR[n].sanitize() 148 if err != nil { 149 return err 150 } 151 prefixLengths[prefixLength] = exists{} 152 } 153 154 for n := range i.FromCIDRSet { 155 prefixLength, err := i.FromCIDRSet[n].sanitize() 156 if err != nil { 157 return err 158 } 159 prefixLengths[prefixLength] = exists{} 160 } 161 162 for _, fromEntity := range i.FromEntities { 163 _, ok := EntitySelectorMapping[fromEntity] 164 if !ok { 165 return fmt.Errorf("unsupported entity: %s", fromEntity) 166 } 167 } 168 169 // FIXME GH-1781 count coalesced CIDRs and restrict the number of 170 // prefix lengths based on the CIDRSet exclusions. 171 if l := len(prefixLengths); l > MaxCIDRPrefixLengths { 172 return fmt.Errorf("too many ingress CIDR prefix lengths %d/%d", l, MaxCIDRPrefixLengths) 173 } 174 175 i.SetAggregatedSelectors() 176 177 return nil 178 } 179 180 func (e *EgressRule) sanitize() error { 181 l3Members := map[string]int{ 182 "ToCIDR": len(e.ToCIDR), 183 "ToCIDRSet": len(e.ToCIDRSet), 184 "ToEndpoints": len(e.ToEndpoints), 185 "ToEntities": len(e.ToEntities), 186 "ToServices": len(e.ToServices), 187 "ToFQDNs": len(e.ToFQDNs), 188 "ToGroups": len(e.ToGroups), 189 } 190 l3DependentL4Support := map[interface{}]bool{ 191 "ToCIDR": true, 192 "ToCIDRSet": true, 193 "ToEndpoints": true, 194 "ToEntities": true, 195 "ToServices": true, 196 "ToFQDNs": true, 197 "ToGroups": true, 198 } 199 l7Members := countL7Rules(e.ToPorts) 200 l7EgressSupport := map[string]bool{ 201 "DNS": true, 202 "Kafka": false, 203 "HTTP": true, 204 } 205 206 for m1 := range l3Members { 207 for m2 := range l3Members { 208 if m2 != m1 && l3Members[m1] > 0 && l3Members[m2] > 0 { 209 return fmt.Errorf("Combining %s and %s is not supported yet", m1, m2) 210 } 211 } 212 } 213 for member := range l3Members { 214 if l3Members[member] > 0 && len(e.ToPorts) > 0 && !l3DependentL4Support[member] { 215 return fmt.Errorf("Combining %s and ToPorts is not supported yet", member) 216 } 217 } 218 219 if len(l7Members) > 0 && !option.Config.EnableL7Proxy { 220 return errors.New("L7 policy is not supported since L7 proxy is not enabled") 221 } 222 for member := range l7Members { 223 if l7Members[member] > 0 && !l7EgressSupport[member] { 224 return fmt.Errorf("L7 protocol %s is not supported on egress yet", member) 225 } 226 } 227 228 for _, es := range e.ToEndpoints { 229 if err := es.sanitize(); err != nil { 230 return err 231 } 232 } 233 234 for _, es := range e.ToRequires { 235 if err := es.sanitize(); err != nil { 236 return err 237 } 238 } 239 240 for i := range e.ToPorts { 241 if err := e.ToPorts[i].sanitize(); err != nil { 242 return err 243 } 244 } 245 246 prefixLengths := map[int]exists{} 247 for i := range e.ToCIDR { 248 prefixLength, err := e.ToCIDR[i].sanitize() 249 if err != nil { 250 return err 251 } 252 prefixLengths[prefixLength] = exists{} 253 } 254 for i := range e.ToCIDRSet { 255 prefixLength, err := e.ToCIDRSet[i].sanitize() 256 if err != nil { 257 return err 258 } 259 prefixLengths[prefixLength] = exists{} 260 } 261 262 for _, toEntity := range e.ToEntities { 263 _, ok := EntitySelectorMapping[toEntity] 264 if !ok { 265 return fmt.Errorf("unsupported entity: %s", toEntity) 266 } 267 } 268 269 for i := range e.ToFQDNs { 270 err := e.ToFQDNs[i].sanitize() 271 if err != nil { 272 return err 273 } 274 } 275 276 // FIXME GH-1781 count coalesced CIDRs and restrict the number of 277 // prefix lengths based on the CIDRSet exclusions. 278 if l := len(prefixLengths); l > MaxCIDRPrefixLengths { 279 return fmt.Errorf("too many egress CIDR prefix lengths %d/%d", l, MaxCIDRPrefixLengths) 280 } 281 282 e.SetAggregatedSelectors() 283 284 return nil 285 } 286 287 // Sanitize sanitizes Kafka rules 288 // TODO we need to add support to check 289 // wildcard and prefix/suffix later on. 290 func (kr *PortRuleKafka) Sanitize() error { 291 if (len(kr.APIKey) > 0) && (len(kr.Role) > 0) { 292 return fmt.Errorf("Cannot set both Role:%q and APIKey :%q together", kr.Role, kr.APIKey) 293 } 294 295 if len(kr.APIKey) > 0 { 296 n, ok := KafkaAPIKeyMap[strings.ToLower(kr.APIKey)] 297 if !ok { 298 return fmt.Errorf("invalid Kafka APIKey :%q", kr.APIKey) 299 } 300 kr.apiKeyInt = append(kr.apiKeyInt, n) 301 } 302 303 if len(kr.Role) > 0 { 304 err := kr.MapRoleToAPIKey() 305 if err != nil { 306 return fmt.Errorf("invalid Kafka APIRole :%q", kr.Role) 307 } 308 309 } 310 311 if len(kr.APIVersion) > 0 { 312 n, err := strconv.ParseInt(kr.APIVersion, 10, 16) 313 if err != nil { 314 return fmt.Errorf("invalid Kafka APIVersion :%q", 315 kr.APIVersion) 316 } 317 n16 := int16(n) 318 kr.apiVersionInt = &n16 319 } 320 321 if len(kr.Topic) > 0 { 322 if len(kr.Topic) > KafkaMaxTopicLen { 323 return fmt.Errorf("kafka topic exceeds maximum len of %d", 324 KafkaMaxTopicLen) 325 } 326 // This check allows suffix and prefix matching 327 // for topic. 328 if KafkaTopicValidChar.MatchString(kr.Topic) == false { 329 return fmt.Errorf("invalid Kafka Topic name \"%s\"", kr.Topic) 330 } 331 } 332 return nil 333 } 334 335 func (pr *L7Rules) sanitize(ports []PortProtocol) error { 336 nTypes := 0 337 338 if pr.HTTP != nil { 339 nTypes++ 340 for i := range pr.HTTP { 341 if err := pr.HTTP[i].Sanitize(); err != nil { 342 return err 343 } 344 } 345 } 346 347 if pr.Kafka != nil { 348 nTypes++ 349 for i := range pr.Kafka { 350 if err := pr.Kafka[i].Sanitize(); err != nil { 351 return err 352 } 353 } 354 } 355 356 if pr.DNS != nil { 357 // Forthcoming TPROXY redirection restricts DNS proxy to the standard DNS port (53). 358 // Require the port 53 be explicitly configured, and disallow other port numbers. 359 if len(ports) == 0 { 360 return fmt.Errorf("Port 53 must be specified for DNS rules") 361 } 362 363 nTypes++ 364 for i := range pr.DNS { 365 if err := pr.DNS[i].Sanitize(); err != nil { 366 return err 367 } 368 } 369 } 370 371 if pr.L7 != nil && pr.L7Proto == "" { 372 return fmt.Errorf("'l7' may only be specified when a 'l7proto' is also specified") 373 } 374 if pr.L7Proto != "" { 375 nTypes++ 376 for i := range pr.L7 { 377 if err := pr.L7[i].Sanitize(); err != nil { 378 return err 379 } 380 } 381 } 382 383 if nTypes > 1 { 384 return fmt.Errorf("multiple L7 protocol rule types specified in single rule") 385 } 386 return nil 387 } 388 389 func (pr *PortRule) sanitize() error { 390 if len(pr.Ports) > maxPorts { 391 return fmt.Errorf("too many ports, the max is %d", maxPorts) 392 } 393 for i := range pr.Ports { 394 if err := pr.Ports[i].sanitize(); err != nil { 395 return err 396 } 397 398 // DNS L7 rules can be TCP, UDP or ANY, all others are TCP only. 399 switch { 400 case pr.Rules.IsEmpty(), pr.Rules != nil && len(pr.Rules.DNS) > 0: 401 // nothing to do if no rules OR they are DNS rules (note the comma above) 402 case pr.Ports[i].Protocol != ProtoTCP: 403 return fmt.Errorf("L7 rules can only apply to TCP (not %s) except for DNS rules", pr.Ports[i].Protocol) 404 } 405 } 406 407 // Sanitize L7 rules 408 if !pr.Rules.IsEmpty() { 409 if err := pr.Rules.sanitize(pr.Ports); err != nil { 410 return err 411 } 412 } 413 return nil 414 } 415 416 func (pp *PortProtocol) sanitize() error { 417 if pp.Port == "" { 418 return fmt.Errorf("Port must be specified") 419 } 420 421 p, err := strconv.ParseUint(pp.Port, 0, 16) 422 if err != nil { 423 return fmt.Errorf("Unable to parse port: %s", err) 424 } 425 426 if p == 0 { 427 return fmt.Errorf("Port cannot be 0") 428 } 429 430 pp.Protocol, err = ParseL4Proto(string(pp.Protocol)) 431 if err != nil { 432 return err 433 } 434 435 return nil 436 } 437 438 // sanitize the given CIDR. If successful, returns the prefixLength specified 439 // in the cidr and nil. Otherwise, returns (0, nil). 440 func (cidr CIDR) sanitize() (prefixLength int, err error) { 441 strCIDR := string(cidr) 442 if strCIDR == "" { 443 return 0, fmt.Errorf("IP must be specified") 444 } 445 446 _, ipnet, err := net.ParseCIDR(strCIDR) 447 if err == nil { 448 var bits int 449 prefixLength, bits = ipnet.Mask.Size() 450 if prefixLength == 0 && bits == 0 { 451 return 0, fmt.Errorf("CIDR cannot specify non-contiguous mask %s", 452 ipnet.Mask.String()) 453 } 454 } else { 455 // Try to parse as a fully masked IP or an IP subnetwork 456 ip := net.ParseIP(strCIDR) 457 if ip == nil { 458 return 0, fmt.Errorf("Unable to parse CIDR: %s", err) 459 } 460 } 461 462 return prefixLength, nil 463 } 464 465 // sanitize validates a CIDRRule by checking that the CIDR prefix itself is 466 // valid, and ensuring that all of the exception CIDR prefixes are contained 467 // within the allowed CIDR prefix. 468 func (c *CIDRRule) sanitize() (prefixLength int, err error) { 469 470 // Only allow notation <IP address>/<prefix>. Note that this differs from 471 // the logic in api.CIDR.Sanitize(). 472 _, cidrNet, err := net.ParseCIDR(string(c.Cidr)) 473 if err != nil { 474 return 0, fmt.Errorf("Unable to parse CIDRRule %q: %s", c.Cidr, err) 475 } 476 477 var bits int 478 prefixLength, bits = cidrNet.Mask.Size() 479 if prefixLength == 0 && bits == 0 { 480 return 0, fmt.Errorf("CIDR cannot specify non-contiguous mask %s", 481 cidrNet.Mask.String()) 482 } 483 484 // Ensure that each provided exception CIDR prefix is formatted correctly, 485 // and is contained within the CIDR prefix to/from which we want to allow 486 // traffic. 487 for _, p := range c.ExceptCIDRs { 488 exceptCIDRAddr, _, err := net.ParseCIDR(string(p)) 489 if err != nil { 490 return 0, err 491 } 492 493 // Note: this also checks that the allow CIDR prefix and the exception 494 // CIDR prefixes are part of the same address family. 495 if !cidrNet.Contains(exceptCIDRAddr) { 496 return 0, fmt.Errorf("allow CIDR prefix %s does not contain "+ 497 "exclude CIDR prefix %s", c.Cidr, p) 498 } 499 } 500 501 return prefixLength, nil 502 }