github.phpd.cn/cilium/cilium@v1.6.12/test/runtime/Policies.go (about)

     1  // Copyright 2017-2019 Authors of Cilium
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package RuntimeTest
    16  
    17  import (
    18  	"context"
    19  	"fmt"
    20  	"os"
    21  	"sync"
    22  	"time"
    23  
    24  	"github.com/cilium/cilium/api/v1/models"
    25  	"github.com/cilium/cilium/pkg/policy/api"
    26  	. "github.com/cilium/cilium/test/ginkgo-ext"
    27  	"github.com/cilium/cilium/test/helpers"
    28  	"github.com/cilium/cilium/test/helpers/constants"
    29  	. "github.com/onsi/gomega"
    30  	"github.com/onsi/gomega/types"
    31  	"github.com/sirupsen/logrus"
    32  )
    33  
    34  const (
    35  	// Commands
    36  	ping         = "ping"
    37  	ping6        = "ping6"
    38  	http         = "http"
    39  	http6        = "http6"
    40  	httpPrivate  = "http_private"
    41  	http6Private = "http6_private"
    42  
    43  	httpPathRewrite  = "http_path_rewrite"
    44  	http6PathRewrite = "http6_path_rewrite"
    45  
    46  	// Policy files
    47  	policyJSON                      = "policy.json"
    48  	invalidJSON                     = "invalid.json"
    49  	multL7PoliciesJSON              = "Policies-l7-multiple.json"
    50  	policiesL7JSON                  = "Policies-l7-simple.json"
    51  	policiesL3JSON                  = "Policies-l3-policy.json"
    52  	policiesL4Json                  = "Policies-l4-policy.json"
    53  	policiesL3DependentL7EgressJSON = "Policies-l3-dependent-l7-egress.json"
    54  	policiesReservedInitJSON        = "Policies-reserved-init.json"
    55  )
    56  
    57  var _ = Describe("RuntimePolicies", func() {
    58  
    59  	var (
    60  		vm            *helpers.SSHMeta
    61  		monitorStop   = func() error { return nil }
    62  		initContainer string
    63  		cleanup       = func() { return }
    64  	)
    65  
    66  	BeforeAll(func() {
    67  		vm = helpers.InitRuntimeHelper(helpers.Runtime, logger)
    68  		// Make sure that Cilium is started with appropriate CLI options
    69  		// (specifically to exclude the local addresses that are populated for
    70  		// CIDR policy tests).
    71  		Expect(vm.SetUpCilium()).To(BeNil())
    72  		ExpectCiliumReady(vm)
    73  		vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
    74  		vm.PolicyDelAll()
    75  
    76  		initContainer = "initContainer"
    77  
    78  		areEndpointsReady := vm.WaitEndpointsReady()
    79  		Expect(areEndpointsReady).Should(BeTrue(), "Endpoints are not ready after timeout")
    80  	})
    81  
    82  	BeforeEach(func() {
    83  		ExpectPolicyEnforcementUpdated(vm, helpers.PolicyEnforcementDefault)
    84  		cleanup = func() { return }
    85  	})
    86  
    87  	AfterEach(func() {
    88  		vm.PolicyDelAll().ExpectSuccess("Unable to delete all policies")
    89  		cleanup()
    90  	})
    91  
    92  	JustBeforeEach(func() {
    93  		monitorStop = vm.MonitorStart()
    94  	})
    95  
    96  	JustAfterEach(func() {
    97  		vm.ValidateNoErrorsInLogs(CurrentGinkgoTestDescription().Duration)
    98  		Expect(monitorStop()).To(BeNil(), "cannot stop monitor command")
    99  	})
   100  
   101  	AfterFailed(func() {
   102  		vm.ReportFailed()
   103  	})
   104  
   105  	AfterAll(func() {
   106  		vm.PolicyDelAll().ExpectSuccess("Unable to delete all policies")
   107  		vm.SampleContainersActions(helpers.Delete, helpers.CiliumDockerNetwork)
   108  		vm.CloseSSHClient()
   109  	})
   110  
   111  	pingRequests := []string{ping, ping6}
   112  	httpRequestsPublic := []string{http, http6}
   113  	httpRequestsPrivate := []string{httpPrivate, http6Private}
   114  	httpRequestsPathRewrite := []string{httpPathRewrite, http6PathRewrite}
   115  	httpRequests := append(httpRequestsPublic, httpRequestsPrivate...)
   116  	httpRequests = append(httpRequests, httpRequestsPathRewrite...)
   117  	allRequests := append(pingRequests, httpRequests...)
   118  	connectivityTest := func(tests []string, client, server string, expectsSuccess bool) {
   119  		var assertFn func() types.GomegaMatcher
   120  		if expectsSuccess {
   121  			assertFn = BeTrue
   122  		} else {
   123  			assertFn = BeFalse
   124  		}
   125  
   126  		if client != helpers.Host {
   127  			_, err := vm.ContainerInspectNet(client)
   128  			ExpectWithOffset(1, err).Should(BeNil(), fmt.Sprintf(
   129  				"could not get container %q (client) meta", client))
   130  		}
   131  
   132  		srvIP, err := vm.ContainerInspectNet(server)
   133  		ExpectWithOffset(1, err).Should(BeNil(), fmt.Sprintf(
   134  			"could not get container %q (server) meta", server))
   135  		for _, test := range tests {
   136  			var command, commandName, dst, resultName string
   137  			switch test {
   138  			case ping:
   139  				command = helpers.Ping(srvIP[helpers.IPv4])
   140  				dst = srvIP[helpers.IPv4]
   141  			case ping6:
   142  				command = helpers.Ping6(srvIP[helpers.IPv6])
   143  				dst = srvIP[helpers.IPv6]
   144  			case http, httpPrivate, httpPathRewrite:
   145  				dst = srvIP[helpers.IPv4]
   146  			case http6, http6Private, http6PathRewrite:
   147  				dst = fmt.Sprintf("[%s]", srvIP[helpers.IPv6])
   148  			}
   149  			switch test {
   150  			case ping:
   151  				commandName = "ping"
   152  			case ping6:
   153  				commandName = "ping6"
   154  			case http:
   155  				commandName = "curl public IPv4 URL on"
   156  				command = helpers.CurlFail("http://%s:80/public", dst)
   157  			case http6:
   158  				commandName = "curl public IPv6 URL on"
   159  				command = helpers.CurlFail("http://%s:80/public", dst)
   160  			case httpPrivate:
   161  				commandName = "curl private IPv4 URL on"
   162  				command = helpers.CurlFail("http://%s:80/private", dst)
   163  			case http6Private:
   164  				commandName = "curl private IPv6 URL on"
   165  				command = helpers.CurlFail("http://%s:80/private", dst)
   166  			case httpPathRewrite:
   167  				commandName = "curl path rewrite IPv4 URL on"
   168  				command = helpers.CurlFail("http://%s:80/public/../private", dst)
   169  			case http6PathRewrite:
   170  				commandName = "curl path rewrite IPv6 URL on"
   171  				command = helpers.CurlFail("http://%s:80/public/../private", dst)
   172  			}
   173  			if expectsSuccess {
   174  				resultName = "succeed"
   175  			} else {
   176  				resultName = "fail"
   177  			}
   178  			By("%q attempting to %q %q", client, commandName, server)
   179  			var res *helpers.CmdRes
   180  			if client != helpers.Host {
   181  				res = vm.ContainerExec(client, command)
   182  			} else {
   183  				res = vm.Exec(command)
   184  			}
   185  			ExpectWithOffset(1, res.WasSuccessful()).Should(assertFn(),
   186  				fmt.Sprintf("%q expects %s %s (%s) to %s", client, commandName, server, dst, resultName))
   187  		}
   188  	}
   189  
   190  	checkProxyStatistics := func(epID string, reqsFwd, reqsReceived, reqsDenied, respFwd, respReceived int) {
   191  		epModel := vm.EndpointGet(epID)
   192  		Expect(epModel).To(Not(BeNil()), "nil model returned for endpoint %s", epID)
   193  		for _, epProxyStatistics := range epModel.Status.Policy.ProxyStatistics {
   194  			if epProxyStatistics.Location == models.ProxyStatisticsLocationEgress {
   195  				ExpectWithOffset(1, epProxyStatistics.Statistics.Requests.Forwarded).To(BeEquivalentTo(reqsFwd), "Unexpected number of forwarded requests to proxy")
   196  				ExpectWithOffset(1, epProxyStatistics.Statistics.Requests.Received).To(BeEquivalentTo(reqsReceived), "Unexpected number of received requests to proxy")
   197  				ExpectWithOffset(1, epProxyStatistics.Statistics.Requests.Denied).To(BeEquivalentTo(reqsDenied), "Unexpected number of denied requests to proxy")
   198  				ExpectWithOffset(1, epProxyStatistics.Statistics.Responses.Forwarded).To(BeEquivalentTo(respFwd), "Unexpected number of forwarded responses from proxy")
   199  				ExpectWithOffset(1, epProxyStatistics.Statistics.Responses.Received).To(BeEquivalentTo(respReceived), "Unexpected number of received responses from proxy")
   200  			}
   201  		}
   202  	}
   203  
   204  	It("L3/L4 Checks", func() {
   205  		_, err := vm.PolicyImportAndWait(vm.GetFullPath(policiesL3JSON), helpers.HelperTimeout)
   206  		Expect(err).Should(BeNil())
   207  
   208  		//APP1 can connect to all Httpd1
   209  		connectivityTest(allRequests, helpers.App1, helpers.Httpd1, true)
   210  
   211  		//APP2 can't connect to Httpd1
   212  		connectivityTest([]string{http}, helpers.App2, helpers.Httpd1, false)
   213  
   214  		// APP1 can reach using TCP HTTP2
   215  		connectivityTest(httpRequestsPublic, helpers.App1, helpers.Httpd2, true)
   216  
   217  		// APP2 can't reach using TCP to HTTP2
   218  		connectivityTest(httpRequestsPublic, helpers.App2, helpers.Httpd2, false)
   219  
   220  		// APP3 can reach using TCP to HTTP2, but can't ping due to egress rule.
   221  		connectivityTest(httpRequestsPublic, helpers.App3, helpers.Httpd2, true)
   222  		connectivityTest(pingRequests, helpers.App3, helpers.Httpd2, false)
   223  
   224  		// APP3 can't reach using TCP to HTTP3
   225  		connectivityTest(allRequests, helpers.App3, helpers.Httpd3, false)
   226  
   227  		// app2 can reach httpd3 for all requests due to l3-only label-based allow policy.
   228  		connectivityTest(allRequests, helpers.App2, helpers.Httpd3, true)
   229  
   230  		// app2 cannot reach httpd2 for all requests.
   231  		connectivityTest(allRequests, helpers.App2, helpers.Httpd2, false)
   232  
   233  		By("Deleting all policies; all tests should succeed")
   234  
   235  		status := vm.PolicyDelAll()
   236  		status.ExpectSuccess()
   237  
   238  		vm.WaitEndpointsReady()
   239  
   240  		connectivityTest(allRequests, helpers.App1, helpers.Httpd1, true)
   241  		connectivityTest(allRequests, helpers.App2, helpers.Httpd1, true)
   242  	})
   243  
   244  	It("L4Policy Checks", func() {
   245  		_, err := vm.PolicyImportAndWait(vm.GetFullPath(policiesL4Json), helpers.HelperTimeout)
   246  		Expect(err).Should(BeNil())
   247  
   248  		for _, app := range []string{helpers.App1, helpers.App2} {
   249  			connectivityTest(pingRequests, app, helpers.Httpd1, false)
   250  			connectivityTest(httpRequestsPublic, app, helpers.Httpd1, true)
   251  			connectivityTest(pingRequests, app, helpers.Httpd2, false)
   252  			connectivityTest(httpRequestsPublic, app, helpers.Httpd2, true)
   253  		}
   254  		connectivityTest(allRequests, helpers.App3, helpers.Httpd1, false)
   255  		connectivityTest(pingRequests, helpers.App1, helpers.Httpd3, false)
   256  
   257  		By("Disabling all the policies. All should work")
   258  
   259  		vm.PolicyDelAll().ExpectSuccess("cannot delete the policy")
   260  
   261  		vm.WaitEndpointsReady()
   262  
   263  		for _, app := range []string{helpers.App1, helpers.App2} {
   264  			connectivityTest(allRequests, app, helpers.Httpd1, true)
   265  			connectivityTest(allRequests, app, helpers.Httpd2, true)
   266  		}
   267  	})
   268  
   269  	It("Checks that traffic is not dropped when L4 policy is installed and deleted", func() {
   270  		ctx, cancel := context.WithCancel(context.Background())
   271  		defer cancel()
   272  		srvIP, err := vm.ContainerInspectNet(helpers.Httpd1)
   273  		Expect(err).Should(BeNil(), "Cannot get httpd1 server address")
   274  		type BackgroundTestAsserts struct {
   275  			res  *helpers.CmdRes
   276  			time time.Time
   277  		}
   278  		backgroundChecks := []*BackgroundTestAsserts{}
   279  		var wg sync.WaitGroup
   280  		wg.Add(1)
   281  		go func() {
   282  			for {
   283  				select {
   284  				default:
   285  					res := vm.ContainerExec(
   286  						helpers.App1,
   287  						helpers.CurlFail("http://%s/", srvIP[helpers.IPv4]))
   288  					assert := &BackgroundTestAsserts{
   289  						res:  res,
   290  						time: time.Now(),
   291  					}
   292  					backgroundChecks = append(backgroundChecks, assert)
   293  				case <-ctx.Done():
   294  					wg.Done()
   295  					return
   296  				}
   297  			}
   298  		}()
   299  		// Sleep a bit to make sure that the goroutine starts.
   300  		time.Sleep(50 * time.Millisecond)
   301  
   302  		_, err = vm.PolicyImportAndWait(vm.GetFullPath(policiesL4Json), helpers.HelperTimeout)
   303  		Expect(err).Should(BeNil(), "Cannot install L4 policy")
   304  
   305  		By("Uninstalling policy")
   306  		vm.PolicyDelAll().ExpectSuccess("Cannot delete all policies")
   307  		vm.WaitEndpointsReady()
   308  
   309  		By("Canceling background connections from app2 to httpd1")
   310  		cancel()
   311  		wg.Wait()
   312  		GinkgoPrint("Made %d connections in total", len(backgroundChecks))
   313  		Expect(backgroundChecks).ShouldNot(BeEmpty(), "No background connections were made")
   314  		for _, check := range backgroundChecks {
   315  			check.res.ExpectSuccess("Curl from app2 to httpd1 should work but it failed at %s", check.time)
   316  		}
   317  	})
   318  
   319  	It("L7 Checks", func() {
   320  
   321  		_, err := vm.PolicyImportAndWait(vm.GetFullPath(policiesL7JSON), helpers.HelperTimeout)
   322  		Expect(err).Should(BeNil())
   323  
   324  		By("Simple Ingress")
   325  		// app1 can connect to /public, but not to /private.
   326  		connectivityTest(httpRequestsPublic, helpers.App1, helpers.Httpd1, true)
   327  		connectivityTest(httpRequestsPrivate, helpers.App1, helpers.Httpd1, false)
   328  
   329  		// app1 can't exploit path rewrite
   330  		connectivityTest(httpRequestsPathRewrite, helpers.App1, helpers.Httpd1, false)
   331  
   332  		// Host can connect to /public, but not to /private.
   333  		connectivityTest(httpRequestsPublic, helpers.Host, helpers.Httpd1, true)
   334  		connectivityTest(httpRequestsPrivate, helpers.Host, helpers.Httpd1, false)
   335  
   336  		// Host can't exploit path rewrite
   337  		connectivityTest(httpRequestsPathRewrite, helpers.Host, helpers.Httpd1, false)
   338  
   339  		// app cannot connect to httpd1 because httpd1 only allows ingress from app1.
   340  		connectivityTest(httpRequestsPublic, helpers.App2, helpers.Httpd1, false)
   341  
   342  		By("Simple Egress")
   343  
   344  		// app2 can connect to public, but no to private
   345  		connectivityTest(httpRequestsPublic, helpers.App2, helpers.Httpd2, true)
   346  		connectivityTest(httpRequestsPrivate, helpers.App2, helpers.Httpd2, false)
   347  
   348  		// app2 can't exploit path rewrite
   349  		connectivityTest(httpRequestsPathRewrite, helpers.App2, helpers.Httpd2, false)
   350  
   351  		// TODO (1488) - uncomment when l3-dependent-l7 is merged for egress.
   352  		//connectivityTest(httpRequestsPublic, helpers.App3, helpers.Httpd3, true)
   353  		//connectivityTest(httpRequestsPrivate, helpers.App3, helpers.Httpd3, false)
   354  		//connectivityTest(allRequests, helpers.App3, helpers.Httpd2, false)
   355  
   356  		By("Disabling all the policies. All should work")
   357  
   358  		status := vm.PolicyDelAll()
   359  		status.ExpectSuccess()
   360  
   361  		vm.WaitEndpointsReady()
   362  
   363  		connectivityTest(allRequests, helpers.App1, helpers.Httpd1, true)
   364  		connectivityTest(allRequests, helpers.App2, helpers.Httpd1, true)
   365  
   366  		By("Multiple Ingress")
   367  
   368  		vm.PolicyDelAll()
   369  		_, err = vm.PolicyImportAndWait(vm.GetFullPath(multL7PoliciesJSON), helpers.HelperTimeout)
   370  		Expect(err).Should(BeNil())
   371  
   372  		//APP1 can connnect to public, but no to private
   373  
   374  		connectivityTest(httpRequestsPublic, helpers.App1, helpers.Httpd1, true)
   375  		connectivityTest(httpRequestsPrivate, helpers.App1, helpers.Httpd1, false)
   376  
   377  		//App2 can't connect
   378  		connectivityTest(httpRequestsPublic, helpers.App2, helpers.Httpd1, false)
   379  
   380  		By("Multiple Egress")
   381  		// app2 can connect to /public, but not to /private
   382  		connectivityTest(httpRequestsPublic, helpers.App2, helpers.Httpd2, true)
   383  		connectivityTest(httpRequestsPrivate, helpers.App2, helpers.Httpd2, false)
   384  
   385  		By("Disabling all the policies. All should work")
   386  
   387  		status = vm.PolicyDelAll()
   388  		status.ExpectSuccess()
   389  		vm.WaitEndpointsReady()
   390  
   391  		connectivityTest(allRequests, helpers.App1, helpers.Httpd1, true)
   392  		connectivityTest(allRequests, helpers.App2, helpers.Httpd1, true)
   393  	})
   394  
   395  	It("Tests Endpoint Connectivity Functions After Daemon Configuration Is Updated", func() {
   396  		httpd1DockerNetworking, err := vm.ContainerInspectNet(helpers.Httpd1)
   397  		Expect(err).ToNot(HaveOccurred(), "unable to get container networking metadata for %s", helpers.Httpd1)
   398  
   399  		// Importing a policy to ensure that not only does endpoint connectivity
   400  		// work after updating daemon configuration, but that policy works as well.
   401  		By("Importing policy and waiting for revision to increase for endpoints")
   402  		_, err = vm.PolicyImportAndWait(vm.GetFullPath(policiesL7JSON), helpers.HelperTimeout)
   403  		Expect(err).ToNot(HaveOccurred(), "unable to import policy after timeout")
   404  
   405  		By("Trying to access %s:80/public from %s before daemon configuration is updated (should be allowed by policy)", helpers.Httpd1, helpers.App1)
   406  		res := vm.ContainerExec(helpers.App1, helpers.CurlFail("http://%s:80/public", httpd1DockerNetworking[helpers.IPv4]))
   407  		res.ExpectSuccess("unable to access %s:80/public from %s (should have worked)", helpers.Httpd1, helpers.App1)
   408  
   409  		By("Trying to access %s:80/private from %s before daemon configuration is updated (should not be allowed by policy)", helpers.Httpd1, helpers.App1)
   410  		res = vm.ContainerExec(helpers.App1, helpers.CurlFail("http://%s:80/private", httpd1DockerNetworking[helpers.IPv4]))
   411  		res.ExpectFail("unable to access %s:80/private from %s (should not have worked)", helpers.Httpd1, helpers.App1)
   412  
   413  		By("Getting configuration for daemon")
   414  		daemonDebugConfig, err := vm.ExecCilium("config -o json").Filter("{.Debug}")
   415  		Expect(err).ToNot(HaveOccurred(), "Unable to get configuration for daemon")
   416  
   417  		daemonDebugConfigString := daemonDebugConfig.String()
   418  
   419  		var daemonDebugConfigSwitched string
   420  
   421  		switch daemonDebugConfigString {
   422  		case "Disabled":
   423  			daemonDebugConfigSwitched = "Enabled"
   424  		case "Enabled":
   425  			daemonDebugConfigSwitched = "Disabled"
   426  		default:
   427  			Fail(fmt.Sprintf("invalid configuration value for daemon: Debug=%s", daemonDebugConfigString))
   428  		}
   429  
   430  		currentRev, err := vm.PolicyGetRevision()
   431  		Expect(err).ToNot(HaveOccurred(), "unable to get policy revision")
   432  
   433  		// TODO: would be a good idea to factor out daemon configuration updates
   434  		// into a function in the future.
   435  		By("Changing daemon configuration from Debug=%s to Debug=%s to induce policy recalculation for endpoints", daemonDebugConfigString, daemonDebugConfigSwitched)
   436  		res = vm.ExecCilium(fmt.Sprintf("config Debug=%s", daemonDebugConfigSwitched))
   437  		res.ExpectSuccess("unable to change daemon configuration")
   438  
   439  		By("Getting policy revision after daemon configuration change")
   440  		revAfterConfig, err := vm.PolicyGetRevision()
   441  		Expect(err).ToNot(HaveOccurred(), "unable to get policy revision")
   442  		Expect(revAfterConfig).To(BeNumerically(">=", currentRev+1))
   443  
   444  		By("Waiting for policy revision to increase after daemon configuration change")
   445  		res = vm.PolicyWait(revAfterConfig)
   446  		res.ExpectSuccess("policy revision was not bumped after daemon configuration changes")
   447  
   448  		By("Changing daemon configuration back from Debug=%s to Debug=%s", daemonDebugConfigSwitched, daemonDebugConfigString)
   449  		res = vm.ExecCilium(fmt.Sprintf("config Debug=%s", daemonDebugConfigString))
   450  		res.ExpectSuccess("unable to change daemon configuration")
   451  
   452  		By("Getting policy revision after daemon configuration change")
   453  		revAfterSecondConfig, err := vm.PolicyGetRevision()
   454  		Expect(err).To(BeNil())
   455  		Expect(revAfterSecondConfig).To(BeNumerically(">=", revAfterConfig+1))
   456  
   457  		By("Waiting for policy revision to increase after daemon configuration change")
   458  		res = vm.PolicyWait(revAfterSecondConfig)
   459  		res.ExpectSuccess("policy revision was not bumped after daemon configuration changes")
   460  
   461  		By("Trying to access %s:80/public from %s after daemon configuration was updated (should be allowed by policy)", helpers.Httpd1, helpers.App1)
   462  		res = vm.ContainerExec(helpers.App1, helpers.CurlFail("http://%s:80/public", httpd1DockerNetworking[helpers.IPv4]))
   463  		res.ExpectSuccess("unable to access %s:80/public from %s (should have worked)", helpers.Httpd1, helpers.App1)
   464  
   465  		By("Trying to access %s:80/private from %s after daemon configuration is updated (should not be allowed by policy)", helpers.Httpd1, helpers.App1)
   466  		res = vm.ContainerExec(helpers.App1, helpers.CurlFail("http://%s:80/private", httpd1DockerNetworking[helpers.IPv4]))
   467  		res.ExpectFail("unable to access %s:80/private from %s (should not have worked)", helpers.Httpd1, helpers.App1)
   468  	})
   469  
   470  	It("L3-Dependent L7 Egress", func() {
   471  		_, err := vm.PolicyImportAndWait(vm.GetFullPath(policiesL3DependentL7EgressJSON), helpers.HelperTimeout)
   472  		Expect(err).Should(BeNil(), "unable to import %s", policiesL3DependentL7EgressJSON)
   473  
   474  		endpointIDS, err := vm.GetEndpointsIds()
   475  		Expect(err).To(BeNil(), "Unable to get IDs of endpoints")
   476  
   477  		app3EndpointID, exists := endpointIDS[helpers.App3]
   478  		Expect(exists).To(BeTrue(), "Expected endpoint ID to exist for %s", helpers.App3)
   479  
   480  		connectivityTest(httpRequestsPublic, helpers.Host, helpers.Httpd2, true)
   481  
   482  		connectivityTest(httpRequestsPublic, helpers.App3, helpers.Httpd1, true)
   483  
   484  		// Since policy allows connectivity on /public to httpd1 from app3, we
   485  		// expect:
   486  		// * two requests to get received by the proxy because connectivityTest
   487  		// connects via http / http6.
   488  		// * two requests to get forwarded by the proxy because policy allows
   489  		// connectivity via http / http6.
   490  		// * two corresponding responses forwarded / received to the aforementioned
   491  		// requests due to policy allowing connectivity via http / http6.
   492  		checkProxyStatistics(app3EndpointID, 2, 2, 0, 2, 2)
   493  
   494  		connectivityTest(httpRequestsPrivate, helpers.App3, helpers.Httpd1, false)
   495  
   496  		// Since policy does not allow connectivity on /private to httpd1 from app3, we expect:
   497  		// * two requests denied due to connectivity not being allowed via http / http6
   498  		// * the count for requests forwarded, and responses forwarded / received to be the same
   499  		// as from the prior test since the requests were denied, and thus no new requests
   500  		// were forwarded, and no new responses forwarded nor received.
   501  		checkProxyStatistics(app3EndpointID, 2, 4, 2, 2, 2)
   502  
   503  		connectivityTest(httpRequestsPublic, helpers.App3, helpers.Httpd2, true)
   504  
   505  		// Since policy allows connectivity on L3 from app3 to httpd2, we expect:
   506  		// * two more requests to get received by the proxy because even though
   507  		// only L3 policy applies for connectivity from app3 to httpd2, because
   508  		// app3 has L7 policy applied to it, all traffic goes through the proxy.
   509  		// * two more requests to get forwarded by the proxy because policy allows
   510  		// app3 to talk to httpd2.
   511  		// * no increase in requests denied by the proxy.
   512  		// * two more corresponding responses forwarded / received to the aforementioned requests due to policy
   513  		// allowing connectivity via http / http6.
   514  		checkProxyStatistics(app3EndpointID, 4, 6, 2, 4, 4)
   515  
   516  		connectivityTest(httpRequestsPrivate, helpers.App3, helpers.Httpd2, true)
   517  
   518  		// Since policy allows connectivity on L3 from app3 to httpd2, we expect:
   519  		// * two more requests to get received by the proxy because even though
   520  		// only L3 policy applies for connectivity from app3 to httpd2, because
   521  		// app3 has L7 policy applied to it, all traffic goes through the proxy.
   522  		// * two more requests to get forwarded by the proxy because policy allows
   523  		// app3 to talk to httpd2, even though it's restricted on L7 for connectivity
   524  		// to httpd1 from app3. This is what tests L3-dependent L7 policy is applied
   525  		// correctly.
   526  		// * no increase in requests denied by the proxy.
   527  		// * two more corresponding responses forwarded / received to the aforementioned requests due to policy
   528  		// allowing connectivity via http / http6.
   529  		checkProxyStatistics(app3EndpointID, 6, 8, 2, 6, 6)
   530  	})
   531  
   532  	It("Checks CIDR L3 Policy", func() {
   533  
   534  		ipv4OtherHost := "192.168.254.111"
   535  		ipv4OtherNet := "99.11.0.0/16"
   536  		httpd2Label := "id.httpd2"
   537  		httpd1Label := "id.httpd1"
   538  		app3Label := "id.app3"
   539  
   540  		logger.WithFields(logrus.Fields{
   541  			"IPv4_host":       helpers.FakeIPv4WorldAddress,
   542  			"IPv4_other_host": ipv4OtherHost,
   543  			"IPv4_other_net":  ipv4OtherNet,
   544  			"IPv6_host":       helpers.FakeIPv6WorldAddress}).
   545  			Info("VM IP address configuration")
   546  
   547  		// If the pseudo host IPs have not been removed since the last run but
   548  		// Cilium was restarted, the IPs may have been picked up as valid host
   549  		// IPs. Remove them from the list so they are not regarded as localhost
   550  		// entries.
   551  		// Don't care about success or failure as the BPF endpoint may not even be
   552  		// present; this is best-effort.
   553  		_ = vm.ExecCilium(fmt.Sprintf("bpf endpoint delete %s", helpers.FakeIPv4WorldAddress))
   554  		_ = vm.ExecCilium(fmt.Sprintf("bpf endpoint delete %s", helpers.FakeIPv6WorldAddress))
   555  
   556  		httpd1DockerNetworking, err := vm.ContainerInspectNet(helpers.Httpd1)
   557  		Expect(err).Should(BeNil(), fmt.Sprintf(
   558  			"could not get container %s Docker networking", helpers.Httpd1))
   559  
   560  		ipv6Prefix := fmt.Sprintf("%s/112", httpd1DockerNetworking["IPv6Gateway"])
   561  		ipv4Address := httpd1DockerNetworking[helpers.IPv4]
   562  
   563  		// Get prefix of node-local endpoints.
   564  		By("Getting IPv4 and IPv6 prefixes of node-local endpoints")
   565  		getIpv4Prefix := vm.Exec(fmt.Sprintf(`expr %s : '\([0-9]*\.[0-9]*\.\)'`, ipv4Address)).SingleOut()
   566  		ipv4Prefix := fmt.Sprintf("%s0.0/16", getIpv4Prefix)
   567  		getIpv4PrefixExcept := vm.Exec(fmt.Sprintf(`expr %s : '\([0-9]*\.[0-9]*\.\)'`, ipv4Address)).SingleOut()
   568  		ipv4PrefixExcept := fmt.Sprintf(`%s0.0/18`, getIpv4PrefixExcept)
   569  
   570  		By("IPV6 Prefix: %q", ipv6Prefix)
   571  		By("IPV4 Address Endpoint: %q", ipv4Address)
   572  		By("IPV4 Prefix: %q", ipv4Prefix)
   573  		By("IPV4 Prefix Except: %q", ipv4PrefixExcept)
   574  
   575  		By("Setting PolicyEnforcement to always enforce (default-deny)")
   576  		ExpectPolicyEnforcementUpdated(vm, helpers.PolicyEnforcementAlways)
   577  
   578  		// Delete the pseudo-host IPs that we added to localhost after test
   579  		// finishes. Don't care about success; this is best-effort.
   580  		cleanup = func() {
   581  			_ = vm.RemoveIPFromLoopbackDevice(fmt.Sprintf("%s/32", helpers.FakeIPv4WorldAddress))
   582  			_ = vm.RemoveIPFromLoopbackDevice(fmt.Sprintf("%s/128", helpers.FakeIPv6WorldAddress))
   583  		}
   584  
   585  		By("Adding Pseudo-Host IPs to localhost")
   586  		vm.AddIPToLoopbackDevice(fmt.Sprintf("%s/32", helpers.FakeIPv4WorldAddress)).ExpectSuccess("Unable to add %s to pseudo-host IP to localhost", helpers.FakeIPv4WorldAddress)
   587  		vm.AddIPToLoopbackDevice(fmt.Sprintf("%s/128", helpers.FakeIPv6WorldAddress)).ExpectSuccess("Unable to add %s to pseudo-host IP to localhost", helpers.FakeIPv6WorldAddress)
   588  
   589  		By("Pinging host IPv4 from httpd2 (should NOT work due to default-deny PolicyEnforcement mode)")
   590  
   591  		res := vm.ContainerExec(helpers.Httpd2, helpers.Ping(helpers.FakeIPv4WorldAddress))
   592  		res.ExpectFail("Unexpected success pinging host (%s) from %s", helpers.FakeIPv4WorldAddress, helpers.Httpd2)
   593  
   594  		By("Importing L3 CIDR Policy for IPv4 Egress Allowing Egress to %q, %q from %q", ipv4OtherHost, ipv4OtherHost, httpd2Label)
   595  		script := fmt.Sprintf(`
   596  		[{
   597  			"endpointSelector": {"matchLabels":{"%s":""}},
   598  			"egress":
   599  			[{
   600  				"toCIDR": [
   601  					"%s/24",
   602  					"%s/20"
   603  				]
   604  			}]
   605  		}]`, httpd2Label, ipv4OtherHost, ipv4OtherHost)
   606  		_, err = vm.PolicyRenderAndImport(script)
   607  		Expect(err).To(BeNil(), "Unable to import policy: %s", err)
   608  
   609  		res = vm.ContainerExec(helpers.Httpd2, helpers.Ping(helpers.FakeIPv4WorldAddress))
   610  		res.ExpectSuccess("Unexpected failure pinging host (%s) from %s", helpers.FakeIPv4WorldAddress, helpers.Httpd2)
   611  		vm.PolicyDelAll().ExpectSuccess("Unable to delete all policies")
   612  
   613  		By("Pinging host IPv6 from httpd2 (should NOT work because we did not specify IPv6 CIDR of host as part of previously imported policy)")
   614  		res = vm.ContainerExec(helpers.Httpd2, helpers.Ping6(helpers.FakeIPv6WorldAddress))
   615  		res.ExpectFail("Unexpected success pinging host (%s) from %s", helpers.FakeIPv6WorldAddress, helpers.Httpd2)
   616  
   617  		By("Importing L3 CIDR Policy for IPv6 Egress")
   618  		script = fmt.Sprintf(`
   619  		[{
   620  			"endpointSelector": {"matchLabels":{"%s":""}},
   621  			"egress": [{
   622  				"toCIDR": [
   623  					"%s"
   624  				]
   625  			}]
   626  		}]`, httpd2Label, helpers.FakeIPv6WorldAddress)
   627  		_, err = vm.PolicyRenderAndImport(script)
   628  		Expect(err).To(BeNil(), "Unable to import policy: %s", err)
   629  
   630  		By("Pinging host IPv6 from httpd2 (should work because policy allows IPv6 CIDR %q)", helpers.FakeIPv6WorldAddress)
   631  		res = vm.ContainerExec(helpers.Httpd2, helpers.Ping6(helpers.FakeIPv6WorldAddress))
   632  		res.ExpectSuccess("Unexpected failure pinging host (%s) from %s", helpers.FakeIPv6WorldAddress, helpers.Httpd2)
   633  		vm.PolicyDelAll().ExpectSuccess("Unable to delete all policies")
   634  
   635  		// This test case checks that ping works even without explicit CIDR policies
   636  		// imported.
   637  		By("Importing L3 Label-Based Policy Allowing traffic from httpd2 to httpd1")
   638  		script = fmt.Sprintf(`
   639  		[{
   640  			"endpointSelector": {"matchLabels":{"%[1]s":""}},
   641  			"ingress": [{
   642  				"fromEndpoints": [
   643  					{"matchLabels":{"%[2]s":""}}
   644  				]
   645  			}]
   646  		},
   647  		{
   648  			"endpointSelector": {"matchLabels":{"%[2]s":""}},
   649  			"egress": [{
   650  				"toEndpoints": [
   651  					{"matchLabels":{"%[1]s":""}}
   652  				]
   653  			}]
   654  		}]`, httpd1Label, httpd2Label)
   655  		_, err = vm.PolicyRenderAndImport(script)
   656  		Expect(err).To(BeNil(), "Unable to import policy: %s", err)
   657  
   658  		By("Pinging httpd1 IPV4 from httpd2 (should work because we allowed traffic to httpd1 labels from httpd2 labels)")
   659  		res = vm.ContainerExec(helpers.Httpd2, helpers.Ping(httpd1DockerNetworking[helpers.IPv4]))
   660  		res.ExpectSuccess("Unexpected failure pinging %s (%s) from %s", helpers.Httpd1, httpd1DockerNetworking[helpers.IPv4], helpers.Httpd2)
   661  		By("Pinging httpd1 IPv6 from httpd2 (should work because we allowed traffic to httpd1 labels from httpd2 labels)")
   662  		res = vm.ContainerExec(helpers.Httpd2, helpers.Ping6(httpd1DockerNetworking[helpers.IPv6]))
   663  		res.ExpectSuccess("Unexpected failure pinging %s (%s) from %s", helpers.Httpd1, httpd1DockerNetworking[helpers.IPv6], helpers.Httpd2)
   664  		By("Pinging httpd1 IPv4 from app3 (should NOT work because app3 hasn't been whitelisted to communicate with httpd1)")
   665  		res = vm.ContainerExec(helpers.App3, helpers.Ping(helpers.Httpd1))
   666  		res.ExpectFail("Unexpected success pinging %s IPv4 from %s", helpers.Httpd1, helpers.App3)
   667  		By("Pinging httpd1 IPv6 from app3 (should NOT work because app3 hasn't been whitelisted to communicate with httpd1)")
   668  		res = vm.ContainerExec(helpers.App3, helpers.Ping6(helpers.Httpd1))
   669  		res.ExpectFail("Unexpected success pinging %s IPv6 from %s", helpers.Httpd1, helpers.App3)
   670  		vm.PolicyDelAll().ExpectSuccess("Unable to delete all policies")
   671  
   672  		// Checking combined policy allowing traffic from IPv4 and IPv6 CIDR ranges.
   673  		By("Importing Policy Allowing Ingress From %q --> %q And From CIDRs %q, %q", helpers.Httpd2, helpers.Httpd1, ipv4Prefix, ipv6Prefix)
   674  		script = fmt.Sprintf(`
   675  		[{
   676  			"endpointSelector": {"matchLabels":{"%[1]s":""}},
   677  			"ingress": [{
   678  				"fromEndpoints":  [
   679  					{"matchLabels":{"%[2]s":""}}
   680  				]
   681  			}, {
   682  				"fromCIDR": [
   683  					"%s",
   684  					"%s"
   685  				]
   686  			}]
   687  		},
   688  		{
   689  			"endpointSelector": {"matchLabels":{"%[2]s":""}},
   690  			"egress": [{
   691  				"toEndpoints":  [
   692  					{"matchLabels":{"%[1]s":""}}
   693  				]
   694  			}]
   695  		}]`, httpd1Label, httpd2Label, ipv4Prefix, ipv6Prefix)
   696  
   697  		_, err = vm.PolicyRenderAndImport(script)
   698  		Expect(err).To(BeNil(), "Unable to import policy: %s", err)
   699  
   700  		By("Pinging httpd1 IPV4 from httpd2 (should work because we allowed traffic to httpd1 labels from httpd2 labels)")
   701  		res = vm.ContainerExec(helpers.Httpd2, helpers.Ping(httpd1DockerNetworking[helpers.IPv4]))
   702  		res.ExpectSuccess("Unexpected failure pinging %s (%s) from %s", helpers.Httpd1, httpd1DockerNetworking[helpers.IPv4], helpers.Httpd2)
   703  
   704  		By("Pinging httpd1 IPv6 from httpd2 (should work because we allowed traffic to httpd1 labels from httpd2 labels)")
   705  		res = vm.ContainerExec(helpers.Httpd2, helpers.Ping6(httpd1DockerNetworking[helpers.IPv6]))
   706  		res.ExpectSuccess("Unexpected failure pinging %s (%s) from %s", helpers.Httpd1, httpd1DockerNetworking[helpers.IPv6], helpers.Httpd2)
   707  
   708  		By("Pinging httpd1 IPv4 %q from app3 (shouldn't work because CIDR policies don't apply to endpoint-endpoint communication)", ipv4Prefix)
   709  		res = vm.ContainerExec(helpers.App3, helpers.Ping(helpers.Httpd1))
   710  		res.ExpectFail("Unexpected success pinging %s IPv4 from %s", helpers.Httpd1, helpers.App3)
   711  
   712  		By("Pinging httpd1 IPv6 %q from app3 (shouldn't work because CIDR policies don't apply to endpoint-endpoint communication)", ipv6Prefix)
   713  		res = vm.ContainerExec(helpers.App3, helpers.Ping6(helpers.Httpd1))
   714  		res.ExpectFail("Unexpected success pinging %s IPv6 from %s", helpers.Httpd1, helpers.App3)
   715  
   716  		vm.PolicyDelAll().ExpectSuccess("Unable to delete all policies")
   717  
   718  		// Make sure that combined label-based and CIDR-based policy works.
   719  		By("Importing Policy Allowing Ingress From %s --> %s And From CIDRs %s", helpers.Httpd2, helpers.Httpd1, ipv4OtherNet)
   720  		script = fmt.Sprintf(`
   721  		[{
   722  			"endpointSelector": {"matchLabels":{"%[1]s":""}},
   723  			"ingress": [{
   724  				"fromEndpoints": [
   725  					{"matchLabels":{"%s":""}}
   726  				]
   727  			}, {
   728  				"fromCIDR": [
   729  					"%s"
   730  				]
   731  			}]
   732  		},
   733  		{
   734  			"endpointSelector": {"matchLabels":{"%s":""}},
   735  			"egress": [{
   736  				"toEndpoints": [
   737  					{"matchLabels":{"%[1]s":""}}
   738  				]
   739  			}]
   740  		}]`, httpd1Label, httpd2Label, ipv4OtherNet, app3Label)
   741  		_, err = vm.PolicyRenderAndImport(script)
   742  		Expect(err).To(BeNil(), "Unable to import policy: %s", err)
   743  
   744  		By("Pinging httpd1 IPv4 from app3 (should NOT work because we only allow traffic from %q to %q)", httpd2Label, httpd1Label)
   745  		res = vm.ContainerExec(helpers.App3, helpers.Ping(helpers.Httpd1))
   746  		res.ExpectFail("Unexpected success pinging %s IPv4 from %s", helpers.Httpd1, helpers.App3)
   747  
   748  		By("Pinging httpd1 IPv6 from app3 (should NOT work because we only allow traffic from %q to %q)", httpd2Label, httpd1Label)
   749  		res = vm.ContainerExec(helpers.App3, helpers.Ping6(helpers.Httpd1))
   750  		res.ExpectFail("Unexpected success pinging %s IPv6 from %s", helpers.Httpd1, helpers.App3)
   751  
   752  		vm.PolicyDelAll().ExpectSuccess("Unable to delete all policies")
   753  
   754  		By("Testing CIDR Exceptions in Cilium Policy")
   755  		By("Importing Policy Allowing Ingress From %q --> %q And From CIDRs %q Except %q", helpers.Httpd2, helpers.Httpd1, ipv4Prefix, ipv4PrefixExcept)
   756  		script = fmt.Sprintf(`
   757  		[{
   758  			"endpointSelector": {"matchLabels":{"%s":""}},
   759  			"ingress": [{
   760  				"fromEndpoints": [
   761  					{"matchLabels":{"%s":""}}
   762  				]
   763  			}, {
   764  				"fromCIDRSet": [ {
   765  					"cidr": "%s",
   766  					"except": [
   767  						"%s"
   768  					]
   769  				}
   770  				]
   771  			}]
   772  		}]`, httpd1Label, httpd2Label, ipv4Prefix, ipv4PrefixExcept)
   773  		_, err = vm.PolicyRenderAndImport(script)
   774  		Expect(err).To(BeNil(), "Unable to import policy: %s", err)
   775  
   776  	})
   777  
   778  	It("Extended HTTP Methods tests", func() {
   779  		// This also tests L3-dependent L7.
   780  		httpMethods := []string{"GET", "POST"}
   781  		TestMethodPolicy := func(method string) {
   782  			vm.PolicyDelAll().ExpectSuccess("Cannot delete all policies")
   783  			policy := `
   784  			[{
   785  				"endpointSelector": {"matchLabels": {"id.httpd1": ""}},
   786  				"ingress": [{
   787  					"fromEndpoints": [{"matchLabels": {"id.app1": ""}}],
   788  					"toPorts": [{
   789  						"ports": [{"port": "80", "protocol": "tcp"}],
   790  						"rules": {
   791  							"HTTP": [{
   792  							  "method": "%[1]s",
   793  							  "path": "/public"
   794  							}]
   795  						}
   796  					}]
   797  				}]
   798  			},{
   799  				"endpointSelector": {"matchLabels": {"id.httpd1": ""}},
   800  				"ingress": [{
   801  					"fromEndpoints": [{"matchLabels": {"id.app2": ""}}],
   802  					"toPorts": [{
   803  						"ports": [{"port": "80", "protocol": "tcp"}],
   804  						"rules": {
   805  							"HTTP": [{
   806  								"method": "%[1]s",
   807  								"path": "/public",
   808  								"headers": ["X-Test: True"]
   809  							}]
   810  						}
   811  					}]
   812  				}]
   813  			}]`
   814  
   815  			_, err := vm.PolicyRenderAndImport(fmt.Sprintf(policy, method))
   816  			Expect(err).To(BeNil(), "Cannot import policy for %q", method)
   817  
   818  			srvIP, err := vm.ContainerInspectNet(helpers.Httpd1)
   819  			Expect(err).Should(BeNil(), "could not get container %q meta", helpers.Httpd1)
   820  
   821  			dest := helpers.CurlFail("http://%s/public -X %s", srvIP[helpers.IPv4], method)
   822  			destHeader := helpers.CurlFail("http://%s/public -H 'X-Test: True' -X %s",
   823  				srvIP[helpers.IPv4], method)
   824  
   825  			vm.ContainerExec(helpers.App1, dest).ExpectSuccess(
   826  				"%q cannot http request to Public", helpers.App1)
   827  
   828  			vm.ContainerExec(helpers.App2, dest).ExpectFail(
   829  				"%q can http request to Public", helpers.App2)
   830  
   831  			vm.ContainerExec(helpers.App2, destHeader).ExpectSuccess(
   832  				"%q cannot http request to Public", helpers.App2)
   833  
   834  			vm.ContainerExec(helpers.App1, destHeader).ExpectSuccess(
   835  				"%q can http request to Public", helpers.App1)
   836  
   837  			vm.ContainerExec(helpers.App3, destHeader).ExpectFail(
   838  				"%q can http request to Public", helpers.App3)
   839  
   840  			vm.ContainerExec(helpers.App3, dest).ExpectFail(
   841  				"%q can http request to Public", helpers.App3)
   842  		}
   843  
   844  		for _, method := range httpMethods {
   845  			By("Testing method %q", method)
   846  			TestMethodPolicy(method)
   847  		}
   848  	})
   849  
   850  	It("Tests Egress To World", func() {
   851  		googleDNS := "8.8.8.8"
   852  		googleHTTP := "google.com"
   853  		numberOfTries := 5
   854  		maxNumberOffFailures := 1
   855  
   856  		checkEgressToWorld := func() {
   857  
   858  			res := vm.ContainerExec(helpers.App1, helpers.Ping(helpers.App2))
   859  			ExpectWithOffset(2, res).ShouldNot(helpers.CMDSuccess(),
   860  				"unexpectedly able to ping %q", helpers.App2)
   861  
   862  			By("Testing egress access to the world from %s", helpers.App1)
   863  			pingFailures := 0
   864  			curlFailures := 0
   865  
   866  			for i := 0; i < numberOfTries; i++ {
   867  				res := vm.ContainerExec(helpers.App1, helpers.Ping(googleDNS))
   868  				if !res.WasSuccessful() {
   869  					pingFailures++
   870  				}
   871  				res = vm.ContainerExec(helpers.App1, helpers.CurlFail("-4 http://%s", googleHTTP))
   872  				if !res.WasSuccessful() {
   873  					curlFailures++
   874  				}
   875  				// If no failures, let's skip the next ones.
   876  				if (curlFailures + pingFailures) == 0 {
   877  					break
   878  				}
   879  			}
   880  
   881  			ExpectWithOffset(2, pingFailures).To(BeNumerically("<=", maxNumberOffFailures),
   882  				"%d of %d  pings to %q failed", pingFailures, maxNumberOffFailures, googleDNS)
   883  
   884  			ExpectWithOffset(2, curlFailures).To(BeNumerically("<=", maxNumberOffFailures),
   885  				"%d of %d  HTTP request to %q failed",
   886  				pingFailures, maxNumberOffFailures, googleHTTP)
   887  		}
   888  
   889  		setupPolicyAndTestEgressToWorld := func(policy string) {
   890  			_, err := vm.PolicyRenderAndImport(policy)
   891  			ExpectWithOffset(1, err).To(BeNil(), "Unable to import policy: %s\n%s", err, policy)
   892  
   893  			areEndpointsReady := vm.WaitEndpointsReady()
   894  			ExpectWithOffset(1, areEndpointsReady).Should(BeTrue(), "Endpoints are not ready after timeout")
   895  
   896  			checkEgressToWorld()
   897  		}
   898  
   899  		// Set policy enforcement to default deny so that we can do negative tests
   900  		// before importing policy
   901  		ExpectPolicyEnforcementUpdated(vm, helpers.PolicyEnforcementAlways)
   902  
   903  		failedPing := vm.ContainerExec(helpers.App1, helpers.Ping(googleDNS))
   904  		failedPing.ExpectFail("unexpectedly able to ping %s", googleDNS)
   905  
   906  		By("testing basic egress to world")
   907  		app1Label := fmt.Sprintf("id.%s", helpers.App1)
   908  		policy := fmt.Sprintf(`
   909  		[{
   910  			"endpointSelector": {"matchLabels":{"%s":""}},
   911  			"egress": [{
   912  				"toEntities": [
   913  					"%s"
   914  				]
   915  			}]
   916  		}]`, app1Label, api.EntityWorld)
   917  		setupPolicyAndTestEgressToWorld(policy)
   918  
   919  		vm.PolicyDelAll().ExpectSuccess("Unable to delete all policies")
   920  
   921  		By("testing egress to world with all entity")
   922  		policy = fmt.Sprintf(`
   923  		[{
   924  			"endpointSelector": {"matchLabels":{"%s":""}},
   925  			"egress": [{
   926  				"toEntities": [
   927  					"%s"
   928  				]
   929  			}]
   930  		}]`, app1Label, api.EntityAll)
   931  		setupPolicyAndTestEgressToWorld(policy)
   932  
   933  		vm.PolicyDelAll().ExpectSuccess("Unable to delete all policies")
   934  
   935  		By("testing basic egress to 0.0.0.0/0")
   936  		policy = fmt.Sprintf(`
   937  		[{
   938  			"endpointSelector": {"matchLabels":{"%s":""}},
   939  			"egress": [{
   940  				"toCIDR": [
   941  					"0.0.0.0/0"
   942  				]
   943  			}]
   944  		}]`, app1Label)
   945  		setupPolicyAndTestEgressToWorld(policy)
   946  
   947  		vm.PolicyDelAll().ExpectSuccess("Unable to delete all policies")
   948  
   949  		By("testing that in-cluster L7 doesn't affect egress L3")
   950  		app2Label := fmt.Sprintf("id.%s", helpers.App2)
   951  		policy = fmt.Sprintf(`
   952  		[{
   953  			"endpointSelector": {"matchLabels":{"%s":""}},
   954  			"egress": [{
   955  				"toEntities": [
   956  					"%s"
   957  				]
   958  			}, {
   959  				"toEndpoints": [{"matchLabels": {"%s": ""}}],
   960  				"toPorts": [{
   961  					"ports": [{"port": "80", "protocol": "tcp"}],
   962  					"rules": {
   963  						"HTTP": [{
   964  						  "method": "GET",
   965  						  "path": "/nowhere"
   966  						}]
   967  					}
   968  				}]
   969  			}]
   970  		}]`, app1Label, api.EntityWorld, app2Label)
   971  
   972  		setupPolicyAndTestEgressToWorld(policy)
   973  
   974  		vm.PolicyDelAll().ExpectSuccess("Unable to delete all policies")
   975  	})
   976  
   977  	It("Test egress with L7 policy to outside cluster", func() {
   978  		target := "http://cilium.io"
   979  
   980  		res := vm.ContainerExec(helpers.App1, helpers.CurlFail(target))
   981  		res.ExpectSuccess("Cannot connect without policy")
   982  
   983  		policy := `
   984  		[
   985  		  {
   986  			"endpointSelector": {
   987  			  "matchLabels": {"container:id.app1": ""}
   988  			},
   989  			"egress": [
   990  				{
   991  					"toPorts": [{
   992  						"ports":[{"port": "53", "protocol": "ANY"}],
   993  						"rules": {
   994  							"dns": [
   995  								{"matchPattern": "*"}
   996  							]
   997  						}
   998  					}]
   999  				},
  1000  				{
  1001  					"toFQDNs": [
  1002  					  {"matchPattern": "*.cilium.io"}
  1003  					]
  1004  				},
  1005  				{
  1006  					"toPorts": [{
  1007  						"ports": [
  1008  							{"port": "80", "protocol": "tcp"},
  1009  							{"port": "443", "protocol": "tcp"}
  1010  						],
  1011  						"rules": {
  1012  							"HTTP": [{
  1013  							  "method": "GET"
  1014  							}]
  1015  						}
  1016  					}]
  1017  				}
  1018  			]
  1019  		  }
  1020  		]`
  1021  
  1022  		_, err := vm.PolicyRenderAndImport(policy)
  1023  		Expect(err).To(BeNil(), "Cannot import policy")
  1024  
  1025  		res = vm.ContainerExec(helpers.App1, helpers.CurlFail(target))
  1026  		res.ExpectSuccess("Cannot connect with policy")
  1027  
  1028  		res = vm.ContainerExec(helpers.App1, helpers.CurlWithHTTPCode(
  1029  			fmt.Sprintf(`%s -d ""`, target)))
  1030  		res.ExpectSuccess("403", "Post method is allowed when it should not work")
  1031  	})
  1032  
  1033  	It("Tests EntityNone as a deny-all", func() {
  1034  		worldIP := "1.1.1.1"
  1035  
  1036  		httpd1Label := "id.httpd1"
  1037  		http1IP, err := vm.ContainerInspectNet(helpers.Httpd1)
  1038  		Expect(err).Should(BeNil(), "Cannot get httpd1 server address")
  1039  
  1040  		setupPolicy := func(policy string) {
  1041  			_, err := vm.PolicyRenderAndImport(policy)
  1042  			ExpectWithOffset(1, err).To(BeNil(), "Unable to import policy: %s\n%s", err, policy)
  1043  
  1044  			areEndpointsReady := vm.WaitEndpointsReady()
  1045  			ExpectWithOffset(1, areEndpointsReady).Should(BeTrue(), "Endpoints are not ready after timeout")
  1046  		}
  1047  
  1048  		// curlWithRetry retries the curl, to make sure that allowed curls don't
  1049  		// flake on bad connectivity
  1050  		curlWithRetry := func(name string, cmd string, optionalArgs ...interface{}) (res *helpers.CmdRes) {
  1051  			for try := 0; try < 5; try++ {
  1052  				res = vm.ContainerExec(name, helpers.CurlFail(cmd, optionalArgs...))
  1053  				if res.WasSuccessful() {
  1054  					return res
  1055  				}
  1056  			}
  1057  			return res
  1058  		}
  1059  
  1060  		By("setting policy enforcement to default so that EntityNone is the source of deny-all")
  1061  		ExpectPolicyEnforcementUpdated(vm, helpers.PolicyEnforcementDefault)
  1062  		app1Label := fmt.Sprintf("id.%s", helpers.App1)
  1063  		policy := fmt.Sprintf(`
  1064  		[{
  1065  			"endpointSelector": {"matchLabels":{"%s":""}},
  1066  			"egress": [{
  1067  				"toEntities": [
  1068  					"%s"
  1069  				]
  1070  			}]
  1071  		}]`, app1Label, api.EntityNone)
  1072  		setupPolicy(policy)
  1073  
  1074  		app2Label := fmt.Sprintf("id.%s", helpers.App2)
  1075  		policy = fmt.Sprintf(`
  1076  		[{
  1077  			"endpointSelector": {"matchLabels":{"%s":""}},
  1078  			"egress": [{
  1079  				"toEntities": [
  1080  					"%s"
  1081  				]
  1082  			}]
  1083  		}]`, app2Label, api.EntityNone)
  1084  		setupPolicy(policy)
  1085  
  1086  		By("testing that EntityNone is denying all egress for app1 and app2")
  1087  		vm.ContainerExec(helpers.App1, helpers.CurlFail("http://%s/public", http1IP[helpers.IPv4])).ExpectFail("%q can make http request to pod", helpers.App1)
  1088  		vm.ContainerExec(helpers.App1, helpers.CurlFail("-4 http://%s", worldIP)).ExpectFail("%q can make http request to %s", helpers.App1, worldIP)
  1089  		vm.ContainerExec(helpers.App2, helpers.CurlFail("http://%s/public", http1IP[helpers.IPv4])).ExpectFail("%q can make http request to pod", helpers.App1)
  1090  		vm.ContainerExec(helpers.App2, helpers.CurlFail("-4 http://%s", worldIP)).ExpectFail("%q can make http request to %s", helpers.App1, worldIP)
  1091  
  1092  		By("testing basic egress between endpoints (app1->app2)")
  1093  		policy = fmt.Sprintf(`
  1094  		[{
  1095  			"endpointSelector": {"matchLabels":{"%s":""}},
  1096  			"egress": [{
  1097  				"toEndpoints": [{"matchLabels": {"%s": ""}}]
  1098  			}]
  1099  		}]`, app1Label, httpd1Label)
  1100  		setupPolicy(policy)
  1101  		curlWithRetry(helpers.App1, "http://%s/public", http1IP[helpers.IPv4]).ExpectSuccess("%q cannot make http request to pod", helpers.App1)
  1102  		vm.ContainerExec(helpers.App1, helpers.CurlFail("-4 http://%s", worldIP)).ExpectFail("%q can make http request to %s", helpers.App1, worldIP)
  1103  
  1104  		By("testing basic egress to 1.1.1.1/32")
  1105  		policy = fmt.Sprintf(`
  1106  		[{
  1107  			"endpointSelector": {"matchLabels":{"%s":""}},
  1108  			"egress": [{
  1109  				"toCIDR": [
  1110  					"1.1.1.1/32"
  1111  				]
  1112  			}]
  1113  		}]`, app1Label)
  1114  		setupPolicy(policy)
  1115  		curlWithRetry(helpers.App1, "http://%s/public", http1IP[helpers.IPv4]).ExpectSuccess("%q cannot make http request to pod", helpers.App1)
  1116  		curlWithRetry(helpers.App1, "-4 http://%s", worldIP).ExpectSuccess("%q cannot make http request to pod", helpers.App1)
  1117  
  1118  		By("testing egress toEntity: world in combination with previous rules (app1)")
  1119  		policy = fmt.Sprintf(`
  1120  		[{
  1121  			"endpointSelector": {"matchLabels":{"%s":""}},
  1122  			"egress": [{
  1123  				"toEntities": [
  1124  					"%s"
  1125  				]
  1126  			}]
  1127  		}]`, app1Label, api.EntityAll)
  1128  		setupPolicy(policy)
  1129  		curlWithRetry(helpers.App1, "http://%s/public", http1IP[helpers.IPv4]).ExpectSuccess("%q cannot make http request to pod", helpers.App1)
  1130  		curlWithRetry(helpers.App1, "-4 http://%s", worldIP).ExpectSuccess("%q cannot make http request to pod", helpers.App1)
  1131  
  1132  		By("testing egress toEntity: world alone with EntityNone")
  1133  		policy = fmt.Sprintf(`
  1134  		[{
  1135  			"endpointSelector": {"matchLabels":{"%s":""}},
  1136  			"egress": [{
  1137  				"toEntities": [
  1138  					"%s"
  1139  				]
  1140  			}]
  1141  		}]`, app2Label, api.EntityAll)
  1142  		setupPolicy(policy)
  1143  		curlWithRetry(helpers.App2, "http://%s/public", http1IP[helpers.IPv4]).ExpectSuccess("%q cannot make http request to pod", helpers.App2)
  1144  		curlWithRetry(helpers.App2, "-4 http://%s", worldIP).ExpectSuccess("%q cannot make http request to pod", helpers.App2)
  1145  
  1146  		vm.PolicyDelAll().ExpectSuccess("Unable to delete all policies")
  1147  	})
  1148  
  1149  	Context("TestsEgressToHost", func() {
  1150  		hostDockerContainer := "hostDockerContainer"
  1151  		hostIP := "10.0.2.15"
  1152  		otherHostIP := ""
  1153  
  1154  		BeforeAll(func() {
  1155  			By("Starting httpd server using host networking")
  1156  			res := vm.ContainerCreate(hostDockerContainer, constants.HttpdImage, helpers.HostDockerNetwork, "-l id.hostDockerContainer")
  1157  			res.ExpectSuccess("unable to start Docker container with host networking")
  1158  
  1159  			By("Detecting host IP in world CIDR")
  1160  
  1161  			// docker network inspect bridge | jq -r '.[0]."IPAM"."Config"[0]."Gateway"'
  1162  			res = vm.NetworkGet("bridge")
  1163  			res.ExpectSuccess("No docker bridge available for testing egress CIDR within host")
  1164  			filter := fmt.Sprintf(`{ [0].IPAM.Config[0].Gateway }`)
  1165  			obj, err := res.FindResults(filter)
  1166  			Expect(err).NotTo(HaveOccurred(), "Error occurred while finding docker bridge IP")
  1167  			Expect(obj).To(HaveLen(1), "Unexpectedly found more than one IPAM config element for docker bridge")
  1168  			otherHostIP = obj[0].Interface().(string)
  1169  			Expect(otherHostIP).To(Equal(helpers.DockerBridgeIP), "Please adjust value of DockerBridgeIP")
  1170  			By("Using %q for world CIDR IP", otherHostIP)
  1171  		})
  1172  
  1173  		AfterAll(func() {
  1174  			vm.ContainerRm(hostDockerContainer)
  1175  		})
  1176  
  1177  		BeforeEach(func() {
  1178  			By("Pinging %q from %q before importing policy (should work)", hostIP, helpers.App1)
  1179  			failedPing := vm.ContainerExec(helpers.App1, helpers.Ping(hostIP))
  1180  			failedPing.ExpectSuccess("unable able to ping %q", hostIP)
  1181  
  1182  			By("Pinging %q from %q before importing policy (should work)", otherHostIP, helpers.App1)
  1183  			failedPing = vm.ContainerExec(helpers.App1, helpers.Ping(otherHostIP))
  1184  			failedPing.ExpectSuccess("unable able to ping %q", otherHostIP)
  1185  
  1186  			// Flush global conntrack table to be safe because egress conntrack cleanup
  1187  			// is still to be completed (GH-3393).
  1188  			By("Flushing global connection tracking table before importing policy")
  1189  			vm.FlushGlobalConntrackTable().ExpectSuccess("Unable to flush global conntrack table")
  1190  		})
  1191  
  1192  		AfterEach(func() {
  1193  			vm.PolicyDelAll().ExpectSuccess("Failed to clear policy after egress test")
  1194  		})
  1195  
  1196  		It("Tests Egress To Host", func() {
  1197  			By("Importing policy which allows egress to %q entity from %q", api.EntityHost, helpers.App1)
  1198  			policy := fmt.Sprintf(`
  1199  			[{
  1200  				"endpointSelector": {"matchLabels":{"id.%s":""}},
  1201  				"egress": [{
  1202  					"toEntities": [
  1203  						"%s"
  1204  					]
  1205  				}]
  1206  			}]`, helpers.App1, api.EntityHost)
  1207  
  1208  			_, err := vm.PolicyRenderAndImport(policy)
  1209  			Expect(err).To(BeNil(), "Unable to import policy: %s", err)
  1210  
  1211  			By("Pinging %s from %s (should work)", api.EntityHost, helpers.App1)
  1212  			successPing := vm.ContainerExec(helpers.App1, helpers.Ping(hostIP))
  1213  			successPing.ExpectSuccess("not able to ping %s", hostIP)
  1214  
  1215  			// Docker container running with host networking is accessible via
  1216  			// the host's IP address. See https://docs.docker.com/network/host/.
  1217  			By("Accessing /public using Docker container using host networking from %q (should work)", helpers.App1)
  1218  			successCurl := vm.ContainerExec(helpers.App1, helpers.CurlFail("http://%s/public", hostIP))
  1219  			successCurl.ExpectSuccess("Expected to be able to access /public in host Docker container")
  1220  
  1221  			By("Pinging %s from %s (shouldn't work)", helpers.App2, helpers.App1)
  1222  			failPing := vm.ContainerExec(helpers.App1, helpers.Ping(helpers.App2))
  1223  			failPing.ExpectFail("not able to ping %s", helpers.App2)
  1224  
  1225  			httpd2, err := vm.ContainerInspectNet(helpers.Httpd2)
  1226  			Expect(err).Should(BeNil(), "Unable to get networking information for container %q", helpers.Httpd2)
  1227  
  1228  			By("Accessing /public in %q from %q (shouldn't work)", helpers.App2, helpers.App1)
  1229  			failCurl := vm.ContainerExec(helpers.App1, helpers.CurlFail("http://%s/public", httpd2[helpers.IPv4]))
  1230  			failCurl.ExpectFail("unexpectedly able to access %s when access should only be allowed to host", helpers.Httpd2)
  1231  		})
  1232  
  1233  		// In this test we rely on the hostDockerContainer serving on a
  1234  		// secondary IP, which is otherwise not bound to an identity to
  1235  		// begin with; it would otherwise be part of the cluster. When
  1236  		// we define CIDR policy on it, Cilium allocates an identity
  1237  		// for it.
  1238  		testCIDRL4Policy := func(policy, dstIP, proto string) {
  1239  			_, err := vm.PolicyRenderAndImport(policy)
  1240  			ExpectWithOffset(1, err).To(BeNil(), "Unable to import policy")
  1241  
  1242  			By("Pinging %q from %q (should not work)", api.EntityHost, helpers.App1)
  1243  			res := vm.ContainerExec(helpers.App1, helpers.Ping(dstIP))
  1244  			ExpectWithOffset(1, res).ShouldNot(helpers.CMDSuccess(),
  1245  				"expected ping to %q to fail", dstIP)
  1246  
  1247  			// Docker container running with host networking is accessible via
  1248  			// the docker bridge's IP address. See https://docs.docker.com/network/host/.
  1249  			By("Accessing index.html using Docker container using host networking from %q (should work)", helpers.App1)
  1250  			res = vm.ContainerExec(helpers.App1, helpers.CurlFail("%s://%s/index.html", proto, dstIP))
  1251  			ExpectWithOffset(1, res).To(helpers.CMDSuccess(),
  1252  				"Expected to be able to access /public in host Docker container")
  1253  
  1254  			By("Accessing %q on wrong port from %q should fail", dstIP, helpers.App1)
  1255  			res = vm.ContainerExec(helpers.App1, helpers.CurlFail("http://%s:8080/public", dstIP))
  1256  			ExpectWithOffset(1, res).ShouldNot(helpers.CMDSuccess(),
  1257  				"unexpectedly able to access %q when access should only be allowed to CIDR", dstIP)
  1258  
  1259  			By("Accessing port 80 on wrong destination from %q should fail", helpers.App1)
  1260  			res = vm.ContainerExec(helpers.App1, helpers.CurlFail("%s://%s/public", proto, hostIP))
  1261  			ExpectWithOffset(1, res).ShouldNot(helpers.CMDSuccess(),
  1262  				"unexpectedly able to access %q when access should only be allowed to CIDR", hostIP)
  1263  
  1264  			By("Pinging %q from %q (shouldn't work)", helpers.App2, helpers.App1)
  1265  			res = vm.ContainerExec(helpers.App1, helpers.Ping(helpers.App2))
  1266  			ExpectWithOffset(1, res).ShouldNot(helpers.CMDSuccess(),
  1267  				"expected ping to %q to fail", helpers.App2)
  1268  
  1269  			httpd2, err := vm.ContainerInspectNet(helpers.Httpd2)
  1270  			ExpectWithOffset(1, err).Should(BeNil(),
  1271  				"Unable to get networking information for container %q", helpers.Httpd2)
  1272  
  1273  			By("Accessing /index.html in %q from %q (shouldn't work)", helpers.App2, helpers.App1)
  1274  			res = vm.ContainerExec(helpers.App1, helpers.CurlFail("%s://%s/index.html", proto, httpd2[helpers.IPv4]))
  1275  			ExpectWithOffset(1, res).ShouldNot(helpers.CMDSuccess(),
  1276  				"unexpectedly able to access %q when access should only be allowed to CIDR", helpers.Httpd2)
  1277  		}
  1278  
  1279  		It("Tests egress with CIDR+L4 policy", func() {
  1280  			By("Importing policy which allows egress to %q from %q", otherHostIP, helpers.App1)
  1281  			policy := fmt.Sprintf(`
  1282  			[{
  1283  				"endpointSelector": {"matchLabels":{"id.%s":""}},
  1284  				"egress": [{
  1285  					"toCIDR": [
  1286  						"%s"
  1287  					],
  1288  					"toPorts": [
  1289  						{"ports":[{"port": "80", "protocol": "TCP"}]}
  1290  					]
  1291  				}]
  1292  			}]`, helpers.App1, otherHostIP)
  1293  
  1294  			testCIDRL4Policy(policy, otherHostIP, "http")
  1295  		})
  1296  
  1297  		It("Tests egress with CIDR+L4 policy to external https service", func() {
  1298  			cloudFlare := "1.1.1.1"
  1299  			proto := "https"
  1300  			retries := 5
  1301  
  1302  			By("Checking connectivity to %q without policy", cloudFlare)
  1303  			res := vm.ContainerExec(helpers.App1, helpers.Ping(cloudFlare))
  1304  			res.ExpectSuccess("Expected to be able to connect to cloudflare (%q); external connectivity not available", cloudFlare)
  1305  
  1306  			By("Importing policy which allows egress to %q from %q", otherHostIP, helpers.App1)
  1307  			policy := fmt.Sprintf(`
  1308  			[{
  1309  				"endpointSelector": {"matchLabels":{"id.%s":""}},
  1310  				"egress": [{
  1311  					"toCIDR": [
  1312  						"%s/30"
  1313  					],
  1314  					"toPorts": [
  1315  						{"ports":[{"port": "443", "protocol": "TCP"}]}
  1316  					]
  1317  				}]
  1318  			}]`, helpers.App1, cloudFlare)
  1319  
  1320  			_, err := vm.PolicyRenderAndImport(policy)
  1321  			Expect(err).To(BeNil(), "Unable to import policy")
  1322  
  1323  			httpd2, err := vm.ContainerInspectNet(helpers.Httpd2)
  1324  			Expect(err).Should(BeNil(),
  1325  				"Unable to get networking information for container %q", helpers.Httpd2)
  1326  
  1327  			By("Accessing /index.html in %q from %q (shouldn't work)", helpers.App2, helpers.App1)
  1328  			res = vm.ContainerExec(helpers.App1, helpers.CurlFail("%s://%s/index.html", proto, httpd2[helpers.IPv4]))
  1329  			res.ExpectFail("unexpectedly able to access %q when access should only be allowed to CIDR", helpers.Httpd2)
  1330  
  1331  			By("Testing egress access to the world")
  1332  			curlFailures := 0
  1333  			for i := 0; i < retries; i++ {
  1334  				By("Accessing index.html using Docker container using host networking from %q (should work)", helpers.App1)
  1335  				res = vm.ContainerExec(helpers.App1, helpers.CurlFail("%s://%s/index.html", proto, cloudFlare))
  1336  				if !res.WasSuccessful() {
  1337  					curlFailures++
  1338  				}
  1339  			}
  1340  			Expect(curlFailures).To(BeNumerically("<=", 1), "Curl to %q have failed more than once", cloudFlare)
  1341  
  1342  			By("Pinging %q from %q (should not work)", api.EntityHost, helpers.App1)
  1343  			res = vm.ContainerExec(helpers.App1, helpers.Ping(cloudFlare))
  1344  			res.ExpectFail("expected ping to %q to fail", cloudFlare)
  1345  
  1346  			By("Accessing %q on wrong port from %q should fail", cloudFlare, helpers.App1)
  1347  			res = vm.ContainerExec(helpers.App1, helpers.CurlFail("http://%s:8080/public", cloudFlare))
  1348  			res.ExpectFail("unexpectedly able to access %q when access should only be allowed to CIDR", cloudFlare)
  1349  
  1350  			By("Accessing port 80 on wrong destination from %q should fail", helpers.App1)
  1351  			res = vm.ContainerExec(helpers.App1, helpers.CurlFail("%s://%s/public", proto, hostIP))
  1352  			res.ExpectFail("unexpectedly able to access %q when access should only be allowed to CIDR", hostIP)
  1353  
  1354  			By("Pinging %q from %q (shouldn't work)", helpers.App2, helpers.App1)
  1355  			res = vm.ContainerExec(helpers.App1, helpers.Ping(helpers.App2))
  1356  			res.ExpectFail("expected ping to %q to fail", helpers.App2)
  1357  		})
  1358  
  1359  		It("Tests egress with CIDR+L7 policy", func() {
  1360  			By("Importing policy which allows egress to %q from %q", otherHostIP, helpers.App1)
  1361  			policy := fmt.Sprintf(`
  1362  			[{
  1363  				"endpointSelector": {"matchLabels":{"id.%s":""}},
  1364  				"egress": [{
  1365  					"toCIDR": [
  1366  						"%s/32"
  1367  					],
  1368  					"toPorts": [{
  1369  						"ports":[{"port": "80", "protocol": "TCP"}],
  1370  						"rules": {
  1371  							"HTTP": [{
  1372  							  "method": "GET",
  1373  							  "path": "/index.html"
  1374  							}]
  1375  						}
  1376  					}]
  1377  				}]
  1378  			}]`, helpers.App1, otherHostIP)
  1379  
  1380  			testCIDRL4Policy(policy, otherHostIP, "http")
  1381  
  1382  			By("Accessing /private on %q from %q should fail", otherHostIP, helpers.App1)
  1383  			res := vm.ContainerExec(helpers.App1, helpers.CurlWithHTTPCode("http://%s/private", otherHostIP))
  1384  			res.ExpectContains("403", "unexpectedly able to access http://%q:80/private when access should only be allowed to /index.html", otherHostIP)
  1385  		})
  1386  	})
  1387  	Context("Init Policy Default Drop Test", func() {
  1388  		BeforeEach(func() {
  1389  			vm.ContainerRm(initContainer)
  1390  			ExpectPolicyEnforcementUpdated(vm, helpers.PolicyEnforcementAlways)
  1391  		})
  1392  
  1393  		AfterEach(func() {
  1394  			vm.ContainerRm(initContainer).ExpectSuccess("Container initContainer cannot be deleted")
  1395  		})
  1396  
  1397  		It("Init Ingress Policy Default Drop Test", func() {
  1398  			By("Starting cilium monitor in background")
  1399  			ctx, cancel := context.WithCancel(context.Background())
  1400  			monitorRes := vm.ExecInBackground(ctx, "cilium monitor --type drop --type trace")
  1401  			defer cancel()
  1402  
  1403  			By("Creating an endpoint")
  1404  			res := vm.ContainerCreate(initContainer, constants.NetperfImage, helpers.CiliumDockerNetwork, "-l somelabel")
  1405  			res.ExpectSuccess("Failed to create container")
  1406  
  1407  			endpoints, err := vm.GetAllEndpointsIds()
  1408  			Expect(err).Should(BeNil(), "Unable to get IDs of endpoints")
  1409  			endpointID, exists := endpoints[initContainer]
  1410  			Expect(exists).To(BeTrue(), "Expected endpoint ID to exist for %s", initContainer)
  1411  			ingressEpModel := vm.EndpointGet(endpointID)
  1412  			Expect(ingressEpModel).NotTo(BeNil(), "nil model returned for endpoint %s", endpointID)
  1413  
  1414  			endpointIP := ingressEpModel.Status.Networking.Addressing[0]
  1415  
  1416  			// Normally, we start pinging fast enough that the endpoint still has identity "init" / 5,
  1417  			// and we continue pinging as the endpoint changes its identity for label "somelabel".
  1418  			// So these pings will be dropped by the policies for both identity 5 and the new identity
  1419  			// for label "somelabel".
  1420  			By("Testing ingress with ping from host to endpoint")
  1421  			res = vm.Exec(helpers.Ping(endpointIP.IPV4))
  1422  			res.ExpectFail("Unexpectedly able to ping endpoint with no ingress policy")
  1423  
  1424  			By("Testing cilium monitor output")
  1425  			err = monitorRes.WaitUntilMatch("xx drop (Policy denied")
  1426  			Expect(err).To(BeNil(), "Default drop on ingress failed")
  1427  			monitorRes.ExpectDoesNotContain(fmt.Sprintf("-> endpoint %s ", endpointID),
  1428  				"Unexpected ingress traffic to endpoint")
  1429  		})
  1430  
  1431  		It("Init Egress Policy Default Drop Test", func() {
  1432  			hostIP := "10.0.2.15"
  1433  
  1434  			By("Starting cilium monitor in background")
  1435  			ctx, cancel := context.WithCancel(context.Background())
  1436  			monitorRes := vm.ExecInBackground(ctx, "cilium monitor --type drop --type trace")
  1437  			defer cancel()
  1438  
  1439  			By("Creating an endpoint")
  1440  			res := vm.ContainerCreate(initContainer, constants.NetperfImage, helpers.CiliumDockerNetwork, "-l somelabel", "ping", hostIP)
  1441  			res.ExpectSuccess("Failed to create container")
  1442  
  1443  			endpoints, err := vm.GetAllEndpointsIds()
  1444  			Expect(err).To(BeNil(), "Unable to get IDs of endpoints")
  1445  			endpointID, exists := endpoints[initContainer]
  1446  			Expect(exists).To(BeTrue(), "Expected endpoint ID to exist for %s", initContainer)
  1447  			egressEpModel := vm.EndpointGet(endpointID)
  1448  			Expect(egressEpModel).NotTo(BeNil(), "nil model returned for endpoint %s", endpointID)
  1449  
  1450  			By("Testing cilium monitor output")
  1451  			err = monitorRes.WaitUntilMatch("xx drop (Policy denied")
  1452  			Expect(err).To(BeNil(), "Default drop on egress failed")
  1453  			monitorRes.ExpectDoesNotContain(fmt.Sprintf("-> endpoint %s ", endpointID),
  1454  				"Unexpected reply traffic to endpoint")
  1455  		})
  1456  	})
  1457  	Context("Init Policy Test", func() {
  1458  		BeforeEach(func() {
  1459  			vm.ContainerRm(initContainer)
  1460  			ExpectPolicyEnforcementUpdated(vm, helpers.PolicyEnforcementAlways)
  1461  
  1462  			_, err := vm.PolicyImportAndWait(vm.GetFullPath(policiesReservedInitJSON), helpers.HelperTimeout)
  1463  			Expect(err).Should(BeNil(), "Init policy import failed")
  1464  		})
  1465  
  1466  		AfterEach(func() {
  1467  			vm.ContainerRm(initContainer).ExpectSuccess("Container initContainer cannot be deleted")
  1468  		})
  1469  
  1470  		It("Init Ingress Policy Test", func() {
  1471  			By("Starting cilium monitor in background")
  1472  			ctx, cancel := context.WithCancel(context.Background())
  1473  			monitorRes := vm.ExecInBackground(ctx, "cilium monitor --type drop --type trace")
  1474  			defer cancel()
  1475  
  1476  			By("Creating an endpoint")
  1477  			res := vm.ContainerCreate(initContainer, constants.NetperfImage, helpers.CiliumDockerNetwork, "-l somelabel")
  1478  			res.ExpectSuccess("Failed to create container")
  1479  
  1480  			endpoints, err := vm.GetAllEndpointsIds()
  1481  			Expect(err).Should(BeNil(), "Unable to get IDs of endpoints")
  1482  			endpointID, exists := endpoints[initContainer]
  1483  			Expect(exists).To(BeTrue(), "Expected endpoint ID to exist for %s", initContainer)
  1484  			ingressEpModel := vm.EndpointGet(endpointID)
  1485  			Expect(ingressEpModel).NotTo(BeNil(), "nil model returned for endpoint %s", endpointID)
  1486  
  1487  			endpointIP := ingressEpModel.Status.Networking.Addressing[0]
  1488  
  1489  			// Normally, we start pinging fast enough that the endpoint still has identity "init" / 5,
  1490  			// and we continue pinging as the endpoint changes its identity for label "somelabel".
  1491  			// So these pings will be allowed by the policies for both identity 5 and the new identity
  1492  			// for label "somelabel".
  1493  			By("Testing ingress with ping from host to endpoint")
  1494  			res = vm.Exec(helpers.Ping(endpointIP.IPV4))
  1495  			res.ExpectSuccess("Cannot ping endpoint with init policy")
  1496  
  1497  			By("Testing cilium monitor output")
  1498  			err = monitorRes.WaitUntilMatchRegexp(fmt.Sprintf(`-> endpoint %s flow [^ ]+ identity 1->`, endpointID))
  1499  			Expect(err).To(BeNil(), "Allow on ingress failed")
  1500  			monitorRes.ExpectDoesNotMatchRegexp(fmt.Sprintf(`xx drop \(Policy denied \([^)]+\)\) flow [^ ]+ to endpoint %s, identity 1->[^0]`, endpointID), "Unexpected drop")
  1501  		})
  1502  
  1503  		It("Init Egress Policy Test", func() {
  1504  			hostIP := "10.0.2.15"
  1505  
  1506  			By("Starting cilium monitor in background")
  1507  			ctx, cancel := context.WithCancel(context.Background())
  1508  			monitorRes := vm.ExecInBackground(ctx, "cilium monitor --type drop --type trace")
  1509  			defer cancel()
  1510  
  1511  			By("Creating an endpoint")
  1512  			res := vm.ContainerCreate(initContainer, constants.NetperfImage, helpers.CiliumDockerNetwork, "-l somelabel", "ping", hostIP)
  1513  			res.ExpectSuccess("Failed to create container")
  1514  
  1515  			endpoints, err := vm.GetAllEndpointsIds()
  1516  			Expect(err).To(BeNil(), "Unable to get IDs of endpoints")
  1517  			endpointID, exists := endpoints[initContainer]
  1518  			Expect(exists).To(BeTrue(), "Expected endpoint ID to exist for %s", initContainer)
  1519  			egressEpModel := vm.EndpointGet(endpointID)
  1520  			Expect(egressEpModel).NotTo(BeNil(), "nil model returned for endpoint %s", endpointID)
  1521  
  1522  			By("Testing cilium monitor output")
  1523  			err = monitorRes.WaitUntilMatchRegexp(fmt.Sprintf(`-> endpoint %s flow [^ ]+ identity 1->`, endpointID))
  1524  			Expect(err).To(BeNil(), "Allow on egress failed")
  1525  			monitorRes.ExpectDoesNotMatchRegexp(fmt.Sprintf(`xx drop \(Policy denied \([^)]+\)\) flow [^ ]+ to endpoint %s, identity 1->[^0]`, endpointID), "Unexpected drop")
  1526  		})
  1527  	})
  1528  
  1529  	Context("Test Policy Generation for Already-Allocated Identities", func() {
  1530  		var (
  1531  			newContainerName = fmt.Sprintf("%s-already-allocated-id", helpers.Httpd1)
  1532  		)
  1533  
  1534  		// Apply L3-L4 policy, which will select the already-running containers
  1535  		// that have been created outside of this Context.
  1536  		BeforeEach(func() {
  1537  			By("Importing policy which selects all endpoints with label id.httpd1 to allow ingress traffic on port 80")
  1538  			_, err := vm.PolicyImportAndWait(vm.GetFullPath("Policies-l4-policy.json"), helpers.HelperTimeout)
  1539  			Expect(err).Should(BeNil(), "unable to apply L3-L4 policy")
  1540  		})
  1541  
  1542  		AfterEach(func() {
  1543  			vm.ContainerRm(newContainerName)
  1544  		})
  1545  
  1546  		It("Tests L4 Policy is Generated for Endpoint whose identity has already been allocated", func() {
  1547  			// Create a new container which has labels which have already been
  1548  			// allocated an identity from the key-value store.
  1549  			By("Creating new container with label id.httpd1, which has already " +
  1550  				"been allocated an identity from the key-value store")
  1551  			vm.ContainerCreate(newContainerName, constants.HttpdImage, helpers.CiliumDockerNetwork, fmt.Sprintf("-l id.%s", helpers.Httpd1))
  1552  
  1553  			By("Waiting for newly added endpoint to be ready")
  1554  			areEndpointsReady := vm.WaitEndpointsReady()
  1555  			Expect(areEndpointsReady).Should(BeTrue(), "Endpoints are not ready after timeout")
  1556  
  1557  			// All endpoints should be able to connect to this container on port
  1558  			// 80, but should not be able to ping because ICMP does not use
  1559  			// port 80.
  1560  
  1561  			By("Checking that datapath behavior matches policy which selects this new endpoint")
  1562  			for _, app := range []string{helpers.App1, helpers.App2} {
  1563  				connectivityTest(pingRequests, app, newContainerName, false)
  1564  				connectivityTest(httpRequests, app, newContainerName, true)
  1565  			}
  1566  
  1567  		})
  1568  	})
  1569  })
  1570  
  1571  var _ = Describe("RuntimePolicyImportTests", func() {
  1572  	var (
  1573  		vm *helpers.SSHMeta
  1574  	)
  1575  
  1576  	BeforeAll(func() {
  1577  		vm = helpers.InitRuntimeHelper(helpers.Runtime, logger)
  1578  		ExpectCiliumReady(vm)
  1579  
  1580  		vm.SampleContainersActions(helpers.Create, helpers.CiliumDockerNetwork)
  1581  
  1582  		areEndpointsReady := vm.WaitEndpointsReady()
  1583  		Expect(areEndpointsReady).Should(BeTrue())
  1584  	})
  1585  
  1586  	BeforeEach(func() {
  1587  		ExpectPolicyEnforcementUpdated(vm, helpers.PolicyEnforcementDefault)
  1588  	})
  1589  
  1590  	AfterEach(func() {
  1591  		_ = vm.PolicyDelAll()
  1592  	})
  1593  
  1594  	JustAfterEach(func() {
  1595  		vm.ValidateNoErrorsInLogs(CurrentGinkgoTestDescription().Duration)
  1596  	})
  1597  
  1598  	AfterFailed(func() {
  1599  		vm.ReportFailed("cilium policy get")
  1600  	})
  1601  
  1602  	AfterAll(func() {
  1603  		vm.PolicyDelAll().ExpectSuccess("Unable to delete all policies")
  1604  		vm.SampleContainersActions(helpers.Delete, helpers.CiliumDockerNetwork)
  1605  	})
  1606  
  1607  	It("Invalid Policies", func() {
  1608  
  1609  		testInvalidPolicy := func(data string) {
  1610  			err := helpers.RenderTemplateToFile(invalidJSON, data, 0777)
  1611  			Expect(err).Should(BeNil())
  1612  
  1613  			path := helpers.GetFilePath(invalidJSON)
  1614  			_, err = vm.PolicyImportAndWait(path, helpers.HelperTimeout)
  1615  			Expect(err).Should(HaveOccurred())
  1616  			defer os.Remove(invalidJSON)
  1617  		}
  1618  		By("Invalid Json")
  1619  
  1620  		invalidJSON := fmt.Sprintf(`
  1621  		[{
  1622  			"endpointSelector": {
  1623  				"matchLabels":{"id.httpd1":""}
  1624  			},`)
  1625  		testInvalidPolicy(invalidJSON)
  1626  	})
  1627  
  1628  	Context("Policy command", func() {
  1629  		var (
  1630  			policy = `[{
  1631  			"endpointSelector": {"matchLabels":{"role":"frontend"}},
  1632  				"labels": ["key1"]
  1633  			},{
  1634  			"endpointSelector": {"matchLabels":{"role":"frontend"}},
  1635  				"labels": ["key2"]
  1636  			},{
  1637  			"endpointSelector": {"matchLabels":{"role":"frontend"}},
  1638  				"labels": ["key3"]
  1639  			}]`
  1640  		)
  1641  
  1642  		BeforeEach(func() {
  1643  			err := helpers.RenderTemplateToFile(policyJSON, policy, 0777)
  1644  			Expect(err).Should(BeNil())
  1645  
  1646  			path := helpers.GetFilePath(policyJSON)
  1647  			_, err = vm.PolicyImportAndWait(path, helpers.HelperTimeout)
  1648  			Expect(err).Should(BeNil())
  1649  		})
  1650  
  1651  		AfterEach(func() {
  1652  			_ = vm.PolicyDelAll()
  1653  			_ = os.Remove(policyJSON)
  1654  		})
  1655  
  1656  		It("Tests getting policy by labels", func() {
  1657  			for _, v := range []string{"key1", "key2", "key3"} {
  1658  				res := vm.PolicyGet(v)
  1659  				res.ExpectSuccess(fmt.Sprintf("cannot get key %q", v))
  1660  			}
  1661  		})
  1662  
  1663  		It("Tests deleting policy key", func() {
  1664  			res := vm.PolicyDel("key2")
  1665  			res.ExpectSuccess("Unable to delete policy rule with label key2 despite rule having been imported with that label")
  1666  
  1667  			res = vm.PolicyGet("key2")
  1668  			res.ExpectFail("Was able to retrieve policy rule with label key2 despite having deleted it")
  1669  
  1670  			//Key1 and key3 should still exist. Test to delete it
  1671  			for _, v := range []string{"key1", "key3"} {
  1672  				res := vm.PolicyGet(v)
  1673  				res.ExpectSuccess(fmt.Sprintf("Cannot get policy rule with key %s", v))
  1674  
  1675  				res = vm.PolicyDel(v)
  1676  				res.ExpectSuccess("Unable to delete policy rule with key %s", v)
  1677  			}
  1678  
  1679  			res = vm.PolicyGetAll()
  1680  			res.ExpectSuccess("unable to get policy rules from cilium")
  1681  
  1682  			res = vm.PolicyDelAll()
  1683  			res.ExpectSuccess("deleting all policy rules should not fail even if no rules are imported to cilium")
  1684  		})
  1685  	})
  1686  
  1687  	It("checks policy trace output", func() {
  1688  
  1689  		httpd2Label := "id.httpd2"
  1690  		httpd1Label := "id.httpd1"
  1691  		allowedVerdict := "Final verdict: ALLOWED"
  1692  
  1693  		By("Checking policy trace by labels")
  1694  
  1695  		By("Importing policy that allows ingress to %q from the host and %q", httpd1Label, httpd2Label)
  1696  
  1697  		allowHttpd1IngressHostHttpd2 := fmt.Sprintf(`
  1698  			[{
  1699      			"endpointSelector": {"matchLabels":{"id.httpd1":""}},
  1700      			"ingress": [{
  1701          			"fromEndpoints": [
  1702              			{"matchLabels":{"reserved:host":""}},
  1703              			{"matchLabels":{"id.httpd2":""}}
  1704  					]
  1705      			}]
  1706  			}]`)
  1707  
  1708  		_, err := vm.PolicyRenderAndImport(allowHttpd1IngressHostHttpd2)
  1709  		Expect(err).Should(BeNil(), "Error importing policy: %s", err)
  1710  
  1711  		By("Verifying that trace says that %q can reach %q", httpd2Label, httpd1Label)
  1712  
  1713  		res := vm.Exec(fmt.Sprintf(`cilium policy trace -s %s -d %s/TCP`, httpd2Label, httpd1Label))
  1714  		Expect(res.Output().String()).Should(ContainSubstring(allowedVerdict), "Policy trace did not contain %s", allowedVerdict)
  1715  
  1716  		endpointIDS, err := vm.GetEndpointsIds()
  1717  		Expect(err).To(BeNil(), "Unable to get IDs of endpoints")
  1718  
  1719  		httpd2EndpointID, exists := endpointIDS[helpers.Httpd2]
  1720  		Expect(exists).To(BeTrue(), "Expected endpoint ID to exist for %s", helpers.Httpd2)
  1721  
  1722  		httpd1EndpointID, exists := endpointIDS[helpers.Httpd1]
  1723  		Expect(exists).To(BeTrue(), "Expected endpoint ID to exist for %s", helpers.Httpd1)
  1724  
  1725  		By("Getting models of endpoints to access policy-related metadata")
  1726  		httpd2EndpointModel := vm.EndpointGet(httpd2EndpointID)
  1727  		Expect(httpd2EndpointModel).To(Not(BeNil()), "Expected non-nil model for endpoint %s", helpers.Httpd2)
  1728  		Expect(httpd2EndpointModel.Status.Identity).To(Not(BeNil()), "Expected non-nil identity for endpoint %s", helpers.Httpd2)
  1729  
  1730  		httpd2ContainerLabel := "container:id.httpd2"
  1731  
  1732  		By("Getting all identities which contain label %s", httpd2ContainerLabel)
  1733  		res = vm.ExecCilium(fmt.Sprintf(
  1734  			`identity list -o json | jq 'map(select(.labels[] | contains("%s")).id) | sort'`,
  1735  			httpd2ContainerLabel))
  1736  		var identities []int64
  1737  		err = res.Unmarshal(&identities)
  1738  		Expect(err).Should(BeNil(), "unable to get identities containing label %s", httpd2ContainerLabel)
  1739  		// We expect the host to be allowed.
  1740  		identities = append([]int64{1}, identities...)
  1741  
  1742  		httpd1EndpointModel := vm.EndpointGet(httpd1EndpointID)
  1743  		Expect(httpd1EndpointModel).To(Not(BeNil()), "Expected non-nil model for endpoint %s", helpers.Httpd1)
  1744  		Expect(httpd1EndpointModel.Status.Identity).To(Not(BeNil()), "Expected non-nil identity for endpoint %s", helpers.Httpd1)
  1745  		Expect(httpd1EndpointModel.Status.Policy).To(Not(BeNil()), "Expected non-nil policy for endpoint %s", helpers.Httpd1)
  1746  
  1747  		httpd1SecurityIdentity := httpd1EndpointModel.Status.Identity.ID
  1748  		httpd2SecurityIdentity := httpd2EndpointModel.Status.Identity.ID
  1749  
  1750  		// TODO - remove hardcoding of host identity.
  1751  		By("Verifying allowed identities for ingress traffic to %q", helpers.Httpd1)
  1752  		actualIngressIdentitiesHttpd1 := httpd1EndpointModel.Status.Policy.Realized.AllowedIngressIdentities
  1753  		Expect(identities).Should(ConsistOf(actualIngressIdentitiesHttpd1),
  1754  			"Expected allowed identities %v, but instead got %v",
  1755  			identities, actualIngressIdentitiesHttpd1)
  1756  
  1757  		By("Deleting all policies and adding a new policy to ensure that endpoint policy is updated accordingly")
  1758  		res = vm.PolicyDelAll()
  1759  		res.ExpectSuccess("Unable to delete all policies")
  1760  
  1761  		allowHttpd1IngressHttpd2 := fmt.Sprintf(`
  1762  			[{
  1763      			"endpointSelector": {"matchLabels":{"id.httpd1":""}},
  1764      			"ingress": [{
  1765          			"fromEndpoints": [
  1766              			{"matchLabels":{"id.httpd2":""}}
  1767  					]
  1768      			}]
  1769  			}]`)
  1770  
  1771  		_, err = vm.PolicyRenderAndImport(allowHttpd1IngressHttpd2)
  1772  		Expect(err).Should(BeNil(), "Error importing policy: %s", err)
  1773  
  1774  		By("Verifying verbose trace for expected output using security identities")
  1775  		res = vm.Exec(fmt.Sprintf(
  1776  			`cilium policy trace --src-identity %d --dst-identity %d`,
  1777  			httpd2SecurityIdentity, httpd1SecurityIdentity))
  1778  		res.ExpectContains(allowedVerdict, "Policy trace did not contain %s", allowedVerdict)
  1779  
  1780  		By("Verifying verbose trace for expected output using endpoint IDs")
  1781  		res = vm.Exec(fmt.Sprintf(
  1782  			`cilium policy trace --src-endpoint %s --dst-endpoint %s`,
  1783  			httpd2EndpointID, httpd1EndpointID))
  1784  		res.ExpectContains(allowedVerdict, "Policy trace did not contain %s", allowedVerdict)
  1785  
  1786  		// Have to get models of endpoints again because policy has been updated.
  1787  		By("Getting models of endpoints to access policy-related metadata")
  1788  		httpd2EndpointModel = vm.EndpointGet(httpd2EndpointID)
  1789  		Expect(httpd2EndpointModel).To(Not(BeNil()), "Expected non-nil model for endpoint %s", helpers.Httpd2)
  1790  		Expect(httpd2EndpointModel.Status.Identity).To(Not(BeNil()), "Expected non-nil identity for endpoint %s", helpers.Httpd2)
  1791  
  1792  		httpd1EndpointModel = vm.EndpointGet(httpd1EndpointID)
  1793  		Expect(httpd1EndpointModel).To(Not(BeNil()), "Expected non-nil model for endpoint %s", helpers.Httpd1)
  1794  		Expect(httpd1EndpointModel.Status.Identity).To(Not(BeNil()), "Expected non-nil identity for endpoint %s", helpers.Httpd1)
  1795  		Expect(httpd1EndpointModel.Status.Policy).To(Not(BeNil()), "Expected non-nil policy for endpoint %s", helpers.Httpd1)
  1796  
  1797  		By("Getting all identities which contain label %s", httpd2ContainerLabel)
  1798  		res = vm.ExecCilium(fmt.Sprintf(
  1799  			`identity list -o json | jq 'map(select(.labels[] | contains("%s")).id) | sort'`,
  1800  			httpd2ContainerLabel))
  1801  		identities = []int64{}
  1802  		err = res.Unmarshal(&identities)
  1803  		Expect(err).Should(BeNil(), "unable to get identities containing label %s", httpd2ContainerLabel)
  1804  
  1805  		By("Verifying allowed identities for ingress traffic to %q", helpers.Httpd1)
  1806  		actualIngressIdentitiesHttpd1 = httpd1EndpointModel.Status.Policy.Realized.AllowedIngressIdentities
  1807  		Expect(identities).Should(ConsistOf(actualIngressIdentitiesHttpd1),
  1808  			"Expected allowed identities %v, but instead got %v",
  1809  			identities, actualIngressIdentitiesHttpd1)
  1810  
  1811  		res = vm.PolicyDelAll()
  1812  		res.ExpectSuccess("Unable to delete all policies")
  1813  
  1814  		ExpectPolicyEnforcementUpdated(vm, helpers.PolicyEnforcementDefault)
  1815  
  1816  		By("Checking that policy trace returns allowed verdict without any policies imported")
  1817  		res = vm.Exec(fmt.Sprintf(`cilium policy trace --src-endpoint %s --dst-endpoint %s`, httpd2EndpointID, httpd1EndpointID))
  1818  		res.ExpectContains(allowedVerdict, "Policy trace did not contain %s", allowedVerdict)
  1819  	})
  1820  })