mirror of
https://github.com/kubernetes/kubernetes.git
synced 2026-05-28 04:04:39 -04:00
Merge pull request #137110 from ShaanveerS/more-netpol
netpol: specify explicit per-test pod topologies
This commit is contained in:
commit
099d172c2b
2 changed files with 242 additions and 147 deletions
|
|
@ -22,6 +22,7 @@ import (
|
|||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
imageutils "k8s.io/kubernetes/test/utils/image"
|
||||
)
|
||||
|
|
@ -44,17 +45,28 @@ type Model struct {
|
|||
// The number of containers per pod is the number of ports x the number of protocols.
|
||||
// The *total* number of containers is namespaces x pods x ports x protocols.
|
||||
func NewModel(namespaceNames []string, podNames []string, ports []int32, protocols []v1.Protocol) *Model {
|
||||
podNamesByNamespace := make(map[string]sets.Set[string], len(namespaceNames))
|
||||
for _, ns := range namespaceNames {
|
||||
podNamesByNamespace[ns] = sets.New(podNames...)
|
||||
}
|
||||
return newModelWithPerNamespacePodNames(namespaceNames, podNamesByNamespace, ports, protocols)
|
||||
}
|
||||
|
||||
// newModelWithPerNamespacePodNames instantiates a model where each namespace can define a different pod set.
|
||||
func newModelWithPerNamespacePodNames(namespaceNames []string, podNamesByNamespace map[string]sets.Set[string], ports []int32, protocols []v1.Protocol) *Model {
|
||||
model := &Model{
|
||||
PodNames: podNames,
|
||||
Ports: ports,
|
||||
Protocols: protocols,
|
||||
}
|
||||
allPodNames := sets.New[string]()
|
||||
|
||||
// build the entire "model" for the overall test, which means, building
|
||||
// namespaces, pods, containers for each protocol.
|
||||
for _, ns := range namespaceNames {
|
||||
var pods []*Pod
|
||||
podNames := sets.List(podNamesByNamespace[ns])
|
||||
for _, podName := range podNames {
|
||||
allPodNames.Insert(podName)
|
||||
pods = append(pods, &Pod{
|
||||
Name: podName,
|
||||
Ports: ports,
|
||||
|
|
@ -66,6 +78,7 @@ func NewModel(namespaceNames []string, podNames []string, ports []int32, protoco
|
|||
Pods: pods,
|
||||
})
|
||||
}
|
||||
model.PodNames = sets.List(allPodNames)
|
||||
return model
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
|
|
@ -30,6 +31,7 @@ import (
|
|||
"github.com/onsi/ginkgo/v2"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/kubernetes/test/e2e/feature"
|
||||
"k8s.io/kubernetes/test/e2e/framework"
|
||||
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
|
||||
|
|
@ -62,52 +64,9 @@ You might be wondering, why are there multiple namespaces used for each test cas
|
|||
|
||||
These tests are based on "truth tables" that compare the expected and actual connectivity of each pair of pods.
|
||||
Since network policies live in namespaces, and peers can be selected by namespace,
|
||||
howing the connectivity of pods in other namespaces is key information to show whether a network policy is working as intended or not.
|
||||
showing the connectivity of pods in other namespaces is key information to show whether a network policy is working as intended or not.
|
||||
|
||||
We use 3 namespaces each with 3 pods, and probe all combinations ( 9 pods x 9 pods = 81 data points ) -- including cross-namespace calls.
|
||||
|
||||
Here's an example of a test run, showing the expected and actual connectivity, along with the differences. Note how the
|
||||
visual representation as a truth table greatly aids in understanding what a network policy is intended to do in theory
|
||||
and what is happening in practice:
|
||||
|
||||
Oct 19 10:34:16.907: INFO: expected:
|
||||
|
||||
- x/a x/b x/c y/a y/b y/c z/a z/b z/c
|
||||
x/a X . . . . . . . .
|
||||
x/b X . . . . . . . .
|
||||
x/c X . . . . . . . .
|
||||
y/a . . . . . . . . .
|
||||
y/b . . . . . . . . .
|
||||
y/c . . . . . . . . .
|
||||
z/a X . . . . . . . .
|
||||
z/b X . . . . . . . .
|
||||
z/c X . . . . . . . .
|
||||
|
||||
Oct 19 10:34:16.907: INFO: observed:
|
||||
|
||||
- x/a x/b x/c y/a y/b y/c z/a z/b z/c
|
||||
x/a X . . . . . . . .
|
||||
x/b X . . . . . . . .
|
||||
x/c X . . . . . . . .
|
||||
y/a . . . . . . . . .
|
||||
y/b . . . . . . . . .
|
||||
y/c . . . . . . . . .
|
||||
z/a X . . . . . . . .
|
||||
z/b X . . . . . . . .
|
||||
z/c X . . . . . . . .
|
||||
|
||||
Oct 19 10:34:16.907: INFO: comparison:
|
||||
|
||||
- x/a x/b x/c y/a y/b y/c z/a z/b z/c
|
||||
x/a . . . . . . . . .
|
||||
x/b . . . . . . . . .
|
||||
x/c . . . . . . . . .
|
||||
y/a . . . . . . . . .
|
||||
y/b . . . . . . . . .
|
||||
y/c . . . . . . . . .
|
||||
z/a . . . . . . . . .
|
||||
z/b . . . . . . . . .
|
||||
z/c . . . . . . . . .
|
||||
Each test specifies the exact pod topology it needs (for example: x/a, x/b, y/a, z/a).
|
||||
*/
|
||||
|
||||
var _ = common.SIGDescribe("Netpol", func() {
|
||||
|
|
@ -126,8 +85,11 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
// Only testing port 80
|
||||
ports := []int32{80}
|
||||
|
||||
// Create pods and namespaces for this test
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Namespace X has a default-deny-ingress policy, so we need 2 pods in X to
|
||||
// verify X-to-X ingress is blocked, and 2 pods in Y to verify Y-to-X is
|
||||
// also blocked while X-to-Y and Y-to-Y traffic remain allowed.
|
||||
// (In later tests, we just assume that a policy in X will not affect Y-to-Y traffic.)
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "y/b")
|
||||
|
||||
// Only going to make a policy in namespace X
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
|
@ -148,7 +110,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should support a 'default-deny-all' policy", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Namespace X has a default-deny for both ingress and egress, so we need 2
|
||||
// pods in X to verify X-to-X traffic is blocked, and a pod in Y to verify
|
||||
// X<->Y traffic is blocked in both directions.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
policy := GenNetworkPolicyWithNameAndPodSelector("deny-all", metav1.LabelSelector{}, SetSpecIngressRules(), SetSpecEgressRules())
|
||||
|
|
@ -164,7 +129,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy to allow traffic from pods within server namespace based on PodSelector", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Policy isolates x/a and only allows ingress from x/b, so we need x/b as the
|
||||
// allowed same-namespace peer, x/c as a same-namespace non-matching pod, and
|
||||
// y/a as a cross-namespace peer that must not be able to reach x/a.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "x/c", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedPods := metav1.LabelSelector{
|
||||
|
|
@ -187,7 +155,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy to allow ingress traffic for a target", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Namespace X is default-deny-ingress, but we then allow ingress to x/a.
|
||||
// We need both x/a and x/b to show the target-specific behavior, and y/a to
|
||||
// exercise cross-namespace ingress.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
ginkgo.By("having a deny all ingress policy")
|
||||
|
|
@ -204,7 +175,6 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
reachability := NewReachability(k8s.AllPodStrings(), true)
|
||||
reachability.ExpectAllIngress(NewPodString(nsX, "a"), true)
|
||||
reachability.ExpectAllIngress(NewPodString(nsX, "b"), false)
|
||||
reachability.ExpectAllIngress(NewPodString(nsX, "c"), false)
|
||||
|
||||
ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability})
|
||||
})
|
||||
|
|
@ -212,7 +182,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy to allow ingress traffic from pods in all namespaces", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Policy on x/a should allow ingress from any namespace, including its own.
|
||||
// We include x/b as a same-namespace client, plus one pod in Y and Z as
|
||||
// cross-namespace clients.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "z/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
ingressRule := networkingv1.NetworkPolicyIngressRule{}
|
||||
|
|
@ -227,7 +200,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy to allow traffic only from a different namespace, based on NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Policy on x/a allows ingress only from namespace Y. We need x/b to show
|
||||
// same-namespace ingress is denied, y/a as the allowed source, and z/a as an
|
||||
// unrelated namespace that must be denied.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "z/a")
|
||||
nsX, nsY, nsZ := getK8sNamespaces(k8s)
|
||||
|
||||
ingressRule := networkingv1.NetworkPolicyIngressRule{}
|
||||
|
|
@ -246,7 +222,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy based on PodSelector with MatchExpressions", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Same as the basic PodSelector test, but using MatchExpressions: x/b must be
|
||||
// able to reach x/a, while y/a (other namespace) must not be able to reach x/a.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedPods := metav1.LabelSelector{
|
||||
|
|
@ -271,7 +249,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy based on NamespaceSelector with MatchExpressions", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Same as the basic NamespaceSelector test, but using MatchExpressions: y/a is
|
||||
// the allowed source namespace, while x/b (same namespace) and z/a must be
|
||||
// denied when connecting to x/a.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "z/a")
|
||||
nsX, nsY, nsZ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedNamespaces := &metav1.LabelSelector{
|
||||
|
|
@ -297,7 +278,11 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy based on PodSelector or NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Policy on x/a allows ingress from either (a) any namespace other than X, or
|
||||
// (b) pod x/b. We include x/b as the PodSelector-allowed source, x/c as a
|
||||
// non-matching pod in X that must be denied, and y/a as the NamespaceSelector-
|
||||
// allowed source.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "x/c", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedNamespaces := &metav1.LabelSelector{
|
||||
|
|
@ -327,7 +312,11 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy based on PodSelector and NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Policy on x/a requires BOTH a namespace match and a pod match. We use y/b
|
||||
// and z/b as the sources that match both selectors, x/b as the negative case
|
||||
// (pod matches but namespace does not), and y/a as the negative case (namespace
|
||||
// matches but pod does not).
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "y/b", "z/b")
|
||||
nsX, nsY, nsZ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedNamespaces := &metav1.LabelSelector{
|
||||
|
|
@ -358,8 +347,11 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy based on Multiple PodSelectors and NamespaceSelectors", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
nsX, nsY, nsZ := getK8sNamespaces(k8s)
|
||||
// Policy on x/a allows ingress only from pods {b,c} in namespaces other than X.
|
||||
// We need x/b to prove same-namespace traffic is denied, y/b and y/c as allowed
|
||||
// sources, and y/a as a non-matching pod in an allowed namespace.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "y/b", "y/c")
|
||||
nsX, nsY, _ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedNamespaces := &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{{
|
||||
|
|
@ -383,7 +375,6 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
reachability := NewReachability(k8s.AllPodStrings(), true)
|
||||
reachability.ExpectPeer(&Peer{Namespace: nsX}, &Peer{Namespace: nsX, Pod: "a"}, false)
|
||||
reachability.Expect(NewPodString(nsY, "a"), NewPodString(nsX, "a"), false)
|
||||
reachability.Expect(NewPodString(nsZ, "a"), NewPodString(nsX, "a"), false)
|
||||
|
||||
ValidateOrFail(k8s, &TestCase{ToPort: 80, Protocol: v1.ProtocolTCP, Reachability: reachability})
|
||||
})
|
||||
|
|
@ -391,7 +382,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy based on any PodSelectors", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Policy on x/a uses multiple "from" entries (pod=b OR pod=c). We need both
|
||||
// x/b and x/c as allowed sources and x/a as the isolated target pod.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "x/c")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
ingressRule := networkingv1.NetworkPolicyIngressRule{}
|
||||
|
|
@ -414,7 +407,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy to allow traffic only from a pod in a different namespace based on PodSelector and NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Policy on x/a allows ingress only from y/a (namespace+pod selectors). We keep
|
||||
// x/b as a same-namespace/non-matching pod, plus y/b and z/a as non-matching
|
||||
// cross-namespace pods, to ensure they cannot reach x/a.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "y/b", "z/a")
|
||||
nsX, nsY, _ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedNamespaces := &metav1.LabelSelector{
|
||||
|
|
@ -442,7 +438,11 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy based on Ports", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// This test is port-specific: namespace X should allow ingress to x/a on
|
||||
// port 81 from namespace Y only. We include x/b as a same-namespace source
|
||||
// (denied), y/a,y/b as namespace-Y sources (allowed), and z/a as a denied
|
||||
// third-namespace source.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "y/b", "z/a")
|
||||
nsX, nsY, nsZ := getK8sNamespaces(k8s)
|
||||
|
||||
ginkgo.By("Creating a network allowPort81Policy which only allows allow listed namespaces (y) to connect on exactly one port (81)")
|
||||
|
|
@ -468,7 +468,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce multiple, stacked policies with overlapping podSelectors", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80, 81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// We stack multiple policies to verify per-port behavior and policy precedence.
|
||||
// We need x/a as the target, x/b as a same-namespace source, y/a,y/b as the
|
||||
// allowed namespace, and z/a as a namespace that must remain denied.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "y/b", "z/a")
|
||||
nsX, nsY, nsZ := getK8sNamespaces(k8s)
|
||||
|
||||
ginkgo.By("Creating a network allowPort81Policy which only allows allow listed namespaces (y) to connect on exactly one port (81)")
|
||||
|
|
@ -510,7 +513,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should support allow-all policy", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80, 81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Allow-all should not restrict anything. We include two pods in X and one in Y
|
||||
// so we cover both intra-namespace (x/a<->x/b) and cross-namespace traffic.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
ginkgo.By("Creating a network policy which allows all traffic.")
|
||||
|
|
@ -526,7 +531,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should allow ingress access on one named port", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80, 81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Policy applies to all pods in X and only allows the named port serve-81-tcp.
|
||||
// We need two pods in X to validate X-to-X traffic is blocked on port 80, and a
|
||||
// pod in Y to validate cross-namespace ingress is also restricted as expected.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
ginkgo.By("Blocking all ports other then 81 in the entire namespace")
|
||||
|
|
@ -547,7 +555,11 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should allow ingress access from namespace on one named port", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80, 81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Only namespace Y should be able to reach x/a on the named port serve-80-tcp,
|
||||
// and port 81 should be blocked for everyone. We include x/b as a
|
||||
// same-namespace peer (denied), y/a as the allowed namespace, and z/a as a
|
||||
// denied namespace.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "z/a")
|
||||
nsX, nsY, nsZ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedLabels := &metav1.LabelSelector{
|
||||
|
|
@ -578,7 +590,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should allow egress access on one named port", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80, 81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Policy restricts egress from x/a by named port: port 80 allowed, port 81
|
||||
// denied. One pod in X and one pod in Y is sufficient to validate egress
|
||||
// behavior.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
egressRule := networkingv1.NetworkPolicyEgressRule{}
|
||||
|
|
@ -598,7 +613,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce updated policy", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// This test mutates a policy in namespace X and validates connectivity changes
|
||||
// from allowed -> denied. One pod in X and one in Y is enough to observe the
|
||||
// before/after behavior.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
ginkgo.By("Using the simplest possible mutation: start with allow all, then switch to deny all")
|
||||
|
|
@ -621,7 +639,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should allow ingress access from updated namespace", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Namespace label changes are what we are testing here. We need x/a as the
|
||||
// target and y/a as the external client whose namespace labels are updated.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a")
|
||||
nsX, nsY, _ := getK8sNamespaces(k8s)
|
||||
ginkgo.DeferCleanup(DeleteNamespaceLabel, k8s, nsY, "ns2")
|
||||
|
||||
|
|
@ -652,7 +672,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should allow ingress access from updated pod", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Pod label changes are what we are testing here. We need x/a as the target and
|
||||
// x/b as the client whose labels are updated to match the PodSelector.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
ginkgo.DeferCleanup(ResetPodLabels, k8s, nsX, "b")
|
||||
|
||||
|
|
@ -681,7 +703,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should deny ingress from pods on other namespaces", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Ingress policy in X should only allow same-namespace pods. We need two pods
|
||||
// in X to verify X-to-X is allowed, plus one pod in each of Y and Z to verify
|
||||
// that cross-namespace ingress into X is denied.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "z/a")
|
||||
nsX, nsY, nsZ := getK8sNamespaces(k8s)
|
||||
|
||||
IngressRules := networkingv1.NetworkPolicyIngressRule{}
|
||||
|
|
@ -699,7 +724,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should deny ingress access to updated pod", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// This test verifies that x/a becomes isolated when its labels are updated to
|
||||
// match a deny-ingress policy. We need x/a as the target and y/a as an external
|
||||
// source.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
ginkgo.DeferCleanup(ResetPodLabels, k8s, nsX, "a")
|
||||
|
||||
|
|
@ -721,7 +749,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should deny egress from pods based on PodSelector", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Egress policy selects x/a and denies all egress. We include x/b to verify
|
||||
// non-selected pods are unaffected, plus y/a as a cross-namespace destination.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
policy := GenNetworkPolicyWithNameAndPodSelector("deny-egress-pod-a", metav1.LabelSelector{MatchLabels: map[string]string{"pod": "a"}}, SetSpecEgressRules())
|
||||
|
|
@ -736,7 +766,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should deny egress from all pods in a namespace", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Namespace-wide egress deny in X: we include x/b to verify x/a->x/b is denied,
|
||||
// and y/a to verify cross-namespace egress from X is denied as well.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
policy := GenNetworkPolicyWithNameAndPodSelector("deny-egress-ns-x", metav1.LabelSelector{}, SetSpecEgressRules())
|
||||
|
|
@ -751,7 +783,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should work with Ingress, Egress specified together", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80, 81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Policy on x/a allows ingress only from x/a and x/b, and allows egress only to
|
||||
// port 80. We include x/c as a same-namespace disallowed ingress source and y/a
|
||||
// as a cross-namespace destination for egress checks.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "x/c", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedPodLabels := &metav1.LabelSelector{MatchLabels: map[string]string{"pod": "b"}}
|
||||
|
|
@ -795,7 +830,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// We need x/a as the client selected by the egress policy, and two pods in Y so
|
||||
// we can show y/a is the allowed destination while y/b is denied by egress even
|
||||
// though ingress on the server side allows both.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a", "y/b")
|
||||
nsX, nsY, _ := getK8sNamespaces(k8s)
|
||||
|
||||
// Building egress policy for x/a to y/a only
|
||||
|
|
@ -833,48 +871,7 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
CreatePolicy(ctx, k8s, allowIngressPolicyPodA, nsY)
|
||||
CreatePolicy(ctx, k8s, allowIngressPolicyPodB, nsY)
|
||||
|
||||
// While applying the policies, traffic needs to be allowed by both egress and ingress rules.
|
||||
// Egress rules only
|
||||
// xa xb xc ya yb yc za zb zc
|
||||
// xa X X X . *X* X X X X
|
||||
// xb . . . . . . . . .
|
||||
// xc . . . . . . . . .
|
||||
// ya . . . . . . . . .
|
||||
// yb . . . . . . . . .
|
||||
// yc . . . . . . . . .
|
||||
// za . . . . . . . . .
|
||||
// zb . . . . . . . . .
|
||||
// zc . . . . . . . . .
|
||||
// Ingress rules only
|
||||
// xa xb xc ya yb yc za zb zc
|
||||
// xa . . . *.* . . . . .
|
||||
// xb . . X X . . . . .
|
||||
// xc . . X X . . . . .
|
||||
// ya . . X X . . . . .
|
||||
// yb . . X X . . . . .
|
||||
// yc . . X X . . . . .
|
||||
// za . . X X . . . . .
|
||||
// zb . . X X . . . . .
|
||||
// zc . . X X . . . . .
|
||||
// In the resulting truth table, connections from x/a should only be allowed to y/a. x/a to y/b should be blocked by the egress on x/a.
|
||||
// Expected results
|
||||
// xa xb xc ya yb yc za zb zc
|
||||
// xa X X X . *X* X X X X
|
||||
// xb . . . X X . . . .
|
||||
// xc . . . X X . . . .
|
||||
// ya . . . X X . . . .
|
||||
// yb . . . X X . . . .
|
||||
// yc . . . X X . . . .
|
||||
// za . . . X X . . . .
|
||||
// zb . . . X X . . . .
|
||||
// zc . . . X X . . . .
|
||||
|
||||
reachability := NewReachability(k8s.AllPodStrings(), true)
|
||||
// Default all traffic flows.
|
||||
// Exception: x/a can only egress to y/a, others are false
|
||||
// Exception: y/a can only allow ingress from x/a, others are false
|
||||
// Exception: y/b has no allowed traffic (due to limit on x/a egress)
|
||||
|
||||
reachability.ExpectPeer(&Peer{Namespace: nsX, Pod: "a"}, &Peer{}, false)
|
||||
reachability.ExpectPeer(&Peer{}, &Peer{Namespace: nsY, Pod: "a"}, false)
|
||||
reachability.ExpectPeer(&Peer{Namespace: nsX, Pod: "a"}, &Peer{Namespace: nsY, Pod: "a"}, true)
|
||||
|
|
@ -886,7 +883,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce egress policy allowing traffic to a server in a different namespace based on PodSelector and NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Egress policy on x/a only allows traffic to y/a (namespace+pod selectors).
|
||||
// We include y/b as a non-matching destination to ensure it is denied.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a", "y/b")
|
||||
nsX, nsY, _ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedNamespaces := &metav1.LabelSelector{
|
||||
|
|
@ -913,7 +912,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce ingress policy allowing any port traffic to a server on a specific protocol", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP, protocolUDP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Protocol-only ingress allowlist: one server (x/a) and one client (y/a) is
|
||||
// enough to validate TCP is allowed while UDP is denied.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
ingressRule := networkingv1.NetworkPolicyIngressRule{}
|
||||
|
|
@ -932,7 +933,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce multiple ingress policies with ingress allow-all policy taking precedence", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// We only need x/a as the policy target and y/a as the client to observe that
|
||||
// an allow-all ingress policy overrides a more restrictive ingress policy.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
IngressRules := networkingv1.NetworkPolicyIngressRule{}
|
||||
|
|
@ -958,7 +961,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce multiple egress policies with egress allow-all policy taking precedence", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// We only need x/a as the policy target and y/a as the destination to observe
|
||||
// that an allow-all egress policy overrides a more restrictive egress policy.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
egressRule := networkingv1.NetworkPolicyEgressRule{}
|
||||
|
|
@ -984,7 +989,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should stop enforcing policies after they are deleted", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// A single client/server pair is sufficient to validate connectivity is denied
|
||||
// while the policy exists and returns to allowed after the policy is deleted.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
ginkgo.By("Creating a network policy for the server which denies all traffic.")
|
||||
|
|
@ -1013,7 +1020,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
// Getting podServer's status to get podServer's IP, to create the CIDR
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// This test needs the IP of a real pod to build the IPBlock CIDR. We create y/b
|
||||
// as the server (to source the IP), y/a as a non-matching destination, and x/a
|
||||
// as the client whose egress is restricted.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a", "y/b")
|
||||
nsX, nsY, _ := getK8sNamespaces(k8s)
|
||||
|
||||
podList, err := f.ClientSet.CoreV1().Pods(nsY).List(ctx, metav1.ListOptions{LabelSelector: "pod=b"})
|
||||
|
|
@ -1040,7 +1050,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce except clause while egress access to server in CIDR block", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// We need x/b to supply the IP used in the IPBlock (and as the destination),
|
||||
// x/a as the client, and x/c to verify non-excepted traffic still works.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "x/c")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
// Getting podServer's status to get podServer's IP, to create the CIDR with except clause
|
||||
|
|
@ -1073,7 +1085,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should ensure an IP overlapping both IPBlock.CIDR and IPBlock.Except is allowed", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// We need x/b to supply the IP used in the IPBlock (and as the destination),
|
||||
// x/a as the client, and x/c to verify non-excepted traffic still works for
|
||||
// CIDR/Except overlap behavior.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "x/c")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
// Getting podServer's status to get podServer's IP, to create the CIDR with except clause
|
||||
|
|
@ -1120,7 +1135,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policies to check ingress and egress policies can be controlled independently based on PodSelector", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Policies target x/a. We need x/b to verify a->b allowed while b->a can be
|
||||
// denied, and y/a as an external pod so the initial "all traffic allowed" step
|
||||
// covers cross-namespace connectivity as well.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
/*
|
||||
|
|
@ -1158,7 +1176,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should not mistakenly treat 'protocol: SCTP' as 'protocol: TCP', even if the plugin doesn't support SCTP", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// One server (x/a) and one client (y/a) is sufficient: we create an SCTP-only
|
||||
// allow policy and verify that TCP to the same port remains blocked.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
ginkgo.By("Creating a default-deny ingress policy.")
|
||||
|
|
@ -1184,7 +1204,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should properly isolate pods that are selected by a policy allowing SCTP, even if the plugin doesn't support SCTP", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// One server (x/a) and one client (y/a) is sufficient: an SCTP-only allow rule
|
||||
// should still isolate the pod for TCP traffic on other ports.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
ginkgo.By("Creating a network policy for the server which allows traffic only via SCTP on port 80.")
|
||||
|
|
@ -1202,7 +1224,9 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should not allow access by TCP when a policy specifies only UDP", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// One server (x/a) and one client (y/a) is sufficient: a UDP-only allow rule
|
||||
// must not accidentally permit TCP connectivity.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "y/a")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
ingressRule := networkingv1.NetworkPolicyIngressRule{}
|
||||
|
|
@ -1222,7 +1246,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy to allow traffic based on NamespaceSelector with MatchLabels using default ns label", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// Policy on x/a allows ingress only from namespace Y (using the default ns name
|
||||
// label). We need x/b (same namespace) and z/a (other namespace) as negative
|
||||
// cases.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "z/a")
|
||||
nsX, nsY, nsZ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedLabels := &metav1.LabelSelector{
|
||||
|
|
@ -1246,7 +1273,10 @@ var _ = common.SIGDescribe("Netpol", func() {
|
|||
f.It("should enforce policy based on NamespaceSelector with MatchExpressions using default ns label", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolTCP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// This uses MatchExpressions on the default namespace-name label for an egress
|
||||
// policy selecting x/a. The selector is NotIn{Y}, so x/a -> Y must be denied
|
||||
// while x/a -> X and x/a -> Z remain allowed.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "z/a")
|
||||
nsX, nsY, _ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedNamespaces := &metav1.LabelSelector{
|
||||
|
|
@ -1283,7 +1313,10 @@ var _ = common.SIGDescribe("Netpol [LinuxOnly]", func() {
|
|||
f.It("should support a 'default-deny-ingress' policy", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolUDP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// UDP: namespace X has a default-deny-ingress policy, so we need 2 pods in X to
|
||||
// verify X-to-X ingress is blocked, and 2 pods in Y to verify Y-to-X is also
|
||||
// blocked while X-to-Y and Y-to-Y traffic remain allowed.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "y/b")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
policy := GenNetworkPolicyWithNameAndPodSelector("deny-all", metav1.LabelSelector{}, SetSpecIngressRules())
|
||||
|
|
@ -1298,7 +1331,11 @@ var _ = common.SIGDescribe("Netpol [LinuxOnly]", func() {
|
|||
f.It("should enforce policy based on Ports", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolUDP}
|
||||
ports := []int32{81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// UDP port test: namespace X should allow ingress to x/a on port 81 from
|
||||
// namespace Y only. We include x/b as a same-namespace source (denied) and z/a
|
||||
// as an unrelated namespace (denied). One pod in Y is enough because the rule
|
||||
// is namespace-based.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "z/a")
|
||||
nsX, nsY, nsZ := getK8sNamespaces(k8s)
|
||||
|
||||
ginkgo.By("Creating a network policy allowPort81Policy which only allows allow listed namespaces (y) to connect on exactly one port (81)")
|
||||
|
|
@ -1323,7 +1360,10 @@ var _ = common.SIGDescribe("Netpol [LinuxOnly]", func() {
|
|||
f.It("should enforce policy to allow traffic only from a pod in a different namespace based on PodSelector and NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolUDP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// UDP: policy on x/a allows ingress only from y/a (namespace+pod selectors).
|
||||
// We keep x/b as a same-namespace/non-matching pod to ensure it cannot reach
|
||||
// x/a.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a")
|
||||
nsX, nsY, _ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedNamespaces := &metav1.LabelSelector{
|
||||
|
|
@ -1365,7 +1405,10 @@ var _ = common.SIGDescribe("Netpol", feature.SCTPConnectivity, "[LinuxOnly]", fu
|
|||
f.It("should support a 'default-deny-ingress' policy", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolSCTP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// SCTP: namespace X has a default-deny-ingress policy, so we need 2 pods in X
|
||||
// to verify X-to-X ingress is blocked, and 2 pods in Y to verify Y-to-X is also
|
||||
// blocked while X-to-Y and Y-to-Y traffic remain allowed.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "y/b")
|
||||
nsX, _, _ := getK8sNamespaces(k8s)
|
||||
|
||||
policy := GenNetworkPolicyWithNameAndPodSelector("deny-all", metav1.LabelSelector{}, SetSpecIngressRules())
|
||||
|
|
@ -1380,7 +1423,11 @@ var _ = common.SIGDescribe("Netpol", feature.SCTPConnectivity, "[LinuxOnly]", fu
|
|||
f.It("should enforce policy based on Ports", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolSCTP}
|
||||
ports := []int32{81}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// SCTP port test: namespace X should allow ingress to x/a on port 81 from
|
||||
// namespace Y only. We include x/b as a same-namespace source (denied) and z/a
|
||||
// as an unrelated namespace (denied). One pod in Y is enough because the rule
|
||||
// is namespace-based.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a", "z/a")
|
||||
nsX, nsY, nsZ := getK8sNamespaces(k8s)
|
||||
|
||||
ginkgo.By("Creating a network allowPort81Policy which only allows allow listed namespaces (y) to connect on exactly one port (81)")
|
||||
|
|
@ -1405,7 +1452,10 @@ var _ = common.SIGDescribe("Netpol", feature.SCTPConnectivity, "[LinuxOnly]", fu
|
|||
f.It("should enforce policy to allow traffic only from a pod in a different namespace based on PodSelector and NamespaceSelector", feature.NetworkPolicy, func(ctx context.Context) {
|
||||
protocols := []v1.Protocol{protocolSCTP}
|
||||
ports := []int32{80}
|
||||
k8s = initializeResources(ctx, f, protocols, ports)
|
||||
// SCTP: policy on x/a allows ingress only from y/a (namespace+pod selectors).
|
||||
// We keep x/b as a same-namespace/non-matching pod to ensure it cannot reach
|
||||
// x/a.
|
||||
k8s = initializeResources(ctx, f, protocols, ports, "x/a", "x/b", "y/a")
|
||||
nsX, nsY, _ := getK8sNamespaces(k8s)
|
||||
|
||||
allowedNamespaces := &metav1.LabelSelector{
|
||||
|
|
@ -1445,10 +1495,39 @@ func getNamespaceNames(rootNs string) []string {
|
|||
return []string{nsX, nsY, nsZ}
|
||||
}
|
||||
|
||||
// defaultModel creates a new "model" pod system under namespaces (x,y,z) which has pods a, b, and c. Thus resulting in the
|
||||
// truth table matrix that is identical for all tests, comprising 81 total connections between 9 pods (x/a, x/b, x/c, ..., z/c).
|
||||
func defaultModel(namespaces []string, protocols []v1.Protocol, ports []int32) *Model {
|
||||
return NewModel(namespaces, []string{"a", "b", "c"}, ports, protocols)
|
||||
// modelFromPodStrings converts pod references like "x/a" into a model that uses the
|
||||
// actual generated namespace names.
|
||||
func modelFromPodStrings(namespaces []string, modelPods []string, protocols []v1.Protocol, ports []int32) (*Model, error) {
|
||||
if len(modelPods) == 0 {
|
||||
return nil, fmt.Errorf("model pod list must not be empty")
|
||||
}
|
||||
|
||||
namespaceBySuffix := map[string]string{
|
||||
"x": namespaces[0],
|
||||
"y": namespaces[1],
|
||||
"z": namespaces[2],
|
||||
}
|
||||
podNamesByNamespace := map[string]sets.Set[string]{
|
||||
namespaces[0]: sets.New[string](),
|
||||
namespaces[1]: sets.New[string](),
|
||||
namespaces[2]: sets.New[string](),
|
||||
}
|
||||
|
||||
for _, modelPod := range modelPods {
|
||||
namespaceSuffix, podName, found := strings.Cut(modelPod, "/")
|
||||
if !found || namespaceSuffix == "" || podName == "" {
|
||||
return nil, fmt.Errorf("invalid model pod %q; expected <namespace>/<pod> like x/a", modelPod)
|
||||
}
|
||||
|
||||
namespaceName, found := namespaceBySuffix[namespaceSuffix]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("invalid model pod %q; namespace suffix %q must be one of x, y, z", modelPod, namespaceSuffix)
|
||||
}
|
||||
|
||||
podNamesByNamespace[namespaceName].Insert(podName)
|
||||
}
|
||||
|
||||
return newModelWithPerNamespacePodNames(namespaces, podNamesByNamespace, ports, protocols), nil
|
||||
}
|
||||
|
||||
// getK8sNamespaces returns the 3 actual namespace names.
|
||||
|
|
@ -1457,7 +1536,7 @@ func getK8sNamespaces(k8s *kubeManager) (string, string, string) {
|
|||
return ns[0], ns[1], ns[2]
|
||||
}
|
||||
|
||||
func initializeCluster(ctx context.Context, f *framework.Framework, protocols []v1.Protocol, ports []int32) (*kubeManager, error) {
|
||||
func initializeCluster(ctx context.Context, f *framework.Framework, protocols []v1.Protocol, ports []int32, modelPods ...string) (*kubeManager, error) {
|
||||
dnsDomain := framework.TestContext.ClusterDNSDomain
|
||||
framework.Logf("dns domain: %s", dnsDomain)
|
||||
|
||||
|
|
@ -1465,7 +1544,10 @@ func initializeCluster(ctx context.Context, f *framework.Framework, protocols []
|
|||
rootNs := f.BaseName
|
||||
namespaceNames := getNamespaceNames(rootNs)
|
||||
|
||||
model := defaultModel(namespaceNames, protocols, ports)
|
||||
model, err := modelFromPodStrings(namespaceNames, modelPods, protocols, ports)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
framework.Logf("initializing cluster: ensuring namespaces, pods and services exist and are ready")
|
||||
|
||||
|
|
@ -1485,8 +1567,8 @@ func initializeCluster(ctx context.Context, f *framework.Framework, protocols []
|
|||
// initializeResources uses the e2e framework to create all necessary namespace resources, based on the network policy
|
||||
// model derived from the framework. It then waits for the resources described by the model to be up and running
|
||||
// (i.e. all pods are ready and running in their namespaces).
|
||||
func initializeResources(ctx context.Context, f *framework.Framework, protocols []v1.Protocol, ports []int32) *kubeManager {
|
||||
k8s, err := initializeCluster(ctx, f, protocols, ports)
|
||||
func initializeResources(ctx context.Context, f *framework.Framework, protocols []v1.Protocol, ports []int32, modelPods ...string) *kubeManager {
|
||||
k8s, err := initializeCluster(ctx, f, protocols, ports, modelPods...)
|
||||
framework.ExpectNoError(err, "unable to initialize resources")
|
||||
return k8s
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue