• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

noironetworks / aci-containers / 8836

20 Mar 2024 06:58PM UTC coverage: 71.311% (+0.007%) from 71.304%
8836

push

travis-pro

web-flow
Merge pull request #1289 from noironetworks/skip-netpol-sync-check-on-disable-hpp-rendering

Fix APIC sync not complete when hpp rendering is disabled

0 of 6 new or added lines in 1 file covered. (0.0%)

7 existing lines in 2 files now uncovered.

10701 of 15006 relevant lines covered (71.31%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.29
/pkg/controller/network_policy.go
1
// Copyright 2017 Cisco Systems, Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15
// Handlers for network policy updates.  Generate ACI security groups
16
// based on Kubernetes network policies.
17

18
package controller
19

20
import (
21
        "bytes"
22
        "fmt"
23
        "net"
24
        "reflect"
25
        "sort"
26
        "strconv"
27
        "strings"
28

29
        "github.com/sirupsen/logrus"
30
        "github.com/yl2chen/cidranger"
31

32
        v1 "k8s.io/api/core/v1"
33
        v1net "k8s.io/api/networking/v1"
34
        metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35
        "k8s.io/apimachinery/pkg/fields"
36
        "k8s.io/apimachinery/pkg/labels"
37
        "k8s.io/apimachinery/pkg/util/intstr"
38
        "k8s.io/client-go/kubernetes"
39
        "k8s.io/client-go/tools/cache"
40
        k8util "k8s.io/kubectl/pkg/util"
41

42
        "github.com/noironetworks/aci-containers/pkg/apicapi"
43
        "github.com/noironetworks/aci-containers/pkg/index"
44
        "github.com/noironetworks/aci-containers/pkg/ipam"
45
        "github.com/noironetworks/aci-containers/pkg/util"
46
        discovery "k8s.io/api/discovery/v1"
47
)
48

49
func (cont *AciController) initNetworkPolicyInformerFromClient(
50
        kubeClient kubernetes.Interface) {
×
51
        cont.initNetworkPolicyInformerBase(
×
52
                cache.NewListWatchFromClient(
×
53
                        kubeClient.NetworkingV1().RESTClient(), "networkpolicies",
×
54
                        metav1.NamespaceAll, fields.Everything()))
×
55
}
×
56

57
func (cont *AciController) initNetworkPolicyInformerBase(listWatch *cache.ListWatch) {
1✔
58
        cont.networkPolicyIndexer, cont.networkPolicyInformer =
1✔
59
                cache.NewIndexerInformer(
1✔
60
                        listWatch, &v1net.NetworkPolicy{}, 0,
1✔
61
                        cache.ResourceEventHandlerFuncs{
1✔
62
                                AddFunc: func(obj interface{}) {
2✔
63
                                        cont.networkPolicyAdded(obj)
1✔
64
                                },
1✔
65
                                UpdateFunc: func(oldobj interface{}, newobj interface{}) {
×
66
                                        cont.networkPolicyChanged(oldobj, newobj)
×
67
                                },
×
68
                                DeleteFunc: func(obj interface{}) {
1✔
69
                                        cont.networkPolicyDeleted(obj)
1✔
70
                                },
1✔
71
                        },
72
                        cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
73
                )
74
}
75

76
func (cont *AciController) peerPodSelector(np *v1net.NetworkPolicy,
77
        peers []v1net.NetworkPolicyPeer) []index.PodSelector {
1✔
78
        var ret []index.PodSelector
1✔
79
        for _, peer := range peers {
2✔
80
                podselector, err :=
1✔
81
                        metav1.LabelSelectorAsSelector(peer.PodSelector)
1✔
82
                if err != nil {
1✔
83
                        networkPolicyLogger(cont.log, np).
×
84
                                Error("Could not create selector: ", err)
×
85
                        continue
×
86
                }
87
                nsselector, err := metav1.
1✔
88
                        LabelSelectorAsSelector(peer.NamespaceSelector)
1✔
89
                if err != nil {
1✔
90
                        networkPolicyLogger(cont.log, np).
×
91
                                Error("Could not create selector: ", err)
×
92
                        continue
×
93
                }
94

95
                switch {
1✔
96
                case peer.PodSelector != nil && peer.NamespaceSelector != nil:
1✔
97
                        ret = append(ret, index.PodSelector{
1✔
98
                                NsSelector:  nsselector,
1✔
99
                                PodSelector: podselector,
1✔
100
                        })
1✔
101
                case peer.PodSelector != nil:
1✔
102
                        ret = append(ret, index.PodSelector{
1✔
103
                                Namespace:   &np.ObjectMeta.Namespace,
1✔
104
                                PodSelector: podselector,
1✔
105
                        })
1✔
106
                case peer.NamespaceSelector != nil:
1✔
107
                        ret = append(ret, index.PodSelector{
1✔
108
                                NsSelector:  nsselector,
1✔
109
                                PodSelector: labels.Everything(),
1✔
110
                        })
1✔
111
                }
112
        }
113
        return ret
1✔
114
}
115

116
func (cont *AciController) egressPodSelector(np *v1net.NetworkPolicy) []index.PodSelector {
1✔
117
        var ret []index.PodSelector
1✔
118

1✔
119
        for _, egress := range np.Spec.Egress {
2✔
120
                ret = append(ret, cont.peerPodSelector(np, egress.To)...)
1✔
121
        }
1✔
122

123
        return ret
1✔
124
}
125

126
func (cont *AciController) ingressPodSelector(np *v1net.NetworkPolicy) []index.PodSelector {
1✔
127
        var ret []index.PodSelector
1✔
128

1✔
129
        for _, ingress := range np.Spec.Ingress {
2✔
130
                ret = append(ret, cont.peerPodSelector(np, ingress.From)...)
1✔
131
        }
1✔
132

133
        return ret
1✔
134
}
135

136
func (cont *AciController) initNetPolPodIndex() {
1✔
137
        cont.netPolPods = index.NewPodSelectorIndex(
1✔
138
                cont.log,
1✔
139
                cont.podIndexer, cont.namespaceIndexer, cont.networkPolicyIndexer,
1✔
140
                cache.MetaNamespaceKeyFunc,
1✔
141
                func(obj interface{}) []index.PodSelector {
2✔
142
                        np := obj.(*v1net.NetworkPolicy)
1✔
143
                        return index.PodSelectorFromNsAndSelector(np.ObjectMeta.Namespace,
1✔
144
                                &np.Spec.PodSelector)
1✔
145
                },
1✔
146
        )
147
        cont.netPolPods.SetPodUpdateCallback(func(podkey string) {
2✔
148
                podobj, exists, err := cont.podIndexer.GetByKey(podkey)
1✔
149
                if exists && err == nil {
2✔
150
                        cont.queuePodUpdate(podobj.(*v1.Pod))
1✔
151
                }
1✔
152
        })
153

154
        cont.netPolIngressPods = index.NewPodSelectorIndex(
1✔
155
                cont.log,
1✔
156
                cont.podIndexer, cont.namespaceIndexer, cont.networkPolicyIndexer,
1✔
157
                cache.MetaNamespaceKeyFunc,
1✔
158
                func(obj interface{}) []index.PodSelector {
2✔
159
                        return cont.ingressPodSelector(obj.(*v1net.NetworkPolicy))
1✔
160
                },
1✔
161
        )
162
        cont.netPolEgressPods = index.NewPodSelectorIndex(
1✔
163
                cont.log,
1✔
164
                cont.podIndexer, cont.namespaceIndexer, cont.networkPolicyIndexer,
1✔
165
                cache.MetaNamespaceKeyFunc,
1✔
166
                func(obj interface{}) []index.PodSelector {
2✔
167
                        return cont.egressPodSelector(obj.(*v1net.NetworkPolicy))
1✔
168
                },
1✔
169
        )
170
        npupdate := func(npkey string) {
2✔
171
                npobj, exists, err := cont.networkPolicyIndexer.GetByKey(npkey)
1✔
172
                if exists && err == nil {
2✔
173
                        cont.queueNetPolUpdate(npobj.(*v1net.NetworkPolicy))
1✔
174
                }
1✔
175
        }
176
        nphash := func(pod *v1.Pod) string {
2✔
177
                return pod.Status.PodIP
1✔
178
        }
1✔
179
        cont.netPolIngressPods.SetObjUpdateCallback(npupdate)
1✔
180
        cont.netPolIngressPods.SetPodHashFunc(nphash)
1✔
181
        cont.netPolEgressPods.SetObjUpdateCallback(npupdate)
1✔
182
        cont.netPolEgressPods.SetPodHashFunc(nphash)
1✔
183
}
184

185
func (cont *AciController) staticNetPolObjs() apicapi.ApicSlice {
1✔
186
        hppIngress :=
1✔
187
                apicapi.NewHostprotPol(cont.config.AciPolicyTenant,
1✔
188
                        cont.aciNameForKey("np", "static-ingress"))
1✔
189
        {
2✔
190
                ingressSubj := apicapi.NewHostprotSubj(hppIngress.GetDn(), "ingress")
1✔
191
                if !cont.configuredPodNetworkIps.V6.Empty() {
2✔
192
                        outbound := apicapi.NewHostprotRule(ingressSubj.GetDn(),
1✔
193
                                "allow-all-reflexive-v6")
1✔
194
                        outbound.SetAttr("direction", "ingress")
1✔
195
                        outbound.SetAttr("ethertype", "ipv6")
1✔
196
                        ingressSubj.AddChild(outbound)
1✔
197
                }
1✔
198
                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
199
                        outbound := apicapi.NewHostprotRule(ingressSubj.GetDn(),
1✔
200
                                "allow-all-reflexive")
1✔
201
                        outbound.SetAttr("direction", "ingress")
1✔
202
                        outbound.SetAttr("ethertype", "ipv4")
1✔
203
                        ingressSubj.AddChild(outbound)
1✔
204
                }
1✔
205
                hppIngress.AddChild(ingressSubj)
1✔
206
        }
207

208
        hppEgress :=
1✔
209
                apicapi.NewHostprotPol(cont.config.AciPolicyTenant,
1✔
210
                        cont.aciNameForKey("np", "static-egress"))
1✔
211
        {
2✔
212
                egressSubj := apicapi.NewHostprotSubj(hppEgress.GetDn(), "egress")
1✔
213
                if !cont.configuredPodNetworkIps.V6.Empty() {
2✔
214
                        outbound := apicapi.NewHostprotRule(egressSubj.GetDn(),
1✔
215
                                "allow-all-reflexive-v6")
1✔
216
                        outbound.SetAttr("direction", "egress")
1✔
217
                        outbound.SetAttr("ethertype", "ipv6")
1✔
218
                        egressSubj.AddChild(outbound)
1✔
219
                }
1✔
220
                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
221
                        outbound := apicapi.NewHostprotRule(egressSubj.GetDn(),
1✔
222
                                "allow-all-reflexive")
1✔
223
                        outbound.SetAttr("direction", "egress")
1✔
224
                        outbound.SetAttr("ethertype", "ipv4")
1✔
225
                        egressSubj.AddChild(outbound)
1✔
226
                }
1✔
227
                hppEgress.AddChild(egressSubj)
1✔
228
        }
