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 })