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  }