Skip to content
Open
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
23 changes: 23 additions & 0 deletions conformance/base/admin_tier/standard-egress-isolation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# An Admin-tier ClusterNetworkPolicy with ONLY egress rules on the gryffindor
# subject (and deliberately NO ingress section). Per the API contract — "CNPs with
# no ingress rules do not affect ingress traffic" — the subject's ingress must remain
# fully open even though egress to slytherin is denied.
# Used by CNPAdminTierEgressRulesIngressUnaffected (issue #103).
apiVersion: policy.networking.k8s.io/v1alpha2
kind: ClusterNetworkPolicy
metadata:
name: admin-egress-isolation
spec:
tier: Admin
priority: 20
subject:
namespaces:
matchLabels:
kubernetes.io/metadata.name: network-policy-conformance-gryffindor
egress:
- name: "deny-to-slytherin"
action: "Deny"
to:
- namespaces:
matchLabels:
kubernetes.io/metadata.name: network-policy-conformance-slytherin
23 changes: 23 additions & 0 deletions conformance/base/admin_tier/standard-ingress-isolation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# An Admin-tier ClusterNetworkPolicy with ONLY ingress rules on the gryffindor
# subject (and deliberately NO egress section). Per the API contract — "CNPs with
# no egress rules do not affect egress traffic" — the subject's egress must remain
# fully open even though ingress from slytherin is denied.
# Used by CNPAdminTierIngressRulesEgressUnaffected (issue #103).
apiVersion: policy.networking.k8s.io/v1alpha2
kind: ClusterNetworkPolicy
metadata:
name: admin-ingress-isolation
spec:
tier: Admin
priority: 20
subject:
namespaces:
matchLabels:
kubernetes.io/metadata.name: network-policy-conformance-gryffindor
ingress:
- name: "deny-from-slytherin"
action: "Deny"
from:
- namespaces:
matchLabels:
kubernetes.io/metadata.name: network-policy-conformance-slytherin
23 changes: 23 additions & 0 deletions conformance/base/baseline_tier/standard-egress-isolation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# A Baseline-tier ClusterNetworkPolicy with ONLY egress rules on the gryffindor
# subject (and deliberately NO ingress section). Per the API contract — "CNPs with
# no ingress rules do not affect ingress traffic" — the subject's ingress must remain
# fully open even though egress to slytherin is denied.
# Baseline-tier sibling; used by CNPBaselineTierEgressRulesIngressUnaffected (issue #103).
apiVersion: policy.networking.k8s.io/v1alpha2
kind: ClusterNetworkPolicy
metadata:
name: baseline-egress-isolation
spec:
tier: Baseline
priority: 20
subject:
namespaces:
matchLabels:
kubernetes.io/metadata.name: network-policy-conformance-gryffindor
egress:
- name: "deny-to-slytherin"
action: "Deny"
to:
- namespaces:
matchLabels:
kubernetes.io/metadata.name: network-policy-conformance-slytherin
23 changes: 23 additions & 0 deletions conformance/base/baseline_tier/standard-ingress-isolation.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# A Baseline-tier ClusterNetworkPolicy with ONLY ingress rules on the gryffindor
# subject (and deliberately NO egress section). Per the API contract — "CNPs with
# no egress rules do not affect egress traffic" — the subject's egress must remain
# fully open even though ingress from slytherin is denied.
# Baseline-tier sibling; used by CNPBaselineTierIngressRulesEgressUnaffected (issue #103).
apiVersion: policy.networking.k8s.io/v1alpha2
kind: ClusterNetworkPolicy
metadata:
name: baseline-ingress-isolation
spec:
tier: Baseline
priority: 20
subject:
namespaces:
matchLabels:
kubernetes.io/metadata.name: network-policy-conformance-gryffindor
ingress:
- name: "deny-from-slytherin"
action: "Deny"
from:
- namespaces:
matchLabels:
kubernetes.io/metadata.name: network-policy-conformance-slytherin
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
Copyright 2025 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package tests

