-
Notifications
You must be signed in to change notification settings - Fork 474
/
no_host_checker.go
117 lines (97 loc) · 3.2 KB
/
no_host_checker.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
package authorization
import (
"fmt"
"reflect"
"strings"
core_v1 "k8s.io/api/core/v1"
"github.com/kiali/kiali/kubernetes"
"github.com/kiali/kiali/models"
)
type NoHostChecker struct {
AuthorizationPolicy kubernetes.IstioObject
Namespace string
Namespaces models.Namespaces
ServiceEntries map[string][]string
Services []core_v1.Service
}
func (n NoHostChecker) Check() ([]*models.IstioCheck, bool) {
checks, valid := make([]*models.IstioCheck, 0), true
// Getting rules array. If not present, quitting validation.
rulesStct, ok := n.AuthorizationPolicy.GetSpec()["rules"]
if !ok {
return checks, valid
}
// Getting slice of Rules. Quitting if not an slice.
rules := reflect.ValueOf(rulesStct)
if rules.Kind() != reflect.Slice {
return checks, valid
}
for ruleIdx := 0; ruleIdx < rules.Len(); ruleIdx++ {
rule, ok := rules.Index(ruleIdx).Interface().(map[string]interface{})
if !ok || rule == nil {
continue
}
if rule["to"] != nil {
fromChecks, fromValid := n.validateHost(ruleIdx, rule["to"])
checks = append(checks, fromChecks...)
valid = valid && fromValid
}
}
return checks, valid
}
func (n NoHostChecker) validateHost(ruleIdx int, to interface{}) ([]*models.IstioCheck, bool) {
toSl, ok := to.([]interface{})
if !ok {
return nil, true
}
checks, valid := make([]*models.IstioCheck, 0, len(toSl)), true
for toIdx, toStc := range toSl {
toMap, ok := toStc.(map[string]interface{})
if !ok {
continue
}
sourceMap, ok := toMap["operation"].(map[string]interface{})
if !ok {
continue
}
hostList, ok := sourceMap["hosts"].([]interface{})
if !ok {
continue
}
for hostIdx, h := range hostList {
if dHost, ok := h.(string); ok {
fqdn := kubernetes.GetHost(dHost, n.AuthorizationPolicy.GetObjectMeta().Namespace, n.AuthorizationPolicy.GetObjectMeta().ClusterName, n.Namespaces.GetNames())
if !n.hasMatchingService(fqdn, n.AuthorizationPolicy.GetObjectMeta().Namespace) {
path := fmt.Sprintf("spec/rules[%d]/to[%d]/operation/hosts[%d]", ruleIdx, toIdx, hostIdx)
if fqdn.Namespace != n.AuthorizationPolicy.GetObjectMeta().Namespace && fqdn.Namespace != "" {
validation := models.Build("validation.unable.cross-namespace", path)
valid = valid && true
checks = append(checks, &validation)
} else {
validation := models.Build("authorizationpolicy.nodest.matchingregistry", path)
valid = false
checks = append(checks, &validation)
}
}
}
}
}
return checks, valid
}
func (n NoHostChecker) hasMatchingService(host kubernetes.Host, itemNamespace string) bool {
// Covering 'servicename.namespace' host format scenario
localSvc, localNs := kubernetes.ParseTwoPartHost(host)
// Check wildcard hosts - needs to match "*" and "*.suffix" also..
if strings.HasPrefix(host.Service, "*") && localNs == itemNamespace {
return true
}
// Only find matches for workloads and services in the same namespace
if localNs == itemNamespace {
// Check ServiceNames
if matches := kubernetes.HasMatchingServices(localSvc, n.Services); matches {
return matches
}
}
// Otherwise Check ServiceEntries
return kubernetes.HasMatchingServiceEntries(host.Service, n.ServiceEntries)
}