229

230
        hppDiscovery :=
1✔
231
                apicapi.NewHostprotPol(cont.config.AciPolicyTenant,
1✔
232
                        cont.aciNameForKey("np", "static-discovery"))
1✔
233
        {
2✔
234
                discSubj := apicapi.NewHostprotSubj(hppDiscovery.GetDn(), "discovery")
1✔
235
                discDn := discSubj.GetDn()
1✔
236
                {
2✔
237
                        arpin := apicapi.NewHostprotRule(discDn, "arp-ingress")
1✔
238
                        arpin.SetAttr("direction", "ingress")
1✔
239
                        arpin.SetAttr("ethertype", "arp")
1✔
240
                        arpin.SetAttr("connTrack", "normal")
1✔
241
                        discSubj.AddChild(arpin)
1✔
242
                }
1✔
243
                {
1✔
244
                        arpout := apicapi.NewHostprotRule(discDn, "arp-egress")
1✔
245
                        arpout.SetAttr("direction", "egress")
1✔
246
                        arpout.SetAttr("ethertype", "arp")
1✔
247
                        arpout.SetAttr("connTrack", "normal")
1✔
248
                        discSubj.AddChild(arpout)
1✔
249
                }
1✔
250
                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
251
                        icmpin := apicapi.NewHostprotRule(discDn, "icmp-ingress")
1✔
252
                        icmpin.SetAttr("direction", "ingress")
1✔
253
                        icmpin.SetAttr("ethertype", "ipv4")
1✔
254
                        icmpin.SetAttr("protocol", "icmp")
1✔
255
                        icmpin.SetAttr("connTrack", "normal")
1✔
256
                        discSubj.AddChild(icmpin)
1✔
257
                }
1✔
258

259
                if !cont.configuredPodNetworkIps.V6.Empty() {
2✔
260
                        icmpin := apicapi.NewHostprotRule(discDn, "icmpv6-ingress")
1✔
261
                        icmpin.SetAttr("direction", "ingress")
1✔
262
                        icmpin.SetAttr("ethertype", "ipv6")
1✔
263
                        icmpin.SetAttr("protocol", "icmpv6")
1✔
264
                        icmpin.SetAttr("connTrack", "normal")
1✔
265
                        discSubj.AddChild(icmpin)
1✔
266
                }
1✔
267
                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
268
                        icmpout := apicapi.NewHostprotRule(discDn, "icmp-egress")
1✔
269
                        icmpout.SetAttr("direction", "egress")
1✔
270
                        icmpout.SetAttr("ethertype", "ipv4")
1✔
271
                        icmpout.SetAttr("protocol", "icmp")
1✔
272
                        icmpout.SetAttr("connTrack", "normal")
1✔
273
                        discSubj.AddChild(icmpout)
1✔
274
                }
1✔
275

276
                if !cont.configuredPodNetworkIps.V6.Empty() {
2✔
277
                        icmpout := apicapi.NewHostprotRule(discDn, "icmpv6-egress")
1✔
278
                        icmpout.SetAttr("direction", "egress")
1✔
279
                        icmpout.SetAttr("ethertype", "ipv6")
1✔
280
                        icmpout.SetAttr("protocol", "icmpv6")
1✔
281
                        icmpout.SetAttr("connTrack", "normal")
1✔
282
                        discSubj.AddChild(icmpout)
1✔
283
                }
1✔
284

285
                hppDiscovery.AddChild(discSubj)
1✔
286
        }
287

288
        return apicapi.ApicSlice{hppIngress, hppEgress, hppDiscovery}
1✔
289
}
290

291
func (cont *AciController) initStaticNetPolObjs() {
1✔
292
        cont.apicConn.WriteApicObjects(cont.config.AciPrefix+"_np_static",
1✔
293
                cont.staticNetPolObjs())
1✔
294
}
1✔
295

296
func networkPolicyLogger(log *logrus.Logger,
297
        np *v1net.NetworkPolicy) *logrus.Entry {
1✔
298
        return log.WithFields(logrus.Fields{
1✔
299
                "namespace": np.ObjectMeta.Namespace,
1✔
300
                "name":      np.ObjectMeta.Name,
1✔
301
        })
1✔
302
}
1✔
303

304
func (cont *AciController) queueNetPolUpdateByKey(key string) {
1✔
305
        cont.netPolQueue.Add(key)
1✔
306
}
1✔
307

308
func (cont *AciController) queueNetPolUpdate(netpol *v1net.NetworkPolicy) {
1✔
309
        key, err := cache.MetaNamespaceKeyFunc(netpol)
1✔
310
        if err != nil {
1✔
311
                networkPolicyLogger(cont.log, netpol).
×
312
                        Error("Could not create network policy key: ", err)
×
313
                return
×
314
        }
×
315
        cont.netPolQueue.Add(key)
1✔
316
}
317

318
func (cont *AciController) peerMatchesPod(npNs string,
319
        peer *v1net.NetworkPolicyPeer, pod *v1.Pod, podNs *v1.Namespace) bool {
1✔
320
        if peer.PodSelector != nil && npNs == pod.ObjectMeta.Namespace {
2✔
321
                selector, err :=
1✔
322
                        metav1.LabelSelectorAsSelector(peer.PodSelector)
1✔
323
                if err != nil {
1✔
324
                        cont.log.Error("Could not parse pod selector: ", err)
×
325
                } else {
1✔
326
                        return selector.Matches(labels.Set(pod.ObjectMeta.Labels))
1✔
327
                }
1✔
328
        }
329
        if peer.NamespaceSelector != nil {
2✔
330
                selector, err :=
1✔
331
                        metav1.LabelSelectorAsSelector(peer.NamespaceSelector)
1✔
332
                if err != nil {
1✔
333
                        cont.log.Error("Could not parse namespace selector: ", err)
×
334
                } else {
1✔
335
                        return selector.Matches(labels.Set(podNs.ObjectMeta.Labels))
1✔
336
                }
1✔
337
        }
338
        return false
×
339
}
340

341
func ipsForPod(pod *v1.Pod) []string {
1✔
342
        var ips []string
1✔
343
        podIPsField := reflect.ValueOf(pod.Status).FieldByName("PodIPs")
1✔
344
        if podIPsField.IsValid() {
2✔
345
                if len(pod.Status.PodIPs) > 0 {
1✔
346
                        for _, ip := range pod.Status.PodIPs {
×
347
                                ips = append(ips, ip.IP)
×
348
                        }
×
349
                        return ips
×
350
                }
351
        }
352
        if pod.Status.PodIP != "" {
2✔
353
                return []string{pod.Status.PodIP}
1✔
354
        }
1✔
355
        return nil
1✔
356
}
357

358
func ipBlockToSubnets(ipblock *v1net.IPBlock) ([]string, error) {
1✔
359
        _, nw, err := net.ParseCIDR(ipblock.CIDR)
1✔
360
        if err != nil {
1✔
361
                return nil, err
×
362
        }
×
363
        ips := ipam.New()
1✔
364
        ips.AddSubnet(nw)
1✔
365
        for _, except := range ipblock.Except {
2✔
366
                _, nw, err = net.ParseCIDR(except)
1✔
367
                if err != nil {
1✔
368
                        return nil, err
×
369
                }
×
370
                ips.RemoveSubnet(nw)
1✔
371
        }
372
        var subnets []string
1✔
373
        for _, r := range ips.FreeList {
2✔
374
                ipnets := ipam.Range2Cidr(r.Start, r.End)
1✔
375
                for _, n := range ipnets {
2✔
376
                        subnets = append(subnets, n.String())
1✔
377
                }
1✔
378
        }
379
        return subnets, nil
1✔
380
}
381

382
func parseCIDR(sub string) *net.IPNet {
1✔
383
        _, netw, err := net.ParseCIDR(sub)
1✔
384
        if err == nil {
2✔
385
                return netw
1✔
386
        }
1✔
387
        ip := net.ParseIP(sub)
1✔
388
        if ip == nil {
1✔
389
                return nil
×
390
        }
×
391
        var mask net.IPMask
1✔
392
        if ip.To4() != nil {
2✔
393
                mask = net.CIDRMask(32, 32)
1✔
394
        } else if ip.To16() != nil {
3✔
395
                mask = net.CIDRMask(128, 128)
1✔
396
        } else {
1✔
397
                return nil
×
398
        }
×
399
        return &net.IPNet{
1✔
400
                IP:   ip,
1✔
401
                Mask: mask,
1✔
402
        }
1✔
403
}
404

405
func netEqual(a, b net.IPNet) bool {
1✔
406
        return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask)
1✔
407
}
1✔
408

