github.com/cilium/cilium@v1.16.2/pkg/egressgateway/manager_privileged_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package egressgateway 5 6 import ( 7 "context" 8 "fmt" 9 "net/netip" 10 "testing" 11 "time" 12 13 "github.com/cilium/ebpf/rlimit" 14 "github.com/cilium/hive/hivetest" 15 "github.com/google/uuid" 16 "github.com/spf13/afero" 17 "github.com/stretchr/testify/require" 18 "github.com/vishvananda/netlink" 19 "k8s.io/apimachinery/pkg/types" 20 21 "github.com/cilium/cilium/pkg/bpf" 22 "github.com/cilium/cilium/pkg/datapath/linux/sysctl" 23 "github.com/cilium/cilium/pkg/hive" 24 "github.com/cilium/cilium/pkg/identity" 25 cilium_api_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" 26 "github.com/cilium/cilium/pkg/k8s/resource" 27 slimv1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" 28 k8sTypes "github.com/cilium/cilium/pkg/k8s/types" 29 "github.com/cilium/cilium/pkg/labels" 30 "github.com/cilium/cilium/pkg/lock" 31 "github.com/cilium/cilium/pkg/maps/egressmap" 32 "github.com/cilium/cilium/pkg/node/addressing" 33 nodeTypes "github.com/cilium/cilium/pkg/node/types" 34 "github.com/cilium/cilium/pkg/option" 35 "github.com/cilium/cilium/pkg/testutils" 36 testidentity "github.com/cilium/cilium/pkg/testutils/identity" 37 ) 38 39 const ( 40 testInterface1 = "cilium_egw1" 41 testInterface2 = "cilium_egw2" 42 43 node1 = "k8s1" 44 node2 = "k8s2" 45 46 node1IP = "192.168.1.1" 47 node2IP = "192.168.1.2" 48 49 ep1IP = "10.0.0.1" 50 ep2IP = "10.0.0.2" 51 ep3IP = "10.0.0.3" 52 53 destCIDR = "1.1.1.0/24" 54 destCIDR3 = "1.1.3.0/24" 55 allZeroDestCIDR = "0.0.0.0/0" 56 excludedCIDR1 = "1.1.1.22/32" 57 excludedCIDR2 = "1.1.1.240/30" 58 59 egressIP1 = "192.168.101.1" 60 egressCIDR1 = "192.168.101.1/24" 61 egressIP2 = "192.168.102.1" 62 egressCIDR2 = "192.168.102.1/24" 63 64 zeroIP4 = "0.0.0.0" 65 66 // Special values for gatewayIP, see pkg/egressgateway/manager.go 67 gatewayNotFoundValue = "0.0.0.0" 68 gatewayExcludedCIDRValue = "0.0.0.1" 69 70 // Special values for egressIP, see pkg/egressgateway/manager.go 71 egressIPNotFoundValue = "0.0.0.0" 72 ) 73 74 var ( 75 ep1Labels = map[string]string{"test-key": "test-value-1"} 76 ep2Labels = map[string]string{"test-key": "test-value-2"} 77 78 identityAllocator = testidentity.NewMockIdentityAllocator(nil) 79 80 nodeGroupNotFoundLabels = map[string]string{"label1": "notfound"} 81 nodeGroup1Labels = map[string]string{"label1": "1"} 82 nodeGroup2Labels = map[string]string{"label2": "2"} 83 ) 84 85 type egressRule struct { 86 sourceIP string 87 destCIDR string 88 egressIP string 89 gatewayIP string 90 } 91 92 type parsedEgressRule struct { 93 sourceIP netip.Addr 94 destCIDR netip.Prefix 95 egressIP netip.Addr 96 gatewayIP netip.Addr 97 } 98 99 type rpFilterSetting struct { 100 iFaceName string 101 rpFilterSetting string 102 } 103 104 type EgressGatewayTestSuite struct { 105 manager *Manager 106 policies fakeResource[*Policy] 107 nodes fakeResource[*cilium_api_v2.CiliumNode] 108 endpoints fakeResource[*k8sTypes.CiliumEndpoint] 109 sysctl sysctl.Sysctl 110 } 111 112 func setupEgressGatewayTestSuite(t *testing.T) *EgressGatewayTestSuite { 113 testutils.PrivilegedTest(t) 114 115 bpf.CheckOrMountFS("") 116 err := rlimit.RemoveMemlock() 117 require.NoError(t, err) 118 119 nodeTypes.SetName(node1) 120 121 k := &EgressGatewayTestSuite{} 122 k.policies = make(fakeResource[*Policy]) 123 k.nodes = make(fakeResource[*cilium_api_v2.CiliumNode]) 124 k.endpoints = make(fakeResource[*k8sTypes.CiliumEndpoint]) 125 k.sysctl = sysctl.NewDirectSysctl(afero.NewOsFs(), "/proc") 126 127 lc := hivetest.Lifecycle(t) 128 policyMap := egressmap.CreatePrivatePolicyMap(lc, egressmap.DefaultPolicyConfig) 129 130 k.manager, err = newEgressGatewayManager(Params{ 131 Lifecycle: lc, 132 Config: Config{1 * time.Millisecond}, 133 DaemonConfig: &option.DaemonConfig{ConfigPatchMutex: new(lock.RWMutex)}, 134 IdentityAllocator: identityAllocator, 135 PolicyMap: policyMap, 136 Policies: k.policies, 137 Nodes: k.nodes, 138 Endpoints: k.endpoints, 139 Sysctl: k.sysctl, 140 }) 141 require.NoError(t, err) 142 require.NotNil(t, k.manager) 143 144 return k 145 } 146 147 func TestEgressGatewayCEGPParser(t *testing.T) { 148 setupEgressGatewayTestSuite(t) 149 // must specify name 150 policy := policyParams{ 151 name: "", 152 destinationCIDR: destCIDR, 153 iface: testInterface1, 154 } 155 156 cegp, _ := newCEGP(&policy) 157 _, err := ParseCEGP(cegp) 158 require.Error(t, err) 159 160 // catch nil DestinationCIDR field 161 policy = policyParams{ 162 name: "policy-1", 163 iface: testInterface1, 164 } 165 166 cegp, _ = newCEGP(&policy) 167 cegp.Spec.DestinationCIDRs = nil 168 _, err = ParseCEGP(cegp) 169 require.Error(t, err) 170 171 // must specify at least one DestinationCIDR 172 policy = policyParams{ 173 name: "policy-1", 174 iface: testInterface1, 175 } 176 177 cegp, _ = newCEGP(&policy) 178 _, err = ParseCEGP(cegp) 179 require.Error(t, err) 180 181 // catch nil EgressGateway field 182 policy = policyParams{ 183 name: "policy-1", 184 destinationCIDR: destCIDR, 185 iface: testInterface1, 186 } 187 188 cegp, _ = newCEGP(&policy) 189 cegp.Spec.EgressGateway = nil 190 _, err = ParseCEGP(cegp) 191 require.Error(t, err) 192 193 // must specify some sort of endpoint selector 194 policy = policyParams{ 195 name: "policy-1", 196 destinationCIDR: destCIDR, 197 iface: testInterface1, 198 } 199 200 cegp, _ = newCEGP(&policy) 201 cegp.Spec.Selectors[0].NamespaceSelector = nil 202 cegp.Spec.Selectors[0].PodSelector = nil 203 _, err = ParseCEGP(cegp) 204 require.Error(t, err) 205 206 // can't specify both egress iface and IP 207 policy = policyParams{ 208 name: "policy-1", 209 destinationCIDR: destCIDR, 210 iface: testInterface1, 211 egressIP: egressIP1, 212 } 213 214 cegp, _ = newCEGP(&policy) 215 _, err = ParseCEGP(cegp) 216 require.Error(t, err) 217 } 218 219 func TestEgressGatewayManager(t *testing.T) { 220 k := setupEgressGatewayTestSuite(t) 221 createTestInterface(t, k.sysctl, testInterface1, egressCIDR1) 222 createTestInterface(t, k.sysctl, testInterface2, egressCIDR2) 223 224 policyMap := k.manager.policyMap 225 egressGatewayManager := k.manager 226 reconciliationEventsCount := egressGatewayManager.reconciliationEventsCount.Load() 227 228 k.policies.sync(t) 229 k.nodes.sync(t) 230 k.endpoints.sync(t) 231 232 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 233 234 node1 := newCiliumNode(node1, node1IP, nodeGroup1Labels) 235 k.nodes.process(t, resource.Event[*cilium_api_v2.CiliumNode]{ 236 Kind: resource.Upsert, 237 Object: node1.ToCiliumNode(), 238 }) 239 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 240 241 node2 := newCiliumNode(node2, node2IP, nodeGroup2Labels) 242 k.nodes.process(t, resource.Event[*cilium_api_v2.CiliumNode]{ 243 Kind: resource.Upsert, 244 Object: node2.ToCiliumNode(), 245 }) 246 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 247 248 // Create a new policy 249 policy1 := policyParams{ 250 name: "policy-1", 251 endpointLabels: ep1Labels, 252 destinationCIDR: destCIDR, 253 nodeLabels: nodeGroup1Labels, 254 iface: testInterface1, 255 } 256 257 addPolicy(t, k.policies, &policy1) 258 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 259 260 assertRPFilter(t, k.sysctl, []rpFilterSetting{ 261 {iFaceName: testInterface1, rpFilterSetting: "2"}, 262 {iFaceName: testInterface2, rpFilterSetting: "1"}, 263 }) 264 assertEgressRules(t, policyMap, []egressRule{}) 265 266 // Add a new endpoint & ID which matches policy-1 267 ep1, id1 := newEndpointAndIdentity("ep-1", ep1IP, ep1Labels) 268 addEndpoint(t, k.endpoints, &ep1) 269 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 270 271 assertEgressRules(t, policyMap, []egressRule{ 272 {ep1IP, destCIDR, egressIP1, node1IP}, 273 }) 274 275 // Update the endpoint labels in order for it to not be a match 276 id1 = updateEndpointAndIdentity(&ep1, id1, map[string]string{}) 277 addEndpoint(t, k.endpoints, &ep1) 278 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 279 280 assertEgressRules(t, policyMap, []egressRule{}) 281 282 // Restore the old endpoint lables in order for it to be a match 283 id1 = updateEndpointAndIdentity(&ep1, id1, ep1Labels) 284 addEndpoint(t, k.endpoints, &ep1) 285 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 286 287 assertEgressRules(t, policyMap, []egressRule{ 288 {ep1IP, destCIDR, egressIP1, node1IP}, 289 }) 290 291 // Changing the DestCIDR to 0.0.0.0 results in a conflict with 292 // the existing IP rules. Test that the manager is able to 293 // resolve this conflict. 294 policy1.destinationCIDR = allZeroDestCIDR 295 addPolicy(t, k.policies, &policy1) 296 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 297 298 assertEgressRules(t, policyMap, []egressRule{ 299 {ep1IP, allZeroDestCIDR, egressIP1, node1IP}, 300 }) 301 302 // Restore old DestCIDR 303 policy1.destinationCIDR = destCIDR 304 addPolicy(t, k.policies, &policy1) 305 306 // Create a new policy 307 addPolicy(t, k.policies, &policyParams{ 308 name: "policy-2", 309 endpointLabels: ep2Labels, 310 destinationCIDR: destCIDR, 311 nodeLabels: nodeGroup2Labels, 312 iface: testInterface1, 313 }) 314 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 315 316 assertEgressRules(t, policyMap, []egressRule{ 317 {ep1IP, destCIDR, egressIP1, node1IP}, 318 }) 319 320 // Add a new endpoint and ID which matches policy-2 321 ep2, _ := newEndpointAndIdentity("ep-2", ep2IP, ep2Labels) 322 addEndpoint(t, k.endpoints, &ep2) 323 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 324 325 assertEgressRules(t, policyMap, []egressRule{ 326 {ep1IP, destCIDR, egressIP1, node1IP}, 327 {ep2IP, destCIDR, zeroIP4, node2IP}, 328 }) 329 330 // Test excluded CIDRs by adding one to policy-1 331 addPolicy(t, k.policies, &policyParams{ 332 name: "policy-1", 333 endpointLabels: ep1Labels, 334 destinationCIDR: destCIDR, 335 excludedCIDRs: []string{excludedCIDR1}, 336 nodeLabels: nodeGroup1Labels, 337 iface: testInterface1, 338 }) 339 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 340 341 assertEgressRules(t, policyMap, []egressRule{ 342 {ep1IP, destCIDR, egressIP1, node1IP}, 343 {ep1IP, excludedCIDR1, egressIP1, gatewayExcludedCIDRValue}, 344 {ep2IP, destCIDR, zeroIP4, node2IP}, 345 }) 346 347 // Add a second excluded CIDR to policy-1 348 addPolicy(t, k.policies, &policyParams{ 349 name: "policy-1", 350 endpointLabels: ep1Labels, 351 destinationCIDR: destCIDR, 352 excludedCIDRs: []string{excludedCIDR1, excludedCIDR2}, 353 nodeLabels: nodeGroup1Labels, 354 iface: testInterface1, 355 }) 356 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 357 358 assertEgressRules(t, policyMap, []egressRule{ 359 {ep1IP, destCIDR, egressIP1, node1IP}, 360 {ep1IP, excludedCIDR1, egressIP1, gatewayExcludedCIDRValue}, 361 {ep1IP, excludedCIDR2, egressIP1, gatewayExcludedCIDRValue}, 362 {ep2IP, destCIDR, zeroIP4, node2IP}, 363 }) 364 365 // Remove the first excluded CIDR from policy-1 366 addPolicy(t, k.policies, &policyParams{ 367 name: "policy-1", 368 endpointLabels: ep1Labels, 369 destinationCIDR: destCIDR, 370 excludedCIDRs: []string{excludedCIDR2}, 371 nodeLabels: nodeGroup1Labels, 372 iface: testInterface1, 373 }) 374 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 375 376 assertEgressRules(t, policyMap, []egressRule{ 377 {ep1IP, destCIDR, egressIP1, node1IP}, 378 {ep1IP, excludedCIDR2, egressIP1, gatewayExcludedCIDRValue}, 379 {ep2IP, destCIDR, zeroIP4, node2IP}, 380 }) 381 382 // Remove the second excluded CIDR 383 addPolicy(t, k.policies, &policyParams{ 384 name: "policy-1", 385 endpointLabels: ep1Labels, 386 destinationCIDR: destCIDR, 387 nodeLabels: nodeGroup1Labels, 388 iface: testInterface1, 389 }) 390 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 391 392 assertEgressRules(t, policyMap, []egressRule{ 393 {ep1IP, destCIDR, egressIP1, node1IP}, 394 {ep2IP, destCIDR, zeroIP4, node2IP}, 395 }) 396 397 // Test matching no gateway 398 addPolicy(t, k.policies, &policyParams{ 399 name: "policy-1", 400 endpointLabels: ep1Labels, 401 destinationCIDR: destCIDR, 402 nodeLabels: nodeGroupNotFoundLabels, 403 iface: testInterface1, 404 }) 405 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 406 407 assertEgressRules(t, policyMap, []egressRule{ 408 {ep1IP, destCIDR, zeroIP4, gatewayNotFoundValue}, 409 {ep2IP, destCIDR, zeroIP4, node2IP}, 410 }) 411 412 // Test a policy without valid egressIP 413 addPolicy(t, k.policies, &policyParams{ 414 name: "policy-3", 415 endpointLabels: ep1Labels, 416 destinationCIDR: destCIDR3, 417 nodeLabels: nodeGroup1Labels, 418 iface: "no_interface", 419 }) 420 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 421 422 assertEgressRules(t, policyMap, []egressRule{ 423 {ep1IP, destCIDR, zeroIP4, gatewayNotFoundValue}, 424 {ep1IP, destCIDR3, egressIPNotFoundValue, node1IP}, 425 {ep2IP, destCIDR, zeroIP4, node2IP}, 426 }) 427 428 // Update the endpoint labels in order for it to not be a match 429 _ = updateEndpointAndIdentity(&ep1, id1, map[string]string{}) 430 addEndpoint(t, k.endpoints, &ep1) 431 waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 432 433 assertEgressRules(t, policyMap, []egressRule{ 434 {ep2IP, destCIDR, zeroIP4, node2IP}, 435 }) 436 } 437 438 func TestEndpointDataStore(t *testing.T) { 439 k := setupEgressGatewayTestSuite(t) 440 441 createTestInterface(t, k.sysctl, testInterface1, egressCIDR1) 442 443 policyMap := k.manager.policyMap 444 egressGatewayManager := k.manager 445 446 k.policies.sync(t) 447 k.nodes.sync(t) 448 k.endpoints.sync(t) 449 450 reconciliationEventsCount := egressGatewayManager.reconciliationEventsCount.Load() 451 452 node1 := newCiliumNode(node1, node1IP, nodeGroup1Labels) 453 k.nodes.process(t, resource.Event[*cilium_api_v2.CiliumNode]{ 454 Kind: resource.Upsert, 455 Object: node1.ToCiliumNode(), 456 }) 457 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 458 459 // Create a new policy 460 policy1 := policyParams{ 461 name: "policy-1", 462 endpointLabels: ep1Labels, 463 destinationCIDR: destCIDR, 464 nodeLabels: nodeGroup1Labels, 465 iface: testInterface1, 466 } 467 468 addPolicy(t, k.policies, &policy1) 469 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 470 471 assertEgressRules(t, policyMap, []egressRule{}) 472 473 // Add a new endpoint & ID which matches policy-1 474 ep1, _ := newEndpointAndIdentity("ep-1", ep1IP, ep1Labels) 475 addEndpoint(t, k.endpoints, &ep1) 476 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 477 478 assertEgressRules(t, policyMap, []egressRule{ 479 {ep1IP, destCIDR, egressIP1, node1IP}, 480 }) 481 482 // Simulate statefulset pod migrations to a different node. 483 484 // Produce a new endpoint ep2 similar to ep1 - with the same name & labels, but with a different IP address. 485 // The ep1 will be deleted. 486 ep2, _ := newEndpointAndIdentity(ep1.Name, ep2IP, ep1Labels) 487 488 // Test event order: add new -> delete old 489 addEndpoint(t, k.endpoints, &ep2) 490 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 491 deleteEndpoint(t, k.endpoints, &ep1) 492 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 493 494 assertEgressRules(t, policyMap, []egressRule{ 495 {ep2IP, destCIDR, egressIP1, node1IP}, 496 }) 497 498 // Produce a new endpoint ep3 similar to ep2 (and ep1) - with the same name & labels, but with a different IP address. 499 ep3, _ := newEndpointAndIdentity(ep1.Name, ep3IP, ep1Labels) 500 501 // Test event order: delete old -> update new 502 deleteEndpoint(t, k.endpoints, &ep2) 503 reconciliationEventsCount = waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 504 addEndpoint(t, k.endpoints, &ep3) 505 waitForReconciliationRun(t, egressGatewayManager, reconciliationEventsCount) 506 507 assertEgressRules(t, policyMap, []egressRule{ 508 {ep3IP, destCIDR, egressIP1, node1IP}, 509 }) 510 } 511 512 func TestCell(t *testing.T) { 513 err := hive.New(Cell).Populate(hivetest.Logger(t)) 514 if err != nil { 515 t.Fatal(err) 516 } 517 } 518 519 func createTestInterface(tb testing.TB, sysctl sysctl.Sysctl, iface string, addr string) { 520 tb.Helper() 521 522 la := netlink.NewLinkAttrs() 523 la.Name = iface 524 dummy := &netlink.Dummy{LinkAttrs: la} 525 if err := netlink.LinkAdd(dummy); err != nil { 526 tb.Fatal(err) 527 } 528 529 link, err := netlink.LinkByName(iface) 530 if err != nil { 531 tb.Fatal(err) 532 } 533 534 tb.Cleanup(func() { 535 if err := netlink.LinkDel(link); err != nil { 536 tb.Error(err) 537 } 538 }) 539 540 if err := netlink.LinkSetUp(link); err != nil { 541 tb.Fatal(err) 542 } 543 544 a, _ := netlink.ParseAddr(addr) 545 if err := netlink.AddrAdd(link, a); err != nil { 546 tb.Fatal(err) 547 } 548 549 ensureRPFilterIsEnabled(tb, sysctl, iface) 550 } 551 552 func ensureRPFilterIsEnabled(tb testing.TB, sysctl sysctl.Sysctl, iface string) { 553 rpFilterSetting := []string{"net", "ipv4", "conf", iface, "rp_filter"} 554 555 for i := 0; i < 10; i++ { 556 if err := sysctl.Enable(rpFilterSetting); err != nil { 557 tb.Fatal(err) 558 } 559 560 time.Sleep(100 * time.Millisecond) 561 562 if val, err := sysctl.Read(rpFilterSetting); err == nil { 563 if val == "1" { 564 return 565 } 566 } 567 } 568 569 tb.Fatal("failed to enable rp_filter") 570 } 571 572 func waitForReconciliationRun(tb testing.TB, egressGatewayManager *Manager, currentRun uint64) uint64 { 573 for i := 0; i < 100; i++ { 574 count := egressGatewayManager.reconciliationEventsCount.Load() 575 if count > currentRun { 576 return count 577 } 578 579 time.Sleep(10 * time.Millisecond) 580 } 581 582 tb.Fatal("Reconciliation is taking too long to run") 583 return 0 584 } 585 586 func newCiliumNode(name, nodeIP string, nodeLabels map[string]string) nodeTypes.Node { 587 return nodeTypes.Node{ 588 Name: name, 589 Labels: nodeLabels, 590 IPAddresses: []nodeTypes.Address{ 591 { 592 Type: addressing.NodeInternalIP, 593 IP: netip.MustParseAddr(nodeIP).AsSlice(), 594 }, 595 }, 596 } 597 } 598 599 // Mock the creation of endpoint and its corresponding identity, returns endpoint and ID. 600 func newEndpointAndIdentity(name, ip string, epLabels map[string]string) (k8sTypes.CiliumEndpoint, *identity.Identity) { 601 id, _, _ := identityAllocator.AllocateIdentity(context.Background(), labels.Map2Labels(epLabels, labels.LabelSourceK8s), true, identity.InvalidIdentity) 602 603 return k8sTypes.CiliumEndpoint{ 604 ObjectMeta: slimv1.ObjectMeta{ 605 Name: name, 606 UID: types.UID(uuid.New().String()), 607 }, 608 Identity: &cilium_api_v2.EndpointIdentity{ 609 ID: int64(id.ID), 610 }, 611 Networking: &cilium_api_v2.EndpointNetworking{ 612 Addressing: cilium_api_v2.AddressPairList{ 613 &cilium_api_v2.AddressPair{ 614 IPV4: ip, 615 }, 616 }, 617 }, 618 }, id 619 } 620 621 // Mock the update of endpoint and its corresponding identity, with new labels. Returns new ID. 622 func updateEndpointAndIdentity(endpoint *k8sTypes.CiliumEndpoint, oldID *identity.Identity, newEpLabels map[string]string) *identity.Identity { 623 ctx := context.Background() 624 625 identityAllocator.Release(ctx, oldID, true) 626 newID, _, _ := identityAllocator.AllocateIdentity(ctx, labels.Map2Labels(newEpLabels, labels.LabelSourceK8s), true, identity.InvalidIdentity) 627 endpoint.Identity.ID = int64(newID.ID) 628 return newID 629 } 630 631 func parseEgressRule(sourceIP, destCIDR, egressIP, gatewayIP string) parsedEgressRule { 632 sip := netip.MustParseAddr(sourceIP) 633 dc := netip.MustParsePrefix(destCIDR) 634 eip := netip.MustParseAddr(egressIP) 635 gip := netip.MustParseAddr(gatewayIP) 636 637 return parsedEgressRule{ 638 sourceIP: sip, 639 destCIDR: dc, 640 egressIP: eip, 641 gatewayIP: gip, 642 } 643 } 644 645 func assertEgressRules(t *testing.T, policyMap egressmap.PolicyMap, rules []egressRule) { 646 t.Helper() 647 648 err := tryAssertEgressRules(policyMap, rules) 649 require.NoError(t, err) 650 } 651 652 func tryAssertEgressRules(policyMap egressmap.PolicyMap, rules []egressRule) error { 653 parsedRules := []parsedEgressRule{} 654 for _, r := range rules { 655 parsedRules = append(parsedRules, parseEgressRule(r.sourceIP, r.destCIDR, r.egressIP, r.gatewayIP)) 656 } 657 658 for _, r := range parsedRules { 659 policyVal, err := policyMap.Lookup(r.sourceIP, r.destCIDR) 660 if err != nil { 661 return fmt.Errorf("cannot lookup policy entry: %w", err) 662 } 663 664 if policyVal.GetEgressAddr() != r.egressIP { 665 return fmt.Errorf("mismatched egress IP") 666 } 667 668 if policyVal.GetGatewayAddr() != r.gatewayIP { 669 return fmt.Errorf("mismatched gateway IP") 670 } 671 } 672 673 untrackedRule := false 674 policyMap.IterateWithCallback( 675 func(key *egressmap.EgressPolicyKey4, val *egressmap.EgressPolicyVal4) { 676 for _, r := range parsedRules { 677 if key.Match(r.sourceIP, r.destCIDR) && val.Match(r.egressIP, r.gatewayIP) { 678 return 679 } 680 } 681 682 untrackedRule = true 683 }) 684 685 if untrackedRule { 686 return fmt.Errorf("Untracked egress policy") 687 } 688 689 return nil 690 } 691 692 func assertRPFilter(t *testing.T, sysctl sysctl.Sysctl, rpFilterSettings []rpFilterSetting) { 693 t.Helper() 694 695 err := tryAssertRPFilterSettings(sysctl, rpFilterSettings) 696 require.NoError(t, err) 697 } 698 699 func tryAssertRPFilterSettings(sysctl sysctl.Sysctl, rpFilterSettings []rpFilterSetting) error { 700 for _, setting := range rpFilterSettings { 701 if val, err := sysctl.Read([]string{"net", "ipv4", "conf", setting.iFaceName, "rp_filter"}); err != nil { 702 return fmt.Errorf("failed to read rp_filter") 703 } else if val != setting.rpFilterSetting { 704 return fmt.Errorf("mismatched rp_filter iface: %s rp_filter: %s", setting.iFaceName, val) 705 } 706 } 707 708 return nil 709 }