Skip to content

Commit 23c8eb7

Browse files
authored
Merge pull request #48 from cybozu-go/add-excludeLabelExpressions
Add excludeLabelExpressions
2 parents 77ed1f5 + 69a35f5 commit 23c8eb7

File tree

10 files changed

+146
-11
lines changed

10 files changed

+146
-11
lines changed

api/v1beta2/networkpolicyadmissionrule_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ type NetworkPolicyAdmissionRuleSpec struct {
4848
type NetworkPolicyAdmissionRuleNamespaceSelector struct {
4949
// ExcludeLabels defines labels through which a namespace should be excluded.
5050
ExcludeLabels map[string]string `json:"excludeLabels,omitempty"`
51+
52+
// ExcludeLabelExpressions defines labels through which a namespace should be excluded by some expressions.
53+
ExcludeLabelExpressions []metav1.LabelSelectorRequirement `json:"excludeLabelExpressions,omitempty"`
5154
}
5255

5356
// NetworkPolicyAdmissionRuleForbiddenIPRanges defines forbidden IP ranges.

api/v1beta2/zz_generated.deepcopy.go

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

charts/tenet/templates/generated/crds/tenet.cybozu.io_crds.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,37 @@ spec:
105105
description: NamespaceSelector qualifies which namespaces the rules
106106
should apply to.
107107
properties:
108+
excludeLabelExpressions:
109+
description: ExcludeLabelExpressions defines labels through which
110+
a namespace should be excluded by some expressions.
111+
items:
112+
description: |-
113+
A label selector requirement is a selector that contains values, a key, and an operator that
114+
relates the key and values.
115+
properties:
116+
key:
117+
description: key is the label key that the selector applies
118+
to.
119+
type: string
120+
operator:
121+
description: |-
122+
operator represents a key's relationship to a set of values.
123+
Valid operators are In, NotIn, Exists and DoesNotExist.
124+
type: string
125+
values:
126+
description: |-
127+
values is an array of string values. If the operator is In or NotIn,
128+
the values array must be non-empty. If the operator is Exists or DoesNotExist,
129+
the values array must be empty. This array is replaced during a strategic
130+
merge patch.
131+
items:
132+
type: string
133+
type: array
134+
required:
135+
- key
136+
- operator
137+
type: object
138+
type: array
108139
excludeLabels:
109140
additionalProperties:
110141
type: string

config/crd/bases/tenet.cybozu.io_networkpolicyadmissionrules.yaml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,37 @@ spec:
8989
description: NamespaceSelector qualifies which namespaces the rules
9090
should apply to.
9191
properties:
92+
excludeLabelExpressions:
93+
description: ExcludeLabelExpressions defines labels through which
94+
a namespace should be excluded by some expressions.
95+
items:
96+
description: |-
97+
A label selector requirement is a selector that contains values, a key, and an operator that
98+
relates the key and values.
99+
properties:
100+
key:
101+
description: key is the label key that the selector applies
102+
to.
103+
type: string
104+
operator:
105+
description: |-
106+
operator represents a key's relationship to a set of values.
107+
Valid operators are In, NotIn, Exists and DoesNotExist.
108+
type: string
109+
values:
110+
description: |-
111+
values is an array of string values. If the operator is In or NotIn,
112+
the values array must be non-empty. If the operator is Exists or DoesNotExist,
113+
the values array must be empty. This array is replaced during a strategic
114+
merge patch.
115+
items:
116+
type: string
117+
type: array
118+
required:
119+
- key
120+
- operator
121+
type: object
122+
type: array
92123
excludeLabels:
93124
additionalProperties:
94125
type: string

e2e/e2e_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ var (
3535
//go:embed t/node-entity-allow-cnp.yaml
3636
nodeEntityAllowCiliumNetworkPolicy []byte
3737

38+
//go:embed t/world-entity-allow-cnp.yaml
39+
worldEntityAllowCiliumNetworkPolicy []byte
40+
3841
//go:embed t/legal-cnp.yaml
3942
legalCiliumNetworkPolicy []byte
4043
)
@@ -317,6 +320,26 @@ var _ = Describe("NetworkPolicyAdmissionRule", func() {
317320
}).Should(Succeed())
318321
})
319322