409
func (cont *AciController) updateIpIndexEntry(index cidranger.Ranger,
410
        subnetStr string, key string, add bool) bool {
1✔
411
        cidr := parseCIDR(subnetStr)
1✔
412
        if cidr == nil {
1✔
413
                cont.log.WithFields(logrus.Fields{
×
414
                        "subnet": subnetStr,
×
415
                        "netpol": key,
×
416
                }).Warning("Invalid subnet or IP")
×
417
                return false
×
418
        }
×
419

420
        entries, err := index.CoveredNetworks(*cidr)
1✔
421
        if err != nil {
1✔
422
                cont.log.Error("Corrupted subnet index: ", err)
×
423
                return false
×
424
        }
×
425
        if add {
2✔
426
                for _, entryObj := range entries {
2✔
427
                        if netEqual(entryObj.Network(), *cidr) {
2✔
428
                                entry := entryObj.(*ipIndexEntry)
1✔
429
                                existing := entry.keys[key]
1✔
430
                                entry.keys[key] = true
1✔
431
                                return !existing
1✔
432
                        }
1✔
433
                }
434

435
                entry := &ipIndexEntry{
1✔
436
                        ipNet: *cidr,
1✔
437
                        keys: map[string]bool{
1✔
438
                                key: true,
1✔
439
                        },
1✔
440
                }
1✔
441
                index.Insert(entry)
1✔
442
                return true
1✔
443
        } else {
1✔
444
                var existing bool
1✔
445
                for _, entryObj := range entries {
2✔
446
                        entry := entryObj.(*ipIndexEntry)
1✔
447
                        if entry.keys[key] {
2✔
448
                                existing = true
1✔
449
                                delete(entry.keys, key)
1✔
450
                        }
1✔
451
                        if len(entry.keys) == 0 {
2✔
452
                                index.Remove(entry.Network())
1✔
453
                        }
1✔
454
                }
455
                return existing
1✔
456
        }
457
}
458

459
func (cont *AciController) updateIpIndex(index cidranger.Ranger,
460
        oldSubnets map[string]bool, newSubnets map[string]bool, key string) {
1✔
461
        for subStr := range oldSubnets {
2✔
462
                if newSubnets[subStr] {
2✔
463
                        continue
1✔
464
                }
465
                cont.updateIpIndexEntry(index, subStr, key, false)
1✔
466
        }
467
        for subStr := range newSubnets {
2✔
468
                if oldSubnets[subStr] {
2✔
469
                        continue
1✔
470
                }
471
                cont.updateIpIndexEntry(index, subStr, key, true)
1✔
472
        }
473
}
474

475
func (cont *AciController) updateTargetPortIndex(service bool, key string,
476
        oldPorts map[string]targetPort, newPorts map[string]targetPort) {
1✔
477
        for portkey := range oldPorts {
2✔
478
                if _, ok := newPorts[portkey]; ok {
1✔
479
                        continue
×
480
                }
481

482
                entry, ok := cont.targetPortIndex[portkey]
1✔
483
                if !ok {
1✔
484
                        continue
×
485
                }
486

487
                if service {
1✔
488
                        delete(entry.serviceKeys, key)
×
489
                } else {
1✔
490
                        delete(entry.networkPolicyKeys, key)
1✔
491
                }
1✔
492
                if len(entry.serviceKeys) == 0 && len(entry.networkPolicyKeys) == 0 {
2✔
493
                        delete(cont.targetPortIndex, portkey)
1✔
494
                }
1✔
495
        }
496
        for portkey, port := range newPorts {
2✔
497
                if _, ok := oldPorts[portkey]; ok {
1✔
498
                        continue
×
499
                }
500
                entry := cont.targetPortIndex[portkey]
1✔
501
                if entry == nil {
2✔
502
                        entry = &portIndexEntry{
1✔
503
                                port:              port,
1✔
504
                                serviceKeys:       make(map[string]bool),
1✔
505
                                networkPolicyKeys: make(map[string]bool),
1✔
506
                        }
1✔
507
                        cont.targetPortIndex[portkey] = entry
1✔
508
                } else {
2✔
509
                        entry.port.ports = port.ports
1✔
510
                }
1✔
511

512
                if service {
2✔
513
                        entry.serviceKeys[key] = true
1✔
514
                } else {
2✔
515
                        entry.networkPolicyKeys[key] = true
1✔
516
                }
1✔
517
        }
518
}
519

520
func (cont *AciController) getPortNumsFromPortName(podKeys []string, portName string) []int {
1✔
521
        var ports []int
1✔
522
        portmap := make(map[int]bool)
1✔
523
        for _, podkey := range podKeys {
2✔
524
                podobj, exists, err := cont.podIndexer.GetByKey(podkey)
1✔
525
                if exists && err == nil {
2✔
526
                        pod := podobj.(*v1.Pod)
1✔
527
                        port, err := k8util.LookupContainerPortNumberByName(*pod, portName)
1✔
528
                        if err != nil {
1✔
529
                                continue
×
530
                        }
531
                        if _, ok := portmap[int(port)]; !ok {
2✔
532
                                ports = append(ports, int(port))
1✔
533
                                portmap[int(port)] = true
1✔
534
                        }
1✔
535
                }
536
        }
537
        if len(ports) == 0 {
2✔
538
                cont.log.Infof("No matching portnumbers for portname %s: ", portName)
1✔
539
        }
1✔
540
        cont.log.Debug("PortName: ", portName, "Mapping port numbers: ", ports)
1✔
541
        return ports
1✔
542
}
543

544
// get a map of target ports for egress rules that have no "To" clause
545
func (cont *AciController) getNetPolTargetPorts(np *v1net.NetworkPolicy) map[string]targetPort {
1✔
546
        ports := make(map[string]targetPort)
1✔
547
        for _, egress := range np.Spec.Egress {
2✔
548
                if len(egress.To) != 0 && !isNamedPortPresenInNp(np) {
2✔
549
                        continue
1✔
550
                }
551
                for _, port := range egress.Ports {
2✔
552
                        if port.Port == nil {
1✔
553
                                continue
×
554
                        }
555
                        proto := v1.ProtocolTCP
1✔
556
                        if port.Protocol != nil {
2✔
557
                                proto = *port.Protocol
1✔
558
                        }
1✔
559
                        npKey, _ := cache.MetaNamespaceKeyFunc(np)
1✔
560
                        var key string
1✔
561
                        var portnums []int
1✔
562
                        if port.Port.Type == intstr.Int {
2✔
563
                                key = portProto(&proto) + "-num-" + port.Port.String()
1✔
564
                                portnums = append(portnums, port.Port.IntValue())
1✔
565
                        } else {
2✔
566
                                if len(egress.To) != 0 {
2✔
567
                                        // TODO optimize this code instead going through all matching pods every time
1✔
568
                                        podKeys := cont.netPolEgressPods.GetPodForObj(npKey)
1✔
569
                                        portnums = cont.getPortNumsFromPortName(podKeys, port.Port.String())
1✔
570
                                } else {
2✔
571
                                        ctrNmpEntry, ok := cont.ctrPortNameCache[port.Port.String()]
1✔
572
                                        if ok {
2✔
573
                                                for key := range ctrNmpEntry.ctrNmpToPods {
2✔
574
                                                        val := strings.Split(key, "-")
1✔
575
                                                        if len(val) != 2 {
1✔
576
                                                                continue
×
577
                                                        }
578
                                                        if val[0] == portProto(&proto) {
2✔
579
                                                                port, _ := strconv.Atoi(val[1])
1✔
580
                                                                portnums = append(portnums, port)
1✔
581
                                                        }
1✔
582
                                                }
583
                                        }
584
                                }
585
                                if len(portnums) == 0 {
2✔
586
                                        continue
1✔
587
                                }
588
                                key = portProto(&proto) + "-name-" + port.Port.String()
1✔
589
                        }
590
                        ports[key] = targetPort{
1✔
591
                                proto: proto,
1✔
592
                                ports: portnums,
1✔
593
                        }
1✔
594
                }
595
        }
596
        return ports
1✔
597
}
598

599
func (cont *AciController) getPeerRemoteSubnets(peers []v1net.NetworkPolicyPeer,
600
        namespace string, peerPods []*v1.Pod, peerNs map[string]*v1.Namespace,
601
        logger *logrus.Entry) ([]string, map[string]bool) {
1✔
602
        var remoteSubnets []string
1✔
603
        subnetMap := make(map[string]bool)
1✔
604
        if len(peers) > 0 {
2✔
605
                // only applies to matching pods
1✔
606
                for _, pod := range peerPods {
2✔
607
                        for peerIx := range peers {
2✔
608
                                if ns, ok := peerNs[pod.ObjectMeta.Namespace]; ok &&
1✔
609
                                        cont.peerMatchesPod(namespace,
1✔
610
                                                &peers[peerIx], pod, ns) {
2✔
611
                                        podIps := ipsForPod(pod)
1✔
612
                                        for _, ip := range podIps {
2✔
613
                                                if _, exists := subnetMap[ip]; !exists {
2✔
614
                                                        subnetMap[ip] = true
1✔
615
                                                        remoteSubnets = append(remoteSubnets, ip)
1✔
616
                                                }
1✔
617
                                        }
618
                                }
619
                        }
620
                }
621
                for _, peer := range peers {
2✔
622
                        if peer.IPBlock == nil {
2✔
623
                                continue
1✔
624
                        }
625
                        subs, err := ipBlockToSubnets(peer.IPBlock)
1✔
626
                        if err != nil {
1✔
627
                                logger.Warning("Invalid IPBlock in network policy rule: ", err)
×
628
                        } else {
1✔
629
                                for _, subnet := range subs {
2✔
630
                                        subnetMap[subnet] = true
1✔
631
                                }
1✔
632
                                remoteSubnets = append(remoteSubnets, subs...)
1✔
633
                        }
634
                }
635
        }
636
        sort.Strings(remoteSubnets)
1✔
637
        return remoteSubnets, subnetMap
1✔
638
}
639

640
func (cont *AciController) ipInPodSubnet(ip net.IP) bool {
×
641
        for _, podsubnet := range cont.config.PodSubnet {
×
642
                _, subnet, err := net.ParseCIDR(podsubnet)
×
643
                if err == nil && subnet != nil {
×
644
                        if subnet.Contains(ip) {
×
645
                                return true
×
646
                        }
×
647
                }
648
        }
649
        return false
×
650
}
651

