@@ -12,6 +12,8 @@ import (
12
12
"github.com/cilium/cilium/pkg/labels"
13
13
"github.com/olvrng/ujson"
14
14
"github.com/spf13/viper"
15
+ rbac "k8s.io/api/rbac/v1"
16
+ "k8s.io/utils/ptr"
15
17
16
18
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
17
19
)
@@ -107,39 +109,58 @@ func LoadConfig(configPath string) (*ClusterConfig, error) {
107
109
}
108
110
109
111
type Metadata struct {
110
- Annotations map [string ]string
111
- Labels map [string ]string
112
- OwnerReferences map [string ]string
113
- CreationTimestamp string
114
- ResourceVersion string
115
- Kind string
116
- ApiVersion string
117
- PodSelectorMatchLabels map [string ]string
118
- PodSpecLabels map [string ]string
112
+ Annotations map [string ]string
113
+ Labels map [string ]string
114
+ OwnerReferences map [string ]string
115
+ CreationTimestamp string
116
+ ResourceVersion string
117
+ Kind string
118
+ ApiVersion string
119
+ Namespace string
120
+
121
+ // workloads
122
+ PodSpecLabels map [string ]string
123
+ // network policies
124
+ NetworkPolicyPodSelectorMatchLabels map [string ]string
125
+ HasEgressRules * bool
126
+ HasIngressRules * bool
127
+
128
+ // services
129
+ ServicePodSelectorMatchLabels map [string ]string
130
+ // for role bindings
131
+ Subjects []rbac.Subject
132
+ RoleRef * rbac.RoleRef
119
133
}
120
134
121
135
// ExtractMetadataFromBytes extracts metadata from the JSON bytes of a Kubernetes object
122
136
func ExtractMetadataFromJsonBytes (input []byte ) (Metadata , error ) {
123
137
// output values
124
138
m := Metadata {
125
- Annotations : map [string ]string {},
126
- Labels : map [string ]string {},
127
- OwnerReferences : map [string ]string {},
128
- PodSpecLabels : map [string ]string {},
129
- PodSelectorMatchLabels : map [string ]string {},
139
+ Annotations : map [string ]string {},
140
+ Labels : map [string ]string {},
141
+ OwnerReferences : map [string ]string {},
142
+ PodSpecLabels : map [string ]string {},
143
+ NetworkPolicyPodSelectorMatchLabels : map [string ]string {},
144
+ ServicePodSelectorMatchLabels : map [string ]string {},
130
145
}
146
+
147
+ currentSubjectIndex := - 1
148
+
131
149
// ujson parsing
132
150
jsonPathElements := make ([]string , 0 )
133
151
err := ujson .Walk (input , func (level int , key , value []byte ) bool {
134
152
if level > 0 {
135
153
jsonPathElements = slices .Replace (jsonPathElements , level - 1 , len (jsonPathElements ), unquote (key ))
136
154
}
137
155
jsonPath := strings .Join (jsonPathElements , "." )
156
+
138
157
switch {
139
158
case jsonPath == "kind" :
140
159
m .Kind = unquote (value )
141
160
case jsonPath == "apiVersion" :
142
161
m .ApiVersion = unquote (value )
162
+ case jsonPath == "metadata.namespace" :
163
+ m .Namespace = unquote (value )
143
164
case jsonPath == "metadata.creationTimestamp" :
144
165
m .CreationTimestamp = unquote (value )
145
166
case jsonPath == "metadata.resourceVersion" :
@@ -154,20 +175,80 @@ func ExtractMetadataFromJsonBytes(input []byte) (Metadata, error) {
154
175
m .PodSpecLabels [unquote (key )] = unquote (value )
155
176
case strings .HasPrefix (jsonPath , "spec.jobTemplate.spec.template.metadata.labels." ):
156
177
m .PodSpecLabels [unquote (key )] = unquote (value )
157
- case m .ApiVersion == "cilium.io/v2" && strings .HasPrefix (jsonPath , "spec.endpointSelector.matchLabels." ):
158
- addCiliumMatchLabels (m .PodSelectorMatchLabels , key , value )
159
- case m .ApiVersion == "networking.k8s.io/v1" && strings .HasPrefix (jsonPath , "spec.podSelector.matchLabels." ):
160
- m .PodSelectorMatchLabels [unquote (key )] = unquote (value )
178
+ case strings .HasPrefix (jsonPath , "subjects." ):
179
+ parseRoleBindingSubjects (& m , & currentSubjectIndex , key , value )
180
+ case strings .HasPrefix (jsonPath , "roleRef." ):
181
+ parseRoleBindingRoleRef (& m , key , value )
182
+ case m .Kind == "Service" && strings .HasPrefix (jsonPath , "spec.selector." ):
183
+ m .ServicePodSelectorMatchLabels [unquote (key )] = unquote (value )
184
+ // cilium network policies
185
+ case m .ApiVersion == "cilium.io/v2" :
186
+ if strings .HasPrefix (jsonPath , "spec.endpointSelector.matchLabels." ) {
187
+ addCiliumMatchLabels (m .NetworkPolicyPodSelectorMatchLabels , key , value )
188
+ } else if jsonPath == "spec.egress." || jsonPath == "spec.egressDeny." {
189
+ setHasEgress (& m )
190
+ } else if jsonPath == "spec.ingress." || jsonPath == "spec.ingressDeny." {
191
+ setHasIngress (& m )
192
+ } else if jsonPath == "specs..ingress" || jsonPath == "specs..ingressDeny" {
193
+ setHasIngress (& m )
194
+ } else if jsonPath == "specs..egress" || jsonPath == "specs..egressDeny" {
195
+ setHasEgress (& m )
196
+ }
197
+ // k8s network policies
198
+ case m .ApiVersion == "networking.k8s.io/v1" :
199
+ if strings .HasPrefix (jsonPath , "spec.podSelector.matchLabels." ) {
200
+ m .NetworkPolicyPodSelectorMatchLabels [unquote (key )] = unquote (value )
201
+ } else if strings .HasPrefix (jsonPath , "spec.policyTypes." ) {
202
+ val := unquote (value )
203
+ if val == "Egress" {
204
+ setHasEgress (& m )
205
+ } else if val == "Ingress" {
206
+ setHasIngress (& m )
207
+ }
208
+ } else if jsonPath == "spec.egress" {
209
+ setHasEgress (& m )
210
+ } else if jsonPath == "spec.ingress" {
211
+ setHasIngress (& m )
212
+ }
213
+ // istio network policies
161
214
case m .ApiVersion == "security.istio.io/v1" && strings .HasPrefix (jsonPath , "spec.selector.matchLabels." ):
162
- m .PodSelectorMatchLabels [unquote (key )] = unquote (value )
163
- case m .ApiVersion == "projectcalico.org/v3" && jsonPath == "spec.selector" :
164
- m .PodSelectorMatchLabels = ParseCalicoSelector (value )
215
+ m .NetworkPolicyPodSelectorMatchLabels [unquote (key )] = unquote (value )
216
+ // calico
217
+ case m .ApiVersion == "projectcalico.org/v3" :
218
+ if jsonPath == "spec.selector" {
219
+ m .NetworkPolicyPodSelectorMatchLabels = ParseCalicoSelector (value )
220
+ } else if strings .HasPrefix (jsonPath , "spec.types." ) {
221
+ val := unquote (value )
222
+ if val == "Egress" {
223
+ setHasEgress (& m )
224
+ }
225
+ if val == "Ingress" {
226
+ setHasIngress (& m )
227
+ }
228
+ } else if jsonPath == "spec.egress" {
229
+ setHasEgress (& m )
230
+ } else if jsonPath == "spec.ingress" {
231
+ setHasIngress (& m )
232
+ }
165
233
}
166
234
return true
167
235
})
236
+
168
237
return m , err
169
238
}
170
239
240
+ func setHasEgress (m * Metadata ) {
241
+ if m .HasEgressRules == nil {
242
+ m .HasEgressRules = ptr .To (true )
243
+ }
244
+ }
245
+
246
+ func setHasIngress (m * Metadata ) {
247
+ if m .HasIngressRules == nil {
248
+ m .HasIngressRules = ptr .To (true )
249
+ }
250
+ }
251
+
171
252
func ParseCalicoSelector (value []byte ) map [string ]string {
172
253
selector := map [string ]string {}
173
254
for _ , rule := range strings .Split (unquote (value ), "&&" ) {
@@ -210,3 +291,43 @@ func unquote(value []byte) string {
210
291
}
211
292
return string (buf )
212
293
}
294
+
295
+ func parseRoleBindingRoleRef (m * Metadata , key , value []byte ) {
296
+ if m .RoleRef == nil {
297
+ m .RoleRef = & rbac.RoleRef {}
298
+ }
299
+
300
+ k := unquote (key )
301
+ switch k {
302
+ case "apiGroup" :
303
+ m .RoleRef .APIGroup = unquote (value )
304
+ case "kind" :
305
+ m .RoleRef .Kind = unquote (value )
306
+ case "name" :
307
+ m .RoleRef .Name = unquote (value )
308
+ }
309
+ }
310
+
311
+ func parseRoleBindingSubjects (m * Metadata , currentSubjectIndex * int , key , value []byte ) {
312
+ v := unquote (value )
313
+ if v == "{" {
314
+ if m .Subjects == nil {
315
+ m .Subjects = make ([]rbac.Subject , 0 )
316
+ }
317
+ * currentSubjectIndex += 1
318
+ m .Subjects = append (m .Subjects , rbac.Subject {})
319
+ return
320
+ }
321
+
322
+ k := unquote (key )
323
+ switch k {
324
+ case "apiGroup" :
325
+ m .Subjects [* currentSubjectIndex ].APIGroup = v
326
+ case "kind" :
327
+ m .Subjects [* currentSubjectIndex ].Kind = v
328
+ case "name" :
329
+ m .Subjects [* currentSubjectIndex ].Name = v
330
+ case "namespace" :
331
+ m .Subjects [* currentSubjectIndex ].Namespace = v
332
+ }
333
+ }
0 commit comments