323+
It("should not accept CiliumNetworkPolicy with forbidden rules except for excluded namespaces using expressions", func() {
324+
By("setting up namespace")
325+
ns := uuid.NewString()
326+
kubectlSafe(nil, "create", "ns", ns)
327+
necoNS := uuid.NewString()
328+
kubectlSafe(nil, "create", "ns", necoNS)
329+
kubectlSafe(nil, "label", "ns", necoNS, "team=neco")
330+
tenantNS := uuid.NewString()
331+
kubectlSafe(nil, "create", "ns", tenantNS)
332+
kubectlSafe(nil, "label", "ns", tenantNS, "team=tenant")
333+
334+
By("applying world entity CiliumNetworkPolicy which is forbidden in namespaces except for neco")
335+
_, err := kubectl(worldEntityAllowCiliumNetworkPolicy, "apply", "-n", ns, "-f", "-")
336+
Expect(err).To(HaveOccurred())
337+
_, err = kubectl(worldEntityAllowCiliumNetworkPolicy, "apply", "-n", necoNS, "-f", "-")
338+
Expect(err).NotTo(HaveOccurred())
339+
_, err = kubectl(worldEntityAllowCiliumNetworkPolicy, "apply", "-n", tenantNS, "-f", "-")
340+
Expect(err).NotTo(HaveOccurred())
341+
})
342+
320343
It("should not reject a legal CiliumNetworkPolicy", func() {
321344
By("setting up namespace")
322345
nsName := uuid.NewString()

e2e/suite_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ var (
2727

2828
//go:embed t/exclude-only-npar.yaml
2929
excludeOnlyNetworkPolicyAdmissionRule []byte
30+
31+
//go:embed t/exclude-expressions-npar.yaml
32+
excludeExpressionsNetworkPolicyAdmissionRule []byte
3033
)
3134

3235
func TestE2E(t *testing.T) {
@@ -51,4 +54,5 @@ var _ = BeforeSuite(func() {
5154
kubectlSafe(bmcDenyNetworkPolicyAdmissionRule, "apply", "-f", "-")
5255
kubectlSafe(nodeDenyNetworkPolicyAdmissionRule, "apply", "-f", "-")
5356
kubectlSafe(excludeOnlyNetworkPolicyAdmissionRule, "apply", "-f", "-")
57+
kubectlSafe(excludeExpressionsNetworkPolicyAdmissionRule, "apply", "-f", "-")
5458
})
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
apiVersion: tenet.cybozu.io/v1beta2
2+
kind: NetworkPolicyAdmissionRule
3+
metadata:
4+
name: exclude-only-npar
5+
spec:
6+
namespaceSelector:
7+
excludeLabelExpressions:
8+
- key: team
9+
operator: In
10+
values:
11+
- neco
12+
- tenant
13+
forbiddenEntities:
14+
- entity: world
15+
type: all

e2e/t/world-entity-allow-cnp.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
apiVersion: cilium.io/v2
2+
kind: CiliumNetworkPolicy
3+
metadata:
4+
name: dummy
5+
spec:
6+
endpointSelector: {}
7+
egress:
8+
- toEntities:
9+
- world

hooks/ciliumnetworkpolicy.go

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import (
66

77
admissionv1 "k8s.io/api/admission/v1"
88
corev1 "k8s.io/api/core/v1"
9+
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
910
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
11+
"k8s.io/apimachinery/pkg/labels"
1012
"sigs.k8s.io/controller-runtime/pkg/client"
1113
"sigs.k8s.io/controller-runtime/pkg/manager"
1214
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -113,7 +115,10 @@ func (v *ciliumNetworkPolicyValidator) validateEntity(nparl tenetv1beta2.Network
113115
if err != nil {
114116
return admission.Errored(http.StatusBadRequest, err)
115117
}
116-
egressFilters, ingressFilters := v.gatherEntityFilters(&nparl, ls)
118+
egressFilters, ingressFilters, err := v.gatherEntityFilters(&nparl, ls)
119+
if err != nil {
120+
return admission.Errored(http.StatusBadRequest, err)
121+
}
117122
for _, egressPolicy := range egressPolicies {
118123
for _, egressFilter := range egressFilters {
119124
if egressPolicy == egressFilter {
@@ -131,13 +136,15 @@ func (v *ciliumNetworkPolicyValidator) validateEntity(nparl tenetv1beta2.Network
131136
return admission.Allowed("")
132137
}
133138

134-
func (v *ciliumNetworkPolicyValidator) shouldValidate(npar *tenetv1beta2.NetworkPolicyAdmissionRule, ls map[string]string) bool {
135-
for k, v := range npar.Spec.NamespaceSelector.ExcludeLabels {
136-
if ls[k] == v {
137-
return false
138-
}
139+
func (v *ciliumNetworkPolicyValidator) shouldExclude(npar *tenetv1beta2.NetworkPolicyAdmissionRule, ls map[string]string) (bool, error) {
140+
s, err := v1.LabelSelectorAsSelector(&v1.LabelSelector{
141+
MatchLabels: npar.Spec.NamespaceSelector.ExcludeLabels,
142+
MatchExpressions: npar.Spec.NamespaceSelector.ExcludeLabelExpressions,
143+
})
144+
if err != nil {
145+
return false, err
139146
}
140-
return true
147+
return s.Matches(labels.Set(ls)), nil
141148
}
142149

143150
func SetupCiliumNetworkPolicyWebhook(mgr manager.Manager, dec admission.Decoder, sa string) {

hooks/ciliumnetworkpolicy_util.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ func (v *ciliumNetworkPolicyValidator) toIPNetSlice(raw []string) ([]*net.IPNet,
5757
func (v *ciliumNetworkPolicyValidator) gatherIPFilters(nparl *tenetv1beta2.NetworkPolicyAdmissionRuleList, ls map[string]string) ([]*net.IPNet, []*net.IPNet, error) {
5858
var egressFilters, ingressFilters []*net.IPNet
5959
for _, npar := range nparl.Items {
60-
if !v.shouldValidate(&npar, ls) {
60+
if matched, err := v.shouldExclude(&npar, ls); err != nil {
61+
return nil, nil, err
62+
} else if matched {
6163
continue
6264
}
6365

@@ -84,10 +86,12 @@ func (v *ciliumNetworkPolicyValidator) gatherEntityPolicies(cnp *unstructured.Un
8486
return v.gatherPolicies(cnp, cilium.EntityRuleKey, v.gatherPoliciesFromStringRule)
8587
}
8688

87-
func (v *ciliumNetworkPolicyValidator) gatherEntityFilters(nparl *tenetv1beta2.NetworkPolicyAdmissionRuleList, ls map[string]string) ([]string, []string) {
89+
func (v *ciliumNetworkPolicyValidator) gatherEntityFilters(nparl *tenetv1beta2.NetworkPolicyAdmissionRuleList, ls map[string]string) ([]string, []string, error) {
8890
var egressFilters, ingressFilters []string
8991
for _, npar := range nparl.Items {
90-
if !v.shouldValidate(&npar, ls) {
92+
if matched, err := v.shouldExclude(&npar, ls); err != nil {
93+
return nil, nil, err
94+
} else if matched {
9195
continue
9296
}
9397

@@ -103,7 +107,7 @@ func (v *ciliumNetworkPolicyValidator) gatherEntityFilters(nparl *tenetv1beta2.N
103107
}
104108
}
105109
}
106-
return egressFilters, ingressFilters
110+
return egressFilters, ingressFilters, nil
107111
}
108112

109113
func (v *ciliumNetworkPolicyValidator) intersectIP(cidr1, cidr2 *net.IPNet) bool {

0 commit comments

Comments
 (0)