github.com/cilium/cilium@v1.16.2/pkg/policy/correlation/correlation.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package correlation 5 6 import ( 7 "github.com/sirupsen/logrus" 8 9 flowpb "github.com/cilium/cilium/api/v1/flow" 10 "github.com/cilium/cilium/pkg/hubble/parser/getters" 11 "github.com/cilium/cilium/pkg/identity" 12 k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" 13 "github.com/cilium/cilium/pkg/labels" 14 "github.com/cilium/cilium/pkg/logging" 15 "github.com/cilium/cilium/pkg/logging/logfields" 16 monitorAPI "github.com/cilium/cilium/pkg/monitor/api" 17 "github.com/cilium/cilium/pkg/policy" 18 "github.com/cilium/cilium/pkg/policy/trafficdirection" 19 "github.com/cilium/cilium/pkg/source" 20 "github.com/cilium/cilium/pkg/u8proto" 21 ) 22 23 var logger = logging.DefaultLogger.WithField(logfields.LogSubsys, "hubble-flow-policy-correlation") 24 25 // CorrelatePolicy updates the IngressAllowedBy/EgressAllowedBy fields on the 26 // provided flow. 27 func CorrelatePolicy(endpointGetter getters.EndpointGetter, f *flowpb.Flow) { 28 if f.GetEventType().GetType() != int32(monitorAPI.MessageTypePolicyVerdict) { 29 // If it's not a policy verdict, we don't care. 30 return 31 } 32 33 // We are only interested in flows which are either allowed (i.e. the verdict is either 34 // FORWARDED or REDIRECTED) or explicitly denied (i.e. DROPPED, and matched by a deny policy), 35 // since we cannot usefully annotate the verdict otherwise. (Put differently, which policy 36 // should be listed in {in|e}gress_denied_by for an unmatched flow?) 37 verdict := f.GetVerdict() 38 allowed := verdict == flowpb.Verdict_FORWARDED || verdict == flowpb.Verdict_REDIRECTED 39 denied := verdict == flowpb.Verdict_DROPPED && f.GetDropReasonDesc() == flowpb.DropReason_POLICY_DENY 40 if !(allowed || denied) { 41 return 42 } 43 44 // extract fields relevant for looking up the policy 45 direction, endpointID, remoteIdentity, proto, dport := extractFlowKey(f) 46 if dport == 0 || proto == 0 { 47 logger.WithField(logfields.EndpointID, endpointID).Debug("failed to extract flow key") 48 return 49 } 50 51 // obtain reference to endpoint on which the policy verdict was taken 52 epInfo, ok := endpointGetter.GetEndpointInfoByID(endpointID) 53 if !ok { 54 logger.WithField(logfields.EndpointID, endpointID).Debug("failed to lookup endpoint") 55 return 56 } 57 58 derivedFrom, rev, ok := lookupPolicyForKey(epInfo, policy.Key{ 59 Identity: uint32(remoteIdentity), 60 DestPort: dport, 61 Nexthdr: uint8(proto), 62 TrafficDirection: uint8(direction), 63 }, f.GetPolicyMatchType()) 64 if !ok { 65 logger.WithFields(logrus.Fields{ 66 logfields.Identity: remoteIdentity, 67 logfields.Port: dport, 68 logfields.Protocol: proto, 69 logfields.TrafficDirection: direction, 70 }).Debug("unable to find policy for policy verdict notification") 71 return 72 } 73 74 rules := toProto(derivedFrom, rev) 75 switch { 76 case direction == trafficdirection.Egress && allowed: 77 f.EgressAllowedBy = rules 78 case direction == trafficdirection.Egress && denied: 79 f.EgressDeniedBy = rules 80 case direction == trafficdirection.Ingress && allowed: 81 f.IngressAllowedBy = rules 82 case direction == trafficdirection.Ingress && denied: 83 f.IngressDeniedBy = rules 84 } 85 } 86 87 func extractFlowKey(f *flowpb.Flow) ( 88 direction trafficdirection.TrafficDirection, 89 endpointID uint16, 90 remoteIdentity identity.NumericIdentity, 91 proto u8proto.U8proto, 92 dport uint16) { 93 94 switch f.GetTrafficDirection() { 95 case flowpb.TrafficDirection_EGRESS: 96 direction = trafficdirection.Egress 97 // We only get a uint32 because proto has no 16-bit types. 98 endpointID = uint16(f.GetSource().GetID()) 99 remoteIdentity = identity.NumericIdentity(f.GetDestination().GetIdentity()) 100 case flowpb.TrafficDirection_INGRESS: 101 direction = trafficdirection.Ingress 102 endpointID = uint16(f.GetDestination().GetID()) 103 remoteIdentity = identity.NumericIdentity(f.GetSource().GetIdentity()) 104 default: 105 direction = trafficdirection.Invalid 106 endpointID = 0 107 remoteIdentity = identity.IdentityUnknown 108 } 109 110 if tcp := f.GetL4().GetTCP(); tcp != nil { 111 proto = u8proto.TCP 112 dport = uint16(tcp.GetDestinationPort()) 113 } else if udp := f.GetL4().GetUDP(); udp != nil { 114 proto = u8proto.UDP 115 dport = uint16(udp.GetDestinationPort()) 116 } else if icmpv4 := f.GetL4().GetICMPv4(); icmpv4 != nil { 117 proto = u8proto.ICMP 118 dport = uint16(icmpv4.Type) 119 } else if icmpv6 := f.GetL4().GetICMPv6(); icmpv6 != nil { 120 proto = u8proto.ICMPv6 121 dport = uint16(icmpv6.Type) 122 } else if sctp := f.GetL4().GetSCTP(); sctp != nil { 123 proto = u8proto.SCTP 124 dport = uint16(sctp.GetDestinationPort()) 125 } else { 126 proto = u8proto.ANY 127 dport = 0 128 } 129 130 return 131 } 132 133 func lookupPolicyForKey(ep getters.EndpointInfo, key policy.Key, matchType uint32) (derivedFrom labels.LabelArrayList, rev uint64, ok bool) { 134 switch matchType { 135 case monitorAPI.PolicyMatchL3L4, monitorAPI.PolicyMatchL4Only: 136 // Check for L4 policy rules 137 derivedFrom, rev, ok = ep.GetRealizedPolicyRuleLabelsForKey(key) 138 case monitorAPI.PolicyMatchL3Only: 139 // Check for L3 policy rules 140 derivedFrom, rev, ok = ep.GetRealizedPolicyRuleLabelsForKey(policy.Key{ 141 Identity: key.Identity, 142 DestPort: 0, 143 InvertedPortMask: 0xffff, // this is a wildcard 144 Nexthdr: 0, 145 TrafficDirection: key.TrafficDirection, 146 }) 147 case monitorAPI.PolicyMatchAll: 148 // Check for allow-all policy rules 149 derivedFrom, rev, ok = ep.GetRealizedPolicyRuleLabelsForKey(policy.Key{ 150 Identity: 0, 151 DestPort: 0, 152 InvertedPortMask: 0xffff, // this is a wildcard 153 Nexthdr: 0, 154 TrafficDirection: key.TrafficDirection, 155 }) 156 } 157 158 return derivedFrom, rev, ok 159 } 160 161 func toProto(derivedFrom labels.LabelArrayList, rev uint64) (policies []*flowpb.Policy) { 162 for i, lbl := range derivedFrom { 163 // derivedFrom may contain a duplicate policies if the policy had 164 // multiple that contributed to the same policy map entry. 165 // We can easily detect the duplicates here, because derivedFrom is 166 // sorted. 167 if i > 0 && lbl.Equals(derivedFrom[i-1]) { 168 continue 169 } 170 171 policy := &flowpb.Policy{ 172 Labels: lbl.GetModel(), 173 Revision: rev, 174 } 175 176 var ns, name string 177 for _, l := range lbl { 178 if l.Source == string(source.Kubernetes) { 179 switch l.Key { 180 case k8sConst.PolicyLabelName: 181 name = l.Value 182 case k8sConst.PolicyLabelNamespace: 183 ns = l.Value 184 } 185 } 186 187 if name != "" && ns != "" { 188 policy.Name = name 189 policy.Namespace = ns 190 break 191 } 192 } 193 194 policies = append(policies, policy) 195 } 196 197 return policies 198 }