import (
"testing"

"sigs.k8s.io/network-policy-api/conformance/utils/kubernetes"
"sigs.k8s.io/network-policy-api/conformance/utils/suite"
)

func init() {
ConformanceTests = append(ConformanceTests,
CNPAdminTierIngressRulesEgressUnaffected,
CNPAdminTierEgressRulesIngressUnaffected,
)
}

// CNPAdminTierIngressRulesEgressUnaffected verifies that an Admin-tier
// ClusterNetworkPolicy which declares only ingress rules has no side-effect on the
// subject's egress. The v1alpha2 API contract states "CNPs with no egress rules do
// not affect egress traffic", so even though ingress from slytherin is denied, the
// gryffindor subject must still egress freely. The existing ingress-rule tests only
// probe traffic *toward* the subject and never check this. See issue
// kubernetes-sigs/network-policy-api#103.
var CNPAdminTierIngressRulesEgressUnaffected = suite.ConformanceTest{
ShortName: "CNPAdminTierIngressRulesEgressUnaffected",
Description: "An Admin CNP with only ingress rules must not affect the subject's egress",
Features: []suite.SupportedFeature{
suite.SupportClusterNetworkPolicy,
},
Manifests: []string{"base/admin_tier/standard-ingress-isolation.yaml"},
Test: func(t *testing.T, s *suite.ConformanceTestSuite) {

t.Run("Should enforce the ingress rule: deny ingress from slytherin to gryffindor", func(t *testing.T) {
// Positive control: confirms the ingress-only policy is actually active,
// so the egress checks below are meaningful (not just an inert policy).
serverPod := kubernetes.GetPod(t, s.Client, "network-policy-conformance-gryffindor", "harry-potter-0", s.TimeoutConfig.GetTimeout)
kubernetes.PokeServer(t, s.ClientSet, &s.KubeConfig, "network-policy-conformance-slytherin", "draco-malfoy-0", "tcp",
serverPod.Status.PodIP, int32(80), s.TimeoutConfig, false)
})

t.Run("Should not affect egress: gryffindor can still egress to ravenclaw", func(t *testing.T) {
// ravenclaw is not referenced by the policy, so the reply path
// (ravenclaw->gryffindor ingress) is unaffected and cannot confound this
// egress check. With zero egress rules, this egress must be allowed.
serverPod := kubernetes.GetPod(t, s.Client, "network-policy-conformance-ravenclaw", "luna-lovegood-0", s.TimeoutConfig.GetTimeout)
kubernetes.PokeServer(t, s.ClientSet, &s.KubeConfig, "network-policy-conformance-gryffindor", "harry-potter-0", "tcp",
serverPod.Status.PodIP, int32(80), s.TimeoutConfig, true)
})

t.Run("Should not affect egress: gryffindor can still egress to hufflepuff", func(t *testing.T) {
serverPod := kubernetes.GetPod(t, s.Client, "network-policy-conformance-hufflepuff", "cedric-diggory-0", s.TimeoutConfig.GetTimeout)
kubernetes.PokeServer(t, s.ClientSet, &s.KubeConfig, "network-policy-conformance-gryffindor", "harry-potter-0", "tcp",
serverPod.Status.PodIP, int32(80), s.TimeoutConfig, true)
})
},
}

