github.com/imran-kn/cilium-fork@v1.6.9/pkg/policy/distillery_test.go (about)

     1  // Copyright 2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // +build !privileged_tests
    16  
    17  package policy
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io"
    23  	"testing"
    24  
    25  	"github.com/cilium/cilium/pkg/checker"
    26  	"github.com/cilium/cilium/pkg/identity"
    27  	"github.com/cilium/cilium/pkg/identity/cache"
    28  	"github.com/cilium/cilium/pkg/labels"
    29  	"github.com/cilium/cilium/pkg/policy/api"
    30  	"github.com/cilium/cilium/pkg/policy/trafficdirection"
    31  	"github.com/cilium/cilium/pkg/testutils"
    32  
    33  	logging "github.com/op/go-logging"
    34  	. "gopkg.in/check.v1"
    35  )
    36  
    37  //
    38  // Distillery unit tests
    39  //
    40  
    41  type DistilleryTestSuite struct{}
    42  
    43  var (
    44  	_ = Suite(&DistilleryTestSuite{})
    45  
    46  	ep1 = newTestEP()
    47  	ep2 = newTestEP()
    48  )
    49  
    50  // testEP wraps the testutils endpoint implementation to provide
    51  // LookupRedirectPort() until tproxy support makes this redundant.
    52  // This avoids import cycles when adding policy to the imports in testutils.
    53  type testEP struct {
    54  	testutils.TestEndpoint
    55  }
    56  
    57  func newTestEP() *testEP {
    58  	return &testEP{
    59  		testutils.NewTestEndpoint(),
    60  	}
    61  }
    62  
    63  func (ep *testEP) WithIdentity(id int64) *testEP {
    64  	ep.SetIdentity(id, true)
    65  	return ep
    66  }
    67  
    68  func (ep *testEP) LookupRedirectPort(l4 *L4Filter) uint16 {
    69  	return 42
    70  }
    71  
    72  func (s *DistilleryTestSuite) TestCacheManagement(c *C) {
    73  	repo := NewPolicyRepository()
    74  	cache := repo.policyCache
    75  	identity := ep1.GetSecurityIdentity()
    76  	c.Assert(ep2.GetSecurityIdentity(), Equals, identity)
    77  
    78  	// Nonsense delete of entry that isn't yet inserted
    79  	deleted := cache.delete(identity)
    80  	c.Assert(deleted, Equals, false)
    81  
    82  	// Insert identity twice. Should be the same policy.
    83  	policy1, _ := cache.insert(identity)
    84  	policy2, _ := cache.insert(identity)
    85  	c.Assert(policy1, Equals, policy2)
    86  
    87  	// Despite two insert calls, there is no reference tracking; any delete
    88  	// will clear the cache.
    89  	cacheCleared := cache.delete(identity)
    90  	c.Assert(cacheCleared, Equals, true)
    91  	cacheCleared = cache.delete(identity)
    92  	c.Assert(cacheCleared, Equals, false)
    93  
    94  	// Insert two distinct identities, then delete one. Other should still
    95  	// be there.
    96  	ep3 := newTestEP().WithIdentity(1234)
    97  	identity3 := ep3.GetSecurityIdentity()
    98  	c.Assert(identity3, Not(Equals), identity)
    99  	policy1, _ = cache.insert(identity)
   100  	policy3, _ := cache.insert(identity3)
   101  	c.Assert(policy1, Not(Equals), policy3)
   102  	_ = cache.delete(identity)
   103  	policy3, _ = cache.lookupOrCreate(identity3, false)
   104  	c.Assert(policy3, NotNil)
   105  }
   106  
   107  func (s *DistilleryTestSuite) TestCachePopulation(c *C) {
   108  	repo := NewPolicyRepository()
   109  	repo.revision = 42
   110  	cache := repo.policyCache
   111  
   112  	identity1 := ep1.GetSecurityIdentity()
   113  	c.Assert(ep2.GetSecurityIdentity(), Equals, identity1)
   114  	policy1, computed := cache.insert(identity1)
   115  	c.Assert(computed, Equals, false)
   116  
   117  	// Calculate the policy and observe that it's cached
   118  	updated, err := cache.updateSelectorPolicy(identity1)
   119  	c.Assert(err, IsNil)
   120  	c.Assert(updated, Equals, true)
   121  	updated, err = cache.updateSelectorPolicy(identity1)
   122  	c.Assert(err, IsNil)
   123  	c.Assert(updated, Equals, false)
   124  	policy2, computed := cache.insert(identity1)
   125  	c.Assert(computed, Equals, true)
   126  	idp1 := policy1.(*cachedSelectorPolicy).getPolicy()
   127  	idp2 := policy2.(*cachedSelectorPolicy).getPolicy()
   128  	c.Assert(idp1, Equals, idp2)
   129  
   130  	// Remove the identity and observe that it is no longer available
   131  	cacheCleared := cache.delete(identity1)
   132  	c.Assert(cacheCleared, Equals, true)
   133  	updated, err = cache.updateSelectorPolicy(identity1)
   134  	c.Assert(err, NotNil)
   135  
   136  	// Attempt to update policy for non-cached endpoint and observe failure
   137  	ep3 := newTestEP().WithIdentity(1234)
   138  	_, err = cache.updateSelectorPolicy(ep3.GetSecurityIdentity())
   139  	c.Assert(err, NotNil)
   140  	c.Assert(updated, Equals, false)
   141  
   142  	// Insert endpoint with different identity and observe that the cache
   143  	// is different from ep1, ep2
   144  	policy1, computed = cache.insert(identity1)
   145  	c.Assert(computed, Equals, false)
   146  	idp1 = policy1.(*cachedSelectorPolicy).getPolicy()
   147  	c.Assert(idp1, NotNil)
   148  	identity3 := ep3.GetSecurityIdentity()
   149  	policy3, computed := cache.insert(identity3)
   150  	c.Assert(policy3, Not(Equals), policy1)
   151  	c.Assert(computed, Equals, false)
   152  	updated, err = cache.updateSelectorPolicy(identity3)
   153  	c.Assert(err, IsNil)
   154  	c.Assert(updated, Equals, true)
   155  	idp3 := policy3.(*cachedSelectorPolicy).getPolicy()
   156  	c.Assert(idp3, Not(Equals), idp1)
   157  
   158  	// If there's an error during policy resolution, update should fail
   159  	//repo.err = fmt.Errorf("not implemented!")
   160  	//repo.revision++
   161  	//_, err = cache.updateSelectorPolicy(identity3)
   162  	//c.Assert(err, NotNil)
   163  }
   164  
   165  //
   166  // Distillery integration tests
   167  //
   168  
   169  var (
   170  	// Identity, labels, selectors for an endpoint named "foo"
   171  	identityFoo = uint32(100)
   172  	labelsFoo   = labels.ParseSelectLabelArray("foo", "red")
   173  	selectFoo_  = api.NewESFromLabels(labels.ParseSelectLabel("foo"))
   174  	allowFooL3_ = selectFoo_
   175  
   176  	// Identity, labels, selectors for an endpoint named "bar"
   177  	identityBar = uint32(200)
   178  	labelsBar   = labels.ParseSelectLabelArray("bar", "blue")
   179  	selectBar_  = api.NewESFromLabels(labels.ParseSelectLabel("bar"))
   180  	allowBarL3_ = selectBar_
   181  
   182  	// API rule sections for composability
   183  	// L4 rule sections
   184  	allowAllL4_ []api.PortRule
   185  	allowPort80 = []api.PortRule{{
   186  		Ports: []api.PortProtocol{
   187  			{Port: "80", Protocol: api.ProtoTCP},
   188  		},
   189  	}}
   190  	// L7 rule sections
   191  	allowHTTPRoot = &api.L7Rules{
   192  		HTTP: []api.PortRuleHTTP{
   193  			{Method: "GET", Path: "/"},
   194  		},
   195  		L7Proto: ParserTypeHTTP.String(),
   196  	}
   197  	// API rule definitions for default-deny, L3, L3L4, L3L4L7, L4, L4L7
   198  	rule____NoAllow = api.NewRule().
   199  			WithIngressRules([]api.IngressRule{{}})
   200  	ruleL3____Allow = api.NewRule().
   201  			WithIngressRules([]api.IngressRule{{
   202  			FromEndpoints: []api.EndpointSelector{allowFooL3_},
   203  			ToPorts:       allowAllL4_,
   204  		}})
   205  	ruleL3L4__Allow = api.NewRule().
   206  			WithIngressRules([]api.IngressRule{{
   207  			FromEndpoints: []api.EndpointSelector{allowFooL3_},
   208  			ToPorts:       allowPort80,
   209  		}})
   210  	ruleL3L4L7Allow = api.NewRule().
   211  			WithIngressRules([]api.IngressRule{{
   212  			FromEndpoints: []api.EndpointSelector{allowFooL3_},
   213  			ToPorts:       combineL4L7(allowPort80, allowHTTPRoot),
   214  		}})
   215  	rule__L4__Allow = api.NewRule().
   216  			WithIngressRules([]api.IngressRule{{
   217  			ToPorts: allowPort80,
   218  		}})
   219  	rule__L4L7Allow = api.NewRule().
   220  			WithIngressRules([]api.IngressRule{{
   221  			ToPorts: combineL4L7(allowPort80, allowHTTPRoot),
   222  		}})
   223  
   224  	rule__L3AllowFoo = api.NewRule().
   225  				WithIngressRules([]api.IngressRule{{
   226  			FromEndpoints: []api.EndpointSelector{allowFooL3_},
   227  		}})
   228  
   229  	rule__L3AllowBar = api.NewRule().
   230  				WithIngressRules([]api.IngressRule{{
   231  			FromEndpoints: []api.EndpointSelector{allowBarL3_},
   232  		}})
   233  	rule____AllowAll = api.NewRule().
   234  				WithIngressRules([]api.IngressRule{{
   235  			FromEndpoints: []api.EndpointSelector{api.WildcardEndpointSelector},
   236  		}})
   237  
   238  	// Misc other bpf key fields for convenience / readability.
   239  	l7RedirectNone_ = uint16(0)
   240  	l7RedirectProxy = uint16(1)
   241  	dirIngress      = trafficdirection.Ingress.Uint8()
   242  	// Desired map keys for L3, L3-dependent L4, L4
   243  	mapKeyAllowFoo__ = Key{identityFoo, 0, 0, dirIngress}
   244  	mapKeyAllowBar__ = Key{identityBar, 0, 0, dirIngress}
   245  	mapKeyAllowFooL4 = Key{identityFoo, 80, 6, dirIngress}
   246  	mapKeyAllow___L4 = Key{0, 80, 6, dirIngress}
   247  	mapKeyAllowAll__ = Key{0, 0, 0, dirIngress}
   248  	// Desired map entries for no L7 redirect / redirect to Proxy
   249  	mapEntryL7None_ = MapStateEntry{l7RedirectNone_}
   250  	mapEntryL7Proxy = MapStateEntry{l7RedirectProxy}
   251  )
   252  
   253  // combineL4L7 returns a new PortRule that refers to the specified l4 ports and
   254  // l7 rules.
   255  func combineL4L7(l4 []api.PortRule, l7 *api.L7Rules) []api.PortRule {
   256  	result := make([]api.PortRule, len(l4))
   257  	for _, pr := range l4 {
   258  		result = append(result, api.PortRule{
   259  			Ports: pr.Ports,
   260  			Rules: l7,
   261  		})
   262  	}
   263  	return result
   264  }
   265  
   266  // policyDistillery is a convenience wrapper around the existing policy engine,
   267  // allowing simple direct evaluation of L3 and L4 state into "MapState".
   268  type policyDistillery struct {
   269  	*Repository
   270  	log io.Writer
   271  }
   272  
   273  func newPolicyDistillery(selectorCache *SelectorCache) *policyDistillery {
   274  	ret := &policyDistillery{
   275  		Repository: NewPolicyRepository(),
   276  	}
   277  	ret.selectorCache = selectorCache
   278  	return ret
   279  }
   280  
   281  func (d *policyDistillery) WithLogBuffer(w io.Writer) *policyDistillery {
   282  	return &policyDistillery{
   283  		Repository: d.Repository,
   284  		log:        w,
   285  	}
   286  }
   287  
   288  // distillPolicy distills the policy repository into a set of bpf map state
   289  // entries for an endpoint with the specified labels.
   290  func (d *policyDistillery) distillPolicy(epLabels labels.LabelArray) (MapState, error) {
   291  	result := make(MapState)
   292  
   293  	endpointSelected, _ := d.Repository.GetRulesMatching(epLabels)
   294  	io.WriteString(d.log, fmt.Sprintf("[distill] Endpoint selected by policy: %t\n", endpointSelected))
   295  	if !endpointSelected {
   296  		allowAllIngress := true
   297  		allowAllEgress := false // Skip egress
   298  		result.AllowAllIdentities(allowAllIngress, allowAllEgress)
   299  		return result, nil
   300  	}
   301  
   302  	// Prepare the L4 policy so we know whether L4 policy may apply
   303  	ingressL4 := SearchContext{
   304  		To:    epLabels,
   305  		Trace: TRACE_VERBOSE,
   306  	}
   307  	ingressL4.Logging = logging.NewLogBackend(d.log, "", 0)
   308  	io.WriteString(d.log, fmt.Sprintf("[distill] Evaluating L4 -> %s", epLabels))
   309  	l4IngressPolicy, err := d.Repository.ResolveL4IngressPolicy(&ingressL4)
   310  	if err != nil {
   311  		return nil, err
   312  	}
   313  
   314  	// Handle L4 ingress from each identity in the cache to the endpoint.
   315  	io.WriteString(d.log, "[distill] Producing L4 filter keys\n")
   316  	for _, l4 := range l4IngressPolicy {
   317  		io.WriteString(d.log, fmt.Sprintf("[distill] Processing L4Filter (l3: %+v), (l4: %d/%s), (l7: %+v)\n", l4.CachedSelectors, l4.Port, l4.Protocol, l4.L7RulesPerEp))
   318  		for _, key := range l4.ToKeys(0) {
   319  			io.WriteString(d.log, fmt.Sprintf("[distill] L4 ingress allow %+v (parser=%s, redirect=%t)\n", key, l4.L7Parser, l4.IsRedirect()))
   320  			if l4.IsRedirect() {
   321  				result[key] = MapStateEntry{l7RedirectProxy}
   322  			} else {
   323  				result[key] = MapStateEntry{l7RedirectNone_}
   324  			}
   325  		}
   326  	}
   327  	l4IngressPolicy.Detach(d.Repository.GetSelectorCache())
   328  
   329  	// Handle L3-wildcard of L7 destinations
   330  	// Eg, when you have L4+L7 "allow /public on 80" with L3 "allow all from foo"
   331  	// Initially, three keys would be generated: L4+L7, L3+L4+L7, and L3.
   332  	// Here, we remove the L3+L4+L7 key if it overlaps with the L4+L7,
   333  	// but only if they have the same L7 redirect.
   334  	//
   335  	// For these the BPF policy order of attempting L3+L4 lookup, L4 lookup,
   336  	// then L3 lookup means that three keys are not strictly necessary; the
   337  	// correct behaviour can be encoded with two keys - an L4 key and L3 key.
   338  	nKeys := 0
   339  	l3l4keys := make([]Key, len(result))
   340  	for k := range result {
   341  		if k.Identity > 0 && k.DestPort > 0 {
   342  			l3l4keys[nKeys] = k
   343  			nKeys++
   344  		}
   345  	}
   346  	for i := 0; i < nKeys; i++ {
   347  		k := l3l4keys[i]
   348  		io.WriteString(d.log, fmt.Sprintf("[distill] Squashing L3-dependent L4 key %+v\n", k))
   349  		wildcardL3 := Key{DestPort: k.DestPort, Nexthdr: k.Nexthdr, TrafficDirection: k.TrafficDirection}
   350  		wildcardL4 := Key{Identity: k.Identity, TrafficDirection: k.TrafficDirection}
   351  		if _, ok := result[wildcardL4]; ok {
   352  			io.WriteString(d.log, fmt.Sprintf("[distill] -> Found L3 overlap %+v\n", wildcardL4))
   353  			if entry, ok := result[wildcardL3]; ok {
   354  				io.WriteString(d.log, fmt.Sprintf("[distill] -> Found L4 overlap %+v:%+v\n", wildcardL3, entry))
   355  				if entry.ProxyPort == result[k].ProxyPort {
   356  					io.WriteString(d.log, fmt.Sprintf("[distill] -> Removing L3-dependent L4 %+v\n", k))
   357  					delete(result, k)
   358  				}
   359  			}
   360  		}
   361  	}
   362  
   363  	return result, nil
   364  }
   365  
   366  func Test_MergeL3(t *testing.T) {
   367  	identityCache := cache.IdentityCache{
   368  		identity.NumericIdentity(identityFoo): labelsFoo,
   369  		identity.NumericIdentity(identityBar): labelsBar,
   370  	}
   371  	selectorCache := testNewSelectorCache(identityCache)
   372  
   373  	tests := []struct {
   374  		test   int
   375  		rules  api.Rules
   376  		result MapState
   377  	}{
   378  		{0, api.Rules{rule__L3AllowFoo, rule__L3AllowBar}, MapState{mapKeyAllowFoo__: mapEntryL7None_, mapKeyAllowBar__: mapEntryL7None_}},
   379  		{1, api.Rules{rule__L3AllowFoo, ruleL3L4__Allow}, MapState{mapKeyAllowFoo__: mapEntryL7None_, mapKeyAllowFooL4: mapEntryL7None_}},
   380  	}
   381  
   382  	for _, tt := range tests {
   383  		repo := newPolicyDistillery(selectorCache)
   384  		for _, r := range tt.rules {
   385  			if r != nil {
   386  				rule := r.WithEndpointSelector(selectFoo_)
   387  				_, _ = repo.AddList(api.Rules{rule})
   388  			}
   389  		}
   390  		t.Run(fmt.Sprintf("permutation_%d", tt.test), func(t *testing.T) {
   391  			logBuffer := new(bytes.Buffer)
   392  			repo = repo.WithLogBuffer(logBuffer)
   393  			mapstate, err := repo.distillPolicy(labelsFoo)
   394  			if err != nil {
   395  				t.Errorf("Policy resolution failure: %s", err)
   396  			}
   397  			if equal, err := checker.DeepEqual(mapstate, tt.result); !equal {
   398  				t.Logf("Rules:\n%s\n\n", tt.rules.String())
   399  				t.Logf("Policy Trace: \n%s\n", logBuffer.String())
   400  				t.Errorf("Policy obtained didn't match expected for endpoint %s:\n%s", labelsFoo, err)
   401  			}
   402  		})
   403  	}
   404  }
   405  
   406  func Test_MergeRules(t *testing.T) {
   407  	identityCache := cache.IdentityCache{
   408  		identity.NumericIdentity(identityFoo): labelsFoo,
   409  	}
   410  	selectorCache := testNewSelectorCache(identityCache)
   411  
   412  	tests := []struct {
   413  		test   int
   414  		rules  api.Rules
   415  		result MapState
   416  	}{
   417  		// The following table is derived from the Google Doc here:
   418  		// https://docs.google.com/spreadsheets/d/1WANIoZGB48nryylQjjOw6lKjI80eVgPShrdMTMalLEw/edit?usp=sharing
   419  		//
   420  		//  Rule 0                   | Rule 1         | Rule 2         | Rule 3         | Rule 4         | Desired BPF map state
   421  		{0, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{}},
   422  		{1, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFoo__: mapEntryL7None_}},
   423  		{2, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7None_}},
   424  		{3, api.Rules{rule____NoAllow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}},
   425  		{4, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7None_}},
   426  		{5, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}},
   427  		{6, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7None_}},                                    // Differs from spreadsheet(!)
   428  		{7, api.Rules{rule____NoAllow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!)
   429  		{8, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy}},
   430  		{9, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}},
   431  		{10, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy}},
   432  		{11, api.Rules{rule____NoAllow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}},
   433  		{12, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7Proxy}},
   434  		{13, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!)
   435  		{14, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7Proxy}},
   436  		{15, api.Rules{rule____NoAllow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!)
   437  		{16, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy}},
   438  		{17, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}},
   439  		// TODO: Tests 22-23 reveal a bug in the redirect logic (GH-7438).
   440  		//{18, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7None_}},
   441  		//{19, api.Rules{ruleL3L4L7Allow, rule____NoAllow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}},
   442  		{20, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy}},
   443  		{21, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}},
   444  		// TODO: Tests 22-23 reveal a bug in the redirect logic (GH-7438).
   445  		//{22, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7None_}},
   446  		//{23, api.Rules{ruleL3L4L7Allow, rule____NoAllow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7None_, mapKeyAllowFoo__: mapEntryL7None_}},
   447  		{24, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7Proxy}},
   448  		{25, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!)
   449  		{26, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7Proxy}},
   450  		{27, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, rule____NoAllow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!)
   451  		{28, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7Proxy}},
   452  		{29, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule____NoAllow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!)
   453  		{30, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, rule____NoAllow}, MapState{mapKeyAllowFooL4: mapEntryL7Proxy, mapKeyAllow___L4: mapEntryL7Proxy}},
   454  		{31, api.Rules{ruleL3L4L7Allow, rule__L4L7Allow, ruleL3L4__Allow, rule__L4__Allow, ruleL3____Allow}, MapState{mapKeyAllow___L4: mapEntryL7Proxy, mapKeyAllowFoo__: mapEntryL7None_}}, // Differs from spreadsheet(!)
   455  	}
   456  	for _, tt := range tests {
   457  		repo := newPolicyDistillery(selectorCache)
   458  		for _, r := range tt.rules {
   459  			if r != nil {
   460  				rule := r.WithEndpointSelector(selectFoo_)
   461  				_, _ = repo.AddList(api.Rules{rule})
   462  			}
   463  		}
   464  		t.Run(fmt.Sprintf("permutation_%d", tt.test), func(t *testing.T) {
   465  			logBuffer := new(bytes.Buffer)
   466  			repo = repo.WithLogBuffer(logBuffer)
   467  			mapstate, err := repo.distillPolicy(labelsFoo)
   468  			if err != nil {
   469  				t.Errorf("Policy resolution failure: %s", err)
   470  			}
   471  			if equal, err := checker.DeepEqual(mapstate, tt.result); !equal {
   472  				t.Logf("Rules:\n%s\n\n", tt.rules.String())
   473  				t.Logf("Policy Trace: \n%s\n", logBuffer.String())
   474  				t.Errorf("Policy obtained didn't match expected for endpoint %s:\n%s", labelsFoo, err)
   475  			}
   476  		})
   477  	}
   478  }
   479  
   480  func Test_AllowAll(t *testing.T) {
   481  	identityCache := cache.IdentityCache{
   482  		identity.NumericIdentity(identityFoo): labelsFoo,
   483  		identity.NumericIdentity(identityBar): labelsBar,
   484  	}
   485  	selectorCache := testNewSelectorCache(identityCache)
   486  
   487  	tests := []struct {
   488  		test     int
   489  		selector api.EndpointSelector
   490  		rules    api.Rules
   491  		result   MapState
   492  	}{
   493  		{0, api.EndpointSelectorNone, api.Rules{rule____AllowAll}, MapState{mapKeyAllowAll__: mapEntryL7None_}},
   494  		{1, api.WildcardEndpointSelector, api.Rules{rule____AllowAll}, MapState{mapKeyAllowAll__: mapEntryL7None_}},
   495  	}
   496  
   497  	for _, tt := range tests {
   498  		repo := newPolicyDistillery(selectorCache)
   499  		for _, r := range tt.rules {
   500  			if r != nil {
   501  				rule := r.WithEndpointSelector(tt.selector)
   502  				_, _ = repo.AddList(api.Rules{rule})
   503  			}
   504  		}
   505  		t.Run(fmt.Sprintf("permutation_%d", tt.test), func(t *testing.T) {
   506  			logBuffer := new(bytes.Buffer)
   507  			repo = repo.WithLogBuffer(logBuffer)
   508  			mapstate, err := repo.distillPolicy(labelsFoo)
   509  			if err != nil {
   510  				t.Errorf("Policy resolution failure: %s", err)
   511  			}
   512  			if equal, err := checker.DeepEqual(mapstate, tt.result); !equal {
   513  				t.Logf("Rules:\n%s\n\n", tt.rules.String())
   514  				t.Logf("Policy Trace: \n%s\n", logBuffer.String())
   515  				t.Errorf("Policy obtained didn't match expected for endpoint %s:\n%s", labelsFoo, err)
   516  			}
   517  		})
   518  	}
   519  }