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

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Cilium
     3  
     4  package policy
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	stdlog "log"
    10  	"testing"
    11  
    12  	"github.com/cilium/proxy/pkg/policy/api/kafka"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  	"k8s.io/apimachinery/pkg/util/intstr"
    16  
    17  	"github.com/cilium/cilium/api/v1/models"
    18  	"github.com/cilium/cilium/pkg/identity"
    19  	ipcachetypes "github.com/cilium/cilium/pkg/ipcache/types"
    20  	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
    21  	slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1"
    22  	"github.com/cilium/cilium/pkg/labels"
    23  	"github.com/cilium/cilium/pkg/option"
    24  	"github.com/cilium/cilium/pkg/policy/api"
    25  )
    26  
    27  // mustAdd inserts a rule into the policy repository
    28  // This is just a helper function for unit testing.
    29  // Only returns error for signature reasons
    30  func (p *Repository) mustAdd(r api.Rule) (uint64, map[uint16]struct{}, error) {
    31  	p.Mutex.Lock()
    32  	defer p.Mutex.Unlock()
    33  
    34  	if err := r.Sanitize(); err != nil {
    35  		panic(err)
    36  	}
    37  
    38  	newList := make([]*api.Rule, 1)
    39  	newList[0] = &r
    40  	_, rev := p.AddListLocked(newList)
    41  	return rev, map[uint16]struct{}{}, nil
    42  }
    43  
    44  func TestComputePolicyEnforcementAndRules(t *testing.T) {
    45  
    46  	// Cache policy enforcement value from when test was ran to avoid pollution
    47  	// across tests.
    48  	oldPolicyEnable := GetPolicyEnabled()
    49  	defer SetPolicyEnabled(oldPolicyEnable)
    50  
    51  	SetPolicyEnabled(option.DefaultEnforcement)
    52  
    53  	td := newTestData()
    54  	repo := td.repo
    55  
    56  	fooSelectLabel := labels.ParseSelectLabel("foo")
    57  	fooNumericIdentity := 9001
    58  	fooIdentity := identity.NewIdentity(identity.NumericIdentity(fooNumericIdentity), lbls)
    59  	td.addIdentity(fooIdentity)
    60  	fooIngressRule1Label := labels.NewLabel(k8sConst.PolicyLabelName, "fooIngressRule1", labels.LabelSourceAny)
    61  	fooIngressRule2Label := labels.NewLabel(k8sConst.PolicyLabelName, "fooIngressRule2", labels.LabelSourceAny)
    62  	fooEgressRule1Label := labels.NewLabel(k8sConst.PolicyLabelName, "fooEgressRule1", labels.LabelSourceAny)
    63  	fooEgressRule2Label := labels.NewLabel(k8sConst.PolicyLabelName, "fooEgressRule2", labels.LabelSourceAny)
    64  	combinedLabel := labels.NewLabel(k8sConst.PolicyLabelName, "combined", labels.LabelSourceAny)
    65  	initIdentity := identity.LookupReservedIdentity(identity.ReservedIdentityInit)
    66  
    67  	fooIngressRule1 := api.Rule{
    68  		EndpointSelector: api.NewESFromLabels(fooSelectLabel),
    69  		Ingress: []api.IngressRule{
    70  			{
    71  				IngressCommonRule: api.IngressCommonRule{
    72  					FromEndpoints: []api.EndpointSelector{
    73  						api.NewESFromLabels(fooSelectLabel),
    74  					},
    75  				},
    76  			},
    77  		},
    78  		Labels: labels.LabelArray{
    79  			fooIngressRule1Label,
    80  		},
    81  	}
    82  	fooIngressRule1.Sanitize()
    83  
    84  	fooIngressRule2 := api.Rule{
    85  		EndpointSelector: api.NewESFromLabels(fooSelectLabel),
    86  		Ingress: []api.IngressRule{
    87  			{
    88  				IngressCommonRule: api.IngressCommonRule{
    89  					FromEndpoints: []api.EndpointSelector{
    90  						api.NewESFromLabels(fooSelectLabel),
    91  					},
    92  				},
    93  			},
    94  		},
    95  		Labels: labels.LabelArray{
    96  			fooIngressRule2Label,
    97  		},
    98  	}
    99  	fooIngressRule2.Sanitize()
   100  
   101  	fooEgressRule1 := api.Rule{
   102  		EndpointSelector: api.NewESFromLabels(fooSelectLabel),
   103  		Egress: []api.EgressRule{
   104  			{
   105  				EgressCommonRule: api.EgressCommonRule{
   106  					ToEndpoints: []api.EndpointSelector{
   107  						api.NewESFromLabels(fooSelectLabel),
   108  					},
   109  				},
   110  			},
   111  		},
   112  		Labels: labels.LabelArray{
   113  			fooEgressRule1Label,
   114  		},
   115  	}
   116  	fooEgressRule1.Sanitize()
   117  
   118  	fooEgressRule2 := api.Rule{
   119  		EndpointSelector: api.NewESFromLabels(fooSelectLabel),
   120  		Egress: []api.EgressRule{
   121  			{
   122  				EgressCommonRule: api.EgressCommonRule{
   123  					ToEndpoints: []api.EndpointSelector{
   124  						api.NewESFromLabels(fooSelectLabel),
   125  					},
   126  				},
   127  			},
   128  		},
   129  		Labels: labels.LabelArray{
   130  			fooEgressRule2Label,
   131  		},
   132  	}
   133  	fooEgressRule2.Sanitize()
   134  
   135  	combinedRule := api.Rule{
   136  		EndpointSelector: api.NewESFromLabels(fooSelectLabel),
   137  		Ingress: []api.IngressRule{
   138  			{
   139  				IngressCommonRule: api.IngressCommonRule{
   140  					FromEndpoints: []api.EndpointSelector{
   141  						api.NewESFromLabels(fooSelectLabel),
   142  					},
   143  				},
   144  			},
   145  		},
   146  		Egress: []api.EgressRule{
   147  			{
   148  				EgressCommonRule: api.EgressCommonRule{
   149  					ToEndpoints: []api.EndpointSelector{
   150  						api.NewESFromLabels(fooSelectLabel),
   151  					},
   152  				},
   153  			},
   154  		},
   155  		Labels: labels.LabelArray{
   156  			combinedLabel,
   157  		},
   158  	}
   159  	combinedRule.Sanitize()
   160  
   161  	ing, egr, matchingRules := repo.computePolicyEnforcementAndRules(fooIdentity)
   162  	require.Equal(t, false, ing, "ingress policy enforcement should not apply since no rules are in repository")
   163  	require.Equal(t, false, egr, "egress policy enforcement should not apply since no rules are in repository")
   164  	require.EqualValues(t, ruleSlice{}, matchingRules, "returned matching rules did not match")
   165  
   166  	_, _, err := repo.mustAdd(fooIngressRule1)
   167  	require.NoError(t, err, "unable to add rule to policy repository")
   168  	ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity)
   169  	require.Equal(t, true, ing, "ingress policy enforcement should apply since ingress rule selects")
   170  	require.Equal(t, false, egr, "egress policy enforcement should not apply since no egress rules select")
   171  	require.EqualValues(t, fooIngressRule1, matchingRules[0].Rule, "returned matching rules did not match")
   172  
   173  	_, _, err = repo.mustAdd(fooIngressRule2)
   174  	require.NoError(t, err, "unable to add rule to policy repository")
   175  	ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity)
   176  	require.Equal(t, true, ing, "ingress policy enforcement should apply since ingress rule selects")
   177  	require.Equal(t, false, egr, "egress policy enforcement should not apply since no egress rules select")
   178  	require.ElementsMatch(t, matchingRules.AsPolicyRules(), api.Rules{&fooIngressRule1, &fooIngressRule2})
   179  
   180  	_, _, numDeleted := repo.DeleteByLabelsLocked(labels.LabelArray{fooIngressRule1Label})
   181  	require.Equal(t, 1, numDeleted)
   182  	require.NoError(t, err, "unable to add rule to policy repository")
   183  	ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity)
   184  	require.Equal(t, true, ing, "ingress policy enforcement should apply since ingress rule selects")
   185  	require.Equal(t, false, egr, "egress policy enforcement should not apply since no egress rules select")
   186  	require.EqualValues(t, fooIngressRule2, matchingRules[0].Rule, "returned matching rules did not match")
   187  
   188  	_, _, numDeleted = repo.DeleteByLabelsLocked(labels.LabelArray{fooIngressRule2Label})
   189  	require.Equal(t, 1, numDeleted)
   190  
   191  	ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity)
   192  	require.Equal(t, false, ing, "ingress policy enforcement should not apply since no rules are in repository")
   193  	require.Equal(t, false, egr, "egress policy enforcement should not apply since no rules are in repository")
   194  	require.EqualValues(t, ruleSlice{}, matchingRules, "returned matching rules did not match")
   195  
   196  	_, _, err = repo.mustAdd(fooEgressRule1)
   197  	require.NoError(t, err, "unable to add rule to policy repository")
   198  	ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity)
   199  	require.Equal(t, false, ing, "ingress policy enforcement should not apply since no ingress rules select")
   200  	require.Equal(t, true, egr, "egress policy enforcement should apply since egress rules select")
   201  	require.EqualValues(t, fooEgressRule1, matchingRules[0].Rule, "returned matching rules did not match")
   202  	_, _, numDeleted = repo.DeleteByLabelsLocked(labels.LabelArray{fooEgressRule1Label})
   203  	require.Equal(t, 1, numDeleted)
   204  
   205  	_, _, err = repo.mustAdd(fooEgressRule2)
   206  	require.NoError(t, err, "unable to add rule to policy repository")
   207  	ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity)
   208  	require.Equal(t, false, ing, "ingress policy enforcement should not apply since no ingress rules select")
   209  	require.Equal(t, true, egr, "egress policy enforcement should apply since egress rules select")
   210  	require.EqualValues(t, fooEgressRule2, matchingRules[0].Rule, "returned matching rules did not match")
   211  
   212  	_, _, numDeleted = repo.DeleteByLabelsLocked(labels.LabelArray{fooEgressRule2Label})
   213  	require.Equal(t, 1, numDeleted)
   214  
   215  	_, _, err = repo.mustAdd(combinedRule)
   216  	require.NoError(t, err, "unable to add rule to policy repository")
   217  	ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity)
   218  	require.Equal(t, true, ing, "ingress policy enforcement should apply since ingress rule selects")
   219  	require.Equal(t, true, egr, "egress policy enforcement should apply since egress rules selects")
   220  	require.EqualValues(t, combinedRule, matchingRules[0].Rule, "returned matching rules did not match")
   221  	_, _, numDeleted = repo.DeleteByLabelsLocked(labels.LabelArray{combinedLabel})
   222  	require.Equal(t, 1, numDeleted)
   223  
   224  	SetPolicyEnabled(option.AlwaysEnforce)
   225  	require.NoError(t, err, "unable to add rule to policy repository")
   226  	ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity)
   227  	require.Equal(t, true, ing, "ingress policy enforcement should apply since ingress rule selects")
   228  	require.Equal(t, true, egr, "egress policy enforcement should apply since egress rules selects")
   229  	require.EqualValues(t, ruleSlice{}, matchingRules, "returned matching rules did not match")
   230  
   231  	SetPolicyEnabled(option.NeverEnforce)
   232  	_, _, err = repo.mustAdd(combinedRule)
   233  	require.NoError(t, err, "unable to add rule to policy repository")
   234  	ing, egr, matchingRules = repo.computePolicyEnforcementAndRules(fooIdentity)
   235  	require.Equal(t, false, ing, "ingress policy enforcement should not apply since policy enforcement is disabled ")
   236  	require.Equal(t, false, egr, "egress policy enforcement should not apply since policy enforcement is disabled")
   237  	require.Nil(t, matchingRules, "no rules should be returned since policy enforcement is disabled")
   238  
   239  	// Test init identity.
   240  
   241  	SetPolicyEnabled(option.DefaultEnforcement)
   242  	// If the mode is "default", check that the policy is always enforced for
   243  	// endpoints with the reserved:init label. If no policy rules match
   244  	// reserved:init, this drops all ingress and egress traffic.
   245  	ingress, egress, matchingRules := repo.computePolicyEnforcementAndRules(initIdentity)
   246  	require.Equal(t, true, ingress)
   247  	require.Equal(t, true, egress)
   248  	require.EqualValues(t, ruleSlice{}, matchingRules, "no rules should be returned since policy enforcement is disabled")
   249  
   250  	// Check that the "always" and "never" modes are not affected.
   251  	SetPolicyEnabled(option.AlwaysEnforce)
   252  	ingress, egress, _ = repo.computePolicyEnforcementAndRules(initIdentity)
   253  	require.Equal(t, true, ingress)
   254  	require.Equal(t, true, egress)
   255  
   256  	SetPolicyEnabled(option.NeverEnforce)
   257  	ingress, egress, _ = repo.computePolicyEnforcementAndRules(initIdentity)
   258  	require.Equal(t, false, ingress)
   259  	require.Equal(t, false, egress)
   260  
   261  }
   262  
   263  func TestAddSearchDelete(t *testing.T) {
   264  	td := newTestData()
   265  	repo := td.repo
   266  
   267  	lbls1 := labels.LabelArray{
   268  		labels.ParseLabel("tag1"),
   269  		labels.ParseLabel("tag2"),
   270  	}
   271  	rule1 := api.Rule{
   272  		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("foo")),
   273  		Labels:           lbls1,
   274  	}
   275  	rule1.Sanitize()
   276  	rule2 := api.Rule{
   277  		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")),
   278  		Labels:           lbls1,
   279  	}
   280  	rule2.Sanitize()
   281  	lbls2 := labels.LabelArray{labels.ParseSelectLabel("tag3")}
   282  	rule3 := api.Rule{
   283  		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")),
   284  		Labels:           lbls2,
   285  	}
   286  	rule3.Sanitize()
   287  
   288  	nextRevision := uint64(1)
   289  
   290  	require.Equal(t, nextRevision, repo.GetRevision())
   291  	nextRevision++
   292  
   293  	// add rule1,rule2
   294  	rev, _, err := repo.mustAdd(rule1)
   295  	require.Nil(t, err)
   296  	require.Equal(t, nextRevision, rev)
   297  	nextRevision++
   298  	rev, _, err = repo.mustAdd(rule2)
   299  	require.Nil(t, err)
   300  	require.Equal(t, nextRevision, rev)
   301  	nextRevision++
   302  
   303  	// rule3 should not be in there yet
   304  	repo.Mutex.RLock()
   305  	require.EqualValues(t, api.Rules{}, repo.SearchRLocked(lbls2))
   306  	repo.Mutex.RUnlock()
   307  
   308  	// add rule3
   309  	rev, _, err = repo.mustAdd(rule3)
   310  	require.Nil(t, err)
   311  	require.Equal(t, nextRevision, rev)
   312  	nextRevision++
   313  
   314  	// search rule1,rule2
   315  	repo.Mutex.RLock()
   316  	require.ElementsMatch(t, api.Rules{&rule1, &rule2}, repo.SearchRLocked(lbls1))
   317  	require.ElementsMatch(t, api.Rules{&rule3}, repo.SearchRLocked(lbls2))
   318  	repo.Mutex.RUnlock()
   319  
   320  	// delete rule1, rule2
   321  	rev, n := repo.DeleteByLabels(lbls1)
   322  	require.Equal(t, 2, n)
   323  	require.Equal(t, nextRevision, rev)
   324  	nextRevision++
   325  
   326  	// delete rule1, rule2 again has no effect
   327  	rev, n = repo.DeleteByLabels(lbls1)
   328  	require.Equal(t, 0, n)
   329  	require.Equal(t, nextRevision-1, rev)
   330  
   331  	// rule3 can still be found
   332  	repo.Mutex.RLock()
   333  	require.EqualValues(t, api.Rules{&rule3}, repo.SearchRLocked(lbls2))
   334  	repo.Mutex.RUnlock()
   335  
   336  	// delete rule3
   337  	rev, n = repo.DeleteByLabels(lbls2)
   338  	require.Equal(t, 1, n)
   339  	require.Equal(t, nextRevision, rev)
   340  
   341  	// rule1 is gone
   342  	repo.Mutex.RLock()
   343  	require.EqualValues(t, api.Rules{}, repo.SearchRLocked(lbls2))
   344  	repo.Mutex.RUnlock()
   345  }
   346  
   347  func BenchmarkParseLabel(b *testing.B) {
   348  	td := newTestData()
   349  	repo := td.repo
   350  
   351  	b.ResetTimer()
   352  	var err error
   353  	var cntAdd, cntFound int
   354  
   355  	lbls := make([]labels.LabelArray, 100)
   356  	for i := 0; i < 100; i++ {
   357  		I := fmt.Sprintf("%d", i)
   358  		lbls[i] = labels.LabelArray{labels.NewLabel("tag3", I, labels.LabelSourceK8s), labels.NewLabel("namespace", "default", labels.LabelSourceK8s)}
   359  	}
   360  	for i := 0; i < b.N; i++ {
   361  		for j := 0; j < 100; j++ {
   362  			J := fmt.Sprintf("%d", j)
   363  			_, _, err = repo.mustAdd(api.Rule{
   364  				EndpointSelector: api.NewESFromLabels(labels.NewLabel("foo", J, labels.LabelSourceK8s), labels.NewLabel("namespace", "default", labels.LabelSourceK8s)),
   365  				Labels: labels.LabelArray{
   366  					labels.ParseLabel("k8s:tag1"),
   367  					labels.NewLabel("namespace", "default", labels.LabelSourceK8s),
   368  					labels.NewLabel("tag3", J, labels.LabelSourceK8s),
   369  				},
   370  			})
   371  			if err == nil {
   372  				cntAdd++
   373  			}
   374  		}
   375  
   376  		repo.Mutex.RLock()
   377  		for j := 0; j < 100; j++ {
   378  			cntFound += len(repo.SearchRLocked(lbls[j]))
   379  		}
   380  		repo.Mutex.RUnlock()
   381  	}
   382  	b.Log("Added: ", cntAdd)
   383  	b.Log("found: ", cntFound)
   384  }
   385  
   386  func TestAllowsIngress(t *testing.T) {
   387  	td := newTestData()
   388  	repo := td.repo
   389  
   390  	fooToBar := &SearchContext{
   391  		From: labels.ParseSelectLabelArray("foo"),
   392  		To:   labels.ParseSelectLabelArray("bar"),
   393  	}
   394  
   395  	repo.Mutex.RLock()
   396  	// no rules loaded: Allows() => denied
   397  	require.Equal(t, api.Denied, repo.AllowsIngressRLocked(fooToBar))
   398  	repo.Mutex.RUnlock()
   399  
   400  	tag1 := labels.LabelArray{labels.ParseLabel("tag1")}
   401  	rule1 := api.Rule{
   402  		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")),
   403  		Ingress: []api.IngressRule{
   404  			{
   405  				IngressCommonRule: api.IngressCommonRule{
   406  					FromEndpoints: []api.EndpointSelector{
   407  						api.NewESFromLabels(labels.ParseSelectLabel("foo")),
   408  					},
   409  				},
   410  			},
   411  		},
   412  		Labels: tag1,
   413  	}
   414  
   415  	// selector: groupA
   416  	// require: groupA
   417  	rule2 := api.Rule{
   418  		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("groupA")),
   419  		Ingress: []api.IngressRule{
   420  			{
   421  				IngressCommonRule: api.IngressCommonRule{
   422  					FromRequires: []api.EndpointSelector{
   423  						api.NewESFromLabels(labels.ParseSelectLabel("groupA")),
   424  					},
   425  				},
   426  			},
   427  		},
   428  		Labels: tag1,
   429  	}
   430  	rule3 := api.Rule{
   431  		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar2")),
   432  		Ingress: []api.IngressRule{
   433  			{
   434  				IngressCommonRule: api.IngressCommonRule{
   435  					FromEndpoints: []api.EndpointSelector{
   436  						api.NewESFromLabels(labels.ParseSelectLabel("foo")),
   437  					},
   438  				},
   439  			},
   440  		},
   441  		Labels: tag1,
   442  	}
   443  
   444  	_, _, err := repo.mustAdd(rule1)
   445  	require.Nil(t, err)
   446  	_, _, err = repo.mustAdd(rule2)
   447  	require.Nil(t, err)
   448  	_, _, err = repo.mustAdd(rule3)
   449  	require.Nil(t, err)
   450  
   451  	// foo=>bar is OK
   452  	require.Equal(t, api.Allowed, repo.AllowsIngressRLocked(fooToBar))
   453  
   454  	// foo=>bar2 is OK
   455  	require.Equal(t, api.Allowed, repo.AllowsIngressRLocked(&SearchContext{
   456  		From: labels.ParseSelectLabelArray("foo"),
   457  		To:   labels.ParseSelectLabelArray("bar2"),
   458  	}))
   459  
   460  	// foo=>bar inside groupA is OK
   461  	require.Equal(t, api.Allowed, repo.AllowsIngressRLocked(&SearchContext{
   462  		From: labels.ParseSelectLabelArray("foo", "groupA"),
   463  		To:   labels.ParseSelectLabelArray("bar", "groupA"),
   464  	}))
   465  
   466  	// groupB can't talk to groupA => Denied
   467  	require.Equal(t, api.Denied, repo.AllowsIngressRLocked(&SearchContext{
   468  		From: labels.ParseSelectLabelArray("foo", "groupB"),
   469  		To:   labels.ParseSelectLabelArray("bar", "groupA"),
   470  	}))
   471  
   472  	// no restriction on groupB, unused label => OK
   473  	require.Equal(t, api.Allowed, repo.AllowsIngressRLocked(&SearchContext{
   474  		From: labels.ParseSelectLabelArray("foo", "groupB"),
   475  		To:   labels.ParseSelectLabelArray("bar", "groupB"),
   476  	}))
   477  
   478  	// foo=>bar3, no rule => Denied
   479  	require.Equal(t, api.Denied, repo.AllowsIngressRLocked(&SearchContext{
   480  		From: labels.ParseSelectLabelArray("foo"),
   481  		To:   labels.ParseSelectLabelArray("bar3"),
   482  	}))
   483  }
   484  
   485  func TestAllowsEgress(t *testing.T) {
   486  	td := newTestData()
   487  	repo := td.repo
   488  
   489  	fooToBar := &SearchContext{
   490  		From: labels.ParseSelectLabelArray("foo"),
   491  		To:   labels.ParseSelectLabelArray("bar"),
   492  	}
   493  
   494  	repo.Mutex.RLock()
   495  	// no rules loaded: Allows() => denied
   496  	require.Equal(t, api.Denied, repo.AllowsEgressRLocked(fooToBar))
   497  	repo.Mutex.RUnlock()
   498  
   499  	tag1 := labels.LabelArray{labels.ParseLabel("tag1")}
   500  	rule1 := api.Rule{
   501  		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("foo")),
   502  		Egress: []api.EgressRule{
   503  			{
   504  				EgressCommonRule: api.EgressCommonRule{
   505  					ToEndpoints: []api.EndpointSelector{
   506  						api.NewESFromLabels(labels.ParseSelectLabel("bar")),
   507  					},
   508  				},
   509  			},
   510  		},
   511  		Labels: tag1,
   512  	}
   513  
   514  	// selector: groupA
   515  	// require: groupA
   516  	rule2 := api.Rule{
   517  		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("groupA")),
   518  		Egress: []api.EgressRule{
   519  			{
   520  				EgressCommonRule: api.EgressCommonRule{
   521  					ToRequires: []api.EndpointSelector{
   522  						api.NewESFromLabels(labels.ParseSelectLabel("groupA")),
   523  					},
   524  				},
   525  			},
   526  		},
   527  		Labels: tag1,
   528  	}
   529  	rule3 := api.Rule{
   530  		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("foo")),
   531  		Egress: []api.EgressRule{
   532  			{
   533  				EgressCommonRule: api.EgressCommonRule{
   534  					ToEndpoints: []api.EndpointSelector{
   535  						api.NewESFromLabels(labels.ParseSelectLabel("bar2")),
   536  					},
   537  				},
   538  			},
   539  		},
   540  		Labels: tag1,
   541  	}
   542  	_, _, err := repo.mustAdd(rule1)
   543  	require.Nil(t, err)
   544  	_, _, err = repo.mustAdd(rule2)
   545  	require.Nil(t, err)
   546  	_, _, err = repo.mustAdd(rule3)
   547  	require.Nil(t, err)
   548  
   549  	// foo=>bar is OK
   550  	logBuffer := new(bytes.Buffer)
   551  	result := repo.AllowsEgressRLocked(fooToBar.WithLogger(logBuffer))
   552  	if !assert.EqualValues(t, api.Allowed, result) {
   553  		t.Logf("%s", logBuffer.String())
   554  		t.Errorf("Resolved policy did not match expected: \n%s", err)
   555  	}
   556  
   557  	// foo=>bar2 is OK
   558  	require.Equal(t, api.Allowed, repo.AllowsEgressRLocked(&SearchContext{
   559  		From: labels.ParseSelectLabelArray("foo"),
   560  		To:   labels.ParseSelectLabelArray("bar2"),
   561  	}))
   562  
   563  	// foo=>bar inside groupA is OK
   564  	require.Equal(t, api.Allowed, repo.AllowsEgressRLocked(&SearchContext{
   565  		From: labels.ParseSelectLabelArray("foo", "groupA"),
   566  		To:   labels.ParseSelectLabelArray("bar", "groupA"),
   567  	}))
   568  
   569  	buffer := new(bytes.Buffer)
   570  	// groupB can't talk to groupA => Denied
   571  	ctx := &SearchContext{
   572  		To:      labels.ParseSelectLabelArray("foo", "groupB"),
   573  		From:    labels.ParseSelectLabelArray("bar", "groupA"),
   574  		Logging: stdlog.New(buffer, "", 0),
   575  		Trace:   TRACE_VERBOSE,
   576  	}
   577  	verdict := repo.AllowsEgressRLocked(ctx)
   578  	require.Equal(t, api.Denied, verdict)
   579  
   580  	// no restriction on groupB, unused label => OK
   581  	require.Equal(t, api.Allowed, repo.AllowsEgressRLocked(&SearchContext{
   582  		From: labels.ParseSelectLabelArray("foo", "groupB"),
   583  		To:   labels.ParseSelectLabelArray("bar", "groupB"),
   584  	}))
   585  
   586  	// foo=>bar3, no rule => Denied
   587  	require.Equal(t, api.Denied, repo.AllowsEgressRLocked(&SearchContext{
   588  		From: labels.ParseSelectLabelArray("foo"),
   589  		To:   labels.ParseSelectLabelArray("bar3"),
   590  	}))
   591  }
   592  
   593  func TestWildcardL3RulesIngress(t *testing.T) {
   594  	td := newTestData()
   595  	repo := td.repo
   596  
   597  	labelsL3 := labels.LabelArray{labels.ParseLabel("L3")}
   598  	labelsKafka := labels.LabelArray{labels.ParseLabel("kafka")}
   599  	labelsICMP := labels.LabelArray{labels.ParseLabel("icmp")}
   600  	labelsICMPv6 := labels.LabelArray{labels.ParseLabel("icmpv6")}
   601  	labelsHTTP := labels.LabelArray{labels.ParseLabel("http")}
   602  	labelsL7 := labels.LabelArray{labels.ParseLabel("l7")}
   603  
   604  	l3Rule := api.Rule{
   605  		EndpointSelector: selFoo,
   606  		Ingress: []api.IngressRule{
   607  			{
   608  				IngressCommonRule: api.IngressCommonRule{
   609  					FromEndpoints: []api.EndpointSelector{selBar1},
   610  				},
   611  			},
   612  		},
   613  		Labels: labelsL3,
   614  	}
   615  	l3Rule.Sanitize()
   616  	_, _, err := repo.mustAdd(l3Rule)
   617  	require.Nil(t, err)
   618  
   619  	kafkaRule := api.Rule{
   620  		EndpointSelector: selFoo,
   621  		Ingress: []api.IngressRule{
   622  			{
   623  				IngressCommonRule: api.IngressCommonRule{
   624  					FromEndpoints: []api.EndpointSelector{selBar2},
   625  				},
   626  				ToPorts: []api.PortRule{{
   627  					Ports: []api.PortProtocol{
   628  						{Port: "9092", Protocol: api.ProtoTCP},
   629  					},
   630  					Rules: &api.L7Rules{
   631  						Kafka: []kafka.PortRule{
   632  							{APIKey: "produce"},
   633  						},
   634  					},
   635  				}},
   636  			},
   637  		},
   638  		Labels: labelsKafka,
   639  	}
   640  	kafkaRule.Sanitize()
   641  	_, _, err = repo.mustAdd(kafkaRule)
   642  	require.Nil(t, err)
   643  
   644  	httpRule := api.Rule{
   645  		EndpointSelector: selFoo,
   646  		Ingress: []api.IngressRule{
   647  			{
   648  				IngressCommonRule: api.IngressCommonRule{
   649  					FromEndpoints: []api.EndpointSelector{selBar2},
   650  				},
   651  				ToPorts: []api.PortRule{{
   652  					Ports: []api.PortProtocol{
   653  						{Port: "80", Protocol: api.ProtoTCP},
   654  					},
   655  					Rules: &api.L7Rules{
   656  						HTTP: []api.PortRuleHTTP{
   657  							{Method: "GET", Path: "/"},
   658  						},
   659  					},
   660  				}},
   661  			},
   662  		},
   663  		Labels: labelsHTTP,
   664  	}
   665  	_, _, err = repo.mustAdd(httpRule)
   666  	require.Nil(t, err)
   667  
   668  	l7Rule := api.Rule{
   669  		EndpointSelector: selFoo,
   670  		Ingress: []api.IngressRule{
   671  			{
   672  				IngressCommonRule: api.IngressCommonRule{
   673  					FromEndpoints: []api.EndpointSelector{selBar2},
   674  				},
   675  				ToPorts: []api.PortRule{{
   676  					Ports: []api.PortProtocol{
   677  						{Port: "9090", Protocol: api.ProtoTCP},
   678  					},
   679  					Rules: &api.L7Rules{
   680  						L7Proto: "tester",
   681  						L7:      []api.PortRuleL7{map[string]string{"method": "GET", "path": "/"}},
   682  					},
   683  				}},
   684  			},
   685  		},
   686  		Labels: labelsL7,
   687  	}
   688  	_, _, err = repo.mustAdd(l7Rule)
   689  	require.Nil(t, err)
   690  
   691  	icmpV4Type := intstr.FromInt(8)
   692  	icmpRule := api.Rule{
   693  		EndpointSelector: selFoo,
   694  		Ingress: []api.IngressRule{
   695  			{
   696  				IngressCommonRule: api.IngressCommonRule{
   697  					FromEndpoints: []api.EndpointSelector{selBar2},
   698  				},
   699  				ICMPs: api.ICMPRules{{
   700  					Fields: []api.ICMPField{{
   701  						Type: &icmpV4Type,
   702  					}},
   703  				}},
   704  			},
   705  		},
   706  		Labels: labelsICMP,
   707  	}
   708  	_, _, err = repo.mustAdd(icmpRule)
   709  	require.Nil(t, err)
   710  
   711  	icmpV6Type := intstr.FromInt(128)
   712  	icmpV6Rule := api.Rule{
   713  		EndpointSelector: selFoo,
   714  		Ingress: []api.IngressRule{
   715  			{
   716  				IngressCommonRule: api.IngressCommonRule{
   717  					FromEndpoints: []api.EndpointSelector{selBar2},
   718  				},
   719  				ICMPs: api.ICMPRules{{
   720  					Fields: []api.ICMPField{{
   721  						Type:   &icmpV6Type,
   722  						Family: api.IPv6Family,
   723  					}},
   724  				}},
   725  			},
   726  		},
   727  		Labels: labelsICMPv6,
   728  	}
   729  	_, _, err = repo.mustAdd(icmpV6Rule)
   730  	require.Nil(t, err)
   731  
   732  	ctx := &SearchContext{
   733  		To: labels.ParseSelectLabelArray("id=foo"),
   734  	}
   735  
   736  	repo.Mutex.RLock()
   737  	defer repo.Mutex.RUnlock()
   738  
   739  	policy, err := repo.ResolveL4IngressPolicy(ctx)
   740  	require.Nil(t, err)
   741  
   742  	expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{
   743  		"0/ANY": {
   744  			Port:     0,
   745  			Protocol: api.ProtoAny,
   746  			U8Proto:  0x0,
   747  			PerSelectorPolicies: L7DataMap{
   748  				td.cachedSelectorBar1: nil,
   749  			},
   750  			Ingress:    true,
   751  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar1: {labelsL3}},
   752  		},
   753  		"8/ICMP": {
   754  			Port:     8,
   755  			Protocol: api.ProtoICMP,
   756  			U8Proto:  0x1,
   757  			PerSelectorPolicies: L7DataMap{
   758  				td.cachedSelectorBar2: nil,
   759  			},
   760  			Ingress:    true,
   761  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsICMP}},
   762  		},
   763  		"128/ICMPV6": {
   764  			Port:     128,
   765  			Protocol: api.ProtoICMPv6,
   766  			U8Proto:  0x3A,
   767  			PerSelectorPolicies: L7DataMap{
   768  				td.cachedSelectorBar2: nil,
   769  			},
   770  			Ingress:    true,
   771  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsICMPv6}},
   772  		},
   773  		"9092/TCP": {
   774  			Port:     9092,
   775  			Protocol: api.ProtoTCP,
   776  			U8Proto:  0x6,
   777  			L7Parser: ParserTypeKafka,
   778  			Ingress:  true,
   779  			PerSelectorPolicies: L7DataMap{
   780  				td.cachedSelectorBar2: &PerSelectorPolicy{
   781  					L7Rules: api.L7Rules{
   782  						Kafka: []kafka.PortRule{kafkaRule.Ingress[0].ToPorts[0].Rules.Kafka[0]},
   783  					},
   784  					isRedirect: true,
   785  				},
   786  			},
   787  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsKafka}},
   788  		},
   789  		"80/TCP": {
   790  			Port:     80,
   791  			Protocol: api.ProtoTCP,
   792  			U8Proto:  0x6,
   793  			L7Parser: ParserTypeHTTP,
   794  			Ingress:  true,
   795  			PerSelectorPolicies: L7DataMap{
   796  				td.cachedSelectorBar2: &PerSelectorPolicy{
   797  					L7Rules: api.L7Rules{
   798  						HTTP: []api.PortRuleHTTP{httpRule.Ingress[0].ToPorts[0].Rules.HTTP[0]},
   799  					},
   800  					isRedirect: true,
   801  				},
   802  			},
   803  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsHTTP}},
   804  		},
   805  		"9090/TCP": {
   806  			Port:     9090,
   807  			Protocol: api.ProtoTCP,
   808  			U8Proto:  0x6,
   809  			L7Parser: L7ParserType("tester"),
   810  			Ingress:  true,
   811  			PerSelectorPolicies: L7DataMap{
   812  				td.cachedSelectorBar2: &PerSelectorPolicy{
   813  					L7Rules: api.L7Rules{
   814  						L7Proto: "tester",
   815  						L7:      []api.PortRuleL7{l7Rule.Ingress[0].ToPorts[0].Rules.L7[0]},
   816  					},
   817  					isRedirect: true,
   818  				},
   819  			},
   820  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsL7}},
   821  		},
   822  	})
   823  	require.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy))
   824  	policy.Detach(repo.GetSelectorCache())
   825  }
   826  
   827  func TestWildcardL4RulesIngress(t *testing.T) {
   828  	td := newTestData()
   829  	repo := td.repo
   830  
   831  	selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo"))
   832  	selBar1 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar1"))
   833  	selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2"))
   834  
   835  	labelsL4Kafka := labels.LabelArray{labels.ParseLabel("L4-kafka")}
   836  	labelsL7Kafka := labels.LabelArray{labels.ParseLabel("kafka")}
   837  	labelsL4HTTP := labels.LabelArray{labels.ParseLabel("L4-http")}
   838  	labelsL7HTTP := labels.LabelArray{labels.ParseLabel("http")}
   839  
   840  	l49092Rule := api.Rule{
   841  		EndpointSelector: selFoo,
   842  		Ingress: []api.IngressRule{
   843  			{
   844  				IngressCommonRule: api.IngressCommonRule{
   845  					FromEndpoints: []api.EndpointSelector{selBar1},
   846  				},
   847  				ToPorts: []api.PortRule{{
   848  					Ports: []api.PortProtocol{
   849  						{Port: "9092", Protocol: api.ProtoTCP},
   850  					},
   851  				}},
   852  			},
   853  		},
   854  		Labels: labelsL4Kafka,
   855  	}
   856  	l49092Rule.Sanitize()
   857  	_, _, err := repo.mustAdd(l49092Rule)
   858  	require.Nil(t, err)
   859  
   860  	kafkaRule := api.Rule{
   861  		EndpointSelector: selFoo,
   862  		Ingress: []api.IngressRule{
   863  			{
   864  				IngressCommonRule: api.IngressCommonRule{
   865  					FromEndpoints: []api.EndpointSelector{selBar2},
   866  				},
   867  				ToPorts: []api.PortRule{{
   868  					Ports: []api.PortProtocol{
   869  						{Port: "9092", Protocol: api.ProtoTCP},
   870  					},
   871  					Rules: &api.L7Rules{
   872  						Kafka: []kafka.PortRule{
   873  							{APIKey: "produce"},
   874  						},
   875  					},
   876  				}},
   877  			},
   878  		},
   879  		Labels: labelsL7Kafka,
   880  	}
   881  	kafkaRule.Sanitize()
   882  	_, _, err = repo.mustAdd(kafkaRule)
   883  	require.Nil(t, err)
   884  
   885  	l480Rule := api.Rule{
   886  		EndpointSelector: selFoo,
   887  		Ingress: []api.IngressRule{
   888  			{
   889  				IngressCommonRule: api.IngressCommonRule{
   890  					FromEndpoints: []api.EndpointSelector{selBar1},
   891  				},
   892  				ToPorts: []api.PortRule{{
   893  					Ports: []api.PortProtocol{
   894  						{Port: "80", Protocol: api.ProtoTCP},
   895  					},
   896  				}},
   897  			},
   898  		},
   899  		Labels: labelsL4HTTP,
   900  	}
   901  	l480Rule.Sanitize()
   902  	_, _, err = repo.mustAdd(l480Rule)
   903  	require.Nil(t, err)
   904  
   905  	httpRule := api.Rule{
   906  		EndpointSelector: selFoo,
   907  		Ingress: []api.IngressRule{
   908  			{
   909  				IngressCommonRule: api.IngressCommonRule{
   910  					FromEndpoints: []api.EndpointSelector{selBar2},
   911  				},
   912  				ToPorts: []api.PortRule{{
   913  					Ports: []api.PortProtocol{
   914  						{Port: "80", Protocol: api.ProtoTCP},
   915  					},
   916  					Rules: &api.L7Rules{
   917  						HTTP: []api.PortRuleHTTP{
   918  							{Method: "GET", Path: "/"},
   919  						},
   920  					},
   921  				}},
   922  			},
   923  		},
   924  		Labels: labelsL7HTTP,
   925  	}
   926  	_, _, err = repo.mustAdd(httpRule)
   927  	require.Nil(t, err)
   928  
   929  	ctx := &SearchContext{
   930  		To: labels.ParseSelectLabelArray("id=foo"),
   931  	}
   932  
   933  	repo.Mutex.RLock()
   934  	defer repo.Mutex.RUnlock()
   935  
   936  	policy, err := repo.ResolveL4IngressPolicy(ctx)
   937  	require.Nil(t, err)
   938  
   939  	expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{
   940  		"80/TCP": {
   941  			Port:     80,
   942  			Protocol: api.ProtoTCP,
   943  			U8Proto:  0x6,
   944  			L7Parser: ParserTypeHTTP,
   945  			Ingress:  true,
   946  			PerSelectorPolicies: L7DataMap{
   947  				td.cachedSelectorBar1: nil,
   948  				td.cachedSelectorBar2: &PerSelectorPolicy{
   949  					L7Rules: api.L7Rules{
   950  						HTTP: []api.PortRuleHTTP{httpRule.Ingress[0].ToPorts[0].Rules.HTTP[0]},
   951  					},
   952  					isRedirect: true,
   953  				},
   954  			},
   955  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{
   956  				td.cachedSelectorBar1: {labelsL4HTTP},
   957  				td.cachedSelectorBar2: {labelsL7HTTP},
   958  			},
   959  		},
   960  		"9092/TCP": {
   961  			Port:     9092,
   962  			Protocol: api.ProtoTCP,
   963  			U8Proto:  0x6,
   964  			L7Parser: ParserTypeKafka,
   965  			Ingress:  true,
   966  			PerSelectorPolicies: L7DataMap{
   967  				td.cachedSelectorBar1: nil,
   968  				td.cachedSelectorBar2: &PerSelectorPolicy{
   969  					L7Rules: api.L7Rules{
   970  						Kafka: []kafka.PortRule{kafkaRule.Ingress[0].ToPorts[0].Rules.Kafka[0]},
   971  					},
   972  					isRedirect: true,
   973  				},
   974  			},
   975  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{
   976  				td.cachedSelectorBar1: {labelsL4Kafka},
   977  				td.cachedSelectorBar2: {labelsL7Kafka},
   978  			},
   979  		},
   980  	})
   981  	require.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy))
   982  	policy.Detach(repo.GetSelectorCache())
   983  }
   984  
   985  func TestL3DependentL4IngressFromRequires(t *testing.T) {
   986  	td := newTestData()
   987  	repo := td.repo
   988  
   989  	selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo"))
   990  	selBar1 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar1"))
   991  	selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2"))
   992  
   993  	l480Rule := api.Rule{
   994  		EndpointSelector: selFoo,
   995  		Ingress: []api.IngressRule{
   996  			{
   997  				IngressCommonRule: api.IngressCommonRule{
   998  					FromEndpoints: []api.EndpointSelector{
   999  						selBar1,
  1000  					},
  1001  				},
  1002  				ToPorts: []api.PortRule{{
  1003  					Ports: []api.PortProtocol{
  1004  						{Port: "80", Protocol: api.ProtoTCP},
  1005  					},
  1006  				}},
  1007  			},
  1008  			{
  1009  				IngressCommonRule: api.IngressCommonRule{
  1010  					FromRequires: []api.EndpointSelector{selBar2},
  1011  				},
  1012  			},
  1013  		},
  1014  	}
  1015  	l480Rule.Sanitize()
  1016  	_, _, err := repo.mustAdd(l480Rule)
  1017  	require.Nil(t, err)
  1018  
  1019  	ctx := &SearchContext{
  1020  		To: labels.ParseSelectLabelArray("id=foo"),
  1021  	}
  1022  
  1023  	repo.Mutex.RLock()
  1024  	defer repo.Mutex.RUnlock()
  1025  
  1026  	policy, err := repo.ResolveL4IngressPolicy(ctx)
  1027  	require.Nil(t, err)
  1028  
  1029  	expectedSelector := api.NewESFromMatchRequirements(map[string]string{"any.id": "bar1"}, []slim_metav1.LabelSelectorRequirement{
  1030  		{
  1031  			Key:      "any.id",
  1032  			Operator: slim_metav1.LabelSelectorOpIn,
  1033  			Values:   []string{"bar2"},
  1034  		},
  1035  	})
  1036  	expectedCachedSelector, _ := td.sc.AddIdentitySelector(dummySelectorCacheUser, nil, expectedSelector)
  1037  
  1038  	expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{
  1039  		"80/TCP": {
  1040  			Port:     80,
  1041  			Protocol: api.ProtoTCP,
  1042  			U8Proto:  0x6,
  1043  			PerSelectorPolicies: L7DataMap{
  1044  				expectedCachedSelector: nil,
  1045  			},
  1046  			Ingress: true,
  1047  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{
  1048  				expectedCachedSelector: {nil},
  1049  			},
  1050  		},
  1051  	})
  1052  	require.Equal(t, expectedPolicy, policy)
  1053  	policy.Detach(repo.GetSelectorCache())
  1054  }
  1055  
  1056  func TestL3DependentL4EgressFromRequires(t *testing.T) {
  1057  	td := newTestData()
  1058  	repo := td.repo
  1059  
  1060  	selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo"))
  1061  	selBar1 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar1"))
  1062  	selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2"))
  1063  
  1064  	l480Rule := api.Rule{
  1065  		EndpointSelector: selFoo,
  1066  		Egress: []api.EgressRule{
  1067  			{
  1068  				EgressCommonRule: api.EgressCommonRule{
  1069  					ToEndpoints: []api.EndpointSelector{
  1070  						selBar1,
  1071  					},
  1072  				},
  1073  				ToPorts: []api.PortRule{{
  1074  					Ports: []api.PortProtocol{
  1075  						{Port: "80", Protocol: api.ProtoTCP},
  1076  					},
  1077  				}},
  1078  			},
  1079  			{
  1080  				EgressCommonRule: api.EgressCommonRule{
  1081  					ToEndpoints: []api.EndpointSelector{
  1082  						api.WildcardEndpointSelector,
  1083  					},
  1084  					ToRequires: []api.EndpointSelector{selBar2},
  1085  				},
  1086  			},
  1087  		},
  1088  	}
  1089  	l480Rule.Sanitize()
  1090  	_, _, err := repo.mustAdd(l480Rule)
  1091  	require.Nil(t, err)
  1092  
  1093  	ctx := &SearchContext{
  1094  		From: labels.ParseSelectLabelArray("id=foo"),
  1095  	}
  1096  
  1097  	repo.Mutex.RLock()
  1098  	defer repo.Mutex.RUnlock()
  1099  
  1100  	logBuffer := new(bytes.Buffer)
  1101  	policy, err := repo.ResolveL4EgressPolicy(ctx.WithLogger(logBuffer))
  1102  	require.Nil(t, err)
  1103  
  1104  	expectedSelector := api.NewESFromMatchRequirements(map[string]string{"any.id": "bar1"}, []slim_metav1.LabelSelectorRequirement{
  1105  		{
  1106  			Key:      "any.id",
  1107  			Operator: slim_metav1.LabelSelectorOpIn,
  1108  			Values:   []string{"bar2"},
  1109  		},
  1110  	})
  1111  	expectedSelector2 := api.NewESFromMatchRequirements(map[string]string{}, []slim_metav1.LabelSelectorRequirement{
  1112  		{
  1113  			Key:      "any.id",
  1114  			Operator: slim_metav1.LabelSelectorOpIn,
  1115  			Values:   []string{"bar2"},
  1116  		},
  1117  	})
  1118  	expectedCachedSelector, _ := td.sc.AddIdentitySelector(dummySelectorCacheUser, nil, expectedSelector)
  1119  	expectedCachedSelector2, _ := td.sc.AddIdentitySelector(dummySelectorCacheUser, nil, expectedSelector2)
  1120  
  1121  	expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{
  1122  		"0/ANY": {
  1123  			Port:     0,
  1124  			Protocol: "ANY",
  1125  			U8Proto:  0x0,
  1126  			PerSelectorPolicies: L7DataMap{
  1127  				expectedCachedSelector2: nil,
  1128  			},
  1129  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{
  1130  				expectedCachedSelector2: {nil},
  1131  			},
  1132  		},
  1133  		"80/TCP": {
  1134  			Port:     80,
  1135  			Protocol: api.ProtoTCP,
  1136  			U8Proto:  0x6,
  1137  			PerSelectorPolicies: L7DataMap{
  1138  				expectedCachedSelector: nil,
  1139  			},
  1140  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{
  1141  				expectedCachedSelector: {nil},
  1142  			},
  1143  		},
  1144  	})
  1145  	if !assert.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy)) {
  1146  		t.Errorf("Policy doesn't match expected:\n%s", logBuffer.String())
  1147  	}
  1148  	policy.Detach(repo.GetSelectorCache())
  1149  }
  1150  
  1151  func TestWildcardL3RulesEgress(t *testing.T) {
  1152  	td := newTestData()
  1153  	repo := td.repo
  1154  
  1155  	selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo"))
  1156  	selBar1 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar1"))
  1157  	selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2"))
  1158  
  1159  	labelsL4 := labels.LabelArray{labels.ParseLabel("L4")}
  1160  	labelsDNS := labels.LabelArray{labels.ParseLabel("dns")}
  1161  	labelsHTTP := labels.LabelArray{labels.ParseLabel("http")}
  1162  	labelsICMP := labels.LabelArray{labels.ParseLabel("icmp")}
  1163  	labelsICMPv6 := labels.LabelArray{labels.ParseLabel("icmpv6")}
  1164  
  1165  	l3Rule := api.Rule{
  1166  		EndpointSelector: selFoo,
  1167  		Egress: []api.EgressRule{
  1168  			{
  1169  				EgressCommonRule: api.EgressCommonRule{
  1170  					ToEndpoints: []api.EndpointSelector{selBar1},
  1171  				},
  1172  			},
  1173  		},
  1174  		Labels: labelsL4,
  1175  	}
  1176  	l3Rule.Sanitize()
  1177  	_, _, err := repo.mustAdd(l3Rule)
  1178  	require.Nil(t, err)
  1179  
  1180  	dnsRule := api.Rule{
  1181  		EndpointSelector: selFoo,
  1182  		Egress: []api.EgressRule{
  1183  			{
  1184  				EgressCommonRule: api.EgressCommonRule{
  1185  					ToEndpoints: []api.EndpointSelector{selBar2},
  1186  				},
  1187  				ToPorts: []api.PortRule{{
  1188  					Ports: []api.PortProtocol{
  1189  						{Port: "53", Protocol: api.ProtoUDP},
  1190  					},
  1191  					Rules: &api.L7Rules{
  1192  						DNS: []api.PortRuleDNS{
  1193  							{MatchName: "empire.gov"},
  1194  						},
  1195  					},
  1196  				}},
  1197  			},
  1198  		},
  1199  		Labels: labelsDNS,
  1200  	}
  1201  	dnsRule.Sanitize()
  1202  	_, _, err = repo.mustAdd(dnsRule)
  1203  	require.Nil(t, err)
  1204  
  1205  	httpRule := api.Rule{
  1206  		EndpointSelector: selFoo,
  1207  		Egress: []api.EgressRule{
  1208  			{
  1209  				EgressCommonRule: api.EgressCommonRule{
  1210  					ToEndpoints: []api.EndpointSelector{selBar2},
  1211  				},
  1212  				ToPorts: []api.PortRule{{
  1213  					Ports: []api.PortProtocol{
  1214  						{Port: "80", Protocol: api.ProtoTCP},
  1215  					},
  1216  					Rules: &api.L7Rules{
  1217  						HTTP: []api.PortRuleHTTP{
  1218  							{Method: "GET", Path: "/"},
  1219  						},
  1220  					},
  1221  				}},
  1222  			},
  1223  		},
  1224  		Labels: labelsHTTP,
  1225  	}
  1226  	_, _, err = repo.mustAdd(httpRule)
  1227  	require.Nil(t, err)
  1228  
  1229  	icmpV4Type := intstr.FromInt(8)
  1230  	icmpRule := api.Rule{
  1231  		EndpointSelector: selFoo,
  1232  		Egress: []api.EgressRule{
  1233  			{
  1234  				EgressCommonRule: api.EgressCommonRule{
  1235  					ToEndpoints: []api.EndpointSelector{selBar2},
  1236  				},
  1237  				ICMPs: api.ICMPRules{{
  1238  					Fields: []api.ICMPField{{
  1239  						Type: &icmpV4Type,
  1240  					}},
  1241  				}},
  1242  			},
  1243  		},
  1244  		Labels: labelsICMP,
  1245  	}
  1246  	_, _, err = repo.mustAdd(icmpRule)
  1247  	require.Nil(t, err)
  1248  
  1249  	icmpV6Type := intstr.FromInt(128)
  1250  	icmpV6Rule := api.Rule{
  1251  		EndpointSelector: selFoo,
  1252  		Egress: []api.EgressRule{
  1253  			{
  1254  				EgressCommonRule: api.EgressCommonRule{
  1255  					ToEndpoints: []api.EndpointSelector{selBar2},
  1256  				},
  1257  				ICMPs: api.ICMPRules{{
  1258  					Fields: []api.ICMPField{{
  1259  						Type:   &icmpV6Type,
  1260  						Family: "IPv6",
  1261  					}},
  1262  				}},
  1263  			},
  1264  		},
  1265  		Labels: labelsICMPv6,
  1266  	}
  1267  	_, _, err = repo.mustAdd(icmpV6Rule)
  1268  	require.Nil(t, err)
  1269  
  1270  	ctx := &SearchContext{
  1271  		From: labels.ParseSelectLabelArray("id=foo"),
  1272  	}
  1273  
  1274  	repo.Mutex.RLock()
  1275  	defer repo.Mutex.RUnlock()
  1276  
  1277  	logBuffer := new(bytes.Buffer)
  1278  	policy, err := repo.ResolveL4EgressPolicy(ctx.WithLogger(logBuffer))
  1279  	require.Nil(t, err)
  1280  
  1281  	// Traffic to bar1 should not be forwarded to the DNS or HTTP
  1282  	// proxy at all, but if it is (e.g., for visibility, the
  1283  	// "0/ANY" rule should allow such traffic through.
  1284  	expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{
  1285  		"53/UDP": {
  1286  			Port:     53,
  1287  			Protocol: api.ProtoUDP,
  1288  			U8Proto:  0x11,
  1289  			L7Parser: ParserTypeDNS,
  1290  			Ingress:  false,
  1291  			PerSelectorPolicies: L7DataMap{
  1292  				td.cachedSelectorBar2: &PerSelectorPolicy{
  1293  					L7Rules: api.L7Rules{
  1294  						DNS: []api.PortRuleDNS{dnsRule.Egress[0].ToPorts[0].Rules.DNS[0]},
  1295  					},
  1296  					isRedirect: true,
  1297  				},
  1298  			},
  1299  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsDNS}},
  1300  		},
  1301  		"80/TCP": {
  1302  			Port:     80,
  1303  			Protocol: api.ProtoTCP,
  1304  			U8Proto:  0x6,
  1305  			L7Parser: ParserTypeHTTP,
  1306  			Ingress:  false,
  1307  			PerSelectorPolicies: L7DataMap{
  1308  				td.cachedSelectorBar2: &PerSelectorPolicy{
  1309  					L7Rules: api.L7Rules{
  1310  						HTTP: []api.PortRuleHTTP{httpRule.Egress[0].ToPorts[0].Rules.HTTP[0]},
  1311  					},
  1312  					isRedirect: true,
  1313  				},
  1314  			},
  1315  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsHTTP}},
  1316  		},
  1317  		"8/ICMP": {
  1318  			Port:     8,
  1319  			Protocol: api.ProtoICMP,
  1320  			U8Proto:  0x1,
  1321  			PerSelectorPolicies: L7DataMap{
  1322  				td.cachedSelectorBar2: nil,
  1323  			},
  1324  			Ingress:    false,
  1325  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsICMP}},
  1326  		},
  1327  		"128/ICMPV6": {
  1328  			Port:     128,
  1329  			Protocol: api.ProtoICMPv6,
  1330  			U8Proto:  0x3A,
  1331  			PerSelectorPolicies: L7DataMap{
  1332  				td.cachedSelectorBar2: nil,
  1333  			},
  1334  			Ingress:    false,
  1335  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsICMPv6}},
  1336  		},
  1337  		"0/ANY": {
  1338  			Port:     0,
  1339  			Protocol: "ANY",
  1340  			U8Proto:  0x0,
  1341  			L7Parser: "",
  1342  			PerSelectorPolicies: L7DataMap{
  1343  				td.cachedSelectorBar1: nil,
  1344  			},
  1345  			Ingress:    false,
  1346  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar1: {labelsL4}},
  1347  		},
  1348  	})
  1349  	if !assert.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy)) {
  1350  		t.Logf("%s", logBuffer.String())
  1351  		t.Errorf("Resolved policy did not match expected: \n%s", err)
  1352  	}
  1353  	policy.Detach(repo.GetSelectorCache())
  1354  }
  1355  
  1356  func TestWildcardL4RulesEgress(t *testing.T) {
  1357  	td := newTestData()
  1358  	repo := td.repo
  1359  
  1360  	selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo"))
  1361  	selBar1 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar1"))
  1362  	selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2"))
  1363  
  1364  	labelsL3DNS := labels.LabelArray{labels.ParseLabel("L3-dns")}
  1365  	labelsL7DNS := labels.LabelArray{labels.ParseLabel("dns")}
  1366  	labelsL3HTTP := labels.LabelArray{labels.ParseLabel("L3-http")}
  1367  	labelsL7HTTP := labels.LabelArray{labels.ParseLabel("http")}
  1368  
  1369  	l453Rule := api.Rule{
  1370  		EndpointSelector: selFoo,
  1371  		Egress: []api.EgressRule{
  1372  			{
  1373  				EgressCommonRule: api.EgressCommonRule{
  1374  					ToEndpoints: []api.EndpointSelector{selBar1},
  1375  				},
  1376  				ToPorts: []api.PortRule{{
  1377  					Ports: []api.PortProtocol{
  1378  						{Port: "53", Protocol: api.ProtoUDP},
  1379  					},
  1380  				}},
  1381  			},
  1382  		},
  1383  		Labels: labelsL3DNS,
  1384  	}
  1385  	l453Rule.Sanitize()
  1386  	_, _, err := repo.mustAdd(l453Rule)
  1387  	require.Nil(t, err)
  1388  
  1389  	dnsRule := api.Rule{
  1390  		EndpointSelector: selFoo,
  1391  		Egress: []api.EgressRule{
  1392  			{
  1393  				EgressCommonRule: api.EgressCommonRule{
  1394  					ToEndpoints: []api.EndpointSelector{selBar2},
  1395  				},
  1396  				ToPorts: []api.PortRule{{
  1397  					Ports: []api.PortProtocol{
  1398  						{Port: "53", Protocol: api.ProtoUDP},
  1399  					},
  1400  					Rules: &api.L7Rules{
  1401  						DNS: []api.PortRuleDNS{
  1402  							{MatchName: "empire.gov"},
  1403  						},
  1404  					},
  1405  				}},
  1406  			},
  1407  		},
  1408  		Labels: labelsL7DNS,
  1409  	}
  1410  	dnsRule.Sanitize()
  1411  	_, _, err = repo.mustAdd(dnsRule)
  1412  	require.Nil(t, err)
  1413  
  1414  	l480Rule := api.Rule{
  1415  		EndpointSelector: selFoo,
  1416  		Egress: []api.EgressRule{
  1417  			{
  1418  				EgressCommonRule: api.EgressCommonRule{
  1419  					ToEndpoints: []api.EndpointSelector{selBar1},
  1420  				},
  1421  				ToPorts: []api.PortRule{{
  1422  					Ports: []api.PortProtocol{
  1423  						{Port: "80", Protocol: api.ProtoTCP},
  1424  					},
  1425  				}},
  1426  			},
  1427  		},
  1428  		Labels: labelsL3HTTP,
  1429  	}
  1430  	l480Rule.Sanitize()
  1431  	_, _, err = repo.mustAdd(l480Rule)
  1432  	require.Nil(t, err)
  1433  
  1434  	httpRule := api.Rule{
  1435  		EndpointSelector: selFoo,
  1436  		Egress: []api.EgressRule{
  1437  			{
  1438  				EgressCommonRule: api.EgressCommonRule{
  1439  					ToEndpoints: []api.EndpointSelector{selBar2},
  1440  				},
  1441  				ToPorts: []api.PortRule{{
  1442  					Ports: []api.PortProtocol{
  1443  						{Port: "80", Protocol: api.ProtoTCP},
  1444  					},
  1445  					Rules: &api.L7Rules{
  1446  						HTTP: []api.PortRuleHTTP{
  1447  							{Method: "GET", Path: "/"},
  1448  						},
  1449  					},
  1450  				}},
  1451  			},
  1452  		},
  1453  		Labels: labelsL7HTTP,
  1454  	}
  1455  	_, _, err = repo.mustAdd(httpRule)
  1456  	require.Nil(t, err)
  1457  
  1458  	ctx := &SearchContext{
  1459  		From: labels.ParseSelectLabelArray("id=foo"),
  1460  	}
  1461  
  1462  	repo.Mutex.RLock()
  1463  	defer repo.Mutex.RUnlock()
  1464  
  1465  	logBuffer := new(bytes.Buffer)
  1466  	policy, err := repo.ResolveL4EgressPolicy(ctx.WithLogger(logBuffer))
  1467  	require.Nil(t, err)
  1468  
  1469  	// Bar1 should not be forwarded to the proxy, but if it is (e.g., for visibility),
  1470  	// the L3/L4 allow should pass it without an explicit L7 wildcard.
  1471  	expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{
  1472  		"80/TCP": {
  1473  			Port:     80,
  1474  			Protocol: api.ProtoTCP,
  1475  			U8Proto:  0x6,
  1476  			L7Parser: ParserTypeHTTP,
  1477  			Ingress:  false,
  1478  			PerSelectorPolicies: L7DataMap{
  1479  				td.cachedSelectorBar1: nil,
  1480  				td.cachedSelectorBar2: &PerSelectorPolicy{
  1481  					L7Rules: api.L7Rules{
  1482  						HTTP: []api.PortRuleHTTP{httpRule.Egress[0].ToPorts[0].Rules.HTTP[0]},
  1483  					},
  1484  					isRedirect: true,
  1485  				},
  1486  			},
  1487  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{
  1488  				td.cachedSelectorBar1: {labelsL3HTTP},
  1489  				td.cachedSelectorBar2: {labelsL7HTTP},
  1490  			},
  1491  		},
  1492  		"53/UDP": {
  1493  			Port:     53,
  1494  			Protocol: api.ProtoUDP,
  1495  			U8Proto:  0x11,
  1496  			L7Parser: ParserTypeDNS,
  1497  			Ingress:  false,
  1498  			PerSelectorPolicies: L7DataMap{
  1499  				td.cachedSelectorBar1: nil,
  1500  				td.cachedSelectorBar2: &PerSelectorPolicy{
  1501  					L7Rules: api.L7Rules{
  1502  						DNS: []api.PortRuleDNS{dnsRule.Egress[0].ToPorts[0].Rules.DNS[0]},
  1503  					},
  1504  					isRedirect: true,
  1505  				},
  1506  			},
  1507  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{
  1508  				td.cachedSelectorBar1: {labelsL3DNS},
  1509  				td.cachedSelectorBar2: {labelsL7DNS},
  1510  			},
  1511  		},
  1512  	})
  1513  	if !assert.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy)) {
  1514  		t.Logf("%s", logBuffer.String())
  1515  		t.Error("Resolved policy did not match expected")
  1516  	}
  1517  	policy.Detach(repo.GetSelectorCache())
  1518  }
  1519  
  1520  func TestWildcardCIDRRulesEgress(t *testing.T) {
  1521  	td := newTestData()
  1522  	repo := td.repo
  1523  
  1524  	labelsL3 := labels.LabelArray{labels.ParseLabel("L3")}
  1525  	labelsHTTP := labels.LabelArray{labels.ParseLabel("http")}
  1526  
  1527  	cidrSlice := api.CIDRSlice{"192.0.0.0/3"}
  1528  	cidrSelectors := cidrSlice.GetAsEndpointSelectors()
  1529  	var cachedSelectors CachedSelectorSlice
  1530  	for i := range cidrSelectors {
  1531  		c, _ := td.sc.AddIdentitySelector(dummySelectorCacheUser, nil, cidrSelectors[i])
  1532  		cachedSelectors = append(cachedSelectors, c)
  1533  		defer td.sc.RemoveSelector(c, dummySelectorCacheUser)
  1534  	}
  1535  	selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo"))
  1536  
  1537  	l480Get := api.Rule{
  1538  		EndpointSelector: selFoo,
  1539  		Egress: []api.EgressRule{
  1540  			{
  1541  				EgressCommonRule: api.EgressCommonRule{
  1542  					ToCIDR: api.CIDRSlice{"192.0.0.0/3"},
  1543  				},
  1544  				ToPorts: []api.PortRule{{
  1545  					Ports: []api.PortProtocol{
  1546  						{
  1547  							Port:     "80",
  1548  							Protocol: api.ProtoTCP,
  1549  						},
  1550  					},
  1551  					Rules: &api.L7Rules{
  1552  						HTTP: []api.PortRuleHTTP{
  1553  							{
  1554  								Headers: []string{"X-My-Header: true"},
  1555  								Method:  "GET",
  1556  								Path:    "/",
  1557  							},
  1558  						},
  1559  					},
  1560  				}},
  1561  			},
  1562  		},
  1563  		Labels: labelsHTTP,
  1564  	}
  1565  	l480Get.Sanitize()
  1566  	_, _, err := repo.mustAdd(l480Get)
  1567  	require.Nil(t, err)
  1568  
  1569  	l3Rule := api.Rule{
  1570  		EndpointSelector: selFoo,
  1571  		Egress: []api.EgressRule{
  1572  			{
  1573  				EgressCommonRule: api.EgressCommonRule{
  1574  					ToCIDR: api.CIDRSlice{"192.0.0.0/3"},
  1575  				},
  1576  			},
  1577  		},
  1578  		Labels: labelsL3,
  1579  	}
  1580  	l3Rule.Sanitize()
  1581  	_, _, err = repo.mustAdd(l3Rule)
  1582  	require.Nil(t, err)
  1583  
  1584  	ctx := &SearchContext{
  1585  		From: labels.ParseSelectLabelArray("id=foo"),
  1586  	}
  1587  
  1588  	repo.Mutex.RLock()
  1589  	defer repo.Mutex.RUnlock()
  1590  
  1591  	logBuffer := new(bytes.Buffer)
  1592  	policy, err := repo.ResolveL4EgressPolicy(ctx.WithLogger(logBuffer))
  1593  	require.Nil(t, err)
  1594  
  1595  	// Port 80 policy does not need the wildcard, as the "0" port policy will allow the traffic.
  1596  	// HTTP rules can have side-effects, so they need to be retained even if shadowed by a wildcard.
  1597  	expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{
  1598  		"80/TCP": {
  1599  			Port:     80,
  1600  			Protocol: api.ProtoTCP,
  1601  			U8Proto:  0x6,
  1602  			L7Parser: ParserTypeHTTP,
  1603  			Ingress:  false,
  1604  			PerSelectorPolicies: L7DataMap{
  1605  				cachedSelectors[0]: &PerSelectorPolicy{
  1606  					L7Rules: api.L7Rules{
  1607  						HTTP: []api.PortRuleHTTP{{
  1608  							Headers: []string{"X-My-Header: true"},
  1609  							Method:  "GET",
  1610  							Path:    "/",
  1611  						}},
  1612  					},
  1613  					isRedirect: true,
  1614  				},
  1615  			},
  1616  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{cachedSelectors[0]: {labelsHTTP}},
  1617  		},
  1618  		"0/ANY": {
  1619  			Port:     0,
  1620  			Protocol: api.ProtoAny,
  1621  			U8Proto:  0x0,
  1622  			L7Parser: ParserTypeNone,
  1623  			Ingress:  false,
  1624  			PerSelectorPolicies: L7DataMap{
  1625  				cachedSelectors[0]: nil,
  1626  			},
  1627  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{cachedSelectors[0]: {labelsL3}},
  1628  		},
  1629  	})
  1630  	if !assert.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy)) {
  1631  		t.Logf("%s", logBuffer.String())
  1632  		t.Error("Resolved policy did not match expected")
  1633  	}
  1634  	policy.Detach(repo.GetSelectorCache())
  1635  }
  1636  
  1637  func TestWildcardL3RulesIngressFromEntities(t *testing.T) {
  1638  	td := newTestData()
  1639  	repo := td.repo
  1640  
  1641  	selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo"))
  1642  	selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2"))
  1643  
  1644  	labelsL3 := labels.LabelArray{labels.ParseLabel("L3")}
  1645  	labelsKafka := labels.LabelArray{labels.ParseLabel("kafka")}
  1646  	labelsHTTP := labels.LabelArray{labels.ParseLabel("http")}
  1647  
  1648  	l3Rule := api.Rule{
  1649  		EndpointSelector: selFoo,
  1650  		Ingress: []api.IngressRule{
  1651  			{
  1652  				IngressCommonRule: api.IngressCommonRule{
  1653  					FromEntities: api.EntitySlice{api.EntityWorld},
  1654  				},
  1655  			},
  1656  		},
  1657  		Labels: labelsL3,
  1658  	}
  1659  	l3Rule.Sanitize()
  1660  	_, _, err := repo.mustAdd(l3Rule)
  1661  	require.Nil(t, err)
  1662  
  1663  	kafkaRule := api.Rule{
  1664  		EndpointSelector: selFoo,
  1665  		Ingress: []api.IngressRule{
  1666  			{
  1667  				IngressCommonRule: api.IngressCommonRule{
  1668  					FromEndpoints: []api.EndpointSelector{selBar2},
  1669  				},
  1670  				ToPorts: []api.PortRule{{
  1671  					Ports: []api.PortProtocol{
  1672  						{Port: "9092", Protocol: api.ProtoTCP},
  1673  					},
  1674  					Rules: &api.L7Rules{
  1675  						Kafka: []kafka.PortRule{
  1676  							{APIKey: "produce"},
  1677  						},
  1678  					},
  1679  				}},
  1680  			},
  1681  		},
  1682  		Labels: labelsKafka,
  1683  	}
  1684  	kafkaRule.Sanitize()
  1685  	_, _, err = repo.mustAdd(kafkaRule)
  1686  	require.Nil(t, err)
  1687  
  1688  	httpRule := api.Rule{
  1689  		EndpointSelector: selFoo,
  1690  		Ingress: []api.IngressRule{
  1691  			{
  1692  				IngressCommonRule: api.IngressCommonRule{
  1693  					FromEndpoints: []api.EndpointSelector{selBar2},
  1694  				},
  1695  				ToPorts: []api.PortRule{{
  1696  					Ports: []api.PortProtocol{
  1697  						{Port: "80", Protocol: api.ProtoTCP},
  1698  					},
  1699  					Rules: &api.L7Rules{
  1700  						HTTP: []api.PortRuleHTTP{
  1701  							{Method: "GET", Path: "/"},
  1702  						},
  1703  					},
  1704  				}},
  1705  			},
  1706  		},
  1707  		Labels: labelsHTTP,
  1708  	}
  1709  	_, _, err = repo.mustAdd(httpRule)
  1710  	require.Nil(t, err)
  1711  
  1712  	ctx := &SearchContext{
  1713  		To: labels.ParseSelectLabelArray("id=foo"),
  1714  	}
  1715  
  1716  	repo.Mutex.RLock()
  1717  	defer repo.Mutex.RUnlock()
  1718  
  1719  	policy, err := repo.ResolveL4IngressPolicy(ctx)
  1720  	require.Nil(t, err)
  1721  	require.Equal(t, 3, policy.Len())
  1722  	selWorld := api.EntitySelectorMapping[api.EntityWorld][0]
  1723  	require.Equal(t, 1, len(policy.ExactLookup("80", 0, "TCP").PerSelectorPolicies))
  1724  	cachedSelectorWorld := td.sc.FindCachedIdentitySelector(selWorld)
  1725  	require.NotNil(t, cachedSelectorWorld)
  1726  
  1727  	cachedSelectorWorldV4 := td.sc.FindCachedIdentitySelector(api.ReservedEndpointSelectors[labels.IDNameWorldIPv4])
  1728  	require.NotNil(t, cachedSelectorWorldV4)
  1729  
  1730  	cachedSelectorWorldV6 := td.sc.FindCachedIdentitySelector(api.ReservedEndpointSelectors[labels.IDNameWorldIPv6])
  1731  	require.NotNil(t, cachedSelectorWorldV6)
  1732  
  1733  	expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{
  1734  		"0/ANY": {
  1735  			Port:     0,
  1736  			Protocol: "ANY",
  1737  			U8Proto:  0x0,
  1738  			L7Parser: "",
  1739  			PerSelectorPolicies: L7DataMap{
  1740  				cachedSelectorWorld:   nil,
  1741  				cachedSelectorWorldV4: nil,
  1742  				cachedSelectorWorldV6: nil,
  1743  			},
  1744  			Ingress: true,
  1745  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{
  1746  				cachedSelectorWorld:   {labelsL3},
  1747  				cachedSelectorWorldV4: {labelsL3},
  1748  				cachedSelectorWorldV6: {labelsL3},
  1749  			},
  1750  		},
  1751  		"9092/TCP": {
  1752  			Port:     9092,
  1753  			Protocol: api.ProtoTCP,
  1754  			U8Proto:  0x6,
  1755  			L7Parser: ParserTypeKafka,
  1756  			Ingress:  true,
  1757  			PerSelectorPolicies: L7DataMap{
  1758  				td.cachedSelectorBar2: &PerSelectorPolicy{
  1759  					L7Rules: api.L7Rules{
  1760  						Kafka: []kafka.PortRule{kafkaRule.Ingress[0].ToPorts[0].Rules.Kafka[0]},
  1761  					},
  1762  					isRedirect: true,
  1763  				},
  1764  			},
  1765  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsKafka}},
  1766  		},
  1767  		"80/TCP": {
  1768  			Port:     80,
  1769  			Protocol: api.ProtoTCP,
  1770  			U8Proto:  0x6,
  1771  			L7Parser: ParserTypeHTTP,
  1772  			Ingress:  true,
  1773  			PerSelectorPolicies: L7DataMap{
  1774  				td.cachedSelectorBar2: &PerSelectorPolicy{
  1775  					L7Rules: api.L7Rules{
  1776  						HTTP: []api.PortRuleHTTP{httpRule.Ingress[0].ToPorts[0].Rules.HTTP[0]},
  1777  					},
  1778  					isRedirect: true,
  1779  				},
  1780  			},
  1781  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsHTTP}},
  1782  		},
  1783  	})
  1784  
  1785  	require.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy))
  1786  	policy.Detach(repo.GetSelectorCache())
  1787  }
  1788  
  1789  func TestWildcardL3RulesEgressToEntities(t *testing.T) {
  1790  	td := newTestData()
  1791  	repo := td.repo
  1792  
  1793  	selFoo := api.NewESFromLabels(labels.ParseSelectLabel("id=foo"))
  1794  	selBar2 := api.NewESFromLabels(labels.ParseSelectLabel("id=bar2"))
  1795  
  1796  	labelsL3 := labels.LabelArray{labels.ParseLabel("L3")}
  1797  	labelsDNS := labels.LabelArray{labels.ParseLabel("dns")}
  1798  	labelsHTTP := labels.LabelArray{labels.ParseLabel("http")}
  1799  
  1800  	l3Rule := api.Rule{
  1801  		EndpointSelector: selFoo,
  1802  		Egress: []api.EgressRule{
  1803  			{
  1804  				EgressCommonRule: api.EgressCommonRule{
  1805  					ToEntities: api.EntitySlice{api.EntityWorld},
  1806  				},
  1807  			},
  1808  		},
  1809  		Labels: labelsL3,
  1810  	}
  1811  	l3Rule.Sanitize()
  1812  	_, _, err := repo.mustAdd(l3Rule)
  1813  	require.Nil(t, err)
  1814  
  1815  	dnsRule := api.Rule{
  1816  		EndpointSelector: selFoo,
  1817  		Egress: []api.EgressRule{
  1818  			{
  1819  				EgressCommonRule: api.EgressCommonRule{
  1820  					ToEndpoints: []api.EndpointSelector{selBar2},
  1821  				},
  1822  				ToPorts: []api.PortRule{{
  1823  					Ports: []api.PortProtocol{
  1824  						{Port: "53", Protocol: api.ProtoUDP},
  1825  					},
  1826  					Rules: &api.L7Rules{
  1827  						DNS: []api.PortRuleDNS{
  1828  							{MatchName: "empire.gov"},
  1829  						},
  1830  					},
  1831  				}},
  1832  			},
  1833  		},
  1834  		Labels: labelsDNS,
  1835  	}
  1836  	dnsRule.Sanitize()
  1837  	_, _, err = repo.mustAdd(dnsRule)
  1838  	require.Nil(t, err)
  1839  
  1840  	httpRule := api.Rule{
  1841  		EndpointSelector: selFoo,
  1842  		Egress: []api.EgressRule{
  1843  			{
  1844  				EgressCommonRule: api.EgressCommonRule{
  1845  					ToEndpoints: []api.EndpointSelector{selBar2},
  1846  				},
  1847  				ToPorts: []api.PortRule{{
  1848  					Ports: []api.PortProtocol{
  1849  						{Port: "80", Protocol: api.ProtoTCP},
  1850  					},
  1851  					Rules: &api.L7Rules{
  1852  						HTTP: []api.PortRuleHTTP{
  1853  							{Method: "GET", Path: "/"},
  1854  						},
  1855  					},
  1856  				}},
  1857  			},
  1858  		},
  1859  		Labels: labelsHTTP,
  1860  	}
  1861  	_, _, err = repo.mustAdd(httpRule)
  1862  	require.Nil(t, err)
  1863  
  1864  	ctx := &SearchContext{
  1865  		From: labels.ParseSelectLabelArray("id=foo"),
  1866  	}
  1867  
  1868  	repo.Mutex.RLock()
  1869  	defer repo.Mutex.RUnlock()
  1870  
  1871  	policy, err := repo.ResolveL4EgressPolicy(ctx)
  1872  	require.Nil(t, err)
  1873  	require.Equal(t, 3, policy.Len())
  1874  	selWorld := api.EntitySelectorMapping[api.EntityWorld][0]
  1875  	require.Equal(t, 1, len(policy.ExactLookup("80", 0, "TCP").PerSelectorPolicies))
  1876  	cachedSelectorWorld := td.sc.FindCachedIdentitySelector(selWorld)
  1877  	require.NotNil(t, cachedSelectorWorld)
  1878  
  1879  	cachedSelectorWorldV4 := td.sc.FindCachedIdentitySelector(api.ReservedEndpointSelectors[labels.IDNameWorldIPv4])
  1880  	require.NotNil(t, cachedSelectorWorldV4)
  1881  
  1882  	cachedSelectorWorldV6 := td.sc.FindCachedIdentitySelector(api.ReservedEndpointSelectors[labels.IDNameWorldIPv6])
  1883  	require.NotNil(t, cachedSelectorWorldV6)
  1884  
  1885  	expectedPolicy := NewL4PolicyMapWithValues(map[string]*L4Filter{
  1886  		"0/ANY": {
  1887  			Port:     0,
  1888  			Protocol: "ANY",
  1889  			U8Proto:  0x0,
  1890  			L7Parser: "",
  1891  			PerSelectorPolicies: L7DataMap{
  1892  				cachedSelectorWorld:   nil,
  1893  				cachedSelectorWorldV4: nil,
  1894  				cachedSelectorWorldV6: nil,
  1895  			},
  1896  			Ingress: false,
  1897  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{
  1898  				cachedSelectorWorld:   {labelsL3},
  1899  				cachedSelectorWorldV4: {labelsL3},
  1900  				cachedSelectorWorldV6: {labelsL3},
  1901  			},
  1902  		},
  1903  		"53/UDP": {
  1904  			Port:     53,
  1905  			Protocol: api.ProtoUDP,
  1906  			U8Proto:  0x11,
  1907  			L7Parser: ParserTypeDNS,
  1908  			Ingress:  false,
  1909  			PerSelectorPolicies: L7DataMap{
  1910  				td.cachedSelectorBar2: &PerSelectorPolicy{
  1911  					L7Rules: api.L7Rules{
  1912  						DNS: []api.PortRuleDNS{dnsRule.Egress[0].ToPorts[0].Rules.DNS[0]},
  1913  					},
  1914  					isRedirect: true,
  1915  				},
  1916  			},
  1917  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsDNS}},
  1918  		},
  1919  		"80/TCP": {
  1920  			Port:     80,
  1921  			Protocol: api.ProtoTCP,
  1922  			U8Proto:  0x6,
  1923  			L7Parser: ParserTypeHTTP,
  1924  			Ingress:  false,
  1925  			PerSelectorPolicies: L7DataMap{
  1926  				td.cachedSelectorBar2: &PerSelectorPolicy{
  1927  					L7Rules: api.L7Rules{
  1928  						HTTP: []api.PortRuleHTTP{httpRule.Egress[0].ToPorts[0].Rules.HTTP[0]},
  1929  					},
  1930  					isRedirect: true,
  1931  				},
  1932  			},
  1933  			RuleOrigin: map[CachedSelector]labels.LabelArrayList{td.cachedSelectorBar2: {labelsHTTP}},
  1934  		},
  1935  	})
  1936  
  1937  	require.True(t, policy.Equals(t, expectedPolicy), policy.Diff(t, expectedPolicy))
  1938  	policy.Detach(repo.GetSelectorCache())
  1939  }
  1940  
  1941  func TestMinikubeGettingStarted(t *testing.T) {
  1942  	td := newTestData()
  1943  	repo := td.repo
  1944  
  1945  	app2Selector := labels.ParseSelectLabelArray("id=app2")
  1946  
  1947  	fromApp2 := &SearchContext{
  1948  		From:  app2Selector,
  1949  		To:    labels.ParseSelectLabelArray("id=app1"),
  1950  		Trace: TRACE_VERBOSE,
  1951  	}
  1952  
  1953  	fromApp3 := &SearchContext{
  1954  		From: labels.ParseSelectLabelArray("id=app3"),
  1955  		To:   labels.ParseSelectLabelArray("id=app1"),
  1956  	}
  1957  
  1958  	repo.Mutex.RLock()
  1959  	// no rules loaded: Allows() => denied
  1960  	require.Equal(t, api.Denied, repo.AllowsIngressRLocked(fromApp2))
  1961  	require.Equal(t, api.Denied, repo.AllowsIngressRLocked(fromApp3))
  1962  	repo.Mutex.RUnlock()
  1963  
  1964  	selFromApp2 := api.NewESFromLabels(
  1965  		labels.ParseSelectLabel("id=app2"),
  1966  	)
  1967  
  1968  	selectorFromApp2 := []api.EndpointSelector{
  1969  		selFromApp2,
  1970  	}
  1971  
  1972  	_, _, err := repo.mustAdd(api.Rule{
  1973  		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("id=app1")),
  1974  		Ingress: []api.IngressRule{
  1975  			{
  1976  				IngressCommonRule: api.IngressCommonRule{
  1977  					FromEndpoints: selectorFromApp2,
  1978  				},
  1979  				ToPorts: []api.PortRule{{
  1980  					Ports: []api.PortProtocol{
  1981  						{Port: "80", Protocol: api.ProtoTCP},
  1982  					},
  1983  				}},
  1984  			},
  1985  		},
  1986  	})
  1987  	require.Nil(t, err)
  1988  
  1989  	_, _, err = repo.mustAdd(api.Rule{
  1990  		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("id=app1")),
  1991  		Ingress: []api.IngressRule{
  1992  			{
  1993  				IngressCommonRule: api.IngressCommonRule{
  1994  					FromEndpoints: selectorFromApp2,
  1995  				},
  1996  				ToPorts: []api.PortRule{{
  1997  					Ports: []api.PortProtocol{
  1998  						{Port: "80", Protocol: api.ProtoTCP},
  1999  					},
  2000  					Rules: &api.L7Rules{
  2001  						HTTP: []api.PortRuleHTTP{
  2002  							{Method: "GET", Path: "/"},
  2003  						},
  2004  					},
  2005  				}},
  2006  			},
  2007  		},
  2008  	})
  2009  	require.Nil(t, err)
  2010  
  2011  	_, _, err = repo.mustAdd(api.Rule{
  2012  		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("id=app1")),
  2013  		Ingress: []api.IngressRule{
  2014  			{
  2015  				IngressCommonRule: api.IngressCommonRule{
  2016  					FromEndpoints: selectorFromApp2,
  2017  				},
  2018  				ToPorts: []api.PortRule{{
  2019  					Ports: []api.PortProtocol{
  2020  						{Port: "80", Protocol: api.ProtoTCP},
  2021  					},
  2022  					Rules: &api.L7Rules{
  2023  						HTTP: []api.PortRuleHTTP{
  2024  							{Method: "GET", Path: "/"},
  2025  						},
  2026  					},
  2027  				}},
  2028  			},
  2029  		},
  2030  	})
  2031  	require.Nil(t, err)
  2032  
  2033  	repo.Mutex.RLock()
  2034  	defer repo.Mutex.RUnlock()
  2035  
  2036  	// L4 from app2 is restricted
  2037  	logBuffer := new(bytes.Buffer)
  2038  	l4IngressPolicy, err := repo.ResolveL4IngressPolicy(fromApp2.WithLogger(logBuffer))
  2039  	require.Nil(t, err)
  2040  
  2041  	cachedSelectorApp2 := td.sc.FindCachedIdentitySelector(selFromApp2)
  2042  	require.NotNil(t, cachedSelectorApp2)
  2043  
  2044  	expected := NewL4Policy(repo.GetRevision())
  2045  	expected.Ingress.PortRules.Upsert("80", 0, "TCP", &L4Filter{
  2046  		Port: 80, Protocol: api.ProtoTCP, U8Proto: 6,
  2047  		L7Parser: ParserTypeHTTP,
  2048  		PerSelectorPolicies: L7DataMap{
  2049  			cachedSelectorApp2: &PerSelectorPolicy{
  2050  				L7Rules: api.L7Rules{
  2051  					HTTP: []api.PortRuleHTTP{{Method: "GET", Path: "/"}, {}},
  2052  				},
  2053  				isRedirect: true,
  2054  			},
  2055  		},
  2056  		Ingress:    true,
  2057  		RuleOrigin: map[CachedSelector]labels.LabelArrayList{cachedSelectorApp2: {nil}},
  2058  	})
  2059  
  2060  	if !assert.EqualValues(t, expected.Ingress.PortRules, l4IngressPolicy) {
  2061  		t.Logf("%s", logBuffer.String())
  2062  		t.Errorf("Resolved policy did not match expected")
  2063  	}
  2064  	l4IngressPolicy.Detach(td.sc)
  2065  	expected.Detach(td.sc)
  2066  
  2067  	// L4 from app3 has no rules
  2068  	expected = NewL4Policy(repo.GetRevision())
  2069  	l4IngressPolicy, err = repo.ResolveL4IngressPolicy(fromApp3)
  2070  	require.Nil(t, err)
  2071  	require.Equal(t, 0, l4IngressPolicy.Len())
  2072  	require.Equal(t, expected.Ingress.PortRules, l4IngressPolicy)
  2073  	l4IngressPolicy.Detach(td.sc)
  2074  	expected.Detach(td.sc)
  2075  }
  2076  
  2077  func buildSearchCtx(from, to string, port uint16) *SearchContext {
  2078  	ports := []*models.Port{{Port: port, Protocol: string(api.ProtoAny)}}
  2079  	return &SearchContext{
  2080  		From:   labels.ParseSelectLabelArray(from),
  2081  		To:     labels.ParseSelectLabelArray(to),
  2082  		DPorts: ports,
  2083  		Trace:  TRACE_ENABLED,
  2084  	}
  2085  }
  2086  
  2087  func buildRule(from, to, port string) api.Rule {
  2088  	reservedES := api.NewESFromLabels(labels.ParseSelectLabel("reserved:host"))
  2089  	fromES := api.NewESFromLabels(labels.ParseSelectLabel(from))
  2090  	toES := api.NewESFromLabels(labels.ParseSelectLabel(to))
  2091  
  2092  	ports := []api.PortRule{}
  2093  	if port != "" {
  2094  		ports = []api.PortRule{
  2095  			{Ports: []api.PortProtocol{{Port: port}}},
  2096  		}
  2097  	}
  2098  	return api.Rule{
  2099  		EndpointSelector: toES,
  2100  		Ingress: []api.IngressRule{
  2101  			{
  2102  				IngressCommonRule: api.IngressCommonRule{
  2103  					FromEndpoints: []api.EndpointSelector{
  2104  						reservedES,
  2105  						fromES,
  2106  					},
  2107  				},
  2108  				ToPorts: ports,
  2109  			},
  2110  		},
  2111  	}
  2112  }
  2113  
  2114  func (repo *Repository) checkTrace(t *testing.T, ctx *SearchContext, trace string,
  2115  	expectedVerdict api.Decision) {
  2116  
  2117  	buffer := new(bytes.Buffer)
  2118  	ctx.Logging = stdlog.New(buffer, "", 0)
  2119  
  2120  	repo.Mutex.RLock()
  2121  	verdict := repo.AllowsIngressRLocked(ctx)
  2122  	repo.Mutex.RUnlock()
  2123  
  2124  	expectedOut := "Tracing " + ctx.String() + "\n" + trace
  2125  	require.EqualValues(t, expectedOut, buffer.String())
  2126  	require.Equal(t, expectedVerdict, verdict)
  2127  }
  2128  
  2129  func TestPolicyTrace(t *testing.T) {
  2130  	td := newTestData()
  2131  	repo := td.repo
  2132  
  2133  	// Add rules to allow foo=>bar
  2134  	l3rule := buildRule("foo", "bar", "")
  2135  	rules := api.Rules{&l3rule}
  2136  	_, _ = repo.MustAddList(rules)
  2137  
  2138  	// foo=>bar is OK
  2139  	expectedOut := `
  2140  Resolving ingress policy for [any:bar]
  2141  * Rule {"matchLabels":{"any:bar":""}}: selected
  2142      Allows from labels {"matchLabels":{"reserved:host":""}}
  2143      Allows from labels {"matchLabels":{"any:foo":""}}
  2144        Found all required labels
  2145  1/1 rules selected
  2146  Found allow rule
  2147  Found no deny rule
  2148  Ingress verdict: allowed
  2149  `
  2150  	ctx := buildSearchCtx("foo", "bar", 0)
  2151  	repo.checkTrace(t, ctx, expectedOut, api.Allowed)
  2152  
  2153  	// foo=>bar:80 is OK
  2154  	ctx = buildSearchCtx("foo", "bar", 80)
  2155  	repo.checkTrace(t, ctx, expectedOut, api.Allowed)
  2156  
  2157  	// bar=>foo is Denied
  2158  	ctx = buildSearchCtx("bar", "foo", 0)
  2159  	expectedOut = `
  2160  Resolving ingress policy for [any:foo]
  2161  0/1 rules selected
  2162  Found no allow rule
  2163  Found no deny rule
  2164  Ingress verdict: denied
  2165  `
  2166  	repo.checkTrace(t, ctx, expectedOut, api.Denied)
  2167  
  2168  	// bar=>foo:80 is also Denied by the same logic
  2169  	ctx = buildSearchCtx("bar", "foo", 80)
  2170  	repo.checkTrace(t, ctx, expectedOut, api.Denied)
  2171  
  2172  	// Now, add extra rules to allow specifically baz=>bar on port 80
  2173  	l4rule := buildRule("baz", "bar", "80")
  2174  	_, _, err := repo.mustAdd(l4rule)
  2175  	require.Nil(t, err)
  2176  
  2177  	// baz=>bar:80 is OK
  2178  	ctx = buildSearchCtx("baz", "bar", 80)
  2179  	expectedOut = `
  2180  Resolving ingress policy for [any:bar]
  2181  * Rule {"matchLabels":{"any:bar":""}}: selected
  2182      Allows from labels {"matchLabels":{"reserved:host":""}}
  2183      Allows from labels {"matchLabels":{"any:foo":""}}
  2184        No label match for [any:baz]
  2185  * Rule {"matchLabels":{"any:bar":""}}: selected
  2186      Allows from labels {"matchLabels":{"reserved:host":""}}
  2187      Allows from labels {"matchLabels":{"any:baz":""}}
  2188        Found all required labels
  2189        Allows port [{80 0 ANY}]
  2190  2/2 rules selected
  2191  Found allow rule
  2192  Found no deny rule
  2193  Ingress verdict: allowed
  2194  `
  2195  	repo.checkTrace(t, ctx, expectedOut, api.Allowed)
  2196  
  2197  	// bar=>bar:80 is Denied
  2198  	ctx = buildSearchCtx("bar", "bar", 80)
  2199  	expectedOut = `
  2200  Resolving ingress policy for [any:bar]
  2201  * Rule {"matchLabels":{"any:bar":""}}: selected
  2202      Allows from labels {"matchLabels":{"reserved:host":""}}
  2203      Allows from labels {"matchLabels":{"any:foo":""}}
  2204        No label match for [any:bar]
  2205  * Rule {"matchLabels":{"any:bar":""}}: selected
  2206      Allows from labels {"matchLabels":{"reserved:host":""}}
  2207      Allows from labels {"matchLabels":{"any:baz":""}}
  2208        No label match for [any:bar]
  2209  2/2 rules selected
  2210  Found no allow rule
  2211  Found no deny rule
  2212  Ingress verdict: denied
  2213  `
  2214  	repo.checkTrace(t, ctx, expectedOut, api.Denied)
  2215  
  2216  	// Test that FromRequires "baz" drops "foo" traffic
  2217  	l3rule = api.Rule{
  2218  		EndpointSelector: api.NewESFromLabels(labels.ParseSelectLabel("bar")),
  2219  		Ingress: []api.IngressRule{{
  2220  			IngressCommonRule: api.IngressCommonRule{
  2221  				FromRequires: []api.EndpointSelector{
  2222  					api.NewESFromLabels(labels.ParseSelectLabel("baz")),
  2223  				},
  2224  			},
  2225  		}},
  2226  	}
  2227  	_, _, err = repo.mustAdd(l3rule)
  2228  	require.Nil(t, err)
  2229  
  2230  	// foo=>bar is now denied due to the FromRequires
  2231  	ctx = buildSearchCtx("foo", "bar", 0)
  2232  	expectedOut = `
  2233  Resolving ingress policy for [any:bar]
  2234  * Rule {"matchLabels":{"any:bar":""}}: selected
  2235      Enforcing requirements [{Key:any.baz Operator:In Values:[]}]
  2236      Allows from labels {"matchLabels":{"reserved:host":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]}
  2237      Allows from labels {"matchLabels":{"any:foo":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]}
  2238        No label match for [any:foo]
  2239  * Rule {"matchLabels":{"any:bar":""}}: selected
  2240      Enforcing requirements [{Key:any.baz Operator:In Values:[]}]
  2241      Allows from labels {"matchLabels":{"reserved:host":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]}
  2242      Allows from labels {"matchLabels":{"any:baz":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]}
  2243        No label match for [any:foo]
  2244  * Rule {"matchLabels":{"any:bar":""}}: selected
  2245  3/3 rules selected
  2246  Found no allow rule
  2247  Found no deny rule
  2248  Ingress verdict: denied
  2249  `
  2250  	repo.checkTrace(t, ctx, expectedOut, api.Denied)
  2251  
  2252  	// baz=>bar is only denied because of the L4 policy
  2253  	ctx = buildSearchCtx("baz", "bar", 0)
  2254  	expectedOut = `
  2255  Resolving ingress policy for [any:bar]
  2256  * Rule {"matchLabels":{"any:bar":""}}: selected
  2257      Enforcing requirements [{Key:any.baz Operator:In Values:[]}]
  2258      Allows from labels {"matchLabels":{"reserved:host":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]}
  2259      Allows from labels {"matchLabels":{"any:foo":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]}
  2260        No label match for [any:baz]
  2261  * Rule {"matchLabels":{"any:bar":""}}: selected
  2262      Enforcing requirements [{Key:any.baz Operator:In Values:[]}]
  2263      Allows from labels {"matchLabels":{"reserved:host":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]}
  2264      Allows from labels {"matchLabels":{"any:baz":""},"matchExpressions":[{"key":"any:baz","operator":"In","values":[""]}]}
  2265        Found all required labels
  2266        Allows port [{80 0 ANY}]
  2267          No port match found
  2268  * Rule {"matchLabels":{"any:bar":""}}: selected
  2269  3/3 rules selected
  2270  Found no allow rule
  2271  Found no deny rule
  2272  Ingress verdict: denied
  2273  `
  2274  	repo.checkTrace(t, ctx, expectedOut, api.Denied)
  2275  
  2276  	// Should still be allowed with the new FromRequires constraint
  2277  	ctx = buildSearchCtx("baz", "bar", 80)
  2278  	repo.Mutex.RLock()
  2279  	verdict := repo.AllowsIngressRLocked(ctx)
  2280  	repo.Mutex.RUnlock()
  2281  	require.Equal(t, api.Allowed, verdict)
  2282  }
  2283  
  2284  func TestIterate(t *testing.T) {
  2285  	td := newTestData()
  2286  	repo := td.repo
  2287  
  2288  	numWithEgress := 0
  2289  	countEgressRules := func(r *api.Rule) {
  2290  		if len(r.Egress) > 0 {
  2291  			numWithEgress++
  2292  		}
  2293  	}
  2294  	repo.Iterate(countEgressRules)
  2295  
  2296  	require.Equal(t, 0, numWithEgress)
  2297  
  2298  	numRules := 10
  2299  	lbls := make([]labels.Label, 10)
  2300  	for i := 0; i < numRules; i++ {
  2301  		it := fmt.Sprintf("baz%d", i)
  2302  		epSelector := api.NewESFromLabels(
  2303  			labels.NewLabel(
  2304  				"foo",
  2305  				it,
  2306  				labels.LabelSourceK8s,
  2307  			),
  2308  		)
  2309  		lbls[i] = labels.NewLabel("tag3", it, labels.LabelSourceK8s)
  2310  		_, _, err := repo.mustAdd(api.Rule{
  2311  			EndpointSelector: epSelector,
  2312  			Labels:           labels.LabelArray{lbls[i]},
  2313  			Egress: []api.EgressRule{
  2314  				{
  2315  					EgressCommonRule: api.EgressCommonRule{
  2316  						ToEndpoints: []api.EndpointSelector{
  2317  							epSelector,
  2318  						},
  2319  					},
  2320  				},
  2321  			},
  2322  		})
  2323  		require.Nil(t, err)
  2324  	}
  2325  
  2326  	numWithEgress = 0
  2327  	repo.Iterate(countEgressRules)
  2328  
  2329  	require.Equal(t, numRules, numWithEgress)
  2330  
  2331  	numModified := 0
  2332  	modifyRules := func(r *api.Rule) {
  2333  		if r.Labels.Contains(labels.LabelArray{lbls[1]}) || r.Labels.Contains(labels.LabelArray{lbls[3]}) {
  2334  			r.Egress = nil
  2335  			numModified++
  2336  		}
  2337  	}
  2338  
  2339  	repo.Iterate(modifyRules)
  2340  
  2341  	require.Equal(t, 2, numModified)
  2342  
  2343  	numWithEgress = 0
  2344  	repo.Iterate(countEgressRules)
  2345  
  2346  	require.Equal(t, numRules-numModified, numWithEgress)
  2347  
  2348  	repo.Mutex.Lock()
  2349  	_, _, numDeleted := repo.DeleteByLabelsLocked(labels.LabelArray{lbls[0]})
  2350  	repo.Mutex.Unlock()
  2351  	require.Equal(t, 1, numDeleted)
  2352  
  2353  	numWithEgress = 0
  2354  	repo.Iterate(countEgressRules)
  2355  
  2356  	require.Equal(t, numRules-numModified-numDeleted, numWithEgress)
  2357  }
  2358  
  2359  // TestDefaultAllow covers the defaulting logic in determining an identity's default rule
  2360  // in the presence or absence of rules that do not enable default-deny mode.
  2361  func TestDefaultAllow(t *testing.T) {
  2362  
  2363  	// Cache policy enforcement value from when test was ran to avoid pollution
  2364  	// across tests.
  2365  	oldPolicyEnable := GetPolicyEnabled()
  2366  	defer SetPolicyEnabled(oldPolicyEnable)
  2367  
  2368  	SetPolicyEnabled(option.DefaultEnforcement)
  2369  
  2370  	fooSelectLabel := labels.ParseSelectLabel("foo")
  2371  
  2372  	genRule := func(ingress, defaultDeny bool) api.Rule {
  2373  		name := fmt.Sprintf("%v_%v", ingress, defaultDeny)
  2374  		r := api.Rule{
  2375  			EndpointSelector: api.NewESFromLabels(fooSelectLabel),
  2376  			Labels:           labels.LabelArray{labels.NewLabel(k8sConst.PolicyLabelName, name, labels.LabelSourceAny)},
  2377  		}
  2378  
  2379  		if ingress {
  2380  			r.Ingress = []api.IngressRule{{
  2381  				IngressCommonRule: api.IngressCommonRule{
  2382  					FromEndpoints: []api.EndpointSelector{api.NewESFromLabels(fooSelectLabel)}}}}
  2383  		} else {
  2384  			r.Egress = []api.EgressRule{{
  2385  				EgressCommonRule: api.EgressCommonRule{
  2386  					ToEndpoints: []api.EndpointSelector{api.NewESFromLabels(fooSelectLabel)}}}}
  2387  		}
  2388  		if ingress {
  2389  			r.EnableDefaultDeny.Ingress = &defaultDeny
  2390  		} else {
  2391  			r.EnableDefaultDeny.Egress = &defaultDeny
  2392  		}
  2393  		require.Nil(t, r.Sanitize())
  2394  		return r
  2395  	}
  2396  
  2397  	iDeny := genRule(true, true)   // ingress default deny
  2398  	iAllow := genRule(true, false) // ingress default allow
  2399  
  2400  	eDeny := genRule(false, true)   // egress default deny
  2401  	eAllow := genRule(false, false) // egress default allow
  2402  
  2403  	type testCase struct {
  2404  		rules           []api.Rule
  2405  		ingress, egress bool
  2406  		ruleC           int // count of rules; indicates wildcard
  2407  	}
  2408  
  2409  	ingressCases := []testCase{
  2410  		{
  2411  			rules: nil, // default case, everything disabled
  2412  		},
  2413  		{
  2414  			rules:   []api.Rule{iDeny},
  2415  			ingress: true,
  2416  			ruleC:   1,
  2417  		},
  2418  		{
  2419  			rules:   []api.Rule{iAllow}, // Just a default-allow rule
  2420  			ingress: true,
  2421  			ruleC:   2, // wildcard must be added
  2422  		},
  2423  		{
  2424  			rules:   []api.Rule{iDeny, iAllow}, // default-deny takes precedence, no wildcard
  2425  			ingress: true,
  2426  			ruleC:   2,
  2427  		},
  2428  	}
  2429  
  2430  	egressCases := []testCase{
  2431  		{
  2432  			rules: nil, // default case, everything disabled
  2433  		},
  2434  		{
  2435  			rules:  []api.Rule{eDeny},
  2436  			egress: true,
  2437  			ruleC:  1,
  2438  		},
  2439  		{
  2440  			rules:  []api.Rule{eAllow}, // Just a default-allow rule
  2441  			egress: true,
  2442  			ruleC:  2, // wildcard must be added
  2443  		},
  2444  		{
  2445  			rules:  []api.Rule{eDeny, eAllow}, // default-deny takes precedence, no wildcard
  2446  			egress: true,
  2447  			ruleC:  2,
  2448  		},
  2449  	}
  2450  
  2451  	// three test runs: ingress, egress, and ingress + egress cartesian
  2452  	for i, tc := range ingressCases {
  2453  		td := newTestData()
  2454  		td.addIdentity(fooIdentity)
  2455  		repo := td.repo
  2456  
  2457  		for _, rule := range tc.rules {
  2458  			_, _, err := repo.mustAdd(rule)
  2459  			require.NoError(t, err, "unable to add rule to policy repository")
  2460  		}
  2461  
  2462  		ing, egr, matchingRules := repo.computePolicyEnforcementAndRules(fooIdentity)
  2463  		require.Equal(t, tc.ingress, ing, "case %d: ingress should match", i)
  2464  		require.Equal(t, tc.egress, egr, "case %d: egress should match", i)
  2465  		require.Equal(t, tc.ruleC, len(matchingRules), "case %d: rule count should match", i)
  2466  	}
  2467  
  2468  	for i, tc := range egressCases {
  2469  		td := newTestData()
  2470  		td.addIdentity(fooIdentity)
  2471  		repo := td.repo
  2472  
  2473  		for _, rule := range tc.rules {
  2474  			_, _, err := repo.mustAdd(rule)
  2475  			require.NoError(t, err, "unable to add rule to policy repository")
  2476  		}
  2477  
  2478  		ing, egr, matchingRules := repo.computePolicyEnforcementAndRules(fooIdentity)
  2479  		require.Equal(t, tc.ingress, ing, "case %d: ingress should match", i)
  2480  		require.Equal(t, tc.egress, egr, "case %d: egress should match", i)
  2481  		require.Equal(t, tc.ruleC, len(matchingRules), "case %d: rule count should match", i)
  2482  	}
  2483  
  2484  	// test all combinations of ingress + egress cases
  2485  	for e, etc := range egressCases {
  2486  		for i, itc := range ingressCases {
  2487  			td := newTestData()
  2488  			td.addIdentity(fooIdentity)
  2489  			repo := td.repo
  2490  
  2491  			for _, rule := range etc.rules {
  2492  				_, _, err := repo.mustAdd(rule)
  2493  				require.NoError(t, err, "unable to add rule to policy repository")
  2494  			}
  2495  
  2496  			for _, rule := range itc.rules {
  2497  				_, _, err := repo.mustAdd(rule)
  2498  				require.NoError(t, err, "unable to add rule to policy repository")
  2499  			}
  2500  
  2501  			ing, egr, matchingRules := repo.computePolicyEnforcementAndRules(fooIdentity)
  2502  			require.Equal(t, itc.ingress, ing, "case ingress %d + egress %d: ingress should match", i, e)
  2503  			require.Equal(t, etc.egress, egr, "case ingress %d + egress %d: egress should match", i, e)
  2504  			require.Equal(t, itc.ruleC+etc.ruleC, len(matchingRules), "case ingress %d + egress %d: rule count should match", i, e)
  2505  		}
  2506  	}
  2507  }
  2508  
  2509  func TestReplaceByResource(t *testing.T) {
  2510  	// don't use the full testdata() here, since we want to watch
  2511  	// selectorcache changes carefully
  2512  	repo := NewPolicyRepository(nil, nil, nil)
  2513  	sc := testNewSelectorCache(nil)
  2514  	repo.selectorCache = sc
  2515  	assert.Len(t, sc.selectors, 0)
  2516  
  2517  	numRules := 10
  2518  	rules := make(api.Rules, 0, numRules)
  2519  	// share the dest selector
  2520  	destSelector := api.NewESFromLabels(labels.NewLabel("peer", "pod", "k8s"))
  2521  	for i := 0; i < numRules; i++ {
  2522  		it := fmt.Sprintf("num-%d", i)
  2523  		epSelector := api.NewESFromLabels(
  2524  			labels.NewLabel(
  2525  				"subject-pod",
  2526  				it,
  2527  				labels.LabelSourceK8s,
  2528  			),
  2529  		)
  2530  		lbl := labels.NewLabel("policy-label", it, labels.LabelSourceK8s)
  2531  		rule := &api.Rule{
  2532  			EndpointSelector: epSelector,
  2533  			Labels:           labels.LabelArray{lbl},
  2534  			Egress: []api.EgressRule{
  2535  				{
  2536  					EgressCommonRule: api.EgressCommonRule{
  2537  						ToEndpoints: []api.EndpointSelector{
  2538  							destSelector,
  2539  						},
  2540  					},
  2541  				},
  2542  			},
  2543  		}
  2544  		require.Nil(t, rule.Sanitize())
  2545  		rules = append(rules, rule)
  2546  	}
  2547  
  2548  	rulesMatch := func(s ruleSlice, rs api.Rules) {
  2549  		t.Helper()
  2550  		ss := make(api.Rules, 0, len(s))
  2551  		for _, rule := range s {
  2552  			ss = append(ss, &rule.Rule)
  2553  		}
  2554  		assert.ElementsMatch(t, ss, rs)
  2555  	}
  2556  	toSlice := func(m map[ruleKey]*rule) ruleSlice {
  2557  		out := ruleSlice{}
  2558  		for _, v := range m {
  2559  			out = append(out, v)
  2560  		}
  2561  		return out
  2562  	}
  2563  
  2564  	rID1 := ipcachetypes.ResourceID("res1")
  2565  	rID2 := ipcachetypes.ResourceID("res2")
  2566  
  2567  	new, old, rev := repo.ReplaceByResourceLocked(rules[0:1], rID1)
  2568  	assert.Len(t, new, 1)
  2569  	assert.Len(t, old, 0)
  2570  	assert.EqualValues(t, rev, 2)
  2571  
  2572  	// check basic bookkeeping
  2573  	assert.Len(t, repo.rules, 1)
  2574  	assert.Len(t, repo.rulesByResource, 1)
  2575  	assert.Len(t, repo.rulesByResource[rID1], 1)
  2576  	rulesMatch(toSlice(repo.rulesByResource[rID1]), rules[0:1])
  2577  
  2578  	// Check that the selectorcache is sane
  2579  	// It should have one selector: the subject pod for rule 0
  2580  	assert.Len(t, sc.selectors, 1)
  2581  
  2582  	// add second resource
  2583  	new, old, rev = repo.ReplaceByResourceLocked(rules[1:3], rID2)
  2584  
  2585  	assert.Len(t, new, 2)
  2586  	assert.Len(t, old, 0)
  2587  	assert.EqualValues(t, rev, 3)
  2588  
  2589  	// check basic bookkeeping
  2590  	assert.Len(t, repo.rules, 3)
  2591  	assert.Len(t, repo.rulesByResource, 2)
  2592  	assert.Len(t, repo.rulesByResource[rID1], 1)
  2593  	assert.Len(t, repo.rulesByResource[rID2], 2)
  2594  	assert.Len(t, sc.selectors, 3)
  2595  
  2596  	// replace rid1 with new rules
  2597  	new, old, _ = repo.ReplaceByResourceLocked(rules[3:5], rID1)
  2598  	assert.Len(t, new, 2)
  2599  	assert.Len(t, old, 1)
  2600  	repo.Release(old)
  2601  
  2602  	// check basic bookkeeping
  2603  	assert.Len(t, repo.rules, 4)
  2604  	assert.Len(t, repo.rulesByResource, 2)
  2605  	assert.Len(t, repo.rulesByResource[rID1], 2)
  2606  	assert.Len(t, repo.rulesByResource[rID2], 2)
  2607  	assert.Len(t, sc.selectors, 4)
  2608  
  2609  	rulesMatch(old, rules[0:1])
  2610  	rulesMatch(new, rules[3:5])
  2611  	rulesMatch(toSlice(repo.rulesByResource[rID1]), rules[3:5])
  2612  	assert.Equal(t, repo.rules[ruleKey{
  2613  		resource: rID1,
  2614  		idx:      0,
  2615  	}].Rule, *rules[3])
  2616  
  2617  	// delete rid1
  2618  	old, _ = repo.DeleteByResourceLocked(rID1)
  2619  	assert.Len(t, old, 2)
  2620  	repo.Release(old)
  2621  
  2622  	assert.Len(t, repo.rules, 2)
  2623  	assert.Len(t, repo.rulesByResource, 1)
  2624  	assert.Len(t, repo.rulesByResource[rID2], 2)
  2625  	assert.Len(t, sc.selectors, 2)
  2626  
  2627  	// delete rid1 again (noop)
  2628  	old, _ = repo.DeleteByResourceLocked(rID1)
  2629  	assert.Len(t, old, 0)
  2630  
  2631  	assert.Len(t, repo.rules, 2)
  2632  	assert.Len(t, repo.rulesByResource, 1)
  2633  	assert.Len(t, repo.rulesByResource[rID2], 2)
  2634  	assert.Len(t, sc.selectors, 2)
  2635  
  2636  	// delete rid2
  2637  	old, _ = repo.DeleteByResourceLocked(rID2)
  2638  	assert.Len(t, old, 2)
  2639  	repo.Release(old)
  2640  
  2641  	assert.Len(t, repo.rules, 0)
  2642  	assert.Len(t, repo.rulesByResource, 0)
  2643  	assert.Len(t, sc.selectors, 0)
  2644  }