github.com/fafucoder/cilium@v1.6.11/pkg/endpoint/endpoint_status.go (about)

     1  // Copyright 2019 Authors of Cilium
     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 endpoint
    16  
    17  import (
    18  	"sort"
    19  	"strings"
    20  	"time"
    21  
    22  	"github.com/cilium/cilium/api/v1/models"
    23  	"github.com/cilium/cilium/pkg/identity"
    24  	identitycache "github.com/cilium/cilium/pkg/identity/cache"
    25  	cilium_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
    26  	"github.com/cilium/cilium/pkg/labels"
    27  	"github.com/cilium/cilium/pkg/node"
    28  	"github.com/cilium/cilium/pkg/option"
    29  	"github.com/cilium/cilium/pkg/policy"
    30  	"github.com/cilium/cilium/pkg/policy/trafficdirection"
    31  )
    32  
    33  func getEndpointStatusControllers(status *models.EndpointStatus) (controllers cilium_v2.ControllerList) {
    34  	for _, c := range status.Controllers {
    35  		if c.Status == nil {
    36  			continue
    37  		}
    38  
    39  		if c.Status.ConsecutiveFailureCount > 0 {
    40  			s := cilium_v2.ControllerStatus{
    41  				Configuration: c.Configuration,
    42  				Name:          c.Name,
    43  				UUID:          string(c.UUID),
    44  				Status: cilium_v2.ControllerStatusStatus{
    45  					ConsecutiveFailureCount: c.Status.ConsecutiveFailureCount,
    46  					FailureCount:            c.Status.FailureCount,
    47  					LastFailureMsg:          c.Status.LastFailureMsg,
    48  					LastFailureTimestamp:    c.Status.LastFailureTimestamp.String(),
    49  					LastSuccessTimestamp:    c.Status.LastSuccessTimestamp.String(),
    50  					SuccessCount:            c.Status.SuccessCount,
    51  				},
    52  			}
    53  			if controllers == nil {
    54  				controllers = cilium_v2.ControllerList{s}
    55  			} else {
    56  				controllers = append(controllers, s)
    57  			}
    58  		}
    59  	}
    60  
    61  	if controllers != nil {
    62  		controllers.Sort()
    63  	}
    64  
    65  	return
    66  }
    67  
    68  func (e *Endpoint) getEndpointStatusLog() (log []*models.EndpointStatusChange) {
    69  	added := 0
    70  
    71  	if s := e.Status; s != nil {
    72  		s.indexMU.RLock()
    73  		defer s.indexMU.RUnlock()
    74  
    75  		for i := s.lastIndex(); ; i-- {
    76  			if i < 0 {
    77  				i = maxLogs - 1
    78  			}
    79  			if i < len(s.Log) && s.Log[i] != nil {
    80  				l := &models.EndpointStatusChange{
    81  					Timestamp: s.Log[i].Timestamp.Format(time.RFC3339),
    82  					Code:      s.Log[i].Status.Code.String(),
    83  					Message:   s.Log[i].Status.Msg,
    84  					State:     models.EndpointState(s.Log[i].Status.State),
    85  				}
    86  
    87  				if strings.ToLower(l.Code) != models.EndpointStatusChangeCodeOk {
    88  					if log == nil {
    89  						log = []*models.EndpointStatusChange{l}
    90  					} else {
    91  						log = append(log, l)
    92  					}
    93  
    94  					// Limit the number of endpoint log
    95  					// entries to keep the size of the
    96  					// EndpointStatus low.
    97  					added++
    98  					if added >= cilium_v2.EndpointStatusLogEntries {
    99  						break
   100  					}
   101  				}
   102  			}
   103  			if i == s.Index {
   104  				break
   105  			}
   106  		}
   107  	}
   108  	return
   109  }
   110  
   111  func getEndpointIdentity(status *models.EndpointStatus) (identity *cilium_v2.EndpointIdentity) {
   112  	if status.Identity != nil {
   113  		identity = &cilium_v2.EndpointIdentity{
   114  			ID: status.Identity.ID,
   115  		}
   116  
   117  		identity.Labels = make([]string, len(status.Identity.Labels))
   118  		copy(identity.Labels, status.Identity.Labels)
   119  		sort.Strings(identity.Labels)
   120  	}
   121  	return
   122  }
   123  
   124  func getEndpointNetworking(status *models.EndpointStatus) (networking *cilium_v2.EndpointNetworking) {
   125  	if status.Networking != nil {
   126  		networking = &cilium_v2.EndpointNetworking{
   127  			Addressing: make(cilium_v2.AddressPairList, len(status.Networking.Addressing)),
   128  		}
   129  
   130  		if option.Config.EnableIPv4 {
   131  			networking.NodeIP = node.GetExternalIPv4().String()
   132  		} else {
   133  			networking.NodeIP = node.GetIPv6().String()
   134  		}
   135  
   136  		i := 0
   137  		for _, pair := range status.Networking.Addressing {
   138  			networking.Addressing[i] = &cilium_v2.AddressPair{
   139  				IPV4: pair.IPV4,
   140  				IPV6: pair.IPV6,
   141  			}
   142  			i++
   143  		}
   144  
   145  		networking.Addressing.Sort()
   146  	}
   147  	return
   148  }
   149  
   150  // updateLabels inserts the labels correnspoding to the specified identity into
   151  // the AllowedIdentityTuple.
   152  func updateLabels(allowedIdentityTuple *cilium_v2.AllowedIdentityTuple, secID identity.NumericIdentity) {
   153  	// IdentityUnknown denotes that this is an L4-only BPF
   154  	// allow, so it applies to all identities. In this case
   155  	// we should skip resolving the labels, because the
   156  	// value 0 does not denote an allow for the "unknown"
   157  	// identity, but instead an allow of all identities for
   158  	// that port.
   159  	if secID != identity.IdentityUnknown {
   160  		identity := identitycache.LookupIdentityByID(secID)
   161  		if identity != nil {
   162  			var l labels.Labels
   163  			if identity.CIDRLabel != nil {
   164  				l = identity.CIDRLabel
   165  			} else {
   166  				l = identity.Labels
   167  			}
   168  
   169  			allowedIdentityTuple.IdentityLabels = l.StringMap()
   170  		}
   171  	}
   172  }
   173  
   174  // populateResponseWithPolicyKey inserts an AllowedIdentityTuple element into 'policy'
   175  // which corresponds to the specified 'desiredPolicy'.
   176  func populateResponseWithPolicyKey(policy *cilium_v2.EndpointPolicy, policyKey *policy.Key) {
   177  	allowedIdentityTuple := cilium_v2.AllowedIdentityTuple{
   178  		DestPort: policyKey.DestPort,
   179  		Protocol: policyKey.Nexthdr,
   180  		Identity: uint64(policyKey.Identity),
   181  	}
   182  
   183  	secID := identity.NumericIdentity(policyKey.Identity)
   184  	updateLabels(&allowedIdentityTuple, secID)
   185  
   186  	switch {
   187  	case policyKey.IsIngress():
   188  		if policy.Ingress.Allowed == nil {
   189  			policy.Ingress.Allowed = cilium_v2.AllowedIdentityList{allowedIdentityTuple}
   190  		} else {
   191  			policy.Ingress.Allowed = append(policy.Ingress.Allowed, allowedIdentityTuple)
   192  		}
   193  	case policyKey.IsEgress():
   194  		if policy.Egress.Allowed == nil {
   195  			policy.Egress.Allowed = cilium_v2.AllowedIdentityList{allowedIdentityTuple}
   196  		} else {
   197  			policy.Egress.Allowed = append(policy.Egress.Allowed, allowedIdentityTuple)
   198  		}
   199  	}
   200  }
   201  
   202  // desiredPolicyAllowsIdentity returns whether the specified policy allows
   203  // ingress and egress traffic for the specified numeric security identity.
   204  // If the 'secID' is zero, it will check if all traffic is allowed.
   205  //
   206  // Returing true for either return value indicates all traffic is allowed.
   207  func desiredPolicyAllowsIdentity(desired *policy.EndpointPolicy, identity identity.NumericIdentity) (ingress, egress bool) {
   208  	key := policy.Key{
   209  		Identity: uint32(identity),
   210  	}
   211  
   212  	key.TrafficDirection = trafficdirection.Ingress.Uint8()
   213  	if _, ok := desired.PolicyMapState[key]; ok || !desired.IngressPolicyEnabled {
   214  		ingress = true
   215  	}
   216  	key.TrafficDirection = trafficdirection.Egress.Uint8()
   217  	if _, ok := desired.PolicyMapState[key]; ok || !desired.EgressPolicyEnabled {
   218  		egress = true
   219  	}
   220  
   221  	return ingress, egress
   222  }
   223  
   224  // getEndpointPolicy returns an API representation of the policy that the
   225  // received Endpoint intends to apply.
   226  func (e *Endpoint) getEndpointPolicy() (policy *cilium_v2.EndpointPolicy) {
   227  	if e.desiredPolicy != nil {
   228  		policy = &cilium_v2.EndpointPolicy{
   229  			Ingress: &cilium_v2.EndpointPolicyDirection{
   230  				Enforcing: e.desiredPolicy.IngressPolicyEnabled,
   231  			},
   232  			Egress: &cilium_v2.EndpointPolicyDirection{
   233  				Enforcing: e.desiredPolicy.EgressPolicyEnabled,
   234  			},
   235  		}
   236  
   237  		// Handle allow-all cases
   238  		allowsAllIngress, allowsAllEgress := desiredPolicyAllowsIdentity(e.desiredPolicy, identity.IdentityUnknown)
   239  		if allowsAllIngress {
   240  			policy.Ingress.Allowed = cilium_v2.AllowedIdentityList{{}}
   241  		}
   242  		if allowsAllEgress {
   243  			policy.Egress.Allowed = cilium_v2.AllowedIdentityList{{}}
   244  		}
   245  
   246  		// If either ingress or egress policy is enabled, go through
   247  		// the desired policy to populate the values.
   248  		if !allowsAllIngress || !allowsAllEgress {
   249  			allowsWorldIngress, allowsWorldEgress := desiredPolicyAllowsIdentity(e.desiredPolicy, identity.ReservedIdentityWorld)
   250  
   251  			for policyKey := range e.desiredPolicy.PolicyMapState {
   252  				// Skip listing identities if enforcement is disabled in direction,
   253  				// or if the identity corresponds to a CIDR identity and the world is allowed.
   254  				id := identity.NumericIdentity(policyKey.Identity)
   255  				switch {
   256  				case policyKey.IsIngress():
   257  					if allowsAllIngress || (id.HasLocalScope() && allowsWorldIngress) {
   258  						continue
   259  					}
   260  				case policyKey.IsEgress():
   261  					if allowsAllEgress || (id.HasLocalScope() && allowsWorldEgress) {
   262  						continue
   263  					}
   264  				}
   265  
   266  				populateResponseWithPolicyKey(policy, &policyKey)
   267  			}
   268  		}
   269  
   270  		if policy.Ingress.Allowed != nil {
   271  			policy.Ingress.Allowed.Sort()
   272  		}
   273  		if policy.Egress.Allowed != nil {
   274  			policy.Egress.Allowed.Sort()
   275  		}
   276  	}
   277  
   278  	return
   279  }
   280  
   281  // GetCiliumEndpointStatus creates a cilium_v2.EndpointStatus of an endpoint.
   282  // See cilium_v2.EndpointStatus for a detailed explanation of each field.
   283  func (e *Endpoint) GetCiliumEndpointStatus() *cilium_v2.EndpointStatus {
   284  	e.mutex.RLock()
   285  	defer e.mutex.RUnlock()
   286  
   287  	model := e.GetModelRLocked()
   288  	modelStatus := model.Status
   289  
   290  	controllers := getEndpointStatusControllers(modelStatus)
   291  	identity := getEndpointIdentity(modelStatus)
   292  	log := e.getEndpointStatusLog()
   293  	networking := getEndpointNetworking(modelStatus)
   294  
   295  	return &cilium_v2.EndpointStatus{
   296  		ID:                  int64(e.ID),
   297  		ExternalIdentifiers: modelStatus.ExternalIdentifiers,
   298  		Controllers:         controllers,
   299  		Identity:            identity,
   300  		Log:                 log,
   301  		Networking:          networking,
   302  		Health:              modelStatus.Health,
   303  		State:               string(modelStatus.State),
   304  		Policy:              e.getEndpointPolicy(),
   305  		Encryption:          cilium_v2.EncryptionSpec{Key: int(node.GetIPsecKeyIdentity())},
   306  
   307  		// Scheduled for deprecation in 1.5
   308  		//
   309  		// Status is deprecated but we have some users depending on
   310  		// these fields so they continue to be populated until version
   311  		// 1.5
   312  		Status: &cilium_v2.DeprecatedEndpointStatus{
   313  			Controllers: controllers,
   314  			Identity:    identity,
   315  			Log:         log,
   316  			Networking:  networking,
   317  		},
   318  	}
   319  }