istio.io/istio@v0.0.0-20240520182934-d79c90f27776/istioctl/pkg/authz/listener.go (about) 1 // Copyright Istio Authors 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 package authz 16 17 import ( 18 "fmt" 19 "io" 20 "regexp" 21 "sort" 22 "strings" 23 "text/tabwriter" 24 25 listener "github.com/envoyproxy/go-control-plane/envoy/config/listener/v3" 26 rbacpb "github.com/envoyproxy/go-control-plane/envoy/config/rbac/v3" 27 rbachttp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/rbac/v3" 28 hcm "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3" 29 rbactcp "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/rbac/v3" 30 "google.golang.org/protobuf/proto" 31 32 "istio.io/istio/pkg/log" 33 "istio.io/istio/pkg/wellknown" 34 ) 35 36 const ( 37 anonymousName = "_anonymous_match_nothing_" 38 ) 39 40 // Matches the policy name in RBAC filter config with format like ns[default]-policy[some-policy]-rule[1]. 41 var re = regexp.MustCompile(`ns\[(.+)\]-policy\[(.+)\]-rule\[(.+)\]`) 42 43 type filterChain struct { 44 rbacHTTP []*rbachttp.RBAC 45 rbacTCP []*rbactcp.RBAC 46 } 47 48 type parsedListener struct { 49 filterChains []*filterChain 50 } 51 52 func getFilterConfig(filter *listener.Filter, out proto.Message) error { 53 switch c := filter.ConfigType.(type) { 54 case *listener.Filter_TypedConfig: 55 if err := c.TypedConfig.UnmarshalTo(out); err != nil { 56 return err 57 } 58 } 59 return nil 60 } 61 62 func getHTTPConnectionManager(filter *listener.Filter) *hcm.HttpConnectionManager { 63 cm := &hcm.HttpConnectionManager{} 64 if err := getFilterConfig(filter, cm); err != nil { 65 log.Errorf("failed to get HTTP connection manager config: %s", err) 66 return nil 67 } 68 return cm 69 } 70 71 func getHTTPFilterConfig(filter *hcm.HttpFilter, out proto.Message) error { 72 switch c := filter.ConfigType.(type) { 73 case *hcm.HttpFilter_TypedConfig: 74 if err := c.TypedConfig.UnmarshalTo(out); err != nil { 75 return err 76 } 77 } 78 return nil 79 } 80 81 func parse(listeners []*listener.Listener) []*parsedListener { 82 var parsedListeners []*parsedListener 83 for _, l := range listeners { 84 parsed := &parsedListener{} 85 for _, fc := range l.FilterChains { 86 parsedFC := &filterChain{} 87 for _, filter := range fc.Filters { 88 switch filter.Name { 89 case wellknown.HTTPConnectionManager, "envoy.http_connection_manager": 90 if cm := getHTTPConnectionManager(filter); cm != nil { 91 for _, httpFilter := range cm.GetHttpFilters() { 92 switch httpFilter.GetName() { 93 case wellknown.HTTPRoleBasedAccessControl: 94 rbacHTTP := &rbachttp.RBAC{} 95 if err := getHTTPFilterConfig(httpFilter, rbacHTTP); err != nil { 96 log.Errorf("found RBAC HTTP filter but failed to parse: %s", err) 97 } else { 98 parsedFC.rbacHTTP = append(parsedFC.rbacHTTP, rbacHTTP) 99 } 100 } 101 } 102 } 103 case wellknown.RoleBasedAccessControl: 104 rbacTCP := &rbactcp.RBAC{} 105 if err := getFilterConfig(filter, rbacTCP); err != nil { 106 log.Errorf("found RBAC network filter but failed to parse: %s", err) 107 } else { 108 parsedFC.rbacTCP = append(parsedFC.rbacTCP, rbacTCP) 109 } 110 } 111 } 112 113 parsed.filterChains = append(parsed.filterChains, parsedFC) 114 } 115 parsedListeners = append(parsedListeners, parsed) 116 } 117 return parsedListeners 118 } 119 120 func extractName(name string) (string, string) { 121 // parts[1] is the namespace, parts[2] is the policy name, parts[3] is the rule index. 122 parts := re.FindStringSubmatch(name) 123 if len(parts) != 4 { 124 log.Errorf("failed to parse policy name: %s", name) 125 return "", "" 126 } 127 return fmt.Sprintf("%s.%s", parts[2], parts[1]), parts[3] 128 } 129 130 // Print prints the AuthorizationPolicy in the listener. 131 func Print(writer io.Writer, listeners []*listener.Listener) { 132 parsedListeners := parse(listeners) 133 if parsedListeners == nil { 134 return 135 } 136 137 actionToPolicy := map[rbacpb.RBAC_Action]map[string]struct{}{} 138 policyToRule := map[string]map[string]struct{}{} 139 140 addPolicy := func(action rbacpb.RBAC_Action, name string, rule string) { 141 if actionToPolicy[action] == nil { 142 actionToPolicy[action] = map[string]struct{}{} 143 } 144 if policyToRule[name] == nil { 145 policyToRule[name] = map[string]struct{}{} 146 } 147 actionToPolicy[action][name] = struct{}{} 148 policyToRule[name][rule] = struct{}{} 149 } 150 151 for _, parsed := range parsedListeners { 152 for _, fc := range parsed.filterChains { 153 for _, rbacHTTP := range fc.rbacHTTP { 154 action := rbacHTTP.GetRules().GetAction() 155 for name := range rbacHTTP.GetRules().GetPolicies() { 156 nameOfPolicy, indexOfRule := extractName(name) 157 addPolicy(action, nameOfPolicy, indexOfRule) 158 } 159 if len(rbacHTTP.GetRules().GetPolicies()) == 0 { 160 addPolicy(action, anonymousName, "0") 161 } 162 } 163 for _, rbacTCP := range fc.rbacTCP { 164 action := rbacTCP.GetRules().GetAction() 165 for name := range rbacTCP.GetRules().GetPolicies() { 166 nameOfPolicy, indexOfRule := extractName(name) 167 addPolicy(action, nameOfPolicy, indexOfRule) 168 } 169 if len(rbacTCP.GetRules().GetPolicies()) == 0 { 170 addPolicy(action, anonymousName, "0") 171 } 172 } 173 } 174 } 175 176 buf := strings.Builder{} 177 buf.WriteString("ACTION\tAuthorizationPolicy\tRULES\n") 178 for _, action := range []rbacpb.RBAC_Action{rbacpb.RBAC_DENY, rbacpb.RBAC_ALLOW, rbacpb.RBAC_LOG} { 179 if names, ok := actionToPolicy[action]; ok { 180 sortedNames := make([]string, 0, len(names)) 181 for name := range names { 182 sortedNames = append(sortedNames, name) 183 } 184 sort.Strings(sortedNames) 185 for _, name := range sortedNames { 186 buf.WriteString(fmt.Sprintf("%s\t%s\t%d\n", action, name, len(policyToRule[name]))) 187 } 188 } 189 } 190 191 w := new(tabwriter.Writer).Init(writer, 0, 8, 3, ' ', 0) 192 if _, err := fmt.Fprint(w, buf.String()); err != nil { 193 log.Errorf("failed to print output: %s", err) 194 } 195 _ = w.Flush() 196 }