652
func (cont *AciController) buildNetPolSubjRule(subj apicapi.ApicObject, ruleName,
653
        direction, ethertype, proto, port string, remoteSubnets []string,
654
        addPodSubnetAsRemIp bool) {
1✔
655
        ruleNameWithEtherType := fmt.Sprintf("%s-%s", ruleName, ethertype)
1✔
656
        rule := apicapi.NewHostprotRule(subj.GetDn(), ruleNameWithEtherType)
1✔
657
        rule.SetAttr("direction", direction)
1✔
658
        rule.SetAttr("ethertype", ethertype)
1✔
659
        if proto != "" {
2✔
660
                rule.SetAttr("protocol", proto)
1✔
661
        }
1✔
662

663
        if addPodSubnetAsRemIp {
1✔
664
                for _, podsubnet := range cont.config.PodSubnet {
×
665
                        _, subnet, err := net.ParseCIDR(podsubnet)
×
666
                        if err == nil && subnet != nil {
×
667
                                if (ethertype == "ipv4" && subnet.IP.To4() != nil) || (ethertype == "ipv6" && subnet.IP.To4() == nil) {
×
668
                                        rule.AddChild(apicapi.NewHostprotRemoteIp(rule.GetDn(), podsubnet))
×
669
                                }
×
670
                        }
671
                }
672
        }
673
        for _, subnetStr := range remoteSubnets {
2✔
674
                _, subnet, err := net.ParseCIDR(subnetStr)
1✔
675
                if err == nil && subnet != nil {
2✔
676
                        // subnetStr is a valid CIDR notation, check its IP version and add the subnet to the rule
1✔
677
                        if (ethertype == "ipv4" && subnet.IP.To4() != nil) || (ethertype == "ipv6" && subnet.IP.To4() == nil) {
2✔
678
                                rule.AddChild(apicapi.NewHostprotRemoteIp(rule.GetDn(), subnetStr))
1✔
679
                        }
1✔
680
                } else if ip := net.ParseIP(subnetStr); ip != nil {
2✔
681
                        if addPodSubnetAsRemIp && cont.ipInPodSubnet(ip) {
1✔
682
                                continue
×
683
                        }
684
                        if ethertype == "ipv6" && (ip.To16() != nil && ip.To4() == nil) || ethertype == "ipv4" && ip.To4() != nil {
2✔
685
                                rule.AddChild(apicapi.NewHostprotRemoteIp(rule.GetDn(), subnetStr))
1✔
686
                        }
1✔
687
                }
688
        }
689
        if port != "" {
2✔
690
                rule.SetAttr("toPort", port)
1✔
691
        }
1✔
692

693
        subj.AddChild(rule)
1✔
694
}
695

696
func (cont *AciController) buildNetPolSubjRules(ruleName string,
697
        subj apicapi.ApicObject, direction string, peers []v1net.NetworkPolicyPeer,
698
        remoteSubnets []string, ports []v1net.NetworkPolicyPort,
699
        logger *logrus.Entry, npKey string, np *v1net.NetworkPolicy,
700
        addPodSubnetAsRemIp bool) {
1✔
701
        if len(peers) > 0 && len(remoteSubnets) == 0 {
2✔
702
                // nonempty From matches no pods or IPBlocks; don't
1✔
703
                // create the rule
1✔
704
                return
1✔
705
        }
1✔
706
        if len(ports) == 0 {
2✔
707
                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
708
                        cont.buildNetPolSubjRule(subj, ruleName, direction,
1✔
709
                                "ipv4", "", "", remoteSubnets, addPodSubnetAsRemIp)
1✔
710
                }
1✔
711
                if !cont.configuredPodNetworkIps.V6.Empty() {
2✔
712
                        cont.buildNetPolSubjRule(subj, ruleName, direction,
1✔
713
                                "ipv6", "", "", remoteSubnets, addPodSubnetAsRemIp)
1✔
714
                }
1✔
715
        } else {
1✔
716
                for j := range ports {
2✔
717
                        proto := portProto(ports[j].Protocol)
1✔
718
                        var portList []string
1✔
719

1✔
720
                        if ports[j].Port != nil {
2✔
721
                                if ports[j].Port.Type == intstr.Int {
2✔
722
                                        portList = append(portList, ports[j].Port.String())
1✔
723
                                } else {
2✔
724
                                        var portnums []int
1✔
725
                                        if direction == "egress" {
2✔
726
                                                portnums = append(portnums, cont.getPortNums(&ports[j])...)
1✔
727
                                        } else {
2✔
728
                                                // TODO need to handle empty Pod Selector
1✔
729
                                                if reflect.DeepEqual(np.Spec.PodSelector, metav1.LabelSelector{}) {
1✔
730
                                                        logger.Warning("Empty PodSelctor for NamedPort is not supported in ingress direction"+
×
731
                                                                "port in network policy: ", ports[j].Port.String())
×
732
                                                        continue
×
733
                                                }
734
                                                podKeys := cont.netPolPods.GetPodForObj(npKey)
1✔
735
                                                portnums = cont.getPortNumsFromPortName(podKeys, ports[j].Port.String())
1✔
736
                                        }
737
                                        if len(portnums) == 0 {
2✔
738
                                                logger.Warning("There is no matching  ports in ingress/egress direction "+
1✔
739
                                                        "port in network policy: ", ports[j].Port.String())
1✔
740
                                                continue
1✔
741
                                        }
742
                                        for _, portnum := range portnums {
2✔
743
                                                portList = append(portList, strconv.Itoa(portnum))
1✔
744
                                        }
1✔
745
                                }
746
                        }
747
                        for i, port := range portList {
2✔
748
                                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
749
                                        cont.buildNetPolSubjRule(subj, ruleName+"_"+strconv.Itoa(i+j), direction,
1✔
750
                                                "ipv4", proto, port, remoteSubnets, addPodSubnetAsRemIp)
1✔
751
                                }
1✔
752
                                if !cont.configuredPodNetworkIps.V6.Empty() {
2✔
753
                                        cont.buildNetPolSubjRule(subj, ruleName+"_"+strconv.Itoa(i+j), direction,
1✔
754
                                                "ipv6", proto, port, remoteSubnets, addPodSubnetAsRemIp)
1✔
755
                                }
1✔
756
                        }
757
                        if len(portList) == 0 && proto != "" {
2✔
758
                                if !cont.configuredPodNetworkIps.V4.Empty() {
2✔
759
                                        cont.buildNetPolSubjRule(subj, ruleName+"_"+strconv.Itoa(j), direction,
1✔
760
                                                "ipv4", proto, "", remoteSubnets, addPodSubnetAsRemIp)
1✔
761
                                }
1✔
762
                                if !cont.configuredPodNetworkIps.V6.Empty() {
1✔
763
                                        cont.buildNetPolSubjRule(subj, ruleName+"_"+strconv.Itoa(j), direction,
×
764
                                                "ipv6", proto, "", remoteSubnets, addPodSubnetAsRemIp)
×
765
                                }
×
766
                        }
767
                }
768
        }
769
}
770

771
func (cont *AciController) getPortNums(port *v1net.NetworkPolicyPort) []int {
1✔
772
        portkey := portKey(port)
1✔
773
        cont.indexMutex.Lock()
1✔
774
        defer cont.indexMutex.Unlock()
1✔
775
        cont.log.Debug("PortKey1: ", portkey)
1✔
776
        entry := cont.targetPortIndex[portkey]
1✔
777
        var length int
1✔
778
        if entry == nil || len(entry.port.ports) == 0 {
2✔
779
                return []int{}
1✔
780
        }
1✔
781
        length = len(entry.port.ports)
1✔
782
        ports := make([]int, length)
1✔
783
        copy(ports, entry.port.ports)
1✔
784
        return ports
1✔
785
}
786
func portProto(protocol *v1.Protocol) string {
1✔
787
        proto := "tcp"
1✔
788
        if protocol != nil && *protocol == v1.ProtocolUDP {
2✔
789
                proto = "udp"
1✔
790
        } else if protocol != nil && *protocol == v1.ProtocolSCTP {
3✔
791
                proto = "sctp"
1✔
792
        }
1✔
793
        return proto
1✔
794
}
795

796
func portKey(p *v1net.NetworkPolicyPort) string {
1✔
797
        portType := ""
1✔
798
        port := ""
1✔
799
        if p != nil && p.Port != nil {
2✔
800
                if p.Port.Type == intstr.Int {
2✔
801
                        portType = "num"
1✔
802
                } else {
2✔
803
                        portType = "name"
1✔
804
                }
1✔
805
                port = p.Port.String()
1✔
806
                return portProto(p.Protocol) + "-" + portType + "-" + port
1✔
807
        }
808
        return ""
1✔
809
}
810

811
func checkEndpoints(subnetIndex cidranger.Ranger,
812
        addresses []v1.EndpointAddress) bool {
1✔
813
        for _, addr := range addresses {
2✔
814
                ip := net.ParseIP(addr.IP)
1✔
815
                if ip == nil {
1✔
816
                        return false
×
817
                }
×
818
                contains, err := subnetIndex.Contains(ip)
1✔
819
                if err != nil || !contains {
2✔
820
                        return false
1✔
821
                }
1✔
822
        }
823

824
        return true
1✔
825
}
826
func checkEndpointslices(subnetIndex cidranger.Ranger,
827
        addresses []string) bool {
1✔
828
        for _, addr := range addresses {
2✔
829
                ip := net.ParseIP(addr)
1✔
830
                if ip == nil {
1✔
831
                        return false
×
832
                }
×
833
                contains, err := subnetIndex.Contains(ip)
1✔
834
                if err != nil || !contains {
2✔
835
                        return false
1✔
836
                }
1✔
837
        }
838
        return true
1✔
839
}
840

841
type portRemoteSubnet struct {
842
        port           *v1net.NetworkPolicyPort
843
        subnetMap      map[string]bool
844
        hasNamedTarget bool
845
}
846

