Skip to content

Commit

Permalink
connectivity: fix node-to-node encryption tests
Browse files Browse the repository at this point in the history
When tunneling is enabled, and the feature is correctly picked up the
the cilium-cli, the encryption test always listens on the tunneling
interface for leaked packets. This is wrong as e.g. node-to-node
communication is not encapsulated and therefore does not pass the
cilium_vxlan interface.

Signed-off-by: Leonard Cohnen <[email protected]>
  • Loading branch information
3u13r authored and michi-covalent committed Aug 21, 2023
1 parent 635ba9e commit f512df4
Showing 1 changed file with 52 additions and 16 deletions.
68 changes: 52 additions & 16 deletions connectivity/tests/encryption.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"sync"
"time"

"github.com/cilium/cilium/pkg/defaults"

"github.com/cilium/cilium-cli/connectivity/check"
)

Expand All @@ -23,26 +25,59 @@ const (
requestICMPEcho
)

// getInterNodeIface determines on which netdev iface to capture pkts. In the
// case of tunneling, we don't expect to see unencrypted pkts on a corresponding
// tunneling iface, so the choice is obvious. In the native routing mode, we run
// "ip route get $DST_IP" from the client pod's node.
// getInterNodeIface determines on which netdev iface to capture pkts.
// We run "ip route get $DST_IP" from the client pod's node to see to
// which interface the traffic is routed to. Additionally, we translate
// the interface name to the tunneling interface name, if the route goes
// through "cilium_host" and tunneling is enabled.
func getInterNodeIface(ctx context.Context, t *check.Test, clientHost *check.Pod, dstIP string) string {
tunnelFeat, ok := t.Context().Feature(check.FeatureTunnel)
if ok && tunnelFeat.Enabled {
cmd := []string{
"/bin/sh", "-c",
fmt.Sprintf("ip -o route get %s | grep -oE 'dev [^ ]*' | cut -d' ' -f2",
dstIP),
}
t.Debugf("Running %s", strings.Join(cmd, " "))
dev, err := clientHost.K8sClient.ExecInPod(ctx, clientHost.Pod.Namespace,
clientHost.Pod.Name, "", cmd)
if err != nil {
t.Fatalf("Failed to get IP route: %s", err)
}

device := strings.TrimRight(dev.String(), "\n\r")

// When tunneling is enabled, and the traffic is routed to the cilium IP space
// we want to capture on the tunnel interface.
if tunnelFeat, ok := t.Context().Feature(check.FeatureTunnel); ok &&
tunnelFeat.Enabled && device == defaults.HostDevice {
return "cilium_" + tunnelFeat.Mode // E.g. cilium_vxlan
}

cmd := []string{"/bin/sh", "-c",
fmt.Sprintf("ip -o r g %s | grep -oE 'dev [^ ]*' | cut -d' ' -f2",
dstIP)}
return device
}

// getSourceAddress determines the source IP address we want to use for
// capturing packet. If direct routing is used, the source IP is the client IP.
func getSourceAddress(ctx context.Context, t *check.Test, client,
clientHost *check.Pod, ipFam check.IPFamily, dstIP string,
) string {
if tunnelStatus, ok := t.Context().Feature(check.FeatureTunnel); ok &&
!tunnelStatus.Enabled {
return client.Address(ipFam)
}

cmd := []string{
"/bin/sh", "-c",
fmt.Sprintf("ip -o route get %s | grep -oE 'src [^ ]*' | cut -d' ' -f2",
dstIP),
}
t.Debugf("Running %s", strings.Join(cmd, " "))
dev, err := clientHost.K8sClient.ExecInPod(ctx, clientHost.Pod.Namespace,
srcIP, err := clientHost.K8sClient.ExecInPod(ctx, clientHost.Pod.Namespace,
clientHost.Pod.Name, "", cmd)
if err != nil {
t.Fatalf("Failed to get IP route: %s", err)
}
return strings.TrimRight(dev.String(), "\n\r")

return strings.TrimRight(srcIP.String(), "\n\r")
}

// PodToPodEncryption is a test case which checks the following:
Expand Down Expand Up @@ -85,11 +120,11 @@ func (s *podToPodEncryption) Run(ctx context.Context, t *check.Test) {
}

func testNoTrafficLeak(ctx context.Context, t *check.Test, s check.Scenario,
client, server, clientHost *check.Pod, reqType requestType, ipFam check.IPFamily) {

client, server, clientHost *check.Pod, reqType requestType, ipFam check.IPFamily,
) {
dstAddr := server.Address(ipFam)
iface := getInterNodeIface(ctx, t, clientHost, dstAddr)
t.Debugf("Detected %s iface for communication among client and server nodes", iface)
srcAddr := getSourceAddress(ctx, t, client, clientHost, ipFam, dstAddr)

bgStdout := &safeBuffer{}
bgStderr := &safeBuffer{}
Expand Down Expand Up @@ -117,10 +152,11 @@ func testNoTrafficLeak(ctx context.Context, t *check.Test, s check.Scenario,
// Unfortunately, we cannot use "host %s and host %s" filter here,
// as IPsec recirculates replies to the iface netdev, which would
// make tcpdump to capture the pkts (false positive).
fmt.Sprintf("src host %s and dst host %s and %s", client.Address(ipFam), dstAddr, protoFilter),
fmt.Sprintf("src host %s and dst host %s and %s", srcAddr, dstAddr, protoFilter),
// Only one pkt is enough, as we don't expect any unencrypted pkt
// to be captured
"-c", "1"}
"-c", "1",
}
t.Debugf("Running in bg: %s", strings.Join(cmd, " "))
err := clientHost.K8sClient.ExecInPodWithWriters(ctx, killCmdCtx,
clientHost.Pod.Namespace, clientHost.Pod.Name, "", cmd, bgStdout, bgStderr)
Expand Down

0 comments on commit f512df4

Please sign in to comment.