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  }