847
func updatePortRemoteSubnets(portRemoteSubs map[string]*portRemoteSubnet,
848
        portkey string, port *v1net.NetworkPolicyPort, subnetMap map[string]bool,
849
        hasNamedTarget bool) {
1✔
850
        if prs, ok := portRemoteSubs[portkey]; ok {
1✔
851
                for s := range subnetMap {
×
852
                        prs.subnetMap[s] = true
×
853
                }
×
854
                prs.hasNamedTarget = hasNamedTarget || prs.hasNamedTarget
×
855
        } else {
1✔
856
                portRemoteSubs[portkey] = &portRemoteSubnet{
1✔
857
                        port:           port,
1✔
858
                        subnetMap:      subnetMap,
1✔
859
                        hasNamedTarget: hasNamedTarget,
1✔
860
                }
1✔
861
        }
1✔
862
}
863

864
func portServiceAugmentKey(proto, port string) string {
1✔
865
        return proto + "-" + port
1✔
866
}
1✔
867

868
type portServiceAugment struct {
869
        proto string
870
        port  string
871
        ipMap map[string]bool
872
}
873

874
func updateServiceAugment(portAugments map[string]*portServiceAugment, proto, port, ip string) {
1✔
875
        key := portServiceAugmentKey(proto, port)
1✔
876
        if psa, ok := portAugments[key]; ok {
1✔
877
                psa.ipMap[ip] = true
×
878
        } else {
1✔
879
                portAugments[key] = &portServiceAugment{
1✔
880
                        proto: proto,
1✔
881
                        port:  port,
1✔
882
                        ipMap: map[string]bool{ip: true},
1✔
883
                }
1✔
884
        }
1✔
885
}
886

887
func updateServiceAugmentForService(portAugments map[string]*portServiceAugment,
888
        proto, port string, service *v1.Service) {
1✔
889
        if service.Spec.ClusterIP != "" {
2✔
890
                updateServiceAugment(portAugments,
1✔
891
                        proto, port, service.Spec.ClusterIP)
1✔
892
        }
1✔
893
        for _, ig := range service.Status.LoadBalancer.Ingress {
1✔
894
                if ig.IP == "" {
×
895
                        continue
×
896
                }
897
                updateServiceAugment(portAugments,
×
898
                        proto, port, ig.IP)
×
899
        }
900
}
901

902
// build service augment by matching peers against the endpoints ip
903
// index
904
func (cont *AciController) getServiceAugmentBySubnet(
905
        prs *portRemoteSubnet, portAugments map[string]*portServiceAugment,
906
        logger *logrus.Entry) {
1✔
907
        matchedServices := make(map[string]bool)
1✔
908
        subnetIndex := cidranger.NewPCTrieRanger()
1✔
909

1✔
910
        // find candidate service endpoints objects that include
1✔
911
        // endpoints selected by the egress rule
1✔
912
        cont.indexMutex.Lock()
1✔
913
        for sub := range prs.subnetMap {
2✔
914
                cidr := parseCIDR(sub)
1✔
915
                if cidr == nil {
1✔
916
                        continue
×
917
                }
918
                subnetIndex.Insert(cidranger.NewBasicRangerEntry(*cidr))
1✔
919

1✔
920
                entries, err := cont.endpointsIpIndex.CoveredNetworks(*cidr)
1✔
921
                if err != nil {
1✔
922
                        logger.Error("endpointsIpIndex corrupted: ", err)
×
923
                        continue
×
924
                }
925
                for _, entry := range entries {
2✔
926
                        e := entry.(*ipIndexEntry)
1✔
927
                        for servicekey := range e.keys {
2✔
928
                                matchedServices[servicekey] = true
1✔
929
                        }
1✔
930
                }
931
        }
932
        cont.indexMutex.Unlock()
1✔
933

1✔
934
        // if all endpoints are selected by egress rule, allow egress
1✔
935
        // to the service cluster IP as well as to the endpoints
1✔
936
        // themselves
1✔
937
        for servicekey := range matchedServices {
2✔
938
                serviceobj, _, err := cont.serviceIndexer.GetByKey(servicekey)
1✔
939
                if err != nil {
1✔
940
                        logger.Error("Could not lookup service for "+
×
941
                                servicekey+": ", err.Error())
×
942
                        continue
×
943
                }
944
                if serviceobj == nil {
1✔
945
                        continue
×
946
                }
947
                service := serviceobj.(*v1.Service)
1✔
948
                cont.serviceEndPoints.SetNpServiceAugmentForService(servicekey, service,
1✔
949
                        prs, portAugments, subnetIndex, logger)
1✔
950
        }
951
}
952

953
// build service augment by matching against services with a given
954
// target port
955
func (cont *AciController) getServiceAugmentByPort(
956
        prs *portRemoteSubnet, portAugments map[string]*portServiceAugment,
957
        logger *logrus.Entry) {
1✔
958
        // nil port means it matches against all ports.  If we're here, it
1✔
959
        // means this is a rule that matches all ports with all
1✔
960
        // destinations, so there's no need to augment anything.
1✔
961
        if prs.port == nil ||
1✔
962
                prs.port.Port == nil {
2✔
963
                return
1✔
964
        }
1✔
965

966
        portkey := portKey(prs.port)
1✔
967
        cont.indexMutex.Lock()
1✔
968
        entries := make(map[string]*portIndexEntry)
1✔
969
        entry := cont.targetPortIndex[portkey]
1✔
970
        if entry != nil && prs.port.Port.Type == intstr.String {
2✔
971
                for _, port := range entry.port.ports {
2✔
972
                        portstring := strconv.Itoa(port)
1✔
973
                        key := portProto(prs.port.Protocol) + "-" + "num" + "-" + portstring
1✔
974
                        portEntry := cont.targetPortIndex[key]
1✔
975
                        if portEntry != nil {
2✔
976
                                entries[portstring] = portEntry
1✔
977
                        }
1✔
978
                }
979
        } else if entry != nil {
2✔
980
                if len(entry.port.ports) > 0 {
2✔
981
                        entries[strconv.Itoa(entry.port.ports[0])] = entry
1✔
982
                }
1✔
983
        }
984
        for key, portentry := range entries {
2✔
985
                for servicekey := range portentry.serviceKeys {
2✔
986
                        serviceobj, _, err := cont.serviceIndexer.GetByKey(servicekey)
1✔
987
                        if err != nil {
1✔
988
                                logger.Error("Could not lookup service for "+
×
989
                                        servicekey+": ", err.Error())
×
990
                                continue
×
991
                        }
992
                        if serviceobj == nil {
1✔
993
                                continue
×
994
                        }
995
                        service := serviceobj.(*v1.Service)
1✔
996

1✔
997
                        for _, svcPort := range service.Spec.Ports {
2✔
998
                                if svcPort.Protocol != *prs.port.Protocol ||
1✔
999
                                        svcPort.TargetPort.String() !=
1✔
1000
                                                key {
1✔
1001
                                        continue
×
1002
                                }
1003
                                proto := portProto(&svcPort.Protocol)
1✔
1004
                                port := strconv.Itoa(int(svcPort.Port))
1✔
1005

1✔
1006
                                updateServiceAugmentForService(portAugments,
1✔
1007
                                        proto, port, service)
1✔
1008

1✔
1009
                                logger.WithFields(logrus.Fields{
1✔
1010
                                        "proto":   proto,
1✔
1011
                                        "port":    port,
1✔
1012
                                        "service": servicekey,
1✔
1013
                                }).Debug("Allowing egress for service by port")
1✔
1014
                        }
1015
                }
1016
        }
1017
        cont.indexMutex.Unlock()
1✔
1018
}
1019

1020
// The egress NetworkPolicy API were designed with the iptables
1021
// implementation in mind and don't contemplate that the layer 4 load
1022
// balancer could happen separately from the policy.  In particular,
1023
// it expects load balancer operations to be applied before the policy
1024
// is applied in both directions, so network policies would apply only
1025
// to pods and not to service IPs. This presents a problem for egress
1026
// policies on ACI since the security groups are applied before load
1027
// balancer operations when egressing, and after when ingressing.
1028
//
1029
// To solve this problem, we use some indexes to discover situations
1030
// when an egress policy covers all the endpoints associated with a
1031
// particular service, and automatically add a rule that allows egress
1032
// to the corresponding service cluster IP and ports.
1033
//
1034
// Note that this differs slightly from the behavior you'd see if you
1035
// applied the load balancer rule first: If the egress policy allows
1036
// access to a subset of the allowed IPs you'd see random failures
1037
// depending on which destination is chosen, while with this approach
1038
// it's all or nothing.  This should not impact any correctly-written
1039
// network policies.
1040
//
1041
// To do this, we work first from the set of pods and subnets matches
1042
// by the egress policy.  We use this to find using the
1043
// endpointsIpIndex all services that contain at least one of the
1044
// matched pods or subnets.  For each of these candidate services, we
1045
// find service ports for which _all_ referenced endpoints are allowed
1046
// by the egress policy.  Note that a service will have the service
1047
// port and the target port; the NetworkPolicy (confusingly) refers to
1048
// the target port.
1049
//
1050
// Once confirmed matches are found, we augment the egress policy with
1051
// extra rules to allow egress to the service IPs and service ports.
1052
//
1053
// As a special case, for rules that match everything, we also have a
1054
// backup index that works through ports which should allow more
1055
// efficient matching when allowing egress to all.
1056
func (cont *AciController) buildServiceAugment(subj apicapi.ApicObject,
1057
        portRemoteSubs map[string]*portRemoteSubnet, logger *logrus.Entry) {
1✔
1058
        portAugments := make(map[string]*portServiceAugment)
1✔
1059
        for _, prs := range portRemoteSubs {
2✔
1060
                // TODO ipv6
1✔
1061
                if prs.subnetMap["0.0.0.0/0"] {
2✔
1062
                        cont.getServiceAugmentByPort(prs, portAugments, logger)
1✔
1063
                } else {
2✔
1064
                        cont.getServiceAugmentBySubnet(prs, portAugments, logger)
1✔
1065
                }
1✔
1066
        }
1067
        for _, augment := range portAugments {
2✔
1068
                var remoteIpsv4 []string
1✔
1069
                var remoteIpsv6 []string
1✔
1070
                for ipstr := range augment.ipMap {
2✔
1071
                        ip := net.ParseIP(ipstr)
1✔
1072
                        if ip == nil {
1✔
1073
                                continue
×
1074
                        } else if ip.To4() != nil {
2✔
1075
                                remoteIpsv4 = append(remoteIpsv4, ipstr)
1✔
1076
                        } else if ip.To16() != nil {
3✔
1077
                                remoteIpsv6 = append(remoteIpsv6, ipstr)
1✔
1078
                        }
1✔
1079
                }
1080
                cont.log.Debug("Service Augment: ", augment)
1✔
1081
                if len(remoteIpsv4) > 0 {
2✔
1082
                        cont.buildNetPolSubjRule(subj,
1✔
1083
                                "service_"+augment.proto+"_"+augment.port,
1✔
1084
                                "egress", "ipv4", augment.proto, augment.port, remoteIpsv4, false)
1✔
1085
                }
1✔
1086
                if len(remoteIpsv6) > 0 {
2✔
1087
                        cont.buildNetPolSubjRule(subj,
1✔
1088
                                "service_"+augment.proto+"_"+augment.port,
1✔
1089
                                "egress", "ipv6", augment.proto, augment.port, remoteIpsv6, false)
1✔
1090
                }
1✔
1091
        }
1092
}
1093