// CNPAdminTierEgressRulesIngressUnaffected verifies that an Admin-tier
// ClusterNetworkPolicy which declares only egress rules has no side-effect on the
// subject's ingress. The v1alpha2 API contract states "CNPs with no ingress rules do
// not affect ingress traffic", so even though egress to slytherin is denied, other
// pods must still reach the gryffindor subject. The existing egress-rule tests only
// probe traffic *from* the subject and never check this. See issue
// kubernetes-sigs/network-policy-api#103.
var CNPAdminTierEgressRulesIngressUnaffected = suite.ConformanceTest{
ShortName: "CNPAdminTierEgressRulesIngressUnaffected",
Description: "An Admin CNP with only egress rules must not affect the subject's ingress",
Features: []suite.SupportedFeature{
suite.SupportClusterNetworkPolicy,
},
Manifests: []string{"base/admin_tier/standard-egress-isolation.yaml"},
Test: func(t *testing.T, s *suite.ConformanceTestSuite) {

t.Run("Should enforce the egress rule: deny egress from gryffindor to slytherin", func(t *testing.T) {
// Positive control: confirms the egress-only policy is actually active.
serverPod := kubernetes.GetPod(t, s.Client, "network-policy-conformance-slytherin", "draco-malfoy-0", s.TimeoutConfig.GetTimeout)
kubernetes.PokeServer(t, s.ClientSet, &s.KubeConfig, "network-policy-conformance-gryffindor", "harry-potter-0", "tcp",
serverPod.Status.PodIP, int32(80), s.TimeoutConfig, false)
})

t.Run("Should not affect ingress: ravenclaw can still reach gryffindor", func(t *testing.T) {
// ravenclaw is not referenced by the policy, so the reply path
// (gryffindor->ravenclaw egress) is unaffected and cannot confound this
// ingress check. With zero ingress rules, this ingress must be allowed.
serverPod := kubernetes.GetPod(t, s.Client, "network-policy-conformance-gryffindor", "harry-potter-0", s.TimeoutConfig.GetTimeout)
kubernetes.PokeServer(t, s.ClientSet, &s.KubeConfig, "network-policy-conformance-ravenclaw", "luna-lovegood-0", "tcp",
serverPod.Status.PodIP, int32(80), s.TimeoutConfig, true)
})

t.Run("Should not affect ingress: hufflepuff can still reach gryffindor", func(t *testing.T) {
serverPod := kubernetes.GetPod(t, s.Client, "network-policy-conformance-gryffindor", "harry-potter-0", s.TimeoutConfig.GetTimeout)
kubernetes.PokeServer(t, s.ClientSet, &s.KubeConfig, "network-policy-conformance-hufflepuff", "cedric-diggory-0", "tcp",
serverPod.Status.PodIP, int32(80), s.TimeoutConfig, true)
})
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
Copyright 2025 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package tests

import (
"testing"

"sigs.k8s.io/network-policy-api/conformance/utils/kubernetes"
"sigs.k8s.io/network-policy-api/conformance/utils/suite"
)

func init() {
ConformanceTests = append(ConformanceTests,
CNPBaselineTierIngressRulesEgressUnaffected,
CNPBaselineTierEgressRulesIngressUnaffected,
)
}

// CNPBaselineTierIngressRulesEgressUnaffected is the Baseline-tier sibling of
// CNPAdminTierIngressRulesEgressUnaffected: a Baseline-tier ClusterNetworkPolicy that
// declares only ingress rules must not affect the subject's egress ("CNPs with no
// egress rules do not affect egress traffic"). This verifies the directional
// isolation guarantee holds in the Baseline code path too. See issue
// kubernetes-sigs/network-policy-api#103.
var CNPBaselineTierIngressRulesEgressUnaffected = suite.ConformanceTest{
ShortName: "CNPBaselineTierIngressRulesEgressUnaffected",
Description: "A Baseline CNP with only ingress rules must not affect the subject's egress",
Features: []suite.SupportedFeature{
suite.SupportClusterNetworkPolicy,
},
Manifests: []string{"base/baseline_tier/standard-ingress-isolation.yaml"},
Test: func(t *testing.T, s *suite.ConformanceTestSuite) {

t.Run("Should enforce the ingress rule: deny ingress from slytherin to gryffindor", func(t *testing.T) {
// Positive control: confirms the ingress-only baseline policy is active.
serverPod := kubernetes.GetPod(t, s.Client, "network-policy-conformance-gryffindor", "harry-potter-0", s.TimeoutConfig.GetTimeout)
kubernetes.PokeServer(t, s.ClientSet, &s.KubeConfig, "network-policy-conformance-slytherin", "draco-malfoy-0", "tcp",
serverPod.Status.PodIP, int32(80), s.TimeoutConfig, false)
})

t.Run("Should not affect egress: gryffindor can still egress to ravenclaw", func(t *testing.T) {
// ravenclaw is not referenced by the policy, so its reply path cannot
// confound this check. With zero egress rules, this egress must be allowed.
serverPod := kubernetes.GetPod(t, s.Client, "network-policy-conformance-ravenclaw", "luna-lovegood-0", s.TimeoutConfig.GetTimeout)
kubernetes.PokeServer(t, s.ClientSet, &s.KubeConfig, "network-policy-conformance-gryffindor", "harry-potter-0", "tcp",
serverPod.Status.PodIP, int32(80), s.TimeoutConfig, true)
})

t.Run("Should not affect egress: gryffindor can still egress to hufflepuff", func(t *testing.T) {
serverPod := kubernetes.GetPod(t, s.Client, "network-policy-conformance-hufflepuff", "cedric-diggory-0", s.TimeoutConfig.GetTimeout)
kubernetes.PokeServer(t, s.ClientSet, &s.KubeConfig, "network-policy-conformance-gryffindor", "harry-potter-0", "tcp",
serverPod.Status.PodIP, int32(80), s.TimeoutConfig, true)
})
},
}

// CNPBaselineTierEgressRulesIngressUnaffected is the Baseline-tier sibling of
// CNPAdminTierEgressRulesIngressUnaffected: a Baseline-tier ClusterNetworkPolicy that
// declares only egress rules must not affect the subject's ingress ("CNPs with no
// ingress rules do not affect ingress traffic"). See issue
// kubernetes-sigs/network-policy-api#103.
var CNPBaselineTierEgressRulesIngressUnaffected = suite.ConformanceTest{
ShortName: "CNPBaselineTierEgressRulesIngressUnaffected",
Description: "A Baseline CNP with only egress rules must not affect the subject's ingress",
Features: []suite.SupportedFeature{
suite.SupportClusterNetworkPolicy,
},
Manifests: []string{"base/baseline_tier/standard-egress-isolation.yaml"},
Test: func(t *testing.T, s *suite.ConformanceTestSuite) {

t.Run("Should enforce the egress rule: deny egress from gryffindor to slytherin", func(t *testing.T) {
// Positive control: confirms the egress-only baseline policy is active.
serverPod := kubernetes.GetPod(t, s.Client, "network-policy-conformance-slytherin", "draco-malfoy-0", s.TimeoutConfig.GetTimeout)
kubernetes.PokeServer(t, s.ClientSet, &s.KubeConfig, "network-policy-conformance-gryffindor", "harry-potter-0", "tcp",
serverPod.Status.PodIP, int32(80), s.TimeoutConfig, false)
})

t.Run("Should not affect ingress: ravenclaw can still reach gryffindor", func(t *testing.T) {
// ravenclaw is not referenced by the policy, so its reply path cannot
// confound this check. With zero ingress rules, this ingress must be allowed.
serverPod := kubernetes.GetPod(t, s.Client, "network-policy-conformance-gryffindor", "harry-potter-0", s.TimeoutConfig.GetTimeout)
kubernetes.PokeServer(t, s.ClientSet, &s.KubeConfig, "network-policy-conformance-ravenclaw", "luna-lovegood-0", "tcp",
serverPod.Status.PodIP, int32(80), s.TimeoutConfig, true)
})

t.Run("Should not affect ingress: hufflepuff can still reach gryffindor", func(t *testing.T) {
serverPod := kubernetes.GetPod(t, s.Client, "network-policy-conformance-gryffindor", "harry-potter-0", s.TimeoutConfig.GetTimeout)
kubernetes.PokeServer(t, s.ClientSet, &s.KubeConfig, "network-policy-conformance-hufflepuff", "cedric-diggory-0", "tcp",
serverPod.Status.PodIP, int32(80), s.TimeoutConfig, true)
})
},
}