Skip to content

Commit a017253

Browse files
authored
Merge pull request kubernetes#88196 from janosi/sctp-e2e
e2e test cases for basic SCTP testing
2 parents 6468bfa + 3ce43a1 commit a017253

File tree

4 files changed

+283
-2
lines changed

4 files changed

+283
-2
lines changed

test/e2e/network/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ go_library(
8282
"//test/e2e/framework/skipper:go_default_library",
8383
"//test/e2e/framework/ssh:go_default_library",
8484
"//test/e2e/network/scale:go_default_library",
85+
"//test/e2e/storage/utils:go_default_library",
8586
"//test/utils:go_default_library",
8687
"//test/utils/image:go_default_library",
8788
"//vendor/github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud:go_default_library",

test/e2e/network/network_policy.go

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ package network
1919
import (
2020
"context"
2121
"encoding/json"
22+
2223
v1 "k8s.io/api/core/v1"
2324
networkingv1 "k8s.io/api/networking/v1"
2425
apierrors "k8s.io/apimachinery/pkg/api/errors"
2526
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2627
"k8s.io/apimachinery/pkg/types"
2728
"k8s.io/apimachinery/pkg/util/intstr"
2829
"k8s.io/kubernetes/test/e2e/framework"
30+
e2enode "k8s.io/kubernetes/test/e2e/framework/node"
2931
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
3032
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
3133
imageutils "k8s.io/kubernetes/test/utils/image"
@@ -1739,9 +1741,48 @@ var _ = SIGDescribe("NetworkPolicy [LinuxOnly]", func() {
17391741
})
17401742
cleanupServerPodAndService(f, podA, serviceA)
17411743
})
1744+
ginkgo.It("should not allow access by TCP when a policy specifies only SCTP [Feature:NetworkPolicy] [Feature:SCTP]", func() {
1745+
ginkgo.By("getting the state of the sctp module on nodes")
1746+
nodes, err := e2enode.GetReadySchedulableNodes(f.ClientSet)
1747+
framework.ExpectNoError(err)
1748+
sctpLoadedAtStart := CheckSCTPModuleLoadedOnNodes(f, nodes)
17421749

1743-
})
1750+
ginkgo.By("Creating a network policy for the server which allows traffic only via SCTP on port 80.")
1751+
protocolSCTP := v1.ProtocolSCTP
1752+
policy := &networkingv1.NetworkPolicy{
1753+
ObjectMeta: metav1.ObjectMeta{
1754+
Name: "allow-only-sctp-ingress-on-port-80",
1755+
},
1756+
Spec: networkingv1.NetworkPolicySpec{
1757+
// Apply to server
1758+
PodSelector: metav1.LabelSelector{
1759+
MatchLabels: map[string]string{
1760+
"pod-name": podServerLabelSelector,
1761+
},
1762+
},
1763+
// Allow traffic only via SCTP on port 80 .
1764+
Ingress: []networkingv1.NetworkPolicyIngressRule{{
1765+
Ports: []networkingv1.NetworkPolicyPort{{
1766+
Port: &intstr.IntOrString{IntVal: 80},
1767+
Protocol: &protocolSCTP,
1768+
}},
1769+
}},
1770+
},
1771+
}
1772+
appliedPolicy, err := f.ClientSet.NetworkingV1().NetworkPolicies(f.Namespace.Name).Create(context.TODO(), policy, metav1.CreateOptions{})
1773+
framework.ExpectNoError(err)
1774+
defer cleanupNetworkPolicy(f, appliedPolicy)
1775+
1776+
ginkgo.By("Testing pods cannot connect on port 80 anymore when not using SCTP as protocol.")
1777+
testCannotConnect(f, f.Namespace, "client-a", service, 80)
17441778

1779+
ginkgo.By("validating sctp module is still not loaded")
1780+
sctpLoadedAtEnd := CheckSCTPModuleLoadedOnNodes(f, nodes)
1781+
if !sctpLoadedAtStart && sctpLoadedAtEnd {
1782+
framework.Failf("The state of the sctp module has changed due to the test case")
1783+
}
1784+
})
1785+
})
17451786
})
17461787