1094
func isAllowAllForAllNamespaces(peers []v1net.NetworkPolicyPeer) bool {
1✔
1095
        addPodSubnetAsRemIp := false
1✔
1096
        if peers != nil && len(peers) > 0 {
2✔
1097
                var emptyPodSel, emptyNsSel bool
1✔
1098
                emptyPodSel = true
1✔
1099
                for _, peer := range peers {
2✔
1100
                        // namespaceSelector: {}
1✔
1101
                        if peer.NamespaceSelector != nil && peer.NamespaceSelector.MatchLabels == nil && peer.NamespaceSelector.MatchExpressions == nil {
1✔
1102
                                emptyNsSel = true
×
1103
                        }
×
1104
                        // podSelector has some fields
1105
                        if peer.PodSelector != nil && (peer.PodSelector.MatchLabels != nil || peer.PodSelector.MatchExpressions != nil) {
2✔
1106
                                emptyPodSel = false
1✔
1107
                        }
1✔
1108
                }
1109
                if emptyNsSel && emptyPodSel {
1✔
1110
                        addPodSubnetAsRemIp = true
×
1111
                }
×
1112
        }
1113
        return addPodSubnetAsRemIp
1✔
1114
}
1115

1116
func (cont *AciController) handleNetPolUpdate(np *v1net.NetworkPolicy) bool {
1✔
1117
        if cont.config.ChainedMode {
1✔
1118
                return false
×
1119
        }
×
1120
        key, err := cache.MetaNamespaceKeyFunc(np)
1✔
1121
        logger := networkPolicyLogger(cont.log, np)
1✔
1122
        if err != nil {
1✔
1123
                logger.Error("Could not create network policy key: ", err)
×
1124
                return false
×
1125
        }
×
1126

1127
        peerPodKeys := cont.netPolIngressPods.GetPodForObj(key)
1✔
1128
        peerPodKeys =
1✔
1129
                append(peerPodKeys, cont.netPolEgressPods.GetPodForObj(key)...)
1✔
1130
        var peerPods []*v1.Pod
1✔
1131
        peerNs := make(map[string]*v1.Namespace)
1✔
1132
        for _, podkey := range peerPodKeys {
2✔
1133
                podobj, exists, err := cont.podIndexer.GetByKey(podkey)
1✔
1134
                if exists && err == nil {
2✔
1135
                        pod := podobj.(*v1.Pod)
1✔
1136
                        if _, nsok := peerNs[pod.ObjectMeta.Namespace]; !nsok {
2✔
1137
                                nsobj, exists, err :=
1✔
1138
                                        cont.namespaceIndexer.GetByKey(pod.ObjectMeta.Namespace)
1✔
1139
                                if !exists || err != nil {
1✔
1140
                                        continue
×
1141
                                }
1142
                                peerNs[pod.ObjectMeta.Namespace] = nsobj.(*v1.Namespace)
1✔
1143
                        }
1144
                        peerPods = append(peerPods, pod)
1✔
1145
                }
1146
        }
1147
        ptypeset := make(map[v1net.PolicyType]bool)
1✔
1148
        for _, t := range np.Spec.PolicyTypes {
2✔
1149
                ptypeset[t] = true
1✔
1150
        }
1✔
1151
        var labelKey string
1✔
1152

1✔
1153
        if cont.config.HppOptimization {
2✔
1154
                hash, err := util.CreateHashFromNetPol(np)
1✔
1155
                if err != nil {
1✔
1156
                        logger.Error("Could not create hash from network policy: ", err)
×
1157
                        return false
×
1158
                }
×
1159
                labelKey = cont.aciNameForKey("np", hash)
1✔
1160
        } else {
1✔
1161
                labelKey = cont.aciNameForKey("np", key)
1✔
1162
        }
1✔
1163
        hpp := apicapi.NewHostprotPol(cont.config.AciPolicyTenant, labelKey)
1✔
1164
        // Generate ingress policies
1✔
1165
        if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeIngress] {
2✔
1166
                subjIngress :=
1✔
1167
                        apicapi.NewHostprotSubj(hpp.GetDn(), "networkpolicy-ingress")
1✔
1168

1✔
1169
                for i, ingress := range np.Spec.Ingress {
2✔
1170
                        addPodSubnetAsRemIp := isAllowAllForAllNamespaces(ingress.From)
1✔
1171
                        remoteSubnets, _ := cont.getPeerRemoteSubnets(ingress.From,
1✔
1172
                                np.Namespace, peerPods, peerNs, logger)
1✔
1173
                        cont.buildNetPolSubjRules(strconv.Itoa(i), subjIngress,
1✔
1174
                                "ingress", ingress.From, remoteSubnets, ingress.Ports, logger, key, np, addPodSubnetAsRemIp)
1✔
1175
                }
1✔
1176
                hpp.AddChild(subjIngress)
1✔
1177
        }
1178
        // Generate egress policies
1179
        if np.Spec.PolicyTypes == nil || ptypeset[v1net.PolicyTypeEgress] {
2✔
1180
                subjEgress :=
1✔
1181
                        apicapi.NewHostprotSubj(hpp.GetDn(), "networkpolicy-egress")
1✔
1182

1✔
1183
                portRemoteSubs := make(map[string]*portRemoteSubnet)
1✔
1184

1✔
1185
                for i, egress := range np.Spec.Egress {
2✔
1186
                        addPodSubnetAsRemIp := isAllowAllForAllNamespaces(egress.To)
1✔
1187
                        remoteSubnets, subnetMap := cont.getPeerRemoteSubnets(egress.To,
1✔
1188
                                np.Namespace, peerPods, peerNs, logger)
1✔
1189
                        cont.buildNetPolSubjRules(strconv.Itoa(i), subjEgress,
1✔
1190
                                "egress", egress.To, remoteSubnets, egress.Ports, logger, key, np, addPodSubnetAsRemIp)
1✔
1191

1✔
1192
                        // creating a rule to egress to all on a given port needs
1✔
1193
                        // to enable access to any service IPs/ports that have
1✔
1194
                        // that port as their target port.
1✔
1195
                        if len(egress.To) == 0 {
2✔
1196
                                subnetMap = map[string]bool{
1✔
1197
                                        "0.0.0.0/0": true,
1✔
1198
                                }
1✔
1199
                        }
1✔
1200
                        for idx := range egress.Ports {
2✔
1201
                                port := egress.Ports[idx]
1✔
1202
                                portkey := portKey(&port)
1✔
1203
                                updatePortRemoteSubnets(portRemoteSubs, portkey, &port, subnetMap,
1✔
1204
                                        port.Port != nil && port.Port.Type == intstr.Int)
1✔
1205
                        }
1✔
1206
                        if len(egress.Ports) == 0 {
2✔
1207
                                updatePortRemoteSubnets(portRemoteSubs, "", nil, subnetMap,
1✔
1208
                                        false)
1✔
1209
                        }
1✔
1210
                }
1211
                cont.buildServiceAugment(subjEgress, portRemoteSubs, logger)
1✔
1212
                hpp.AddChild(subjEgress)
1✔
1213
        }
1214
        if cont.config.HppOptimization {
2✔
1215
                cont.addToHppCache(labelKey, key, apicapi.ApicSlice{hpp})
1✔
1216
        }
1✔
1217
        cont.apicConn.WriteApicObjects(labelKey, apicapi.ApicSlice{hpp})
1✔
1218
        return false
1✔
1219
}
1220

1221
func (cont *AciController) addToHppCache(labelKey, key string, hpp apicapi.ApicSlice) {
1✔
1222
        cont.indexMutex.Lock()
1✔
1223
        hppRef, ok := cont.hppRef[labelKey]
1✔
1224
        if ok {
2✔
1225
                var found bool
1✔
1226
                for _, npkey := range hppRef.Npkeys {
2✔
1227
                        if npkey == key {
2✔
1228
                                found = true
1✔
1229
                                break
1✔
1230
                        }
1231
                }
1232
                if !found {
1✔
1233
                        hppRef.RefCount++
×
1234
                        hppRef.Npkeys = append(hppRef.Npkeys, key)
×
1235
                }
×
1236
                hppRef.HppObj = hpp
1✔
1237
                cont.hppRef[labelKey] = hppRef
1✔
1238
        } else {
1✔
1239
                var newHppRef hppReference
1✔
1240
                newHppRef.RefCount++
1✔
1241
                newHppRef.HppObj = hpp
1✔
1242
                newHppRef.Npkeys = append(newHppRef.Npkeys, key)
1✔
1243
                cont.hppRef[labelKey] = newHppRef
1✔
1244
        }
1✔
1245
        cont.indexMutex.Unlock()
1✔
1246
}
1247

