Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

connectivity: add egress-gateway-with-l7-policy test #2578

Merged
merged 2 commits into from
Jun 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
/connectivity/builder/echo_ingress_mutual_auth_spiffe.go @cilium/sig-servicemesh
/connectivity/builder/egress_gateway.go @cilium/egress-gateway
/connectivity/builder/egress_gateway_excluded_cidrs.go @cilium/egress-gateway
/connectivity/builder/egress_gateway_with_l7_policy.go @cilium/egress-gateway
/connectivity/builder/no_ipsec_xfrm_errors.go @cilium/sig-encryption
/connectivity/builder/node_to_node_encryption.go @cilium/sig-encryption
/connectivity/builder/pod_to_pod_encryption.go @cilium/sig-encryption
Expand Down
1 change: 1 addition & 0 deletions connectivity/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ func concurrentTests(connTests []*check.ConnectivityTest) error {
nodeToNodeEncryption{},
egressGateway{},
egressGatewayExcludedCidrs{},
egressGatewayWithL7Policy{},
podToNodeCidrpolicy{},
northSouthLoadbalancingWithL7Policy{},
echoIngressL7{},
Expand Down
47 changes: 47 additions & 0 deletions connectivity/builder/egress_gateway_with_l7_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium

package builder

import (
_ "embed"

"github.com/cilium/cilium/pkg/versioncheck"

"github.com/cilium/cilium-cli/connectivity/check"
"github.com/cilium/cilium-cli/connectivity/tests"
"github.com/cilium/cilium-cli/utils/features"
)

//go:embed manifests/client-egress-icmp.yaml
var clientEgressICMPYAML string

//go:embed manifests/client-egress-l7-http-external-node.yaml
var clientEgressL7HTTPExternalYAML string

type egressGatewayWithL7Policy struct{}

func (t egressGatewayWithL7Policy) build(ct *check.ConnectivityTest, _ map[string]string) {
newTest("egress-gateway-with-l7-policy", ct).
WithCondition(func() bool {
return versioncheck.MustCompile(">=1.16.0")(ct.CiliumVersion) && ct.Params().IncludeUnsafeTests
}).
WithCiliumPolicy(clientEgressICMPYAML).
jschwinger233 marked this conversation as resolved.
Show resolved Hide resolved
WithCiliumPolicy(clientEgressOnlyDNSPolicyYAML). // DNS resolution only
WithCiliumPolicy(clientEgressL7HTTPExternalYAML). // L7 allow policy with HTTP introspection
WithCiliumEgressGatewayPolicy(check.CiliumEgressGatewayPolicyParams{
Name: "cegp-sample-client",
PodSelectorKind: "client",
}).
WithCiliumEgressGatewayPolicy(check.CiliumEgressGatewayPolicyParams{
Name: "cegp-sample-echo",
PodSelectorKind: "echo",
}).
WithIPRoutesFromOutsideToPodCIDRs().
WithFeatureRequirements(
features.RequireEnabled(features.EgressGateway),
features.RequireEnabled(features.L7Proxy),
features.RequireEnabled(features.NodeWithoutCilium),
).
WithScenarios(tests.EgressGateway())
}
17 changes: 17 additions & 0 deletions connectivity/builder/manifests/client-egress-icmp.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
apiVersion: "cilium.io/v2"
kind: CiliumNetworkPolicy
metadata:
name: client-egress-icmp
spec:
description: "Allow clients to send ICMP"
endpointSelector:
matchLabels:
kind: client
egress:
- icmps:
- fields:
- type: 8
family: IPv4
- type: 128
family: IPv6
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
# All clients are allowed to contact
# echo-external-node.cilium-test.svc.cluster.local/client-ip
# on port http-8080.
# The toFQDNs section relies on DNS introspection being performed by
# the client-egress-only-dns policy.
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: client-egress-l7-http-external-node
spec:
description: "Allow GET echo-external-node.cilium-test.svc.cluster.local:8080/client-ip"
endpointSelector:
matchLabels:
any:kind: client
egress:
- toFQDNs:
- matchName: "echo-external-node.cilium-test.svc.cluster.local"
toPorts:
- ports:
- port: "8080"
protocol: TCP
rules:
http:
- method: GET
path: /client-ip
30 changes: 18 additions & 12 deletions connectivity/check/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,18 +58,19 @@ type ConnectivityTest struct {
// Clients for source and destination clusters.
clients *deploymentClients

ciliumPods map[string]Pod
echoPods map[string]Pod
echoExternalPods map[string]Pod
clientPods map[string]Pod
clientCPPods map[string]Pod
perfClientPods []Pod
perfServerPod []Pod
PerfResults []common.PerfSummary
echoServices map[string]Service
ingressService map[string]Service
k8sService Service
externalWorkloads map[string]ExternalWorkload
ciliumPods map[string]Pod
echoPods map[string]Pod
echoExternalPods map[string]Pod
clientPods map[string]Pod
clientCPPods map[string]Pod
perfClientPods []Pod
perfServerPod []Pod
PerfResults []common.PerfSummary
echoServices map[string]Service
echoExternalServices map[string]Service
ingressService map[string]Service
k8sService Service
externalWorkloads map[string]ExternalWorkload

hostNetNSPodsByNode map[string]Pod
secondaryNetworkNodeIPv4 map[string]string // node name => secondary ip
Expand Down Expand Up @@ -207,6 +208,7 @@ func NewConnectivityTest(client *k8s.Client, p Parameters, version string, logge
perfServerPod: []Pod{},
PerfResults: []common.PerfSummary{},
echoServices: make(map[string]Service),
echoExternalServices: make(map[string]Service),
ingressService: make(map[string]Service),
externalWorkloads: make(map[string]ExternalWorkload),
hostNetNSPodsByNode: make(map[string]Pod),
Expand Down Expand Up @@ -1131,6 +1133,10 @@ func (ct *ConnectivityTest) EchoServicesAll() map[string]Service {
return ct.echoServices
}

func (ct *ConnectivityTest) EchoExternalServices() map[string]Service {
return ct.echoExternalServices
}

func (ct *ConnectivityTest) ExternalEchoPods() map[string]Pod {
return ct.echoExternalPods
}
Expand Down
21 changes: 21 additions & 0 deletions connectivity/check/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,16 @@ func (ct *ConnectivityTest) deploy(ctx context.Context) error {
if err != nil {
return fmt.Errorf("unable to create deployment %s: %s", echoExternalNodeDeploymentName, err)
}

svc := newService(echoExternalNodeDeploymentName,
map[string]string{"name": echoExternalNodeDeploymentName, "kind": kindEchoExternalNodeName},
map[string]string{"kind": kindEchoExternalNodeName}, "http", 8080)
svc.Spec.ClusterIP = corev1.ClusterIPNone
svc.Spec.Type = corev1.ServiceTypeClusterIP
_, err := ct.clients.src.CreateService(ctx, ct.params.TestNamespace, svc, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("unable to create service %s: %w", echoExternalNodeDeploymentName, err)
}
}
} else {
ct.Infof("Skipping tests that require a node Without Cilium")
Expand Down Expand Up @@ -1243,6 +1253,17 @@ func (ct *ConnectivityTest) validateDeployment(ctx context.Context) error {
port: uint32(ct.Params().ExternalDeploymentPort), // listen port of the echo server inside the container
}
}

echoExternalServices, err := ct.clients.dst.ListServices(ctx, ct.params.TestNamespace, metav1.ListOptions{LabelSelector: "kind=" + kindEchoExternalNodeName})
if err != nil {
return fmt.Errorf("unable to list echo external services: %w", err)
}

for _, echoExternalService := range echoExternalServices.Items {
ct.echoExternalServices[echoExternalService.Name] = Service{
Service: echoExternalService.DeepCopy(),
}
}
}

for _, cp := range ct.clientPods {
Expand Down
16 changes: 15 additions & 1 deletion connectivity/check/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ func (s Service) Path() string {
// Address returns the network address of the Service.
func (s Service) Address(family features.IPFamily) string {
// If the cluster IP is empty (headless service case) or the IP family is set to any, return the service name
if s.Service.Spec.ClusterIP == "" || family == features.IPFamilyAny {
if s.Service.Spec.ClusterIP == "" || s.Service.Spec.ClusterIP == corev1.ClusterIPNone || family == features.IPFamilyAny {
return fmt.Sprintf("%s.%s", s.Service.Name, s.Service.Namespace)
}

Expand Down Expand Up @@ -256,6 +256,12 @@ func (s Service) ToNodeportService(node *corev1.Node) NodeportService {
}
}

func (s Service) ToEchoIPService() EchoIPService {
return EchoIPService{
Service: s,
}
}

// NodeportService wraps a Service and exposes it through its nodeport, acting as a peer in a connectivity test.
// It implements interface TestPeer.
type NodeportService struct {
Expand Down Expand Up @@ -499,3 +505,11 @@ type EchoIPPod struct {
func (p EchoIPPod) Path() string {
return p.path + "/client-ip"
}

type EchoIPService struct {
Service
}

func (s EchoIPService) Path() string {
return s.URLPath + "/client-ip"
}
20 changes: 20 additions & 0 deletions connectivity/tests/egressgateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,26 @@ func (s *egressGateway) Run(ctx context.Context, t *check.Test) {
i++
}

// Traffic matching an egress gateway policy should leave the cluster masqueraded with the egress IP (pod to external service using DNS)
i = 0
for _, client := range ct.ClientPods() {
client := client

for _, externalEchoSvc := range ct.EchoExternalServices() {
externalEcho := externalEchoSvc.ToEchoIPService()

t.NewAction(s, fmt.Sprintf("curl-external-echo-service-%d", i), &client, externalEcho, features.IPFamilyV4).Run(func(a *check.Action) {
a.ExecInPod(ctx, ct.CurlCommandWithOutput(externalEcho, features.IPFamilyV4, "-4"))
clientIP := extractClientIPFromResponse(a.CmdOutput())

if !clientIP.Equal(egressGatewayNodeInternalIP) {
t.Fatal("Request reached external echo service with wrong source IP")
}
})
i++
}
}

// 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() {
Expand Down
Loading