17471788
func testCanConnect(f *framework.Framework, ns *v1.Namespace, podName string, service *v1.Service, targetPort int) {

test/e2e/network/service.go

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ import (
5656
e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
5757
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
5858
e2essh "k8s.io/kubernetes/test/e2e/framework/ssh"
59+
"k8s.io/kubernetes/test/e2e/storage/utils"
5960
testutils "k8s.io/kubernetes/test/utils"
6061
imageutils "k8s.io/kubernetes/test/utils/image"
6162
gcecloud "k8s.io/legacy-cloud-providers/gce"
@@ -3863,3 +3864,211 @@ func getApiserverRestartCount(c clientset.Interface) (int32, error) {
38633864
}
38643865
return -1, fmt.Errorf("Failed to find kube-apiserver container in pod")
38653866
}
3867+
3868+
var _ = SIGDescribe("SCTP [Feature:SCTP] [LinuxOnly]", func() {
3869+
f := framework.NewDefaultFramework("sctp")
3870+
3871+
var cs clientset.Interface
3872+
3873+
ginkgo.BeforeEach(func() {
3874+
cs = f.ClientSet
3875+
})
3876+
3877+
ginkgo.It("should allow creating a basic SCTP service with pod and endpoints", func() {
3878+
serviceName := "sctp-endpoint-test"
3879+
ns := f.Namespace.Name
3880+
jig := e2eservice.NewTestJig(cs, ns, serviceName)
3881+
3882+
ginkgo.By("getting the state of the sctp module on nodes")
3883+
nodes, err := e2enode.GetReadySchedulableNodes(cs)
3884+
framework.ExpectNoError(err)
3885+
sctpLoadedAtStart := CheckSCTPModuleLoadedOnNodes(f, nodes)
3886+
3887+
ginkgo.By("creating service " + serviceName + " in namespace " + ns)
3888+
_, err = jig.CreateSCTPServiceWithPort(nil, 5060)
3889+
framework.ExpectNoError(err)
3890+
defer func() {
3891+
err := cs.CoreV1().Services(ns).Delete(context.TODO(), serviceName, metav1.DeleteOptions{})
3892+
framework.ExpectNoError(err, "failed to delete service: %s in namespace: %s", serviceName, ns)
3893+
}()
3894+
3895+
err = e2enetwork.WaitForService(f.ClientSet, ns, serviceName, true, 5*time.Second, e2eservice.TestTimeout)
3896+
framework.ExpectNoError(err, fmt.Sprintf("error while waiting for service:%s err: %v", serviceName, err))
3897+
3898+
ginkgo.By("validating endpoints do not exist yet")
3899+
err = validateEndpointsPorts(cs, ns, serviceName, portsByPodName{})
3900+
framework.ExpectNoError(err, "failed to validate endpoints for service %s in namespace: %s", serviceName, ns)
3901+
3902+
ginkgo.By("creating a pod for the service")
3903+
names := map[string]bool{}
3904+
3905+
name1 := "pod1"
3906+
3907+
createPodOrFail(cs, ns, name1, jig.Labels, []v1.ContainerPort{{ContainerPort: 5060, Protocol: v1.ProtocolSCTP}})
3908+
names[name1] = true
3909+
defer func() {
3910+
for name := range names {
3911+
err := cs.CoreV1().Pods(ns).Delete(context.TODO(), name, metav1.DeleteOptions{})
3912+
framework.ExpectNoError(err, "failed to delete pod: %s in namespace: %s", name, ns)
3913+
}
3914+
}()
3915+
3916+
ginkgo.By("validating endpoints exists")
3917+
err = validateEndpointsPorts(cs, ns, serviceName, portsByPodName{name1: {5060}})
3918+
framework.ExpectNoError(err, "failed to validate endpoints for service %s in namespace: %s", serviceName, ns)
3919+
3920+
ginkgo.By("deleting the pod")
3921+
e2epod.DeletePodOrFail(cs, ns, name1)
3922+
delete(names, name1)
3923+
ginkgo.By("validating endpoints do not exist anymore")
3924+
err = validateEndpointsPorts(cs, ns, serviceName, portsByPodName{})
3925+
framework.ExpectNoError(err, "failed to validate endpoints for service %s in namespace: %s", serviceName, ns)
3926+
3927+
ginkgo.By("validating sctp module is still not loaded")
3928+
sctpLoadedAtEnd := CheckSCTPModuleLoadedOnNodes(f, nodes)
3929+
if !sctpLoadedAtStart && sctpLoadedAtEnd {
3930+
framework.Failf("The state of the sctp module has changed due to the test case")
3931+
}
3932+
})
3933+
3934+
ginkgo.It("should create a Pod with SCTP HostPort", func() {
3935+
ginkgo.By("checking whether kubenet is used")
3936+
node, err := e2enode.GetRandomReadySchedulableNode(cs)
3937+
framework.ExpectNoError(err)
3938+
hostExec := utils.NewHostExec(f)
3939+
defer hostExec.Cleanup()
3940+
cmd := "ps -C kubelet -o cmd= | grep kubenet"
3941+
framework.Logf("Executing cmd %q on node %v", cmd, node.Name)
3942+
err = hostExec.IssueCommand(cmd, node)
3943+
if err != nil {
3944+
e2eskipper.Skipf("Interrogation of kubenet usage failed on node %s", node.Name)
3945+
}
3946+
framework.Logf("kubenet is in use")
3947+
3948+
ginkgo.By("getting the state of the sctp module on the selected node")
3949+
nodes := &v1.NodeList{}
3950+
nodes.Items = append(nodes.Items, *node)
3951+
sctpLoadedAtStart := CheckSCTPModuleLoadedOnNodes(f, nodes)
3952+
3953+
ginkgo.By("creating a pod with hostport on the selected node")
3954+
podName := "hostport"
3955+
3956+
podSpec := &v1.Pod{
3957+
ObjectMeta: metav1.ObjectMeta{
3958+
Name: podName,
3959+
Namespace: f.Namespace.Name,
3960+
Labels: map[string]string{"app": "hostport-pod"},
3961+
},
3962+
Spec: v1.PodSpec{
3963+
NodeName: node.Name,
3964+
Containers: []v1.Container{
3965+
{
3966+
Name: "hostport",
3967+
Image: imageutils.GetE2EImage(imageutils.Agnhost),
3968+
Args: []string{"pause"},
3969+
Ports: []v1.ContainerPort{
3970+
{
3971+
Protocol: v1.ProtocolSCTP,
3972+
ContainerPort: 5060,
3973+
HostPort: 5060,
3974+
},
3975+
},
3976+
ImagePullPolicy: "IfNotPresent",
3977+
},
3978+
},
3979+
},
3980+
}
3981+
3982+
ginkgo.By(fmt.Sprintf("Launching the pod on node %v", node.Name))
3983+
f.PodClient().CreateSync(podSpec)
3984+
defer func() {
3985+
err := cs.CoreV1().Pods(f.Namespace.Name).Delete(context.TODO(), podName, metav1.DeleteOptions{})
3986+
framework.ExpectNoError(err, "failed to delete pod: %s in namespace: %s", podName, f.Namespace.Name)
3987+
}()
3988+
3989+
ginkgo.By("dumping iptables rules on the node")
3990+
cmd = "sudo iptables-save"
3991+
framework.Logf("Executing cmd %q on node %v", cmd, node.Name)
3992+
result, err := hostExec.IssueCommandWithResult(cmd, node)
3993+
if err != nil {
3994+
framework.Failf("Interrogation of iptables rules failed on node %v", node.Name)
3995+
}
3996+
3997+
ginkgo.By("checking that iptables contains the necessary iptables rules")
3998+
found := false
3999+
for _, line := range strings.Split(result, "\n") {
4000+
if strings.Contains(line, "-p sctp") && strings.Contains(line, "--dport 5060") {
4001+
found = true
4002+
break
4003+
}
4004+
}
4005+
if !found {
4006+
framework.Failf("iptables rules are not set for a pod with sctp hostport")
4007+
}
4008+
ginkgo.By("validating sctp module is still not loaded")
4009+
sctpLoadedAtEnd := CheckSCTPModuleLoadedOnNodes(f, nodes)
4010+
if !sctpLoadedAtStart && sctpLoadedAtEnd {
4011+
framework.Failf("The state of the sctp module has changed due to the test case")
4012+
}
4013+
})
4014+
ginkgo.It("should create a ClusterIP Service with SCTP ports", func() {
4015+
ginkgo.By("checking that kube-proxy is in iptables mode")
4016+
if proxyMode, err := proxyMode(f); err != nil {
4017+
e2eskipper.Skipf("Couldn't detect KubeProxy mode - skip, %v", err)
4018+
} else if proxyMode != "iptables" {
4019+
e2eskipper.Skipf("The test doesn't work if kube-proxy is not in iptables mode")
4020+
}
4021+
4022+
serviceName := "sctp-clusterip"
4023+
ns := f.Namespace.Name
4024+
jig := e2eservice.NewTestJig(cs, ns, serviceName)
4025+
4026+
ginkgo.By("getting the state of the sctp module on nodes")
4027+
nodes, err := e2enode.GetReadySchedulableNodes(cs)
4028+
framework.ExpectNoError(err)
4029+
sctpLoadedAtStart := CheckSCTPModuleLoadedOnNodes(f, nodes)
4030+
4031+
ginkgo.By("creating service " + serviceName + " in namespace " + ns)
4032+
_, err = jig.CreateSCTPServiceWithPort(func(svc *v1.Service) {
4033+
svc.Spec.Type = v1.ServiceTypeClusterIP
4034+
svc.Spec.Ports = []v1.ServicePort{{Protocol: v1.ProtocolSCTP, Port: 5060}}
4035+
}, 5060)
4036+
framework.ExpectNoError(err)
4037+
defer func() {
4038+
err := cs.CoreV1().Services(ns).Delete(context.TODO(), serviceName, metav1.DeleteOptions{})
4039+
framework.ExpectNoError(err, "failed to delete service: %s in namespace: %s", serviceName, ns)
4040+
}()
4041+
4042+
err = e2enetwork.WaitForService(f.ClientSet, ns, serviceName, true, 5*time.Second, e2eservice.TestTimeout)
4043+
framework.ExpectNoError(err, fmt.Sprintf("error while waiting for service:%s err: %v", serviceName, err))
4044+
4045+
ginkgo.By("dumping iptables rules on a node")
4046+
hostExec := utils.NewHostExec(f)
4047+
defer hostExec.Cleanup()
4048+
node, err := e2enode.GetRandomReadySchedulableNode(cs)
4049+
framework.ExpectNoError(err)
4050+
cmd := "sudo iptables-save"
4051+
framework.Logf("Executing cmd %q on node %v", cmd, node.Name)
4052+
result, err := hostExec.IssueCommandWithResult(cmd, node)
4053+
if err != nil {
4054+
framework.Failf("Interrogation of iptables rules failed on node %v", node.Name)
4055+
}
4056+
4057+
ginkgo.By("checking that iptables contains the necessary iptables rules")
4058+
kubeService := false
4059+
for _, line := range strings.Split(result, "\n") {
4060+
if strings.Contains(line, "-A KUBE-SERVICES") && strings.Contains(line, "-p sctp") {
4061+
kubeService = true
4062+
break
4063+
}
4064+
}
4065+
if !kubeService {
4066+
framework.Failf("iptables rules are not set for a clusterip service with sctp ports")
4067+
}
4068+
ginkgo.By("validating sctp module is still not loaded")
4069+
sctpLoadedAtEnd := CheckSCTPModuleLoadedOnNodes(f, nodes)
4070+
if !sctpLoadedAtStart && sctpLoadedAtEnd {
4071+
framework.Failf("The state of the sctp module has changed due to the test case")
4072+
}
4073+
})
4074+
})

test/e2e/network/util.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@ package network
1919
import (
2020
"bytes"
2121
"fmt"
22+
"regexp"
23+
"strings"
2224
"time"
2325

24-
"k8s.io/api/core/v1"
26+
v1 "k8s.io/api/core/v1"
2527
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2628
"k8s.io/apimachinery/pkg/util/wait"
2729
"k8s.io/kubernetes/test/e2e/framework"
2830
e2enetwork "k8s.io/kubernetes/test/e2e/framework/network"
31+
"k8s.io/kubernetes/test/e2e/storage/utils"
2932
imageutils "k8s.io/kubernetes/test/utils/image"
3033
)
3134

@@ -76,3 +79,30 @@ func newAgnhostPod(name string, args ...string) *v1.Pod {
7679
},
7780
}
7881
}
82+
83+
// CheckSCTPModuleLoadedOnNodes checks whether any node on the list has the
84+
// sctp.ko module loaded
85+
// For security reasons, and also to allow clusters to use userspace SCTP implementations,
86+
// we require that just creating an SCTP Pod/Service/NetworkPolicy must not do anything
87+
// that would cause the sctp kernel module to be loaded.
88+
func CheckSCTPModuleLoadedOnNodes(f *framework.Framework, nodes *v1.NodeList) bool {
89+
hostExec := utils.NewHostExec(f)
90+
defer hostExec.Cleanup()
91+
re := regexp.MustCompile(`^\s*sctp\s+`)
92+
cmd := "lsmod | grep sctp"
93+
for _, node := range nodes.Items {
94+
framework.Logf("Executing cmd %q on node %v", cmd, node.Name)
95+
result, err := hostExec.IssueCommandWithResult(cmd, &node)
96+
if err != nil {
97+
framework.Logf("sctp module is not loaded or error occurred while executing command %s on node: %v", cmd, err)
98+
}
99+
for _, line := range strings.Split(result, "\n") {
100+
if found := re.Find([]byte(line)); found != nil {
101+
framework.Logf("the sctp module is loaded on node: %v", node.Name)
102+
return true
103+
}
104+
}
105+
framework.Logf("the sctp module is not loaded on node: %v", node.Name)
106+
}
107+
return false
108+
}

0 commit comments

Comments
 (0)