1248
func (cont *AciController) removeFromHppCache(np *v1net.NetworkPolicy, key string) (string, bool) {
×
1249
        var labelKey string
×
1250
        var noRef bool
×
1251
        hash, err := util.CreateHashFromNetPol(np)
×
1252
        if err != nil {
×
1253
                cont.log.Error("Could not create hash from network policy: ", err)
×
1254
                cont.log.Error("Failed to remove np from hpp cache")
×
1255
                return labelKey, noRef
×
1256
        }
×
1257
        labelKey = cont.aciNameForKey("np", hash)
×
1258
        cont.indexMutex.Lock()
×
1259
        hppRef, ok := cont.hppRef[labelKey]
×
1260
        if ok {
×
1261
                for i, npkey := range hppRef.Npkeys {
×
1262
                        if npkey == key {
×
1263
                                hppRef.Npkeys = append(hppRef.Npkeys[:i], hppRef.Npkeys[i+1:]...)
×
1264
                                hppRef.RefCount--
×
1265
                                break
×
1266
                        }
1267
                }
1268
                if hppRef.RefCount > 0 {
×
1269
                        cont.hppRef[labelKey] = hppRef
×
1270
                } else {
×
1271
                        delete(cont.hppRef, labelKey)
×
1272
                        noRef = true
×
1273
                }
×
1274
        }
1275
        cont.indexMutex.Unlock()
×
1276
        return labelKey, noRef
×
1277
}
1278

1279
func getNetworkPolicyEgressIpBlocks(np *v1net.NetworkPolicy) map[string]bool {
1✔
1280
        subnets := make(map[string]bool)
1✔
1281
        for _, egress := range np.Spec.Egress {
2✔
1282
                for _, to := range egress.To {
2✔
1283
                        if to.IPBlock != nil && to.IPBlock.CIDR != "" {
2✔
1284
                                subnets[to.IPBlock.CIDR] = true
1✔
1285
                        }
1✔
1286
                }
1287
        }
1288
        return subnets
1✔
1289
}
1290

1291
func (cont *AciController) networkPolicyAdded(obj interface{}) {
1✔
1292
        np := obj.(*v1net.NetworkPolicy)
1✔
1293
        npkey, err := cache.MetaNamespaceKeyFunc(np)
1✔
1294
        if err != nil {
1✔
1295
                networkPolicyLogger(cont.log, np).
×
1296
                        Error("Could not create network policy key: ", err)
×
1297
                return
×
1298
        }
×
1299
        cont.writeApicNP(npkey, np)
1✔
1300
        if cont.config.ChainedMode {
1✔
1301
                return
×
1302
        }
×
1303
        cont.netPolPods.UpdateSelectorObj(obj)
1✔
1304
        cont.netPolIngressPods.UpdateSelectorObj(obj)
1✔
1305
        cont.netPolEgressPods.UpdateSelectorObj(obj)
1✔
1306
        cont.indexMutex.Lock()
1✔
1307
        subnets := getNetworkPolicyEgressIpBlocks(np)
1✔
1308
        cont.updateIpIndex(cont.netPolSubnetIndex, nil, subnets, npkey)
1✔
1309

1✔
1310
        ports := cont.getNetPolTargetPorts(np)
1✔
1311
        cont.updateTargetPortIndex(false, npkey, nil, ports)
1✔
1312
        if isNamedPortPresenInNp(np) {
2✔
1313
                cont.nmPortNp[npkey] = true
1✔
1314
        }
1✔
1315
        cont.indexMutex.Unlock()
1✔
1316
        cont.queueNetPolUpdateByKey(npkey)
1✔
1317
}
1318

1319
func (cont *AciController) writeApicNP(npKey string, np *v1net.NetworkPolicy) {
1✔
1320
        if cont.config.LBType == lbTypeAci {
2✔
1321
                return
1✔
1322
        }
1✔
1323
        if cont.config.ChainedMode {
×
1324
                return
×
1325
        }
×
1326

1327
        npObj := apicapi.NewVmmInjectedNwPol(cont.vmmDomainProvider(),
×
1328
                cont.config.AciVmmDomain, cont.config.AciVmmController,
×
1329
                np.ObjectMeta.Namespace, np.ObjectMeta.Name)
×
1330
        setAttr := func(name, attr string) {
×
1331
                if attr != "" {
×
1332
                        npObj.SetAttr(name, attr)
×
1333
                }
×
1334
        }
1335
        setAttr("ingress", ingressStr(np))
×
1336
        setAttr("egress", egressStr(np))
×
1337
        key := cont.aciNameForKey("NwPol", npKey)
×
1338
        cont.log.Debugf("Writing %s %+v", key, npObj)
×
1339
        cont.apicConn.WriteApicObjects(key, apicapi.ApicSlice{npObj})
×
1340
}
1341

1342
func peersToStr(peers []v1net.NetworkPolicyPeer) string {
×
1343
        pStr := "["
×
1344
        for _, p := range peers {
×
1345
                if p.IPBlock != nil {
×
1346
                        pStr += p.IPBlock.CIDR
×
1347
                        if len(p.IPBlock.Except) != 0 {
×
1348
                                pStr += "[except"
×
1349
                                for _, e := range p.IPBlock.Except {
×
1350
                                        pStr += fmt.Sprintf("-%s", e)
×
1351
                                }
×
1352
                                pStr += "]"
×
1353
                        }
1354
                        pStr += "+"
×
1355
                }
1356
        }
1357

1358
        pStr = strings.TrimSuffix(pStr, "+")
×
1359
        pStr += "]"
×
1360
        return pStr
×
1361
}
1362

1363
func portsToStr(ports []v1net.NetworkPolicyPort) string {
×
1364
        pStr := "["
×
1365

×
1366
        for _, p := range ports {
×
1367
                if p.Protocol != nil {
×
1368
                        pStr += string(*p.Protocol)
×
1369
                }
×
1370
                if p.Port != nil {
×
1371
                        pStr += ":" + p.Port.String()
×
1372
                }
×
1373
                pStr += "+"
×
1374
        }
1375

1376
        pStr = strings.TrimSuffix(pStr, "+")
×
1377
        pStr += "]"
×
1378
        return pStr
×
1379
}
1380

1381
func ingressStr(np *v1net.NetworkPolicy) string {
×
1382
        iStr := ""
×
1383
        for _, rule := range np.Spec.Ingress {
×
1384
                iStr += peersToStr(rule.From)
×
1385
                iStr += ":" + portsToStr(rule.Ports)
×
1386
                iStr += "+"
×
1387
        }
×
1388
        iStr = strings.TrimSuffix(iStr, "+")
×
1389
        return iStr
×
1390
}
1391

1392
func egressStr(np *v1net.NetworkPolicy) string {
×
1393
        eStr := ""
×
1394
        for _, rule := range np.Spec.Egress {
×
1395
                eStr += peersToStr(rule.To)
×
1396
                eStr += ":" + portsToStr(rule.Ports)
×
1397
                eStr += "+"
×
1398
        }
×
1399
        eStr = strings.TrimSuffix(eStr, "+")
×
1400
        return eStr
×
1401
}
1402

1403
func (cont *AciController) networkPolicyChanged(oldobj interface{},
1404
        newobj interface{}) {
×
1405
        oldnp := oldobj.(*v1net.NetworkPolicy)
×
1406
        newnp := newobj.(*v1net.NetworkPolicy)
×
1407
        npkey, err := cache.MetaNamespaceKeyFunc(newnp)
×
1408
        if err != nil {
×
1409
                networkPolicyLogger(cont.log, newnp).
×
1410
                        Error("Could not create network policy key: ", err)
×
1411
                return
×
1412
        }
×
1413

1414
        if cont.config.HppOptimization {
×
1415
                cont.removeFromHppCache(oldnp, npkey)
×
1416
        }
×
1417

1418
        cont.writeApicNP(npkey, newnp)
×
1419
        cont.indexMutex.Lock()
×
1420
        oldSubnets := getNetworkPolicyEgressIpBlocks(oldnp)
×
1421
        newSubnets := getNetworkPolicyEgressIpBlocks(newnp)
×
1422
        cont.updateIpIndex(cont.netPolSubnetIndex, oldSubnets, newSubnets, npkey)
×
1423

×
1424
        oldPorts := cont.getNetPolTargetPorts(oldnp)
×
1425
        newPorts := cont.getNetPolTargetPorts(newnp)
×
1426
        cont.updateTargetPortIndex(false, npkey, oldPorts, newPorts)
×
1427
        cont.indexMutex.Unlock()
×
1428

×
1429
        if !reflect.DeepEqual(oldnp.Spec.PodSelector, newnp.Spec.PodSelector) {
×
1430
                cont.netPolPods.UpdateSelectorObjNoCallback(newobj)
×
1431
        }
×
1432
        if !reflect.DeepEqual(oldnp.Spec.PolicyTypes, newnp.Spec.PolicyTypes) {
×
1433
                peerPodKeys := cont.netPolPods.GetPodForObj(npkey)
×
1434
                for _, podkey := range peerPodKeys {
×
1435
                        cont.podQueue.Add(podkey)
×
1436
                }
×
1437
        }
1438
        var queue bool
×
1439
        if !reflect.DeepEqual(oldnp.Spec.Ingress, newnp.Spec.Ingress) {
×
1440
                cont.netPolIngressPods.UpdateSelectorObjNoCallback(newobj)
×
1441
                queue = true
×
1442
        }
×
1443
        if !reflect.DeepEqual(oldnp.Spec.Egress, newnp.Spec.Egress) {
×
1444
                cont.netPolEgressPods.UpdateSelectorObjNoCallback(newobj)
×
1445
                queue = true
×
1446
        }
×
1447
        if queue {
×
1448
                cont.queueNetPolUpdateByKey(npkey)
×
1449
        }
×
1450
}
1451

