-
Notifications
You must be signed in to change notification settings - Fork 208
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
connectivity: add full egress gateway test suite
Signed-off-by: Gilberto Bertin <[email protected]>
- Loading branch information
Showing
8 changed files
with
373 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
18 changes: 18 additions & 0 deletions
18
connectivity/manifests/egress-gateway-policy-excluded-cidrs.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
apiVersion: cilium.io/v2 | ||
kind: CiliumEgressGatewayPolicy | ||
metadata: | ||
name: cegp-sample-excluded-cidrs | ||
spec: | ||
selectors: | ||
- podSelector: | ||
matchLabels: | ||
io.kubernetes.pod.namespace: cilium-test | ||
kind: client | ||
destinationCIDRs: | ||
- 0.0.0.0/0 | ||
excludedCIDRs: | ||
- NODE_WITHOUT_CILIUM_PLACEHOLDER/32 | ||
egressGateway: | ||
nodeSelector: | ||
matchLabels: | ||
kubernetes.io/hostname: NODE_NAME_PLACEHOLDER |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// Copyright Authors of Cilium | ||
|
||
package tests | ||
|
||
import ( | ||
"context" | ||
"encoding/json" | ||
"fmt" | ||
"net" | ||
"strings" | ||
"time" | ||
|
||
"github.com/cilium/cilium-cli/connectivity/check" | ||
"github.com/cilium/cilium-cli/defaults" | ||
"github.com/cilium/cilium-cli/internal/utils" | ||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | ||
) | ||
|
||
// EgressGatewayExcludedCIDRs is a test case which, given the cegp-sample | ||
// CiliumEgressGatewayExcludedCIDRsPolicy targeting: | ||
// - a couple of client pods (kind=client) as source | ||
// - the 0.0.0.0/0 destination CIDR | ||
// - kind-worker2 as gateway node | ||
// | ||
// ensures that traffic from both clients reaches the echo-external service with | ||
// the egress IP of the gateway node. | ||
func EgressGatewayExcludedCIDRs() check.Scenario { | ||
return &egressGatewayExcludedCIDRs{} | ||
} | ||
|
||
type egressGatewayExcludedCIDRs struct { | ||
egressGatewayNode string | ||
} | ||
|
||
func (s *egressGatewayExcludedCIDRs) Name() string { | ||
return "egress-gateway-excluded-cidrs" | ||
} | ||
|
||
func (s *egressGatewayExcludedCIDRs) Run(ctx context.Context, t *check.Test) { | ||
ct := t.Context() | ||
|
||
s.egressGatewayNode = t.EgressGatewayNode() | ||
if s.egressGatewayNode == "" { | ||
t.Fatal("Cannot get egress gateway node") | ||
} | ||
|
||
s.waitForBpfPolicyEntries(ctx, t) | ||
|
||
// Traffic matching an egress gateway policy should leave the cluster masqueraded with the egress IP (pod to external service) | ||
i := 0 | ||
for _, client := range ct.ClientPods() { | ||
client := client | ||
|
||
for _, externalEcho := range ct.ExternalEchoPods() { | ||
t.NewAction(s, fmt.Sprintf("curl-%d", i), &client, externalEcho, check.IPFamilyV4).Run(func(a *check.Action) { | ||
a.ExecInPod(ctx, ct.CurlClientIPCommand(externalEcho, check.IPFamilyV4)) | ||
clientIP := extractClientIPFromResponse(a.CmdOutput()) | ||
|
||
if !clientIP.Equal(net.ParseIP(client.Pod.Status.HostIP)) { | ||
t.Fatal("Request reached external echo service with wrong source IP") | ||
} | ||
}) | ||
i++ | ||
} | ||
} | ||
} | ||
|
||
// bpfEgressGatewayExcludedCIDRsPolicyEntry represents an entry in the BPF egress gateway | ||
// policy map | ||
type bpfEgressGatewayExcludedCIDRsPolicyEntry struct { | ||
SourceIP string | ||
DestCIDR string | ||
EgressIP string | ||
GatewayIP string | ||
} | ||
|
||
// matches is an helper used to compare the receiver bpfEgressGatewayExcludedCIDRsPolicyEntry | ||
// with another entry | ||
func (e *bpfEgressGatewayExcludedCIDRsPolicyEntry) matches(t bpfEgressGatewayExcludedCIDRsPolicyEntry) bool { | ||
return t.SourceIP == e.SourceIP && | ||
t.DestCIDR == e.DestCIDR && | ||
t.EgressIP == e.EgressIP && | ||
t.GatewayIP == e.GatewayIP | ||
} | ||
|
||
// waitForBpfPolicyEntries waits for the egress gateway policy maps on each node | ||
// to be populated with the entries for the cegp-sample CiliumEgressGatewayExcludedCIDRsPolicy | ||
func (s *egressGatewayExcludedCIDRs) waitForBpfPolicyEntries(ctx context.Context, t *check.Test) { | ||
ct := t.Context() | ||
|
||
w := utils.NewWaitObserver(ctx, utils.WaitParameters{Timeout: 10 * time.Second}) | ||
defer w.Cancel() | ||
|
||
ensureBpfPolicyEntries := func() error { | ||
gatewayNodeInternalIP := getGatewayNodeInternalIP(ct, s.egressGatewayNode) | ||
if gatewayNodeInternalIP == nil { | ||
t.Fatalf("Cannot retrieve internal IP of gateway node") | ||
} | ||
|
||
for _, ciliumPod := range ct.CiliumPods() { | ||
for _, nodeWithoutCilium := range t.NodesWithoutCilium() { | ||
node, err := t.Context().K8sClient().GetNode(context.Background(), nodeWithoutCilium, metav1.GetOptions{}) | ||
if err != nil { | ||
t.Fatalf("Cannot retrieve external node") | ||
} | ||
|
||
egressIP := "0.0.0.0" | ||
if ciliumPod.Pod.Spec.NodeName == s.egressGatewayNode { | ||
egressIP = gatewayNodeInternalIP.String() | ||
} | ||
|
||
targetEntries := []bpfEgressGatewayExcludedCIDRsPolicyEntry{} | ||
for _, client := range ct.ClientPods() { | ||
targetEntries = append(targetEntries, | ||
bpfEgressGatewayExcludedCIDRsPolicyEntry{ | ||
SourceIP: client.Pod.Status.PodIP, | ||
DestCIDR: "0.0.0.0/0", | ||
EgressIP: egressIP, | ||
GatewayIP: gatewayNodeInternalIP.String(), | ||
}) | ||
|
||
targetEntries = append(targetEntries, | ||
bpfEgressGatewayExcludedCIDRsPolicyEntry{ | ||
SourceIP: client.Pod.Status.PodIP, | ||
DestCIDR: fmt.Sprintf("%s/32", node.Status.Addresses[0].Address), | ||
EgressIP: egressIP, | ||
GatewayIP: "Excluded CIDR", | ||
}) | ||
} | ||
|
||
cmd := strings.Split("cilium bpf egress list -o json", " ") | ||
stdout, err := ciliumPod.K8sClient.ExecInPod(ctx, ciliumPod.Pod.Namespace, ciliumPod.Pod.Name, defaults.AgentContainerName, cmd) | ||
if err != nil { | ||
t.Fatal("failed to run cilium bpf egress list command: %w", err) | ||
} | ||
|
||
entries := []bpfEgressGatewayExcludedCIDRsPolicyEntry{} | ||
json.Unmarshal(stdout.Bytes(), &entries) | ||
|
||
nextTargetEntry: | ||
for _, targetEntry := range targetEntries { | ||
for _, entry := range entries { | ||
if targetEntry.matches(entry) { | ||
continue nextTargetEntry | ||
} | ||
} | ||
|
||
return fmt.Errorf("Could not find egress gateway policy entry matching %+v", targetEntry) | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
for { | ||
if err := ensureBpfPolicyEntries(); err != nil { | ||
if err := w.Retry(err); err != nil { | ||
t.Fatal("Failed to ensure egress gateway policy map is properly populated:", err) | ||
} | ||
|
||
continue | ||
} | ||
|
||
return | ||
} | ||
} |
Oops, something went wrong.