github.com/cilium/cilium@v1.16.2/pkg/policy/api/rule_validation_test.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package api
     5  
     6  import (
     7  	"fmt"
     8  	"testing"
     9  
    10  	"github.com/cilium/proxy/pkg/policy/api/kafka"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/require"
    13  
    14  	"k8s.io/apimachinery/pkg/util/intstr"
    15  
    16  	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    17  	"github.com/cilium/cilium/pkg/labels"
    18  	"github.com/cilium/cilium/pkg/option"
    19  )
    20  
    21  // This test ensures that only PortRules which have L7Rules associated with them
    22  // are invalid if any protocol except TCP is used as a protocol for any port
    23  // in the list of PortProtocol supplied to the rule.
    24  func TestL7RulesWithNonTCPProtocols(t *testing.T) {
    25  	setUpSuite(t)
    26  
    27  	// Rule is valid because only ProtoTCP is allowed for L7 rules (except with ToFQDNs, below).
    28  	validPortRule := Rule{
    29  		EndpointSelector: WildcardEndpointSelector,
    30  		Ingress: []IngressRule{
    31  			{
    32  				IngressCommonRule: IngressCommonRule{
    33  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
    34  				},
    35  				ToPorts: []PortRule{{
    36  					Ports: []PortProtocol{
    37  						{Port: "80", Protocol: ProtoTCP},
    38  						{Port: "81", Protocol: ProtoTCP},
    39  					},
    40  					Rules: &L7Rules{
    41  						HTTP: []PortRuleHTTP{
    42  							{Method: "GET", Path: "/"},
    43  						},
    44  					},
    45  				}},
    46  			},
    47  		},
    48  	}
    49  
    50  	err := validPortRule.Sanitize()
    51  	require.Nil(t, err)
    52  
    53  	// Rule is invalid because no port is specified for DNS proxy rule.
    54  	validPortRule = Rule{
    55  		EndpointSelector: WildcardEndpointSelector,
    56  		Egress: []EgressRule{
    57  			{
    58  				EgressCommonRule: EgressCommonRule{
    59  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
    60  				},
    61  				ToPorts: []PortRule{{
    62  					Rules: &L7Rules{
    63  						DNS: []PortRuleDNS{
    64  							{MatchName: "domain.com"},
    65  						},
    66  					},
    67  				}},
    68  			},
    69  		},
    70  	}
    71  
    72  	err = validPortRule.Sanitize()
    73  	require.Error(t, err, "Port 53 must be specified for DNS rules")
    74  
    75  	// Rule is valid because all protocols are allowed for L7 rules with ToFQDNs.
    76  	validPortRule = Rule{
    77  		EndpointSelector: WildcardEndpointSelector,
    78  		Egress: []EgressRule{
    79  			{
    80  				EgressCommonRule: EgressCommonRule{
    81  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
    82  				},
    83  				ToPorts: []PortRule{{
    84  					Ports: []PortProtocol{
    85  						{Port: "53", Protocol: ProtoTCP},
    86  						{Port: "53", Protocol: ProtoUDP},
    87  					},
    88  					Rules: &L7Rules{
    89  						DNS: []PortRuleDNS{
    90  							{MatchName: "domain.com"},
    91  						},
    92  					},
    93  				}},
    94  			},
    95  		},
    96  	}
    97  
    98  	err = validPortRule.Sanitize()
    99  	require.NoError(t, err, "Saw an error for a L7 rule with DNS rules. This should be allowed.")
   100  
   101  	validSCTPRule := Rule{
   102  		EndpointSelector: WildcardEndpointSelector,
   103  		Egress: []EgressRule{
   104  			{
   105  				EgressCommonRule: EgressCommonRule{
   106  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
   107  				},
   108  				ToPorts: []PortRule{{
   109  					Ports: []PortProtocol{
   110  						{Port: "4000", Protocol: ProtoSCTP},
   111  					},
   112  				}},
   113  			},
   114  		},
   115  	}
   116  
   117  	err = validSCTPRule.Sanitize()
   118  	require.NoError(t, err, "Saw an error for an SCTP rule.")
   119  
   120  	// Rule is invalid because only ProtoTCP is allowed for L7 rules (except with DNS, below).
   121  	invalidPortRule := Rule{
   122  		EndpointSelector: WildcardEndpointSelector,
   123  		Ingress: []IngressRule{
   124  			{
   125  				IngressCommonRule: IngressCommonRule{
   126  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   127  				},
   128  				ToPorts: []PortRule{{
   129  					Ports: []PortProtocol{
   130  						{Port: "80", Protocol: ProtoUDP},
   131  					},
   132  					Rules: &L7Rules{
   133  						HTTP: []PortRuleHTTP{
   134  							{Method: "GET", Path: "/"},
   135  						},
   136  					},
   137  				}},
   138  			},
   139  		},
   140  	}
   141  
   142  	err = invalidPortRule.Sanitize()
   143  	require.ErrorContains(t, err, "L7 rules can only apply to TCP (not UDP) except for DNS rules")
   144  
   145  	// Rule is invalid because DNS proxy rules are not allowed on ingress rules.
   146  	invalidPortRule = Rule{
   147  		EndpointSelector: WildcardEndpointSelector,
   148  		Ingress: []IngressRule{
   149  			{
   150  				IngressCommonRule: IngressCommonRule{
   151  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   152  				},
   153  				ToPorts: []PortRule{{
   154  					Ports: []PortProtocol{
   155  						{Port: "53", Protocol: ProtoAny},
   156  					},
   157  					Rules: &L7Rules{
   158  						DNS: []PortRuleDNS{
   159  							{MatchName: "domain.com"},
   160  						},
   161  					},
   162  				}},
   163  			},
   164  		},
   165  	}
   166  
   167  	err = invalidPortRule.Sanitize()
   168  	require.Error(t, err, "DNS rule should not be allowed on ingress")
   169  
   170  	// Rule is invalid because only ProtoTCP is allowed for L7 rules (except with DNS, below).
   171  	invalidPortRule = Rule{
   172  		EndpointSelector: WildcardEndpointSelector,
   173  		Ingress: []IngressRule{
   174  			{
   175  				IngressCommonRule: IngressCommonRule{
   176  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   177  				},
   178  				ToPorts: []PortRule{{
   179  					Ports: []PortProtocol{
   180  						{Port: "80", Protocol: ProtoAny},
   181  					},
   182  					Rules: &L7Rules{
   183  						HTTP: []PortRuleHTTP{
   184  							{Method: "GET", Path: "/"},
   185  						},
   186  					},
   187  				}},
   188  			},
   189  		},
   190  	}
   191  
   192  	err = invalidPortRule.Sanitize()
   193  	require.NotNil(t, err)
   194  	require.Equal(t, "L7 rules can only apply to TCP (not ANY) except for DNS rules", err.Error())
   195  
   196  	// Rule is invalid because only ProtoTCP is allowed for L7 rules (except with DNS, below).
   197  	invalidPortRule = Rule{
   198  		EndpointSelector: WildcardEndpointSelector,
   199  		Ingress: []IngressRule{
   200  			{
   201  				IngressCommonRule: IngressCommonRule{
   202  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   203  				},
   204  				ToPorts: []PortRule{{
   205  					Ports: []PortProtocol{
   206  						{Port: "80", Protocol: ProtoTCP},
   207  						{Port: "12345", Protocol: ProtoUDP},
   208  					},
   209  					Rules: &L7Rules{
   210  						HTTP: []PortRuleHTTP{
   211  							{Method: "GET", Path: "/"},
   212  						},
   213  					},
   214  				}},
   215  			},
   216  		},
   217  	}
   218  
   219  	err = invalidPortRule.Sanitize()
   220  	require.NotNil(t, err)
   221  	require.Equal(t, "L7 rules can only apply to TCP (not UDP) except for DNS rules", err.Error())
   222  
   223  	// Same as previous rule, but ensure ordering doesn't affect validation.
   224  	invalidPortRule = Rule{
   225  		EndpointSelector: WildcardEndpointSelector,
   226  		Ingress: []IngressRule{
   227  			{
   228  				IngressCommonRule: IngressCommonRule{
   229  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   230  				},
   231  				ToPorts: []PortRule{{
   232  					Ports: []PortProtocol{
   233  						{Port: "80", Protocol: ProtoUDP},
   234  						{Port: "12345", Protocol: ProtoTCP},
   235  					},
   236  					Rules: &L7Rules{
   237  						HTTP: []PortRuleHTTP{
   238  							{Method: "GET", Path: "/"},
   239  						},
   240  					},
   241  				}},
   242  			},
   243  		},
   244  	}
   245  
   246  	err = invalidPortRule.Sanitize()
   247  	require.NotNil(t, err)
   248  	require.Equal(t, "L7 rules can only apply to TCP (not UDP) except for DNS rules", err.Error())
   249  
   250  	// Rule is valid because ServerNames are allowed for SNI enforcement.
   251  	validPortRule = Rule{
   252  		EndpointSelector: WildcardEndpointSelector,
   253  		Egress: []EgressRule{
   254  			{
   255  				EgressCommonRule: EgressCommonRule{
   256  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
   257  				},
   258  				ToPorts: []PortRule{{
   259  					Ports: []PortProtocol{
   260  						{Port: "443", Protocol: ProtoTCP},
   261  					},
   262  					ServerNames: []string{"foo.bar.com", "bar.foo.com"},
   263  				}},
   264  			},
   265  		},
   266  	}
   267  	err = validPortRule.Sanitize()
   268  	require.Nil(t, err)
   269  
   270  	// Rule is invalid because empty ServerNames are not allowed
   271  	invalidPortRule = Rule{
   272  		EndpointSelector: WildcardEndpointSelector,
   273  		Egress: []EgressRule{
   274  			{
   275  				EgressCommonRule: EgressCommonRule{
   276  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
   277  				},
   278  				ToPorts: []PortRule{{
   279  					Ports: []PortProtocol{
   280  						{Port: "443", Protocol: ProtoTCP},
   281  					},
   282  					ServerNames: []string{""},
   283  				}},
   284  			},
   285  		},
   286  	}
   287  	err = invalidPortRule.Sanitize()
   288  	require.NotNil(t, err)
   289  	require.Equal(t, "Empty server name is not allowed", err.Error())
   290  
   291  	//  Rule is invalid because ServerNames with L7 rules are not allowed without TLS termination.
   292  	invalidPortRule = Rule{
   293  		EndpointSelector: WildcardEndpointSelector,
   294  		Egress: []EgressRule{
   295  			{
   296  				EgressCommonRule: EgressCommonRule{
   297  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
   298  				},
   299  				ToPorts: []PortRule{{
   300  					Ports: []PortProtocol{
   301  						{Port: "443", Protocol: ProtoTCP},
   302  					},
   303  					ServerNames: []string{"foo.bar.com", "bar.foo.com"},
   304  					Rules: &L7Rules{
   305  						HTTP: []PortRuleHTTP{
   306  							{Method: "GET", Path: "/"},
   307  						},
   308  					},
   309  				}},
   310  			},
   311  		},
   312  	}
   313  	err = invalidPortRule.Sanitize()
   314  	require.NotNil(t, err)
   315  	require.Equal(t, "ServerNames are not allowed with L7 rules without TLS termination", err.Error())
   316  
   317  	// Rule is valid because ServerNames with L7 rules are allowed with TLS termination.
   318  	validPortRule = Rule{
   319  		EndpointSelector: WildcardEndpointSelector,
   320  		Egress: []EgressRule{
   321  			{
   322  				EgressCommonRule: EgressCommonRule{
   323  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
   324  				},
   325  				ToPorts: []PortRule{{
   326  					Ports: []PortProtocol{
   327  						{Port: "443", Protocol: ProtoTCP},
   328  					},
   329  					TerminatingTLS: &TLSContext{
   330  						Secret: &Secret{
   331  							Name: "test-secret",
   332  						},
   333  					},
   334  					ServerNames: []string{"foo.bar.com", "bar.foo.com"},
   335  					Rules: &L7Rules{
   336  						HTTP: []PortRuleHTTP{
   337  							{Method: "GET", Path: "/"},
   338  						},
   339  					},
   340  				}},
   341  			},
   342  		},
   343  	}
   344  	err = validPortRule.Sanitize()
   345  	require.Nil(t, err)
   346  
   347  	// Rule is valid because Listener is allowed on egress, default Kind
   348  	validPortRule = Rule{
   349  		EndpointSelector: WildcardEndpointSelector,
   350  		Egress: []EgressRule{
   351  			{
   352  				EgressCommonRule: EgressCommonRule{
   353  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
   354  				},
   355  				ToPorts: []PortRule{{
   356  					Ports: []PortProtocol{
   357  						{Port: "443", Protocol: ProtoTCP},
   358  					},
   359  					Listener: &Listener{
   360  						EnvoyConfig: &EnvoyConfig{
   361  							Name: "test-config",
   362  						},
   363  						Name: "myCustomListener",
   364  					},
   365  				}},
   366  			},
   367  		},
   368  	}
   369  	err = validPortRule.Sanitize()
   370  	require.Nil(t, err)
   371  
   372  	// Rule is valid because Listener is allowed on egress, Kind CiliumClusterwideEnvoyConfig
   373  	validPortRule = Rule{
   374  		EndpointSelector: WildcardEndpointSelector,
   375  		Egress: []EgressRule{
   376  			{
   377  				EgressCommonRule: EgressCommonRule{
   378  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
   379  				},
   380  				ToPorts: []PortRule{{
   381  					Ports: []PortProtocol{
   382  						{Port: "443", Protocol: ProtoTCP},
   383  					},
   384  					Listener: &Listener{
   385  						EnvoyConfig: &EnvoyConfig{
   386  							Kind: "CiliumClusterwideEnvoyConfig",
   387  							Name: "shared-config",
   388  						},
   389  						Name: "myCustomListener",
   390  					},
   391  				}},
   392  			},
   393  		},
   394  	}
   395  	err = validPortRule.Sanitize()
   396  	require.Nil(t, err)
   397  
   398  	// Rule is valid because Listener is allowed on egress, Kind CiliumEnvoyConfig
   399  	validPortRule = Rule{
   400  		EndpointSelector: WildcardEndpointSelector,
   401  		Egress: []EgressRule{
   402  			{
   403  				EgressCommonRule: EgressCommonRule{
   404  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
   405  				},
   406  				ToPorts: []PortRule{{
   407  					Ports: []PortProtocol{
   408  						{Port: "443", Protocol: ProtoTCP},
   409  					},
   410  					Listener: &Listener{
   411  						EnvoyConfig: &EnvoyConfig{
   412  							Kind: "CiliumEnvoyConfig",
   413  							Name: "shared-config",
   414  						},
   415  						Name: "myCustomListener",
   416  					},
   417  				}},
   418  			},
   419  		},
   420  	}
   421  	err = validPortRule.Sanitize()
   422  	require.Nil(t, err)
   423  
   424  	// Rule is invalid because Listener is not allowed on ingress (yet)
   425  	invalidPortRule = Rule{
   426  		EndpointSelector: WildcardEndpointSelector,
   427  		Ingress: []IngressRule{
   428  			{
   429  				IngressCommonRule: IngressCommonRule{
   430  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   431  				},
   432  				ToPorts: []PortRule{{
   433  					Ports: []PortProtocol{
   434  						{Port: "443", Protocol: ProtoTCP},
   435  					},
   436  					Listener: &Listener{
   437  						EnvoyConfig: &EnvoyConfig{
   438  							Name: "test-config",
   439  						},
   440  						Name: "myCustomListener",
   441  					},
   442  				}},
   443  			},
   444  		},
   445  	}
   446  	err = invalidPortRule.Sanitize()
   447  	require.NotNil(t, err)
   448  	require.Equal(t, "Listener is not allowed on ingress (myCustomListener)", err.Error())
   449  
   450  	// Rule is invalid because Listener is not allowed with L7 rules
   451  	invalidPortRule = Rule{
   452  		EndpointSelector: WildcardEndpointSelector,
   453  		Egress: []EgressRule{
   454  			{
   455  				EgressCommonRule: EgressCommonRule{
   456  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
   457  				},
   458  				ToPorts: []PortRule{{
   459  					Ports: []PortProtocol{
   460  						{Port: "443", Protocol: ProtoTCP},
   461  					},
   462  					Listener: &Listener{
   463  						EnvoyConfig: &EnvoyConfig{
   464  							Name: "test-config",
   465  						},
   466  						Name: "myCustomListener",
   467  					},
   468  					Rules: &L7Rules{
   469  						HTTP: []PortRuleHTTP{
   470  							{Method: "GET", Path: "/"},
   471  						},
   472  					},
   473  				}},
   474  			},
   475  		},
   476  	}
   477  	err = invalidPortRule.Sanitize()
   478  	require.NotNil(t, err)
   479  	require.Equal(t, "Listener is not allowed with L7 rules (myCustomListener)", err.Error())
   480  }
   481  
   482  // This test ensures that L7 rules reject unspecified ports.
   483  func TestL7RuleRejectsEmptyPort(t *testing.T) {
   484  	setUpSuite(t)
   485  
   486  	invalidL7PortRule := Rule{
   487  		EndpointSelector: WildcardEndpointSelector,
   488  		Ingress: []IngressRule{
   489  			{
   490  				IngressCommonRule: IngressCommonRule{
   491  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   492  				},
   493  				ToPorts: []PortRule{{
   494  					Ports: []PortProtocol{
   495  						{Port: "0", Protocol: ProtoTCP},
   496  					},
   497  					Rules: &L7Rules{
   498  						HTTP: []PortRuleHTTP{
   499  							{},
   500  						},
   501  					},
   502  				}},
   503  			},
   504  		},
   505  	}
   506  
   507  	err := invalidL7PortRule.Sanitize()
   508  	require.NotNil(t, err)
   509  }
   510  
   511  // This test ensures that PortRules using the HTTP protocol have valid regular
   512  // expressions for the method and path fields.
   513  func TestHTTPRuleRegexes(t *testing.T) {
   514  	setUpSuite(t)
   515  
   516  	invalidHTTPRegexPathRule := Rule{
   517  		EndpointSelector: WildcardEndpointSelector,
   518  		Ingress: []IngressRule{
   519  			{
   520  				IngressCommonRule: IngressCommonRule{
   521  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   522  				},
   523  				ToPorts: []PortRule{{
   524  					Ports: []PortProtocol{
   525  						{Port: "80", Protocol: ProtoTCP},
   526  						{Port: "81", Protocol: ProtoTCP},
   527  					},
   528  					Rules: &L7Rules{
   529  						HTTP: []PortRuleHTTP{
   530  							{Method: "GET", Path: "*"},
   531  						},
   532  					},
   533  				}},
   534  			},
   535  		},
   536  	}
   537  
   538  	err := invalidHTTPRegexPathRule.Sanitize()
   539  	require.NotNil(t, err)
   540  
   541  	invalidHTTPRegexMethodRule := Rule{
   542  		EndpointSelector: WildcardEndpointSelector,
   543  		Ingress: []IngressRule{
   544  			{
   545  				IngressCommonRule: IngressCommonRule{
   546  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   547  				},
   548  				ToPorts: []PortRule{{
   549  					Ports: []PortProtocol{
   550  						{Port: "80", Protocol: ProtoTCP},
   551  						{Port: "81", Protocol: ProtoTCP},
   552  					},
   553  					Rules: &L7Rules{
   554  						HTTP: []PortRuleHTTP{
   555  							{Method: "*", Path: "/"},
   556  						},
   557  					},
   558  				}},
   559  			},
   560  		},
   561  	}
   562  
   563  	err = invalidHTTPRegexMethodRule.Sanitize()
   564  	require.NotNil(t, err)
   565  }
   566  
   567  // Test the validation of CIDR rule prefix definitions
   568  func TestCIDRsanitize(t *testing.T) {
   569  	setUpSuite(t)
   570  
   571  	// IPv4
   572  	cidr := CIDRRule{Cidr: "0.0.0.0/0"}
   573  	err := cidr.sanitize()
   574  	require.Nil(t, err)
   575  
   576  	cidr = CIDRRule{Cidr: "10.0.0.0/24"}
   577  	err = cidr.sanitize()
   578  	require.Nil(t, err)
   579  
   580  	cidr = CIDRRule{Cidr: "192.0.2.3/32"}
   581  	err = cidr.sanitize()
   582  	require.Nil(t, err)
   583  
   584  	// IPv6
   585  	cidr = CIDRRule{Cidr: "::/0"}
   586  	err = cidr.sanitize()
   587  	require.Nil(t, err)
   588  
   589  	cidr = CIDRRule{Cidr: "ff02::/64"}
   590  	err = cidr.sanitize()
   591  	require.Nil(t, err)
   592  
   593  	cidr = CIDRRule{Cidr: "", CIDRGroupRef: "cidrgroup"}
   594  	err = cidr.sanitize()
   595  	require.Nil(t, err)
   596  
   597  	cidr = CIDRRule{Cidr: "2001:0db8:85a3:0000:0000:8a2e:0370:7334/128"}
   598  	err = cidr.sanitize()
   599  	require.Nil(t, err)
   600  
   601  	// Non-contiguous mask.
   602  	cidr = CIDRRule{Cidr: "10.0.0.0/254.0.0.255"}
   603  	err = cidr.sanitize()
   604  	require.Error(t, err)
   605  }
   606  
   607  func TestToServicesSanitize(t *testing.T) {
   608  	setUpSuite(t)
   609  
   610  	svcLabels := map[string]string{
   611  		"app": "tested-service",
   612  	}
   613  	selector := ServiceSelector(NewESFromMatchRequirements(svcLabels, nil))
   614  	toServicesL3L4 := Rule{
   615  		EndpointSelector: WildcardEndpointSelector,
   616  		Egress: []EgressRule{
   617  			{
   618  				EgressCommonRule: EgressCommonRule{
   619  					ToServices: []Service{
   620  						{
   621  							K8sServiceSelector: &K8sServiceSelectorNamespace{
   622  								Selector:  selector,
   623  								Namespace: "",
   624  							},
   625  						},
   626  					},
   627  				},
   628  				ToPorts: []PortRule{{
   629  					Ports: []PortProtocol{
   630  						{Port: "80", Protocol: ProtoTCP},
   631  						{Port: "81", Protocol: ProtoTCP},
   632  					},
   633  				}},
   634  			},
   635  		},
   636  	}
   637  
   638  	err := toServicesL3L4.Sanitize()
   639  	require.Error(t, err)
   640  }
   641  
   642  // This test ensures that PortRules using key-value pairs do not have empty keys
   643  func TestL7Rules(t *testing.T) {
   644  	setUpSuite(t)
   645  
   646  	validL7Rule := Rule{
   647  		EndpointSelector: WildcardEndpointSelector,
   648  		Ingress: []IngressRule{
   649  			{
   650  				IngressCommonRule: IngressCommonRule{
   651  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   652  				},
   653  				ToPorts: []PortRule{{
   654  					Ports: []PortProtocol{
   655  						{Port: "80", Protocol: ProtoTCP},
   656  						{Port: "81", Protocol: ProtoTCP},
   657  					},
   658  					Rules: &L7Rules{
   659  						L7Proto: "test.lineparser",
   660  						L7: []PortRuleL7{
   661  							{"method": "PUT", "path": "/"},
   662  							{"method": "GET", "path": "/"},
   663  						},
   664  					},
   665  				}},
   666  			},
   667  		},
   668  	}
   669  
   670  	err := validL7Rule.Sanitize()
   671  	require.Nil(t, err)
   672  
   673  	validL7Rule2 := Rule{
   674  		EndpointSelector: WildcardEndpointSelector,
   675  		Ingress: []IngressRule{
   676  			{
   677  				IngressCommonRule: IngressCommonRule{
   678  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   679  				},
   680  				ToPorts: []PortRule{{
   681  					Ports: []PortProtocol{
   682  						{Port: "80", Protocol: ProtoTCP},
   683  						{Port: "81", Protocol: ProtoTCP},
   684  					},
   685  					Rules: &L7Rules{
   686  						L7Proto: "test.lineparser",
   687  						// No L7 rules
   688  					},
   689  				}},
   690  			},
   691  		},
   692  	}
   693  
   694  	err = validL7Rule2.Sanitize()
   695  	require.Nil(t, err)
   696  
   697  	invalidL7Rule := Rule{
   698  		EndpointSelector: WildcardEndpointSelector,
   699  		Ingress: []IngressRule{
   700  			{
   701  				IngressCommonRule: IngressCommonRule{
   702  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   703  				},
   704  				ToPorts: []PortRule{{
   705  					Ports: []PortProtocol{
   706  						{Port: "80", Protocol: ProtoTCP},
   707  						{Port: "81", Protocol: ProtoTCP},
   708  					},
   709  					Rules: &L7Rules{
   710  						L7Proto: "test.lineparser",
   711  						L7: []PortRuleL7{
   712  							map[string]string{
   713  								"method": "PUT",
   714  								"":       "Foo"},
   715  						},
   716  					},
   717  				}},
   718  			},
   719  		},
   720  	}
   721  
   722  	err = invalidL7Rule.Sanitize()
   723  	require.NotNil(t, err)
   724  }
   725  
   726  // This test ensures that DNS rules do not accept port ranges
   727  func TestPortRangesNotAllowedWithDNSRules(t *testing.T) {
   728  	// Rule is invalid because DNS rules do not support port ranges.
   729  	invalidPortRule := Rule{
   730  		EndpointSelector: WildcardEndpointSelector,
   731  		Egress: []EgressRule{
   732  			{
   733  				EgressCommonRule: EgressCommonRule{
   734  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
   735  				},
   736  				ToPorts: []PortRule{{
   737  					Ports: []PortProtocol{
   738  						{Port: "443", EndPort: 445, Protocol: ProtoTCP},
   739  					},
   740  					Rules: &L7Rules{
   741  						DNS: []PortRuleDNS{
   742  							{MatchName: "www.google.com"},
   743  						},
   744  					},
   745  				}},
   746  			},
   747  		},
   748  	}
   749  	err := invalidPortRule.Sanitize()
   750  	require.NotNil(t, err)
   751  	require.Equal(t, "DNS rules do not support port ranges", err.Error())
   752  }
   753  
   754  // This test ensures that host policies with L7 rules are rejected.
   755  func TestL7RulesWithNodeSelector(t *testing.T) {
   756  	setUpSuite(t)
   757  
   758  	invalidL7RuleIngress := Rule{
   759  		NodeSelector: WildcardEndpointSelector,
   760  		Ingress: []IngressRule{
   761  			{
   762  				IngressCommonRule: IngressCommonRule{
   763  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   764  				},
   765  				ToPorts: []PortRule{{
   766  					Ports: []PortProtocol{
   767  						{Port: "80", Protocol: ProtoTCP},
   768  					},
   769  					Rules: &L7Rules{
   770  						HTTP: []PortRuleHTTP{
   771  							{Method: "PUT", Path: "/"},
   772  						},
   773  					},
   774  				}},
   775  			},
   776  		},
   777  	}
   778  	err := invalidL7RuleIngress.Sanitize()
   779  	require.Equal(t, "host policies do not support L7 rules yet", err.Error())
   780  
   781  	invalidL7RuleEgress := Rule{
   782  		NodeSelector: WildcardEndpointSelector,
   783  		Egress: []EgressRule{
   784  			{
   785  				EgressCommonRule: EgressCommonRule{
   786  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
   787  				},
   788  				ToPorts: []PortRule{{
   789  					Ports: []PortProtocol{
   790  						{Port: "53", Protocol: ProtoUDP},
   791  					},
   792  					Rules: &L7Rules{
   793  						DNS: []PortRuleDNS{
   794  							{MatchName: "domain.com"},
   795  						},
   796  					},
   797  				}},
   798  			},
   799  		},
   800  	}
   801  	err = invalidL7RuleEgress.Sanitize()
   802  	require.Equal(t, "host policies do not support L7 rules yet", err.Error())
   803  
   804  	validL7RuleIngress := Rule{
   805  		NodeSelector: WildcardEndpointSelector,
   806  		Ingress: []IngressRule{
   807  			{
   808  				IngressCommonRule: IngressCommonRule{
   809  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   810  				},
   811  			},
   812  		},
   813  	}
   814  	err = validL7RuleIngress.Sanitize()
   815  	require.Nil(t, err)
   816  }
   817  
   818  func TestInvalidEndpointSelectors(t *testing.T) {
   819  	setUpSuite(t)
   820  
   821  	// Operator in MatchExpressions is invalid, so sanitization should fail.
   822  	labelSel := &slim_metav1.LabelSelector{
   823  		MatchLabels: map[string]string{
   824  			"any.foo": "bar",
   825  			"k8s.baz": "alice",
   826  		},
   827  		MatchExpressions: []slim_metav1.LabelSelectorRequirement{
   828  			{
   829  				Key:      "any.foo",
   830  				Operator: "asdfasdfasdf",
   831  				Values:   []string{"default"},
   832  			},
   833  		},
   834  	}
   835  
   836  	invalidSel := NewESFromK8sLabelSelector(labels.LabelSourceK8sKeyPrefix, labelSel)
   837  
   838  	invalidEpSelectorRule := Rule{
   839  		EndpointSelector: invalidSel,
   840  	}
   841  
   842  	err := invalidEpSelectorRule.Sanitize()
   843  	require.NotNil(t, err)
   844  
   845  	invalidEpSelectorIngress := Rule{
   846  		EndpointSelector: WildcardEndpointSelector,
   847  		Ingress: []IngressRule{
   848  			{
   849  				IngressCommonRule: IngressCommonRule{
   850  					FromEndpoints: []EndpointSelector{invalidSel},
   851  				},
   852  			},
   853  		},
   854  	}
   855  
   856  	err = invalidEpSelectorIngress.Sanitize()
   857  	require.NotNil(t, err)
   858  
   859  	invalidEpSelectorIngressFromReq := Rule{
   860  		EndpointSelector: WildcardEndpointSelector,
   861  		Ingress: []IngressRule{
   862  			{
   863  				IngressCommonRule: IngressCommonRule{
   864  					FromRequires: []EndpointSelector{invalidSel},
   865  				},
   866  			},
   867  		},
   868  	}
   869  
   870  	err = invalidEpSelectorIngressFromReq.Sanitize()
   871  	require.NotNil(t, err)
   872  
   873  	invalidEpSelectorEgress := Rule{
   874  		EndpointSelector: WildcardEndpointSelector,
   875  		Egress: []EgressRule{
   876  			{
   877  				EgressCommonRule: EgressCommonRule{
   878  					ToEndpoints: []EndpointSelector{invalidSel},
   879  				},
   880  			},
   881  		},
   882  	}
   883  
   884  	err = invalidEpSelectorEgress.Sanitize()
   885  	require.NotNil(t, err)
   886  
   887  	invalidEpSelectorEgressToReq := Rule{
   888  		EndpointSelector: WildcardEndpointSelector,
   889  		Egress: []EgressRule{
   890  			{
   891  				EgressCommonRule: EgressCommonRule{
   892  					ToRequires: []EndpointSelector{invalidSel},
   893  				},
   894  			},
   895  		},
   896  	}
   897  
   898  	err = invalidEpSelectorEgressToReq.Sanitize()
   899  	require.NotNil(t, err)
   900  
   901  }
   902  
   903  func TestNodeSelector(t *testing.T) {
   904  	setUpSuite(t)
   905  
   906  	// Operator in MatchExpressions is invalid, so sanitization should fail.
   907  	labelSel := &slim_metav1.LabelSelector{
   908  		MatchLabels: map[string]string{
   909  			"any.foo": "bar",
   910  			"k8s.baz": "alice",
   911  		},
   912  		MatchExpressions: []slim_metav1.LabelSelectorRequirement{
   913  			{
   914  				Key:      "any.foo",
   915  				Operator: "asdfasdfasdf",
   916  				Values:   []string{"default"},
   917  			},
   918  		},
   919  	}
   920  	invalidSel := NewESFromK8sLabelSelector(labels.LabelSourceK8sKeyPrefix, labelSel)
   921  	invalidNodeSelectorRule := Rule{
   922  		NodeSelector: invalidSel,
   923  	}
   924  	err := invalidNodeSelectorRule.Sanitize()
   925  	require.EqualError(t, err, "invalid label selector: matchExpressions[0].operator: Invalid value: \"asdfasdfasdf\": not a valid selector operator")
   926  
   927  	invalidRuleBothSelectors := Rule{
   928  		EndpointSelector: WildcardEndpointSelector,
   929  		NodeSelector:     WildcardEndpointSelector,
   930  	}
   931  	err = invalidRuleBothSelectors.Sanitize()
   932  	require.Equal(t, "rule cannot have both EndpointSelector and NodeSelector", err.Error())
   933  
   934  	invalidRuleNoSelector := Rule{}
   935  	err = invalidRuleNoSelector.Sanitize()
   936  	require.Equal(t, "rule must have one of EndpointSelector or NodeSelector", err.Error())
   937  }
   938  
   939  func TestTooManyPortsRule(t *testing.T) {
   940  	setUpSuite(t)
   941  
   942  	var portProtocols []PortProtocol
   943  
   944  	for i := 80; i <= 80+maxPorts; i++ {
   945  		portProtocols = append(portProtocols, PortProtocol{
   946  			Port:     fmt.Sprintf("%d", i),
   947  			Protocol: ProtoTCP,
   948  		})
   949  	}
   950  
   951  	tooManyPortsRule := Rule{
   952  		EndpointSelector: WildcardEndpointSelector,
   953  		Ingress: []IngressRule{
   954  			{
   955  				IngressCommonRule: IngressCommonRule{
   956  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   957  				},
   958  				ToPorts: []PortRule{{
   959  					Ports: portProtocols,
   960  				}},
   961  			},
   962  		},
   963  	}
   964  	err := tooManyPortsRule.Sanitize()
   965  	require.Error(t, err)
   966  }
   967  
   968  func TestTooManyICMPFields(t *testing.T) {
   969  	setUpSuite(t)
   970  
   971  	var fields []ICMPField
   972  
   973  	for i := 1; i <= 1+maxICMPFields; i++ {
   974  		icmpType := intstr.FromInt(i)
   975  		fields = append(fields, ICMPField{
   976  			Type: &icmpType,
   977  		})
   978  	}
   979  
   980  	tooManyICMPRule := Rule{
   981  		EndpointSelector: WildcardEndpointSelector,
   982  		Ingress: []IngressRule{
   983  			{
   984  				IngressCommonRule: IngressCommonRule{
   985  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
   986  				},
   987  				ICMPs: ICMPRules{{
   988  					Fields: fields,
   989  				}},
   990  			},
   991  		},
   992  	}
   993  	err := tooManyICMPRule.Sanitize()
   994  	require.Error(t, err)
   995  }
   996  
   997  func TestWrongICMPFieldFamily(t *testing.T) {
   998  	setUpSuite(t)
   999  
  1000  	icmpType := intstr.FromInt(0)
  1001  	wrongFamilyICMPRule := Rule{
  1002  		EndpointSelector: WildcardEndpointSelector,
  1003  		Ingress: []IngressRule{
  1004  			{
  1005  				IngressCommonRule: IngressCommonRule{
  1006  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
  1007  				},
  1008  				ICMPs: ICMPRules{{
  1009  					Fields: []ICMPField{{
  1010  						Family: "hoge",
  1011  						Type:   &icmpType,
  1012  					}},
  1013  				}},
  1014  			},
  1015  		},
  1016  	}
  1017  	err := wrongFamilyICMPRule.Sanitize()
  1018  	require.Error(t, err)
  1019  }
  1020  
  1021  func TestICMPRuleWithOtherRuleFailed(t *testing.T) {
  1022  	setUpSuite(t)
  1023  
  1024  	icmpType := intstr.FromInt(8)
  1025  	ingressICMPWithPort := Rule{
  1026  		EndpointSelector: WildcardEndpointSelector,
  1027  		Ingress: []IngressRule{
  1028  			{
  1029  				IngressCommonRule: IngressCommonRule{
  1030  					FromEndpoints: []EndpointSelector{WildcardEndpointSelector},
  1031  				},
  1032  				ToPorts: []PortRule{{
  1033  					Ports: []PortProtocol{
  1034  						{Port: "80", Protocol: ProtoTCP},
  1035  					},
  1036  				}},
  1037  				ICMPs: ICMPRules{{
  1038  					Fields: []ICMPField{{
  1039  						Type: &icmpType,
  1040  					}},
  1041  				}},
  1042  			},
  1043  		},
  1044  	}
  1045  
  1046  	egressICMPWithPort := Rule{
  1047  		EndpointSelector: WildcardEndpointSelector,
  1048  		Egress: []EgressRule{
  1049  			{
  1050  				EgressCommonRule: EgressCommonRule{
  1051  					ToEndpoints: []EndpointSelector{WildcardEndpointSelector},
  1052  				},
  1053  				ToPorts: []PortRule{{
  1054  					Ports: []PortProtocol{
  1055  						{Port: "80", Protocol: ProtoTCP},
  1056  					},
  1057  				}},
  1058  				ICMPs: ICMPRules{{
  1059  					Fields: []ICMPField{{
  1060  						Type: &icmpType,
  1061  					}},
  1062  				}},
  1063  			},
  1064  		},
  1065  	}
  1066  
  1067  	option.Config.EnableICMPRules = true
  1068  	errStr := "The ICMPs block may only be present without ToPorts. Define a separate rule to use ToPorts."
  1069  	err := ingressICMPWithPort.Sanitize()
  1070  	require.ErrorContains(t, err, errStr)
  1071  	err = egressICMPWithPort.Sanitize()
  1072  	require.ErrorContains(t, err, errStr)
  1073  }
  1074  
  1075  // This test ensures that PortRules aren't configured in the wrong direction,
  1076  // which ends up being a no-op with only vague error messages rather than a
  1077  // clear indication that something is wrong in the policy.
  1078  func TestL7RuleDirectionalitySupport(t *testing.T) {
  1079  	setUpSuite(t)
  1080  
  1081  	// Kafka egress is now supported.
  1082  	egressKafkaRule := Rule{
  1083  		EndpointSelector: WildcardEndpointSelector,
  1084  		Egress: []EgressRule{
  1085  			{
  1086  				ToPorts: []PortRule{{
  1087  					Ports: []PortProtocol{
  1088  						{Port: "80", Protocol: ProtoTCP},
  1089  						{Port: "81", Protocol: ProtoTCP},
  1090  					},
  1091  					Rules: &L7Rules{
  1092  						Kafka: []kafka.PortRule{{
  1093  							Role:  "consume",
  1094  							Topic: "deathstar-plans",
  1095  						}},
  1096  					},
  1097  				}},
  1098  			},
  1099  		},
  1100  	}
  1101  
  1102  	err := egressKafkaRule.Sanitize()
  1103  	require.Nil(t, err)
  1104  
  1105  	// DNS ingress is not supported.
  1106  	invalidDNSRule := Rule{
  1107  		EndpointSelector: WildcardEndpointSelector,
  1108  		Ingress: []IngressRule{
  1109  			{
  1110  				ToPorts: []PortRule{{
  1111  					Ports: []PortProtocol{
  1112  						{Port: "53", Protocol: ProtoTCP},
  1113  						{Port: "53", Protocol: ProtoUDP},
  1114  					},
  1115  					Rules: &L7Rules{
  1116  						DNS: []PortRuleDNS{{
  1117  							MatchName: "empire.gov",
  1118  						}},
  1119  					},
  1120  				}},
  1121  			},
  1122  		},
  1123  	}
  1124  
  1125  	err = invalidDNSRule.Sanitize()
  1126  	require.NotNil(t, err)
  1127  
  1128  }
  1129  
  1130  func BenchmarkCIDRSanitize(b *testing.B) {
  1131  	cidr4 := CIDRRule{Cidr: "192.168.100.200/24"}
  1132  	cidr6 := CIDRRule{Cidr: "2001:0db8:85a3:0000:0000:8a2e:0370:7334/128"}
  1133  
  1134  	b.ReportAllocs()
  1135  	b.ResetTimer()
  1136  	for i := 0; i < b.N; i++ {
  1137  		err := cidr4.sanitize()
  1138  		if err != nil {
  1139  			b.Fatal(err)
  1140  		}
  1141  		err = cidr6.sanitize()
  1142  		if err != nil {
  1143  			b.Fatal(err)
  1144  		}
  1145  	}
  1146  }
  1147  
  1148  func TestSanitizeDefaultDeny(t *testing.T) {
  1149  	for _, tc := range []struct {
  1150  		before      Rule
  1151  		wantIngress bool
  1152  		wantEgress  bool
  1153  	}{
  1154  		{
  1155  			before: Rule{},
  1156  		},
  1157  		{
  1158  			before: Rule{
  1159  				Ingress: []IngressRule{{}},
  1160  			},
  1161  			wantIngress: true,
  1162  		},
  1163  		{
  1164  			before: Rule{
  1165  				IngressDeny: []IngressDenyRule{{}},
  1166  			},
  1167  			wantIngress: true,
  1168  		},
  1169  		{
  1170  			before: Rule{
  1171  				Ingress:     []IngressRule{{}},
  1172  				IngressDeny: []IngressDenyRule{{}},
  1173  			},
  1174  			wantIngress: true,
  1175  		},
  1176  		{
  1177  			before: Rule{
  1178  				Egress:     []EgressRule{{}},
  1179  				EgressDeny: []EgressDenyRule{{}},
  1180  			},
  1181  			wantEgress: true,
  1182  		}, {
  1183  			before: Rule{
  1184  				EgressDeny: []EgressDenyRule{{}},
  1185  			},
  1186  			wantEgress: true,
  1187  		},
  1188  		{
  1189  			before: Rule{
  1190  				Egress: []EgressRule{{}},
  1191  			},
  1192  			wantEgress: true,
  1193  		},
  1194  		{
  1195  			before: Rule{
  1196  				Egress:  []EgressRule{{}},
  1197  				Ingress: []IngressRule{{}},
  1198  			},
  1199  			wantEgress:  true,
  1200  			wantIngress: true,
  1201  		},
  1202  	} {
  1203  		b := tc.before
  1204  		b.EndpointSelector = EndpointSelector{LabelSelector: &slim_metav1.LabelSelector{}}
  1205  
  1206  		err := b.Sanitize()
  1207  		assert.Nil(t, err)
  1208  		assert.NotNil(t, b.EnableDefaultDeny.Egress)
  1209  		assert.NotNil(t, b.EnableDefaultDeny.Ingress)
  1210  
  1211  		assert.Equal(t, tc.wantEgress, *b.EnableDefaultDeny.Egress, "Rule.EnableDefaultDeny.Egress should match")
  1212  		assert.Equal(t, tc.wantIngress, *b.EnableDefaultDeny.Ingress, "Rule.EnableDefaultDeny.Ingress should match")
  1213  	}
  1214  }