1452
func (cont *AciController) networkPolicyDeleted(obj interface{}) {
1✔
1453
        np, isNetworkpolicy := obj.(*v1net.NetworkPolicy)
1✔
1454
        if !isNetworkpolicy {
1✔
1455
                deletedState, ok := obj.(cache.DeletedFinalStateUnknown)
×
1456
                if !ok {
×
1457
                        networkPolicyLogger(cont.log, np).
×
1458
                                Error("Received unexpected object: ", obj)
×
1459
                        return
×
1460
                }
×
1461
                np, ok = deletedState.Obj.(*v1net.NetworkPolicy)
×
1462
                if !ok {
×
1463
                        networkPolicyLogger(cont.log, np).
×
1464
                                Error("DeletedFinalStateUnknown contained non-Networkpolicy object: ", deletedState.Obj)
×
1465
                        return
×
1466
                }
×
1467
        }
1468
        npkey, err := cache.MetaNamespaceKeyFunc(np)
1✔
1469
        if err != nil {
1✔
1470
                networkPolicyLogger(cont.log, np).
×
1471
                        Error("Could not create network policy key: ", err)
×
1472
                return
×
1473
        }
×
1474

1475
        if cont.config.LBType != lbTypeAci {
1✔
1476
                cont.apicConn.ClearApicObjects(cont.aciNameForKey("NwPol", npkey))
×
1477
        }
×
1478

1479
        var labelKey string
1✔
1480
        var noHppRef bool
1✔
1481
        if cont.config.HppOptimization {
1✔
1482
                labelKey, noHppRef = cont.removeFromHppCache(np, npkey)
×
1483
        } else {
1✔
1484
                labelKey = cont.aciNameForKey("np", npkey)
1✔
1485
                noHppRef = true
1✔
1486
        }
1✔
1487

1488
        cont.indexMutex.Lock()
1✔
1489
        subnets := getNetworkPolicyEgressIpBlocks(np)
1✔
1490
        cont.updateIpIndex(cont.netPolSubnetIndex, subnets, nil, npkey)
1✔
1491

1✔
1492
        ports := cont.getNetPolTargetPorts(np)
1✔
1493
        cont.updateTargetPortIndex(false, npkey, ports, nil)
1✔
1494
        if isNamedPortPresenInNp(np) {
2✔
1495
                delete(cont.nmPortNp, npkey)
1✔
1496
        }
1✔
1497
        cont.indexMutex.Unlock()
1✔
1498

1✔
1499
        cont.netPolPods.DeleteSelectorObj(obj)
1✔
1500
        cont.netPolIngressPods.DeleteSelectorObj(obj)
1✔
1501
        cont.netPolEgressPods.DeleteSelectorObj(obj)
1✔
1502
        if noHppRef && labelKey != "" {
2✔
1503
                cont.apicConn.ClearApicObjects(labelKey)
1✔
1504
        }
1✔
1505
}
1506

1507
func (sep *serviceEndpoint) SetNpServiceAugmentForService(servicekey string, service *v1.Service, prs *portRemoteSubnet,
1508
        portAugments map[string]*portServiceAugment, subnetIndex cidranger.Ranger, logger *logrus.Entry) {
1✔
1509
        cont := sep.cont
1✔
1510
        endpointsobj, _, err := cont.endpointsIndexer.GetByKey(servicekey)
1✔
1511
        if err != nil {
1✔
1512
                logger.Error("Could not lookup endpoints for "+
×
1513
                        servicekey+": ", err.Error())
×
1514
                return
×
1515
        }
×
1516
        if endpointsobj == nil {
1✔
1517
                return
×
1518
        }
×
1519
        endpoints := endpointsobj.(*v1.Endpoints)
1✔
1520
        portstrings := make(map[string]bool)
1✔
1521
        ports := cont.getPortNums(prs.port)
1✔
1522
        for _, port := range ports {
2✔
1523
                portstrings[strconv.Itoa(port)] = true
1✔
1524
        }
1✔
1525
        for _, svcPort := range service.Spec.Ports {
2✔
1526
                _, ok := portstrings[svcPort.TargetPort.String()]
1✔
1527
                if prs.port != nil &&
1✔
1528
                        (svcPort.Protocol != *prs.port.Protocol || !ok) {
1✔
1529
                        // egress rule does not match service target port
×
1530
                        continue
×
1531
                }
1532
                for _, subset := range endpoints.Subsets {
2✔
1533
                        var foundEpPort *v1.EndpointPort
1✔
1534
                        for ix := range subset.Ports {
2✔
1535
                                if subset.Ports[ix].Name == svcPort.Name ||
1✔
1536
                                        (len(service.Spec.Ports) == 1 &&
1✔
1537
                                                subset.Ports[ix].Name == "") {
2✔
1538
                                        foundEpPort = &subset.Ports[ix]
1✔
1539
                                        break
1✔
1540
                                }
1541
                        }
1542
                        if foundEpPort == nil {
1✔
1543
                                continue
×
1544
                        }
1545

1546
                        incomplete := false
1✔
1547
                        incomplete = incomplete ||
1✔
1548
                                !checkEndpoints(subnetIndex, subset.Addresses)
1✔
1549
                        incomplete = incomplete || !checkEndpoints(subnetIndex,
1✔
1550
                                subset.NotReadyAddresses)
1✔
1551

1✔
1552
                        if incomplete {
2✔
1553
                                continue
1✔
1554
                        }
1555

1556
                        proto := portProto(&foundEpPort.Protocol)
1✔
1557
                        port := strconv.Itoa(int(svcPort.Port))
1✔
1558
                        updateServiceAugmentForService(portAugments,
1✔
1559
                                proto, port, service)
1✔
1560

1✔
1561
                        logger.WithFields(logrus.Fields{
1✔
1562
                                "proto":   proto,
1✔
1563
                                "port":    port,
1✔
1564
                                "service": servicekey,
1✔
1565
                        }).Debug("Allowing egress for service by subnet match")
1✔
1566
                }
1567
        }
1568
}
1569

1570
func (seps *serviceEndpointSlice) SetNpServiceAugmentForService(servicekey string, service *v1.Service,
1571
        prs *portRemoteSubnet, portAugments map[string]*portServiceAugment,
1572
        subnetIndex cidranger.Ranger, logger *logrus.Entry) {
1✔
1573
        cont := seps.cont
1✔
1574
        portstrings := make(map[string]bool)
1✔
1575
        ports := cont.getPortNums(prs.port)
1✔
1576
        for _, port := range ports {
2✔
1577
                portstrings[strconv.Itoa(port)] = true
1✔
1578
        }
1✔
1579
        label := map[string]string{"kubernetes.io/service-name": service.ObjectMeta.Name}
1✔
1580
        selector := labels.SelectorFromSet(label)
1✔
1581
        cache.ListAllByNamespace(cont.endpointSliceIndexer, service.ObjectMeta.Namespace, selector,
1✔
1582
                func(endpointSliceobj interface{}) {
2✔
1583
                        endpointSlices := endpointSliceobj.(*discovery.EndpointSlice)
1✔
1584
                        for _, svcPort := range service.Spec.Ports {
2✔
1585
                                _, ok := portstrings[svcPort.TargetPort.String()]
1✔
1586
                                if prs.port != nil &&
1✔
1587
                                        (svcPort.Protocol != *prs.port.Protocol || !ok) {
1✔
1588
                                        // egress rule does not match service target port
×
1589
                                        continue
×
1590
                                }
1591
                                var foundEpPort *discovery.EndpointPort
1✔
1592
                                for ix := range endpointSlices.Ports {
2✔
1593
                                        if *endpointSlices.Ports[ix].Name == svcPort.Name ||
1✔
1594
                                                (len(service.Spec.Ports) == 1 &&
1✔
1595
                                                        *endpointSlices.Ports[ix].Name == "") {
2✔
1596
                                                foundEpPort = &endpointSlices.Ports[ix]
1✔
1597
                                                cont.log.Debug("Found EpPort: ", foundEpPort)
1✔
1598
                                                break
1✔
1599
                                        }
1600
                                }
1601
                                if foundEpPort == nil {
1✔
1602
                                        return
×
1603
                                }
×
1604
                                // @FIXME for non ready address
1605
                                incomplete := false
1✔
1606
                                for _, endpoint := range endpointSlices.Endpoints {
2✔
1607
                                        incomplete = incomplete || !checkEndpointslices(subnetIndex, endpoint.Addresses)
1✔
1608
                                }
1✔
1609
                                if incomplete {
2✔
1610
                                        continue
1✔
1611
                                }
1612
                                proto := portProto(foundEpPort.Protocol)
1✔
1613
                                port := strconv.Itoa(int(svcPort.Port))
1✔
1614
                                cont.log.Debug("updateServiceAugmentForService: ", service)
1✔
1615
                                updateServiceAugmentForService(portAugments,
1✔
1616
                                        proto, port, service)
1✔
1617

1✔
1618
                                logger.WithFields(logrus.Fields{
1✔
1619
                                        "proto":   proto,
1✔
1620
                                        "port":    port,
1✔
1621
                                        "service": servicekey,
1✔
1622
                                }).Debug("Allowing egress for service by subnet match")
1✔
1623
                        }
1624
                })
1625
}
1626

1627
func isNamedPortPresenInNp(np *v1net.NetworkPolicy) bool {
1✔
1628
        for _, egress := range np.Spec.Egress {
2✔
1629
                for _, p := range egress.Ports {
2✔
1630
                        if p.Port.Type == intstr.String {
2✔
1631
                                return true
1✔
1632
                        }
1✔
1633
                }
1634
        }
1635
        return false
1✔
1636
}
1637

1638
func (cont *AciController) checkPodNmpMatchesNp(npkey, podkey string) bool {
1✔
1639
        podobj, exists, err := cont.podIndexer.GetByKey(podkey)
1✔
1640
        if err != nil {
1✔
1641
                return false
×
1642
        }
×
1643
        if !exists || podobj == nil {
1✔
UNCOV
1644
                return false
×
UNCOV
1645
        }
×
1646
        pod := podobj.(*v1.Pod)
1✔
1647
        npobj, npexists, nperr := cont.networkPolicyIndexer.GetByKey(npkey)
1✔
1648
        if npexists && nperr == nil && npobj != nil {
2✔
1649
                np := npobj.(*v1net.NetworkPolicy)
1✔
1650
                for _, egress := range np.Spec.Egress {
2✔
1651
                        for _, p := range egress.Ports {
2✔
1652
                                if p.Port.Type == intstr.String {
2✔
1653
                                        _, err := k8util.LookupContainerPortNumberByName(*pod, p.Port.String())
1✔
1654
                                        if err == nil {
2✔
1655
                                                return true
1✔
1656
                                        }
1✔
1657
                                }
1658
                        }
1659
                }
1660
        }
1661
        return false
1✔
1662
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc