github.com/cilium/cilium@v1.16.2/pkg/hubble/parser/common/endpoint.go (about)

     1  // SPDX-License-Identifier: Apache-2.0
     2  // Copyright Authors of Hubble
     3  
     4  package common
     5  
     6  import (
     7  	"net/netip"
     8  
     9  	"github.com/sirupsen/logrus"
    10  
    11  	pb "github.com/cilium/cilium/api/v1/flow"
    12  	"github.com/cilium/cilium/pkg/hubble/parser/getters"
    13  	"github.com/cilium/cilium/pkg/identity"
    14  	k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io"
    15  	"github.com/cilium/cilium/pkg/k8s/utils"
    16  	"github.com/cilium/cilium/pkg/logging"
    17  	"github.com/cilium/cilium/pkg/logging/logfields"
    18  	"github.com/cilium/cilium/pkg/time"
    19  )
    20  
    21  type DatapathContext struct {
    22  	SrcIP                 netip.Addr
    23  	SrcLabelID            uint32
    24  	DstIP                 netip.Addr
    25  	DstLabelID            uint32
    26  	TraceObservationPoint pb.TraceObservationPoint
    27  }
    28  
    29  type EndpointResolver struct {
    30  	log            logrus.FieldLogger
    31  	logLimiter     logging.Limiter
    32  	endpointGetter getters.EndpointGetter
    33  	identityGetter getters.IdentityGetter
    34  	ipGetter       getters.IPGetter
    35  }
    36  
    37  func NewEndpointResolver(
    38  	log logrus.FieldLogger,
    39  	endpointGetter getters.EndpointGetter,
    40  	identityGetter getters.IdentityGetter,
    41  	ipGetter getters.IPGetter,
    42  ) *EndpointResolver {
    43  	return &EndpointResolver{
    44  		log:            log,
    45  		logLimiter:     logging.NewLimiter(30*time.Second, 1),
    46  		endpointGetter: endpointGetter,
    47  		identityGetter: identityGetter,
    48  		ipGetter:       ipGetter,
    49  	}
    50  }
    51  
    52  func (r *EndpointResolver) ResolveEndpoint(ip netip.Addr, datapathSecurityIdentity uint32, context DatapathContext) *pb.Endpoint {
    53  	// The datapathSecurityIdentity parameter is the numeric security identity
    54  	// obtained from the datapath.
    55  	// The numeric identity from the datapath can differ from the one we obtain
    56  	// from user-space (e.g. the endpoint manager or the IP cache), because
    57  	// the identity could have changed between the time the datapath event was
    58  	// created and the time the event reaches the Hubble parser.
    59  	// To aid in troubleshooting, we want to preserve what the datapath observed
    60  	// when it made the policy decision.
    61  	resolveIdentityConflict := func(userspaceID identity.NumericIdentity, isLocalEndpoint bool) uint32 {
    62  		// if the datapath did not provide an identity (e.g. FROM_LXC trace
    63  		// points), use what we have in the user-space cache
    64  		datapathID := identity.NumericIdentity(datapathSecurityIdentity)
    65  		if datapathID == identity.IdentityUnknown {
    66  			return userspaceID.Uint32()
    67  		}
    68  
    69  		// Log any identity discrepancies, unless or this is a known case where
    70  		// Hubble does not have the full picture (see inline comments below each case)
    71  		// or we've hit the log rate limit
    72  		if datapathID != userspaceID {
    73  			if context.TraceObservationPoint == pb.TraceObservationPoint_TO_OVERLAY &&
    74  				ip == context.SrcIP && datapathID.Uint32() == context.SrcLabelID &&
    75  				datapathID == identity.ReservedIdentityRemoteNode &&
    76  				userspaceID == identity.ReservedIdentityHost {
    77  				// Ignore
    78  				//
    79  				// When encapsulating a packet for sending via the overlay network, if the source
    80  				// seclabel = HOST_ID, then we reassign seclabel with LOCAL_NODE_ID and then send
    81  				// a trace notify.
    82  			} else if context.TraceObservationPoint == pb.TraceObservationPoint_TO_OVERLAY &&
    83  				ip == context.SrcIP && datapathID.Uint32() == context.SrcLabelID &&
    84  				!datapathID.IsReservedIdentity() && userspaceID == identity.ReservedIdentityHost {
    85  				// Ignore
    86  				//
    87  				// An IPSec encrypted packet will have the local cilium_host IP as the source
    88  				// address, but the datapath seclabel will be the one of the source pod.
    89  			} else if context.TraceObservationPoint == pb.TraceObservationPoint_FROM_ENDPOINT &&
    90  				ip == context.SrcIP && datapathID.Uint32() == context.SrcLabelID &&
    91  				(datapathID == identity.ReservedIdentityHealth || !datapathID.IsReservedIdentity()) &&
    92  				userspaceID.IsWorld() {
    93  				// Ignore
    94  				//
    95  				// Sometimes packets from endpoint link-local addresses are intercepted by
    96  				// cil_from_container. Because link-local addresses are not stored in the IP cache,
    97  				// Hubble assigns them WORLD_ID.
    98  			} else if context.TraceObservationPoint == pb.TraceObservationPoint_FROM_HOST &&
    99  				ip == context.SrcIP && datapathID.Uint32() == context.SrcLabelID &&
   100  				datapathID.IsWorld() && userspaceID == identity.ReservedIdentityKubeAPIServer {
   101  				// Ignore
   102  				//
   103  				// When a pod sends a packet to the Kubernetes API, its IP is masqueraded and then
   104  				// when it receives a response and the masquerade is reversed, cil_from_host
   105  				// determines that the source ID is WORLD_ID because there is no packet mark.
   106  			} else if (context.TraceObservationPoint == pb.TraceObservationPoint_FROM_HOST ||
   107  				context.TraceObservationPoint == pb.TraceObservationPoint_TO_OVERLAY) &&
   108  				ip == context.SrcIP && datapathID.Uint32() == context.SrcLabelID &&
   109  				isLocalEndpoint && userspaceID == identity.ReservedIdentityHost {
   110  				// Ignore
   111  				//
   112  				// When proxied packets (via Cilium DNS proxy) are sent from the host their source
   113  				// IP is that of the host, yet their security identity is retained from the
   114  				// original source pod.
   115  			} else if context.TraceObservationPoint == pb.TraceObservationPoint_TO_ENDPOINT &&
   116  				ip == context.SrcIP && datapathID.Uint32() == context.SrcLabelID &&
   117  				!datapathID.IsReservedIdentity() &&
   118  				(userspaceID == identity.ReservedIdentityHost || userspaceID == identity.ReservedIdentityRemoteNode) {
   119  				// Ignore
   120  				//
   121  				// When proxied packets (via Cilium DNS proxy) are received by the destination
   122  				// host their source IP is that of the proxy, yet their security identity is
   123  				// retained from the original source pod. This is a similar case to #4, but on the
   124  				// receiving side.
   125  			} else if r.logLimiter.Allow() {
   126  				r.log.WithFields(logrus.Fields{
   127  					"datapath-identity":  datapathID.Uint32(),
   128  					"userspace-identity": userspaceID.Uint32(),
   129  					"context":            logfields.Repr(context),
   130  					logfields.IPAddr:     ip,
   131  				}).Debugf("stale identity observed")
   132  			}
   133  		}
   134  
   135  		return datapathID.Uint32()
   136  	}
   137  
   138  	// for local endpoints, use the available endpoint information
   139  	if r.endpointGetter != nil {
   140  		if ep, ok := r.endpointGetter.GetEndpointInfo(ip); ok {
   141  			epIdentity := resolveIdentityConflict(ep.GetIdentity(), true)
   142  			e := &pb.Endpoint{
   143  				ID:        uint32(ep.GetID()),
   144  				Identity:  epIdentity,
   145  				Namespace: ep.GetK8sNamespace(),
   146  				Labels:    SortAndFilterLabels(r.log, ep.GetLabels(), identity.NumericIdentity(epIdentity)),
   147  				PodName:   ep.GetK8sPodName(),
   148  			}
   149  			if pod := ep.GetPod(); pod != nil {
   150  				workload, workloadTypeMeta, ok := utils.GetWorkloadMetaFromPod(pod)
   151  				if ok {
   152  					e.Workloads = []*pb.Workload{{Kind: workloadTypeMeta.Kind, Name: workload.Name}}
   153  				}
   154  			}
   155  			return e
   156  		}
   157  	}
   158  
   159  	// for remote endpoints, assemble the information via ip and identity
   160  	numericIdentity := datapathSecurityIdentity
   161  	var namespace, podName string
   162  	if r.ipGetter != nil {
   163  		if ipIdentity, ok := r.ipGetter.LookupSecIDByIP(ip); ok {
   164  			numericIdentity = resolveIdentityConflict(ipIdentity.ID, false)
   165  		}
   166  		if meta := r.ipGetter.GetK8sMetadata(ip); meta != nil {
   167  			namespace, podName = meta.Namespace, meta.PodName
   168  		}
   169  	}
   170  	var labels []string
   171  	var clusterName string
   172  	if r.identityGetter != nil {
   173  		if id, err := r.identityGetter.GetIdentity(numericIdentity); err != nil {
   174  			r.log.WithError(err).WithField("identity", numericIdentity).
   175  				Debug("failed to resolve identity")
   176  		} else {
   177  			labels = SortAndFilterLabels(r.log, id.Labels.GetModel(), identity.NumericIdentity(numericIdentity))
   178  			clusterName = (id.Labels[k8sConst.PolicyLabelCluster]).Value
   179  		}
   180  	}
   181  
   182  	return &pb.Endpoint{
   183  		Identity:    numericIdentity,
   184  		ClusterName: clusterName,
   185  		Namespace:   namespace,
   186  		Labels:      labels,
   187  		PodName:     podName,
   188  	}
   189  }