github.com/looshlee/beatles@v0.0.0-20220727174639-742810ab631c/pkg/endpoint/endpoint.go (about) 1 // Copyright 2016-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 "context" 19 "encoding/base64" 20 "encoding/json" 21 "fmt" 22 "net" 23 "os" 24 "runtime" 25 "sort" 26 "strconv" 27 "strings" 28 "time" 29 "unsafe" 30 31 "github.com/cilium/cilium/api/v1/models" 32 "github.com/cilium/cilium/common/addressing" 33 "github.com/cilium/cilium/pkg/bpf" 34 "github.com/cilium/cilium/pkg/completion" 35 "github.com/cilium/cilium/pkg/controller" 36 "github.com/cilium/cilium/pkg/datapath/loader" 37 "github.com/cilium/cilium/pkg/endpoint/regeneration" 38 "github.com/cilium/cilium/pkg/eventqueue" 39 "github.com/cilium/cilium/pkg/fqdn" 40 identityPkg "github.com/cilium/cilium/pkg/identity" 41 "github.com/cilium/cilium/pkg/identity/cache" 42 "github.com/cilium/cilium/pkg/identity/identitymanager" 43 ciliumio "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" 44 "github.com/cilium/cilium/pkg/labels" 45 pkgLabels "github.com/cilium/cilium/pkg/labels" 46 "github.com/cilium/cilium/pkg/labels/model" 47 "github.com/cilium/cilium/pkg/lock" 48 "github.com/cilium/cilium/pkg/logging/logfields" 49 "github.com/cilium/cilium/pkg/mac" 50 bpfconfig "github.com/cilium/cilium/pkg/maps/configmap" 51 "github.com/cilium/cilium/pkg/maps/ctmap" 52 "github.com/cilium/cilium/pkg/maps/policymap" 53 "github.com/cilium/cilium/pkg/metrics" 54 "github.com/cilium/cilium/pkg/monitor/notifications" 55 "github.com/cilium/cilium/pkg/option" 56 "github.com/cilium/cilium/pkg/policy" 57 "github.com/cilium/cilium/pkg/policy/trafficdirection" 58 "github.com/cilium/cilium/pkg/proxy/accesslog" 59 "github.com/cilium/cilium/pkg/trigger" 60 61 "github.com/sirupsen/logrus" 62 "golang.org/x/sys/unix" 63 ) 64 65 const ( 66 maxLogs = 256 67 ) 68 69 var ( 70 EndpointMutableOptionLibrary = option.GetEndpointMutableOptionLibrary() 71 ) 72 73 const ( 74 // StateCreating is used to set the endpoint is being created. 75 StateCreating = string(models.EndpointStateCreating) 76 77 // StateWaitingForIdentity is used to set if the endpoint is waiting 78 // for an identity from the KVStore. 79 StateWaitingForIdentity = string(models.EndpointStateWaitingForIdentity) 80 81 // StateReady specifies if the endpoint is ready to be used. 82 StateReady = string(models.EndpointStateReady) 83 84 // StateWaitingToRegenerate specifies when the endpoint needs to be regenerated, but regeneration has not started yet. 85 StateWaitingToRegenerate = string(models.EndpointStateWaitingToRegenerate) 86 87 // StateRegenerating specifies when the endpoint is being regenerated. 88 StateRegenerating = string(models.EndpointStateRegenerating) 89 90 // StateDisconnecting indicates that the endpoint is being disconnected 91 StateDisconnecting = string(models.EndpointStateDisconnecting) 92 93 // StateDisconnected is used to set the endpoint is disconnected. 94 StateDisconnected = string(models.EndpointStateDisconnected) 95 96 // StateRestoring is used to set the endpoint is being restored. 97 StateRestoring = string(models.EndpointStateRestoring) 98 99 // StateInvalid is used when an endpoint failed during creation due to 100 // invalid data. 101 StateInvalid = string(models.EndpointStateInvalid) 102 103 // IpvlanMapName specifies the tail call map for EP on egress used with ipvlan. 104 IpvlanMapName = "cilium_lxc_ipve_" 105 106 // HealthCEPPrefix is the prefix used to name the cilium health endpoints' CEP 107 HealthCEPPrefix = "cilium-health-" 108 ) 109 110 // compile time interface check 111 var _ notifications.RegenNotificationInfo = &Endpoint{} 112 113 // Endpoint represents a container or similar which can be individually 114 // addresses on L3 with its own IP addresses. This structured is managed by the 115 // endpoint manager in pkg/endpointmanager. 116 // 117 // 118 // WARNING - STABLE API 119 // This structure is written as JSON to StateDir/{ID}/lxc_config.h to allow to 120 // restore endpoints when the agent is being restarted. The restore operation 121 // will read the file and re-create all endpoints with all fields which are not 122 // marked as private to JSON marshal. Do NOT modify this structure in ways which 123 // is not JSON forward compatible. 124 // 125 type Endpoint struct { 126 owner regeneration.Owner 127 128 // ID of the endpoint, unique in the scope of the node 129 ID uint16 130 131 // mutex protects write operations to this endpoint structure except 132 // for the logger field which has its own mutex 133 mutex lock.RWMutex 134 135 // ContainerName is the name given to the endpoint by the container runtime 136 ContainerName string 137 138 // ContainerID is the container ID that docker has assigned to the endpoint 139 // Note: The JSON tag was kept for backward compatibility. 140 ContainerID string `json:"dockerID,omitempty"` 141 142 // DockerNetworkID is the network ID of the libnetwork network if the 143 // endpoint is a docker managed container which uses libnetwork 144 DockerNetworkID string 145 146 // DockerEndpointID is the Docker network endpoint ID if managed by 147 // libnetwork 148 DockerEndpointID string 149 150 // Corresponding BPF map identifier for tail call map of ipvlan datapath 151 DatapathMapID int 152 153 // isDatapathMapPinned denotes whether the datapath map has been pinned. 154 isDatapathMapPinned bool 155 156 // IfName is the name of the host facing interface (veth pair) which 157 // connects into the endpoint 158 IfName string 159 160 // IfIndex is the interface index of the host face interface (veth pair) 161 IfIndex int 162 163 // OpLabels is the endpoint's label configuration 164 // 165 // FIXME: Rename this field to Labels 166 OpLabels pkgLabels.OpLabels 167 168 // identityRevision is incremented each time the identity label 169 // information of the endpoint has changed 170 identityRevision int 171 172 // LXCMAC is the MAC address of the endpoint 173 // 174 // FIXME: Rename this field to MAC 175 LXCMAC mac.MAC // Container MAC address. 176 177 // IPv6 is the IPv6 address of the endpoint 178 IPv6 addressing.CiliumIPv6 179 180 // IPv4 is the IPv4 address of the endpoint 181 IPv4 addressing.CiliumIPv4 182 183 // NodeMAC is the MAC of the node (agent). The MAC is different for every endpoint. 184 NodeMAC mac.MAC 185 186 // SecurityIdentity is the security identity of this endpoint. This is computed from 187 // the endpoint's labels. 188 SecurityIdentity *identityPkg.Identity `json:"SecLabel"` 189 190 // hasSidecarProxy indicates whether the endpoint has been injected by 191 // Istio with a Cilium-compatible sidecar proxy. If true, the sidecar proxy 192 // will be used to apply L7 policy rules. Otherwise, Cilium's node-wide 193 // proxy will be used. 194 // TODO: Currently this applies only to HTTP L7 rules. Kafka L7 rules are still enforced by Cilium's node-wide Kafka proxy. 195 hasSidecarProxy bool 196 197 // PolicyMap is the policy related state of the datapath including 198 // reference to all policy related BPF 199 PolicyMap *policymap.PolicyMap `json:"-"` 200 201 // Options determine the datapath configuration of the endpoint. 202 Options *option.IntOptions 203 204 // Status are the last n state transitions this endpoint went through 205 Status *EndpointStatus `json:"-"` 206 207 // DNSHistory is the collection of still-valid DNS responses intercepted for 208 // this endpoint. 209 DNSHistory *fqdn.DNSCache 210 211 // dnsHistoryTrigger is the trigger to write down the lxc_config.h to make 212 // sure that restores when DNS policy is in there are correct 213 dnsHistoryTrigger *trigger.Trigger 214 215 // state is the state the endpoint is in. See SetStateLocked() 216 state string 217 218 // bpfHeaderfileHash is the hash of the last BPF headerfile that has been 219 // compiled and installed. 220 bpfHeaderfileHash string 221 222 // K8sPodName is the Kubernetes pod name of the endpoint 223 K8sPodName string 224 225 // K8sNamespace is the Kubernetes namespace of the endpoint 226 K8sNamespace string 227 228 // policyRevision is the policy revision this endpoint is currently on 229 // to modify this field please use endpoint.setPolicyRevision instead 230 policyRevision uint64 231 232 // policyRevisionSignals contains a map of PolicyRevision signals that 233 // should be triggered once the policyRevision reaches the wanted wantedRev. 234 policyRevisionSignals map[*policySignal]bool 235 236 // proxyPolicyRevision is the policy revision that has been applied to 237 // the proxy. 238 proxyPolicyRevision uint64 239 240 // proxyStatisticsMutex is the mutex that must be held to read or write 241 // proxyStatistics. 242 proxyStatisticsMutex lock.RWMutex 243 244 // proxyStatistics contains statistics of proxy redirects. 245 // They keys in this map are policy.ProxyIDs. 246 // You must hold Endpoint.proxyStatisticsMutex to read or write it. 247 proxyStatistics map[string]*models.ProxyStatistics 248 249 // nextPolicyRevision is the policy revision that the endpoint has 250 // updated to and that will become effective with the next regenerate 251 nextPolicyRevision uint64 252 253 // forcePolicyCompute full endpoint policy recomputation 254 // Set when endpoint options have been changed. Cleared right before releasing the 255 // endpoint mutex after policy recalculation. 256 forcePolicyCompute bool 257 258 // BuildMutex synchronizes builds of individual endpoints and locks out 259 // deletion during builds 260 // 261 // FIXME: Mark private once endpoint deletion can be moved into 262 // `pkg/endpoint` 263 BuildMutex lock.Mutex `json:"-"` 264 265 // logger is a logrus object with fields set to report an endpoints information. 266 // You must hold Endpoint.Mutex to read or write it (but not to log with it). 267 logger unsafe.Pointer 268 269 // controllers is the list of async controllers syncing the endpoint to 270 // other resources 271 controllers *controller.Manager 272 273 // realizedRedirects maps the ID of each proxy redirect that has been 274 // successfully added into a proxy for this endpoint, to the redirect's 275 // proxy port number. 276 // You must hold Endpoint.Mutex to read or write it. 277 realizedRedirects map[string]uint16 278 279 // BPFConfigMap provides access to the endpoint's BPF configuration. 280 bpfConfigMap *bpfconfig.EndpointConfigMap 281 282 // desiredBPFConfig is the BPF Configuration computed from the endpoint. 283 desiredBPFConfig *bpfconfig.EndpointConfig 284 285 // realizedBPFConfig is the config currently active in the BPF datapath. 286 realizedBPFConfig *bpfconfig.EndpointConfig 287 288 // ctCleaned indicates whether the conntrack table has already been 289 // cleaned when this endpoint was first created 290 ctCleaned bool 291 292 hasBPFProgram chan struct{} 293 294 // selectorPolicy represents a reference to the shared SelectorPolicy 295 // for all endpoints that have the same Identity. 296 selectorPolicy policy.SelectorPolicy 297 298 desiredPolicy *policy.EndpointPolicy 299 300 realizedPolicy *policy.EndpointPolicy 301 302 EventQueue *eventqueue.EventQueue `json:"-"` 303 304 // DatapathConfiguration is the endpoint's datapath configuration as 305 // passed in via the plugin that created the endpoint, e.g. the CNI 306 // plugin which performed the plumbing will enable certain datapath 307 // features according to the mode selected. 308 DatapathConfiguration models.EndpointDatapathConfiguration 309 310 regenFailedChan chan struct{} 311 } 312 313 // UpdateController updates the controller with the specified name with the 314 // provided list of parameters in endpoint's list of controllers. 315 func (e *Endpoint) UpdateController(name string, params controller.ControllerParams) *controller.Controller { 316 return e.controllers.UpdateController(name, params) 317 } 318 319 // CloseBPFProgramChannel closes the channel that signals whether the endpoint 320 // has had its BPF program compiled. If the channel is already closed, this is 321 // a no-op. 322 func (e *Endpoint) CloseBPFProgramChannel() { 323 select { 324 case <-e.hasBPFProgram: 325 default: 326 close(e.hasBPFProgram) 327 } 328 } 329 330 // HasBPFProgram returns whether a BPF program has been generated for this 331 // endpoint. 332 func (e *Endpoint) HasBPFProgram() bool { 333 select { 334 case <-e.hasBPFProgram: 335 return true 336 default: 337 return false 338 } 339 } 340 341 // HasIpvlanDataPath returns whether the daemon is running in ipvlan mode. 342 func (e *Endpoint) HasIpvlanDataPath() bool { 343 if e.DatapathMapID > 0 { 344 return true 345 } 346 return false 347 } 348 349 // GetIngressPolicyEnabledLocked returns whether ingress policy enforcement is 350 // enabled for endpoint or not. The endpoint's mutex must be held. 351 func (e *Endpoint) GetIngressPolicyEnabledLocked() bool { 352 return e.desiredPolicy.IngressPolicyEnabled 353 } 354 355 // GetEgressPolicyEnabledLocked returns whether egress policy enforcement is 356 // enabled for endpoint or not. The endpoint's mutex must be held. 357 func (e *Endpoint) GetEgressPolicyEnabledLocked() bool { 358 return e.desiredPolicy.EgressPolicyEnabled 359 } 360 361 // SetDesiredIngressPolicyEnabled sets Endpoint's ingress policy enforcement 362 // configuration to the specified value. The endpoint's mutex must not be held. 363 func (e *Endpoint) SetDesiredIngressPolicyEnabled(ingress bool) { 364 e.UnconditionalLock() 365 e.desiredPolicy.IngressPolicyEnabled = ingress 366 e.Unlock() 367 368 } 369 370 // SetDesiredEgressPolicyEnabled sets Endpoint's egress policy enforcement 371 // configuration to the specified value. The endpoint's mutex must not be held. 372 func (e *Endpoint) SetDesiredEgressPolicyEnabled(egress bool) { 373 e.UnconditionalLock() 374 e.desiredPolicy.EgressPolicyEnabled = egress 375 e.Unlock() 376 } 377 378 // SetDesiredIngressPolicyEnabledLocked sets Endpoint's ingress policy enforcement 379 // configuration to the specified value. The endpoint's mutex must be held. 380 func (e *Endpoint) SetDesiredIngressPolicyEnabledLocked(ingress bool) { 381 e.desiredPolicy.IngressPolicyEnabled = ingress 382 } 383 384 // SetDesiredEgressPolicyEnabledLocked sets Endpoint's egress policy enforcement 385 // configuration to the specified value. The endpoint's mutex must be held. 386 func (e *Endpoint) SetDesiredEgressPolicyEnabledLocked(egress bool) { 387 e.desiredPolicy.EgressPolicyEnabled = egress 388 } 389 390 // WaitForProxyCompletions blocks until all proxy changes have been completed. 391 // Called with BuildMutex held. 392 func (e *Endpoint) WaitForProxyCompletions(proxyWaitGroup *completion.WaitGroup) error { 393 if proxyWaitGroup == nil { 394 return nil 395 } 396 397 err := proxyWaitGroup.Context().Err() 398 if err != nil { 399 return fmt.Errorf("context cancelled before waiting for proxy updates: %s", err) 400 } 401 402 start := time.Now() 403 404 e.getLogger().Debug("Waiting for proxy updates to complete...") 405 err = proxyWaitGroup.Wait() 406 if err != nil { 407 return fmt.Errorf("proxy state changes failed: %s", err) 408 } 409 e.getLogger().Debug("Wait time for proxy updates: ", time.Since(start)) 410 411 return nil 412 } 413 414 // NewEndpointWithState creates a new endpoint useful for testing purposes 415 func NewEndpointWithState(owner regeneration.Owner, ID uint16, state string) *Endpoint { 416 ep := &Endpoint{ 417 owner: owner, 418 ID: ID, 419 OpLabels: pkgLabels.NewOpLabels(), 420 Status: NewEndpointStatus(), 421 DNSHistory: fqdn.NewDNSCacheWithLimit(option.Config.ToFQDNsMinTTL, option.Config.ToFQDNsMaxIPsPerHost), 422 state: state, 423 hasBPFProgram: make(chan struct{}, 0), 424 controllers: controller.NewManager(), 425 EventQueue: eventqueue.NewEventQueueBuffered(fmt.Sprintf("endpoint-%d", ID), option.Config.EndpointQueueSize), 426 desiredPolicy: policy.NewEndpointPolicy(owner.GetPolicyRepository()), 427 regenFailedChan: make(chan struct{}, 1), 428 } 429 430 ep.realizedPolicy = ep.desiredPolicy 431 432 ep.SetDefaultOpts(option.Config.Opts) 433 ep.UpdateLogger(nil) 434 435 ep.EventQueue.Run() 436 437 return ep 438 } 439 440 // NewEndpointFromChangeModel creates a new endpoint from a request 441 func NewEndpointFromChangeModel(owner regeneration.Owner, base *models.EndpointChangeRequest) (*Endpoint, error) { 442 if base == nil { 443 return nil, nil 444 } 445 446 ep := &Endpoint{ 447 owner: owner, 448 ID: uint16(base.ID), 449 ContainerName: base.ContainerName, 450 ContainerID: base.ContainerID, 451 DockerNetworkID: base.DockerNetworkID, 452 DockerEndpointID: base.DockerEndpointID, 453 IfName: base.InterfaceName, 454 K8sPodName: base.K8sPodName, 455 K8sNamespace: base.K8sNamespace, 456 DatapathMapID: int(base.DatapathMapID), 457 IfIndex: int(base.InterfaceIndex), 458 OpLabels: pkgLabels.NewOpLabels(), 459 DNSHistory: fqdn.NewDNSCacheWithLimit(option.Config.ToFQDNsMinTTL, option.Config.ToFQDNsMaxIPsPerHost), 460 state: "", 461 Status: NewEndpointStatus(), 462 hasBPFProgram: make(chan struct{}, 0), 463 desiredPolicy: policy.NewEndpointPolicy(owner.GetPolicyRepository()), 464 controllers: controller.NewManager(), 465 regenFailedChan: make(chan struct{}, 1), 466 } 467 468 ep.realizedPolicy = ep.desiredPolicy 469 470 if base.Mac != "" { 471 m, err := mac.ParseMAC(base.Mac) 472 if err != nil { 473 return nil, err 474 } 475 ep.LXCMAC = m 476 } 477 478 if base.HostMac != "" { 479 m, err := mac.ParseMAC(base.HostMac) 480 if err != nil { 481 return nil, err 482 } 483 ep.NodeMAC = m 484 } 485 486 if base.Addressing != nil { 487 if ip := base.Addressing.IPV6; ip != "" { 488 ip6, err := addressing.NewCiliumIPv6(ip) 489 if err != nil { 490 return nil, err 491 } 492 ep.IPv6 = ip6 493 } 494 495 if ip := base.Addressing.IPV4; ip != "" { 496 ip4, err := addressing.NewCiliumIPv4(ip) 497 if err != nil { 498 return nil, err 499 } 500 ep.IPv4 = ip4 501 } 502 } 503 504 if base.DatapathConfiguration != nil { 505 ep.DatapathConfiguration = *base.DatapathConfiguration 506 } 507 508 ep.SetDefaultOpts(option.Config.Opts) 509 510 ep.UpdateLogger(nil) 511 ep.SetStateLocked(string(base.State), "Endpoint creation") 512 513 return ep, nil 514 } 515 516 // GetModelRLocked returns the API model of endpoint e. 517 // e.mutex must be RLocked. 518 func (e *Endpoint) GetModelRLocked() *models.Endpoint { 519 if e == nil { 520 return nil 521 } 522 523 currentState := models.EndpointState(e.state) 524 if currentState == models.EndpointStateReady && e.Status.CurrentStatus() != OK { 525 currentState = models.EndpointStateNotReady 526 } 527 528 // This returns the most recent log entry for this endpoint. It is backwards 529 // compatible with the json from before we added `cilium endpoint log` but it 530 // only returns 1 entry. 531 statusLog := e.Status.GetModel() 532 if len(statusLog) > 0 { 533 statusLog = statusLog[:1] 534 } 535 536 lblMdl := model.NewModel(&e.OpLabels) 537 538 // Sort these slices since they come out in random orders. This allows 539 // reflect.DeepEqual to succeed. 540 sort.StringSlice(lblMdl.Realized.User).Sort() 541 sort.StringSlice(lblMdl.Disabled).Sort() 542 sort.StringSlice(lblMdl.SecurityRelevant).Sort() 543 sort.StringSlice(lblMdl.Derived).Sort() 544 545 controllerMdl := e.controllers.GetStatusModel() 546 sort.Slice(controllerMdl, func(i, j int) bool { return controllerMdl[i].Name < controllerMdl[j].Name }) 547 548 spec := &models.EndpointConfigurationSpec{ 549 LabelConfiguration: lblMdl.Realized, 550 } 551 552 if e.Options != nil { 553 spec.Options = *e.Options.GetMutableModel() 554 } 555 556 mdl := &models.Endpoint{ 557 ID: int64(e.ID), 558 Spec: spec, 559 Status: &models.EndpointStatus{ 560 // FIXME GH-3280 When we begin implementing revision numbers this will 561 // diverge from models.Endpoint.Spec to reflect the in-datapath config 562 Realized: spec, 563 Identity: e.SecurityIdentity.GetModel(), 564 Labels: lblMdl, 565 Networking: &models.EndpointNetworking{ 566 Addressing: []*models.AddressPair{{ 567 IPV4: e.IPv4.String(), 568 IPV6: e.IPv6.String(), 569 }}, 570 InterfaceIndex: int64(e.IfIndex), 571 InterfaceName: e.IfName, 572 Mac: e.LXCMAC.String(), 573 HostMac: e.NodeMAC.String(), 574 }, 575 ExternalIdentifiers: &models.EndpointIdentifiers{ 576 ContainerID: e.ContainerID, 577 ContainerName: e.ContainerName, 578 DockerEndpointID: e.DockerEndpointID, 579 DockerNetworkID: e.DockerNetworkID, 580 PodName: e.GetK8sNamespaceAndPodNameLocked(), 581 }, 582 // FIXME GH-3280 When we begin returning endpoint revisions this should 583 // change to return the configured and in-datapath policies. 584 Policy: e.GetPolicyModel(), 585 Log: statusLog, 586 Controllers: controllerMdl, 587 State: currentState, // TODO: Validate 588 Health: e.getHealthModel(), 589 }, 590 } 591 592 return mdl 593 } 594 595 // GetHealthModel returns the endpoint's health object. 596 // 597 // Must be called with e.Mutex locked. 598 func (e *Endpoint) getHealthModel() *models.EndpointHealth { 599 // Duplicated from GetModelRLocked. 600 currentState := models.EndpointState(e.state) 601 if currentState == models.EndpointStateReady && e.Status.CurrentStatus() != OK { 602 currentState = models.EndpointStateNotReady 603 } 604 605 h := models.EndpointHealth{ 606 Bpf: models.EndpointHealthStatusDisabled, 607 Policy: models.EndpointHealthStatusDisabled, 608 Connected: false, 609 OverallHealth: models.EndpointHealthStatusDisabled, 610 } 611 switch currentState { 612 case models.EndpointStateRegenerating, models.EndpointStateWaitingToRegenerate, models.EndpointStateDisconnecting: 613 h = models.EndpointHealth{ 614 Bpf: models.EndpointHealthStatusPending, 615 Policy: models.EndpointHealthStatusPending, 616 Connected: true, 617 OverallHealth: models.EndpointHealthStatusPending, 618 } 619 case models.EndpointStateCreating: 620 h = models.EndpointHealth{ 621 Bpf: models.EndpointHealthStatusBootstrap, 622 Policy: models.EndpointHealthStatusDisabled, 623 Connected: true, 624 OverallHealth: models.EndpointHealthStatusDisabled, 625 } 626 case models.EndpointStateWaitingForIdentity: 627 h = models.EndpointHealth{ 628 Bpf: models.EndpointHealthStatusDisabled, 629 Policy: models.EndpointHealthStatusBootstrap, 630 Connected: true, 631 OverallHealth: models.EndpointHealthStatusDisabled, 632 } 633 case models.EndpointStateNotReady: 634 h = models.EndpointHealth{ 635 Bpf: models.EndpointHealthStatusWarning, 636 Policy: models.EndpointHealthStatusWarning, 637 Connected: true, 638 OverallHealth: models.EndpointHealthStatusWarning, 639 } 640 case models.EndpointStateDisconnected: 641 h = models.EndpointHealth{ 642 Bpf: models.EndpointHealthStatusDisabled, 643 Policy: models.EndpointHealthStatusDisabled, 644 Connected: false, 645 OverallHealth: models.EndpointHealthStatusDisabled, 646 } 647 case models.EndpointStateReady: 648 h = models.EndpointHealth{ 649 Bpf: models.EndpointHealthStatusOK, 650 Policy: models.EndpointHealthStatusOK, 651 Connected: true, 652 OverallHealth: models.EndpointHealthStatusOK, 653 } 654 } 655 656 return &h 657 } 658 659 // GetHealthModel returns the endpoint's health object. 660 func (e *Endpoint) GetHealthModel() *models.EndpointHealth { 661 // NOTE: Using rlock on mutex directly because getHealthModel handles removed endpoint properly 662 e.mutex.RLock() 663 defer e.mutex.RUnlock() 664 return e.getHealthModel() 665 } 666 667 // GetModel returns the API model of endpoint e. 668 func (e *Endpoint) GetModel() *models.Endpoint { 669 if e == nil { 670 return nil 671 } 672 // NOTE: Using rlock on mutex directly because GetModelRLocked handles removed endpoint properly 673 e.mutex.RLock() 674 defer e.mutex.RUnlock() 675 676 return e.GetModelRLocked() 677 } 678 679 // GetPolicyModel returns the endpoint's policy as an API model. 680 // 681 // Must be called with e.Mutex locked. 682 func (e *Endpoint) GetPolicyModel() *models.EndpointPolicyStatus { 683 if e == nil { 684 return nil 685 } 686 687 if e.SecurityIdentity == nil { 688 return nil 689 } 690 691 realizedIngressIdentities := make([]int64, 0) 692 realizedEgressIdentities := make([]int64, 0) 693 694 for policyMapKey := range e.realizedPolicy.PolicyMapState { 695 if policyMapKey.DestPort != 0 { 696 // If the port is non-zero, then the Key no longer only applies 697 // at L3. AllowedIngressIdentities and AllowedEgressIdentities 698 // contain sets of which identities (i.e., label-based L3 only) 699 // are allowed, so anything which contains L4-related policy should 700 // not be added to these sets. 701 continue 702 } 703 switch trafficdirection.TrafficDirection(policyMapKey.TrafficDirection) { 704 case trafficdirection.Ingress: 705 realizedIngressIdentities = append(realizedIngressIdentities, int64(policyMapKey.Identity)) 706 case trafficdirection.Egress: 707 realizedEgressIdentities = append(realizedEgressIdentities, int64(policyMapKey.Identity)) 708 default: 709 log.WithField(logfields.TrafficDirection, trafficdirection.TrafficDirection(policyMapKey.TrafficDirection)).Error("Unexpected traffic direction present in realized PolicyMap state for endpoint") 710 } 711 } 712 713 desiredIngressIdentities := make([]int64, 0) 714 desiredEgressIdentities := make([]int64, 0) 715 716 for policyMapKey := range e.desiredPolicy.PolicyMapState { 717 if policyMapKey.DestPort != 0 { 718 // If the port is non-zero, then the Key no longer only applies 719 // at L3. AllowedIngressIdentities and AllowedEgressIdentities 720 // contain sets of which identities (i.e., label-based L3 only) 721 // are allowed, so anything which contains L4-related policy should 722 // not be added to these sets. 723 continue 724 } 725 switch trafficdirection.TrafficDirection(policyMapKey.TrafficDirection) { 726 case trafficdirection.Ingress: 727 desiredIngressIdentities = append(desiredIngressIdentities, int64(policyMapKey.Identity)) 728 case trafficdirection.Egress: 729 desiredEgressIdentities = append(desiredEgressIdentities, int64(policyMapKey.Identity)) 730 default: 731 log.WithField(logfields.TrafficDirection, trafficdirection.TrafficDirection(policyMapKey.TrafficDirection)).Error("Unexpected traffic direction present in desired PolicyMap state for endpoint") 732 } 733 } 734 735 policyEnabled := e.policyStatus() 736 737 e.proxyStatisticsMutex.RLock() 738 proxyStats := make([]*models.ProxyStatistics, 0, len(e.proxyStatistics)) 739 for _, stats := range e.proxyStatistics { 740 proxyStats = append(proxyStats, stats.DeepCopy()) 741 } 742 e.proxyStatisticsMutex.RUnlock() 743 sortProxyStats(proxyStats) 744 745 var ( 746 realizedCIDRPolicy *policy.CIDRPolicy 747 realizedL4Policy *policy.L4Policy 748 ) 749 if e.realizedPolicy != nil { 750 realizedL4Policy = e.realizedPolicy.L4Policy 751 realizedCIDRPolicy = e.realizedPolicy.CIDRPolicy 752 } 753 754 mdl := &models.EndpointPolicy{ 755 ID: int64(e.SecurityIdentity.ID), 756 // This field should be removed. 757 Build: int64(e.policyRevision), 758 PolicyRevision: int64(e.policyRevision), 759 AllowedIngressIdentities: realizedIngressIdentities, 760 AllowedEgressIdentities: realizedEgressIdentities, 761 CidrPolicy: realizedCIDRPolicy.GetModel(), 762 L4: realizedL4Policy.GetModel(), 763 PolicyEnabled: policyEnabled, 764 } 765 766 var ( 767 desiredCIDRPolicy *policy.CIDRPolicy 768 desiredL4Policy *policy.L4Policy 769 ) 770 if e.desiredPolicy != nil { 771 desiredCIDRPolicy = e.desiredPolicy.CIDRPolicy 772 desiredL4Policy = e.desiredPolicy.L4Policy 773 } 774 775 desiredMdl := &models.EndpointPolicy{ 776 ID: int64(e.SecurityIdentity.ID), 777 // This field should be removed. 778 Build: int64(e.nextPolicyRevision), 779 PolicyRevision: int64(e.nextPolicyRevision), 780 AllowedIngressIdentities: desiredIngressIdentities, 781 AllowedEgressIdentities: desiredEgressIdentities, 782 CidrPolicy: desiredCIDRPolicy.GetModel(), 783 L4: desiredL4Policy.GetModel(), 784 PolicyEnabled: policyEnabled, 785 } 786 // FIXME GH-3280 Once we start returning revisions Realized should be the 787 // policy implemented in the data path 788 return &models.EndpointPolicyStatus{ 789 Spec: desiredMdl, 790 Realized: mdl, 791 ProxyPolicyRevision: int64(e.proxyPolicyRevision), 792 ProxyStatistics: proxyStats, 793 } 794 } 795 796 // policyStatus returns the endpoint's policy status 797 // 798 // Must be called with e.Mutex locked. 799 func (e *Endpoint) policyStatus() models.EndpointPolicyEnabled { 800 policyEnabled := models.EndpointPolicyEnabledNone 801 switch { 802 case e.realizedPolicy.IngressPolicyEnabled && e.realizedPolicy.EgressPolicyEnabled: 803 policyEnabled = models.EndpointPolicyEnabledBoth 804 case e.realizedPolicy.IngressPolicyEnabled: 805 policyEnabled = models.EndpointPolicyEnabledIngress 806 case e.realizedPolicy.EgressPolicyEnabled: 807 policyEnabled = models.EndpointPolicyEnabledEgress 808 } 809 return policyEnabled 810 } 811 812 // GetID returns the endpoint's ID as a 64-bit unsigned integer. 813 func (e *Endpoint) GetID() uint64 { 814 return uint64(e.ID) 815 } 816 817 // GetLabels returns the labels as slice 818 func (e *Endpoint) GetLabels() []string { 819 if e.SecurityIdentity == nil { 820 return []string{} 821 } 822 823 return e.SecurityIdentity.Labels.GetModel() 824 } 825 826 // GetSecurityIdentity returns the security identity of the endpoint. It assumes 827 // the endpoint's mutex is held. 828 func (e *Endpoint) GetSecurityIdentity() *identityPkg.Identity { 829 return e.SecurityIdentity 830 } 831 832 // GetID16 returns the endpoint's ID as a 16-bit unsigned integer. 833 func (e *Endpoint) GetID16() uint16 { 834 return e.ID 835 } 836 837 // GetK8sPodLabels returns all labels that exist in the endpoint and were 838 // derived from k8s pod. 839 func (e *Endpoint) GetK8sPodLabels() pkgLabels.Labels { 840 e.UnconditionalRLock() 841 defer e.RUnlock() 842 allLabels := e.OpLabels.AllLabels() 843 if allLabels == nil { 844 return nil 845 } 846 847 allLabelsFromK8s := allLabels.GetFromSource(pkgLabels.LabelSourceK8s) 848 849 k8sEPPodLabels := pkgLabels.Labels{} 850 for k, v := range allLabelsFromK8s { 851 if !strings.HasPrefix(v.Key, ciliumio.PodNamespaceMetaLabels) && 852 !strings.HasPrefix(v.Key, ciliumio.PolicyLabelServiceAccount) && 853 !strings.HasPrefix(v.Key, ciliumio.PodNamespaceLabel) { 854 k8sEPPodLabels[k] = v 855 } 856 } 857 return k8sEPPodLabels 858 } 859 860 // GetLabelsSHA returns the SHA of labels 861 func (e *Endpoint) GetLabelsSHA() string { 862 if e.SecurityIdentity == nil { 863 return "" 864 } 865 866 return e.SecurityIdentity.GetLabelsSHA256() 867 } 868 869 // GetOpLabels returns the labels as slice 870 func (e *Endpoint) GetOpLabels() []string { 871 e.UnconditionalRLock() 872 defer e.RUnlock() 873 return e.OpLabels.IdentityLabels().GetModel() 874 } 875 876 // GetOptions returns the datapath configuration options of the endpoint. 877 func (e *Endpoint) GetOptions() *option.IntOptions { 878 return e.Options 879 } 880 881 // GetIPv4Address returns the IPv4 address of the endpoint as a string 882 func (e *Endpoint) GetIPv4Address() string { 883 return e.IPv4.String() 884 } 885 886 // GetIPv6Address returns the IPv6 address of the endpoint as a string 887 func (e *Endpoint) GetIPv6Address() string { 888 return e.IPv6.String() 889 } 890 891 // IPv4Address returns the IPv4 address of the endpoint 892 func (e *Endpoint) IPv4Address() addressing.CiliumIPv4 { 893 return e.IPv4 894 } 895 896 // IPv6Address returns the IPv6 address of the endpoint 897 func (e *Endpoint) IPv6Address() addressing.CiliumIPv6 { 898 return e.IPv6 899 } 900 901 // GetNodeMAC returns the MAC address of the node from this endpoint's perspective. 902 func (e *Endpoint) GetNodeMAC() mac.MAC { 903 return e.NodeMAC 904 } 905 906 // SetNodeMACLocked updates the node MAC inside the endpoint. 907 func (e *Endpoint) SetNodeMACLocked(m mac.MAC) { 908 e.NodeMAC = m 909 } 910 911 func (e *Endpoint) HasSidecarProxy() bool { 912 return e.hasSidecarProxy 913 } 914 915 // ConntrackName returns the name suffix for the endpoint-specific bpf 916 // conntrack map, which is a 5-digit endpoint ID, or "global" when the 917 // global map should be used. 918 // Must be called with the endpoint locked. 919 func (e *Endpoint) ConntrackName() string { 920 if e.ConntrackLocalLocked() { 921 return fmt.Sprintf("%05d", int(e.ID)) 922 } 923 return "global" 924 } 925 926 // StringID returns the endpoint's ID in a string. 927 func (e *Endpoint) StringID() string { 928 return strconv.Itoa(int(e.ID)) 929 } 930 931 func (e *Endpoint) GetIdentity() identityPkg.NumericIdentity { 932 if e.SecurityIdentity != nil { 933 return e.SecurityIdentity.ID 934 } 935 936 return identityPkg.InvalidIdentity 937 } 938 939 // Allows is only used for unit testing 940 func (e *Endpoint) Allows(id identityPkg.NumericIdentity) bool { 941 e.UnconditionalRLock() 942 defer e.RUnlock() 943 944 keyToLookup := policy.Key{ 945 Identity: uint32(id), 946 TrafficDirection: trafficdirection.Ingress.Uint8(), 947 } 948 949 _, ok := e.desiredPolicy.PolicyMapState[keyToLookup] 950 return ok 951 } 952 953 // String returns endpoint on a JSON format. 954 func (e *Endpoint) String() string { 955 e.UnconditionalRLock() 956 defer e.RUnlock() 957 b, err := json.MarshalIndent(e, "", " ") 958 if err != nil { 959 return err.Error() 960 } 961 return string(b) 962 } 963 964 // optionChanged is a callback used with pkg/option to apply the options to an 965 // endpoint. Not used for anything at the moment. 966 func optionChanged(key string, value option.OptionSetting, data interface{}) { 967 } 968 969 // applyOptsLocked applies the given options to the endpoint's options and 970 // returns true if there were any options changed. 971 func (e *Endpoint) applyOptsLocked(opts option.OptionMap) bool { 972 changed := e.Options.ApplyValidated(opts, optionChanged, e) > 0 973 _, exists := opts[option.Debug] 974 if exists && changed { 975 e.UpdateLogger(nil) 976 } 977 return changed 978 } 979 980 // ForcePolicyCompute marks the endpoint for forced bpf regeneration. 981 func (e *Endpoint) ForcePolicyCompute() { 982 e.forcePolicyCompute = true 983 } 984 985 // SetDefaultOpts initializes the endpoint Options and configures the specified 986 // options. 987 func (e *Endpoint) SetDefaultOpts(opts *option.IntOptions) { 988 if e.Options == nil { 989 e.Options = option.NewIntOptions(&EndpointMutableOptionLibrary) 990 } 991 if e.Options.Library == nil { 992 e.Options.Library = &EndpointMutableOptionLibrary 993 } 994 if e.Options.Opts == nil { 995 e.Options.Opts = option.OptionMap{} 996 } 997 998 if opts != nil { 999 epOptLib := option.GetEndpointMutableOptionLibrary() 1000 for k := range epOptLib { 1001 e.Options.SetValidated(k, opts.GetValue(k)) 1002 } 1003 } 1004 e.UpdateLogger(nil) 1005 } 1006 1007 // ConntrackLocal determines whether this endpoint is currently using a local 1008 // table to handle connection tracking (true), or the global table (false). 1009 func (e *Endpoint) ConntrackLocal() bool { 1010 e.UnconditionalRLock() 1011 defer e.RUnlock() 1012 1013 return e.ConntrackLocalLocked() 1014 } 1015 1016 // ConntrackLocalLocked is the same as ConntrackLocal, but assumes that the 1017 // endpoint is already locked for reading. 1018 func (e *Endpoint) ConntrackLocalLocked() bool { 1019 if e.SecurityIdentity == nil || e.Options == nil || 1020 !e.Options.IsEnabled(option.ConntrackLocal) { 1021 return false 1022 } 1023 1024 return true 1025 } 1026 1027 // base64 returns the endpoint in a base64 format. 1028 func (e *Endpoint) base64() (string, error) { 1029 var ( 1030 jsonBytes []byte 1031 err error 1032 ) 1033 1034 jsonBytes, err = json.Marshal(e) 1035 if err != nil { 1036 return "", err 1037 } 1038 return base64.StdEncoding.EncodeToString(jsonBytes), nil 1039 } 1040 1041 // parseBase64ToEndpoint parses the endpoint stored in the given base64 string. 1042 func parseBase64ToEndpoint(str string, ep *Endpoint) error { 1043 jsonBytes, err := base64.StdEncoding.DecodeString(str) 1044 if err != nil { 1045 return err 1046 } 1047 return json.Unmarshal(jsonBytes, ep) 1048 } 1049 1050 // FilterEPDir returns a list of directories' names that possible belong to an endpoint. 1051 func FilterEPDir(dirFiles []os.FileInfo) []string { 1052 eptsID := []string{} 1053 for _, file := range dirFiles { 1054 if file.IsDir() { 1055 _, err := strconv.ParseUint(file.Name(), 10, 16) 1056 if err == nil || strings.HasSuffix(file.Name(), nextDirectorySuffix) || strings.HasSuffix(file.Name(), nextFailedDirectorySuffix) { 1057 eptsID = append(eptsID, file.Name()) 1058 } 1059 } 1060 } 1061 return eptsID 1062 } 1063 1064 // ParseEndpoint parses the given strEp which is in the form of: 1065 // common.CiliumCHeaderPrefix + common.Version + ":" + endpointBase64 1066 // Note that the parse'd endpoint's identity is only partially restored. The 1067 // caller must call `SetIdentity()` to make the returned endpoint's identity useful. 1068 func ParseEndpoint(owner regeneration.Owner, strEp string) (*Endpoint, error) { 1069 // TODO: Provide a better mechanism to update from old version once we bump 1070 // TODO: cilium version. 1071 strEpSlice := strings.Split(strEp, ":") 1072 if len(strEpSlice) != 2 { 1073 return nil, fmt.Errorf("invalid format %q. Should contain a single ':'", strEp) 1074 } 1075 ep := Endpoint{ 1076 owner: owner, 1077 OpLabels: pkgLabels.NewOpLabels(), 1078 DNSHistory: fqdn.NewDNSCacheWithLimit(option.Config.ToFQDNsMinTTL, option.Config.ToFQDNsMaxIPsPerHost), 1079 } 1080 if err := parseBase64ToEndpoint(strEpSlice[1], &ep); err != nil { 1081 return nil, fmt.Errorf("failed to parse base64toendpoint: %s", err) 1082 } 1083 1084 // Validate the options that were parsed 1085 ep.SetDefaultOpts(ep.Options) 1086 1087 // Initialize fields to values which are non-nil that are not serialized. 1088 ep.hasBPFProgram = make(chan struct{}, 0) 1089 ep.desiredPolicy = policy.NewEndpointPolicy(owner.GetPolicyRepository()) 1090 ep.realizedPolicy = ep.desiredPolicy 1091 ep.controllers = controller.NewManager() 1092 ep.regenFailedChan = make(chan struct{}, 1) 1093 1094 // We need to check for nil in Status, CurrentStatuses and Log, since in 1095 // some use cases, status will be not nil and Cilium will eventually 1096 // error/panic if CurrentStatus or Log are not initialized correctly. 1097 // Reference issue GH-2477 1098 if ep.Status == nil || ep.Status.CurrentStatuses == nil || ep.Status.Log == nil { 1099 ep.Status = NewEndpointStatus() 1100 } 1101 1102 // Make sure the endpoint has an identity, using the 'init' identity if none. 1103 if ep.SecurityIdentity == nil { 1104 ep.SecurityIdentity = identityPkg.LookupReservedIdentity(identityPkg.ReservedIdentityInit) 1105 } 1106 ep.SecurityIdentity.Sanitize() 1107 1108 ep.UpdateLogger(nil) 1109 1110 ep.SetStateLocked(StateRestoring, "Endpoint restoring") 1111 1112 return &ep, nil 1113 } 1114 1115 func (e *Endpoint) LogStatus(typ StatusType, code StatusCode, msg string) { 1116 e.UnconditionalLock() 1117 defer e.Unlock() 1118 // FIXME GH2323 instead of a mutex we could use a channel to send the status 1119 // log message to a single writer? 1120 e.logStatusLocked(typ, code, msg) 1121 } 1122 1123 func (e *Endpoint) LogStatusOK(typ StatusType, msg string) { 1124 e.LogStatus(typ, OK, msg) 1125 } 1126 1127 // LogStatusOKLocked will log an OK message of the given status type with the 1128 // given msg string. 1129 // must be called with endpoint.Mutex held 1130 func (e *Endpoint) LogStatusOKLocked(typ StatusType, msg string) { 1131 e.logStatusLocked(typ, OK, msg) 1132 } 1133 1134 // logStatusLocked logs a status message 1135 // must be called with endpoint.Mutex held 1136 func (e *Endpoint) logStatusLocked(typ StatusType, code StatusCode, msg string) { 1137 e.Status.indexMU.Lock() 1138 defer e.Status.indexMU.Unlock() 1139 sts := &statusLogMsg{ 1140 Status: Status{ 1141 Code: code, 1142 Msg: msg, 1143 Type: typ, 1144 State: e.state, 1145 }, 1146 Timestamp: time.Now().UTC(), 1147 } 1148 e.Status.addStatusLog(sts) 1149 e.getLogger().WithFields(logrus.Fields{ 1150 "code": sts.Status.Code, 1151 "type": sts.Status.Type, 1152 logfields.EndpointState: sts.Status.State, 1153 logfields.PolicyRevision: e.policyRevision, 1154 }).Debug(msg) 1155 } 1156 1157 type UpdateValidationError struct { 1158 msg string 1159 } 1160 1161 func (e UpdateValidationError) Error() string { return e.msg } 1162 1163 type UpdateCompilationError struct { 1164 msg string 1165 } 1166 1167 func (e UpdateCompilationError) Error() string { return e.msg } 1168 1169 // UpdateStateChangeError is an error that indicates that updating the state 1170 // of an endpoint was unsuccessful. 1171 // Implements error interface. 1172 type UpdateStateChangeError struct { 1173 msg string 1174 } 1175 1176 func (e UpdateStateChangeError) Error() string { return e.msg } 1177 1178 // Update modifies the endpoint options and *always* tries to regenerate the 1179 // endpoint's program. Returns an error if the provided options are not valid, 1180 // if there was an issue triggering policy updates for the given endpoint, 1181 // or if endpoint regeneration was unable to be triggered. Note that the 1182 // LabelConfiguration in the EndpointConfigurationSpec is *not* consumed here. 1183 func (e *Endpoint) Update(cfg *models.EndpointConfigurationSpec) error { 1184 om, err := EndpointMutableOptionLibrary.ValidateConfigurationMap(cfg.Options) 1185 if err != nil { 1186 return UpdateValidationError{err.Error()} 1187 } 1188 1189 if err := e.LockAlive(); err != nil { 1190 return err 1191 } 1192 1193 e.getLogger().WithField("configuration-options", cfg).Debug("updating endpoint configuration options") 1194 1195 // CurrentStatus will be not OK when we have an uncleared error in BPF, 1196 // policy or Other. We should keep trying to regenerate in the hopes of 1197 // succeeding. 1198 // Note: This "retry" behaviour is better suited to a controller, and can be 1199 // moved there once we have an endpoint regeneration controller. 1200 regenCtx := ®eneration.ExternalRegenerationMetadata{ 1201 Reason: "endpoint was updated via API", 1202 } 1203 1204 // If configuration options are provided, we only regenerate if necessary. 1205 // Otherwise always regenerate. 1206 if cfg.Options == nil { 1207 regenCtx.RegenerationLevel = regeneration.RegenerateWithDatapathRebuild 1208 regenCtx.Reason = "endpoint was manually regenerated via API" 1209 } else if e.updateAndOverrideEndpointOptions(om) || e.Status.CurrentStatus() != OK { 1210 regenCtx.RegenerationLevel = regeneration.RegenerateWithDatapathRewrite 1211 } 1212 1213 if regenCtx.RegenerationLevel > regeneration.RegenerateWithoutDatapath { 1214 e.getLogger().Debug("need to regenerate endpoint; checking state before" + 1215 " attempting to regenerate") 1216 1217 // TODO / FIXME: GH-3281: need ways to queue up regenerations per-endpoint. 1218 1219 // Default timeout for PATCH /endpoint/{id}/config is 60 seconds, so put 1220 // timeout in this function a bit below that timeout. If the timeout 1221 // for clients in API is below this value, they will get a message containing 1222 // "context deadline exceeded". 1223 timeout := time.After(EndpointGenerationTimeout) 1224 1225 // Check for endpoint state every second. 1226 ticker := time.NewTicker(1 * time.Second) 1227 defer ticker.Stop() 1228 1229 e.Unlock() 1230 for { 1231 select { 1232 case <-ticker.C: 1233 if err := e.LockAlive(); err != nil { 1234 return err 1235 } 1236 // Check endpoint state before attempting configuration update because 1237 // configuration updates can only be applied when the endpoint is in 1238 // specific states. See GH-3058. 1239 stateTransitionSucceeded := e.SetStateLocked(StateWaitingToRegenerate, regenCtx.Reason) 1240 if stateTransitionSucceeded { 1241 e.Unlock() 1242 e.Regenerate(regenCtx) 1243 return nil 1244 } 1245 e.Unlock() 1246 case <-timeout: 1247 e.getLogger().Warning("timed out waiting for endpoint state to change") 1248 return UpdateStateChangeError{fmt.Sprintf("unable to regenerate endpoint program because state transition to %s was unsuccessful; check `cilium endpoint log %d` for more information", StateWaitingToRegenerate, e.ID)} 1249 } 1250 } 1251 1252 } 1253 1254 e.Unlock() 1255 return nil 1256 } 1257 1258 // HasLabels returns whether endpoint e contains all labels l. Will return 'false' 1259 // if any label in l is not in the endpoint's labels. 1260 func (e *Endpoint) HasLabels(l pkgLabels.Labels) bool { 1261 e.UnconditionalRLock() 1262 defer e.RUnlock() 1263 1264 return e.hasLabelsRLocked(l) 1265 } 1266 1267 // hasLabelsRLocked returns whether endpoint e contains all labels l. Will 1268 // return 'false' if any label in l is not in the endpoint's labels. 1269 // e.Mutex must be RLocked 1270 func (e *Endpoint) hasLabelsRLocked(l pkgLabels.Labels) bool { 1271 allEpLabels := e.OpLabels.AllLabels() 1272 1273 for _, v := range l { 1274 found := false 1275 for _, j := range allEpLabels { 1276 if j.Equals(&v) { 1277 found = true 1278 break 1279 } 1280 } 1281 if !found { 1282 return false 1283 } 1284 } 1285 1286 return true 1287 } 1288 1289 // replaceInformationLabels replaces the information labels of the endpoint. 1290 // Passing a nil set of labels will not perform any action. 1291 // Must be called with e.Mutex.Lock(). 1292 func (e *Endpoint) replaceInformationLabels(l pkgLabels.Labels) { 1293 if l == nil { 1294 return 1295 } 1296 e.OpLabels.ReplaceInformationLabels(l, e.getLogger()) 1297 } 1298 1299 // replaceIdentityLabels replaces the identity labels of the endpoint. If a net 1300 // changed occurred, the identityRevision is bumped and returned, otherwise 0 is 1301 // returned. 1302 // Passing a nil set of labels will not perform any action and will return the 1303 // current endpoint's identityRevision. 1304 // Must be called with e.Mutex.Lock(). 1305 func (e *Endpoint) replaceIdentityLabels(l pkgLabels.Labels) int { 1306 if l == nil { 1307 return e.identityRevision 1308 } 1309 1310 changed := e.OpLabels.ReplaceIdentityLabels(l, e.getLogger()) 1311 rev := 0 1312 if changed { 1313 e.identityRevision++ 1314 rev = e.identityRevision 1315 } 1316 1317 return rev 1318 } 1319 1320 // DeleteConfig is the endpoint deletion configuration 1321 type DeleteConfig struct { 1322 NoIPRelease bool 1323 NoIdentityRelease bool 1324 } 1325 1326 // LeaveLocked removes the endpoint's directory from the system. Must be called 1327 // with Endpoint's mutex AND BuildMutex locked. 1328 // 1329 // Note: LeaveLocked() is called indirectly from endpoint restore logic for 1330 // endpoints which failed to be restored. Any cleanup routine of LeaveLocked() 1331 // which depends on kvstore connectivity must be protected by a flag in 1332 // DeleteConfig and the restore logic must opt-out of it. 1333 func (e *Endpoint) LeaveLocked(proxyWaitGroup *completion.WaitGroup, conf DeleteConfig) []error { 1334 errors := []error{} 1335 1336 if !option.Config.DryMode { 1337 loader.Unload(e.createEpInfoCache("")) 1338 } 1339 1340 e.owner.RemoveFromEndpointQueue(uint64(e.ID)) 1341 if e.SecurityIdentity != nil && len(e.realizedRedirects) > 0 { 1342 // Passing a new map of nil will purge all redirects 1343 finalize, _ := e.removeOldRedirects(nil, proxyWaitGroup) 1344 if finalize != nil { 1345 finalize() 1346 } 1347 } 1348 1349 if e.PolicyMap != nil { 1350 if err := e.PolicyMap.Close(); err != nil { 1351 errors = append(errors, fmt.Errorf("unable to close policymap %s: %s", e.PolicyMap.String(), err)) 1352 } 1353 } 1354 1355 if e.bpfConfigMap != nil { 1356 if err := e.bpfConfigMap.Close(); err != nil { 1357 errors = append(errors, fmt.Errorf("unable to close configmap %s: %s", e.BPFConfigMapPath(), err)) 1358 } 1359 } 1360 1361 if !conf.NoIdentityRelease && e.SecurityIdentity != nil { 1362 identitymanager.Remove(e.SecurityIdentity) 1363 1364 releaseCtx, cancel := context.WithTimeout(context.Background(), option.Config.KVstoreConnectivityTimeout) 1365 defer cancel() 1366 1367 _, err := cache.Release(releaseCtx, e.owner, e.SecurityIdentity) 1368 if err != nil { 1369 errors = append(errors, fmt.Errorf("unable to release identity: %s", err)) 1370 } 1371 e.owner.RemoveNetworkPolicy(e) 1372 e.SecurityIdentity = nil 1373 } 1374 1375 e.removeDirectories() 1376 e.controllers.RemoveAll() 1377 e.cleanPolicySignals() 1378 1379 if e.dnsHistoryTrigger != nil { 1380 e.dnsHistoryTrigger.Shutdown() 1381 } 1382 1383 if e.ConntrackLocalLocked() { 1384 ctmap.CloseLocalMaps(e.ConntrackName()) 1385 } else if !option.Config.DryMode { 1386 e.scrubIPsInConntrackTableLocked() 1387 } 1388 1389 e.SetStateLocked(StateDisconnected, "Endpoint removed") 1390 1391 endpointPolicyStatus.Remove(e.ID) 1392 e.getLogger().Info("Removed endpoint") 1393 1394 return errors 1395 } 1396 1397 // RegenerateWait should only be called when endpoint's state has successfully 1398 // been changed to "waiting-to-regenerate" 1399 func (e *Endpoint) RegenerateWait(reason string) error { 1400 if !<-e.Regenerate(®eneration.ExternalRegenerationMetadata{Reason: reason}) { 1401 return fmt.Errorf("error while regenerating endpoint."+ 1402 " For more info run: 'cilium endpoint get %d'", e.ID) 1403 } 1404 return nil 1405 } 1406 1407 // SetContainerName modifies the endpoint's container name 1408 func (e *Endpoint) SetContainerName(name string) { 1409 e.UnconditionalLock() 1410 e.ContainerName = name 1411 e.Unlock() 1412 } 1413 1414 // GetK8sNamespace returns the name of the pod if the endpoint represents a 1415 // Kubernetes pod 1416 func (e *Endpoint) GetK8sNamespace() string { 1417 e.UnconditionalRLock() 1418 ns := e.K8sNamespace 1419 e.RUnlock() 1420 return ns 1421 } 1422 1423 // SetK8sNamespace modifies the endpoint's pod name 1424 func (e *Endpoint) SetK8sNamespace(name string) { 1425 e.UnconditionalLock() 1426 e.K8sNamespace = name 1427 e.UpdateLogger(map[string]interface{}{ 1428 logfields.K8sPodName: e.GetK8sNamespaceAndPodNameLocked(), 1429 }) 1430 e.Unlock() 1431 } 1432 1433 // K8sNamespaceAndPodNameIsSet returns true if the pod name is set 1434 func (e *Endpoint) K8sNamespaceAndPodNameIsSet() bool { 1435 e.UnconditionalLock() 1436 podName := e.GetK8sNamespaceAndPodNameLocked() 1437 e.Unlock() 1438 return podName != "" && podName != "/" 1439 } 1440 1441 // GetK8sPodName returns the name of the pod if the endpoint represents a 1442 // Kubernetes pod 1443 func (e *Endpoint) GetK8sPodName() string { 1444 e.UnconditionalRLock() 1445 k8sPodName := e.K8sPodName 1446 e.RUnlock() 1447 1448 return k8sPodName 1449 } 1450 1451 // HumanStringLocked returns the endpoint's most human readable identifier as string 1452 func (e *Endpoint) HumanStringLocked() string { 1453 if pod := e.GetK8sNamespaceAndPodNameLocked(); pod != "" { 1454 return pod 1455 } 1456 1457 return e.StringID() 1458 } 1459 1460 // GetK8sNamespaceAndPodNameLocked returns the namespace and pod name. This 1461 // function requires e.Mutex to be held. 1462 func (e *Endpoint) GetK8sNamespaceAndPodNameLocked() string { 1463 return e.K8sNamespace + "/" + e.K8sPodName 1464 } 1465 1466 // SetK8sPodName modifies the endpoint's pod name 1467 func (e *Endpoint) SetK8sPodName(name string) { 1468 e.UnconditionalLock() 1469 e.K8sPodName = name 1470 e.UpdateLogger(map[string]interface{}{ 1471 logfields.K8sPodName: e.GetK8sNamespaceAndPodNameLocked(), 1472 }) 1473 e.Unlock() 1474 } 1475 1476 // SetContainerID modifies the endpoint's container ID 1477 func (e *Endpoint) SetContainerID(id string) { 1478 e.UnconditionalLock() 1479 e.ContainerID = id 1480 e.UpdateLogger(map[string]interface{}{ 1481 logfields.ContainerID: e.getShortContainerID(), 1482 }) 1483 e.Unlock() 1484 } 1485 1486 // GetContainerID returns the endpoint's container ID 1487 func (e *Endpoint) GetContainerID() string { 1488 e.UnconditionalRLock() 1489 cID := e.ContainerID 1490 e.RUnlock() 1491 return cID 1492 } 1493 1494 // GetShortContainerID returns the endpoint's shortened container ID 1495 func (e *Endpoint) GetShortContainerID() string { 1496 e.UnconditionalRLock() 1497 defer e.RUnlock() 1498 1499 return e.getShortContainerID() 1500 } 1501 1502 func (e *Endpoint) getShortContainerID() string { 1503 if e == nil { 1504 return "" 1505 } 1506 1507 caplen := 10 1508 if len(e.ContainerID) <= caplen { 1509 return e.ContainerID 1510 } 1511 1512 return e.ContainerID[:caplen] 1513 1514 } 1515 1516 // SetDockerEndpointID modifies the endpoint's Docker Endpoint ID 1517 func (e *Endpoint) SetDockerEndpointID(id string) { 1518 e.UnconditionalLock() 1519 e.DockerEndpointID = id 1520 e.Unlock() 1521 } 1522 1523 // SetDockerNetworkID modifies the endpoint's Docker Endpoint ID 1524 func (e *Endpoint) SetDockerNetworkID(id string) { 1525 e.UnconditionalLock() 1526 e.DockerNetworkID = id 1527 e.Unlock() 1528 } 1529 1530 // GetDockerNetworkID returns the endpoint's Docker Endpoint ID 1531 func (e *Endpoint) GetDockerNetworkID() string { 1532 e.UnconditionalRLock() 1533 defer e.RUnlock() 1534 1535 return e.DockerNetworkID 1536 } 1537 1538 // SetDatapathMapIDAndPinMapLocked modifies the endpoint's datapath map ID 1539 func (e *Endpoint) SetDatapathMapIDAndPinMapLocked(id int) error { 1540 e.DatapathMapID = id 1541 return e.PinDatapathMap() 1542 } 1543 1544 // IsDatapathMapPinnedLocked returns whether the endpoint's datapath map has been pinned 1545 func (e *Endpoint) IsDatapathMapPinnedLocked() bool { 1546 return e.isDatapathMapPinned 1547 } 1548 1549 // GetState returns the endpoint's state 1550 // endpoint.Mutex may only be.RLockAlive()ed 1551 func (e *Endpoint) GetStateLocked() string { 1552 return e.state 1553 } 1554 1555 // GetState returns the endpoint's state 1556 // endpoint.Mutex may only be.RLockAlive()ed 1557 func (e *Endpoint) GetState() string { 1558 e.UnconditionalRLock() 1559 defer e.RUnlock() 1560 return e.GetStateLocked() 1561 } 1562 1563 // SetState modifies the endpoint's state 1564 // Returns true only if endpoints state was changed as requested 1565 func (e *Endpoint) SetState(toState, reason string) bool { 1566 e.UnconditionalLock() 1567 defer e.Unlock() 1568 return e.SetStateLocked(toState, reason) 1569 } 1570 1571 // SetStateLocked modifies the endpoint's state 1572 // endpoint.Mutex must be held 1573 // Returns true only if endpoints state was changed as requested 1574 func (e *Endpoint) SetStateLocked(toState, reason string) bool { 1575 // Validate the state transition. 1576 fromState := e.state 1577 1578 switch fromState { // From state 1579 case "": // Special case for capturing initial state transitions like 1580 // nil --> StateWaitingForIdentity, StateRestoring 1581 switch toState { 1582 case StateWaitingForIdentity, StateRestoring: 1583 goto OKState 1584 } 1585 case StateCreating: 1586 switch toState { 1587 case StateDisconnecting, StateWaitingForIdentity, StateRestoring: 1588 goto OKState 1589 } 1590 case StateWaitingForIdentity: 1591 switch toState { 1592 case StateReady, StateDisconnecting, StateInvalid: 1593 goto OKState 1594 } 1595 case StateReady: 1596 switch toState { 1597 case StateWaitingForIdentity, StateDisconnecting, StateWaitingToRegenerate, StateRestoring: 1598 goto OKState 1599 } 1600 case StateDisconnecting: 1601 switch toState { 1602 case StateDisconnected: 1603 goto OKState 1604 } 1605 case StateDisconnected, StateInvalid: 1606 // No valid transitions, as disconnected and invalid are terminal 1607 // states for the endpoint. 1608 case StateWaitingToRegenerate: 1609 switch toState { 1610 // Note that transitions to StateWaitingToRegenerate are not allowed, 1611 // as callers of this function enqueue regenerations if 'true' is 1612 // returned. We don't want to return 'true' for the case of 1613 // transitioning to StateWaitingToRegenerate, as this means that a 1614 // regeneration is already queued up. Callers would then queue up 1615 // another unneeded regeneration, which is undesired. 1616 case StateWaitingForIdentity, StateDisconnecting, StateRestoring: 1617 goto OKState 1618 // Don't log this state transition being invalid below so that we don't 1619 // put warnings in the logs for a case which does not result in incorrect 1620 // behavior. 1621 case StateWaitingToRegenerate: 1622 return false 1623 } 1624 case StateRegenerating: 1625 switch toState { 1626 // Even while the endpoint is regenerating it is 1627 // possible that further changes require a new 1628 // build. In this case the endpoint is transitioned 1629 // from the regenerating state to 1630 // waiting-for-identity or waiting-to-regenerate state. 1631 case StateWaitingForIdentity, StateDisconnecting, StateWaitingToRegenerate, StateRestoring: 1632 goto OKState 1633 } 1634 case StateRestoring: 1635 switch toState { 1636 case StateDisconnecting, StateRestoring: 1637 goto OKState 1638 } 1639 } 1640 if toState != fromState { 1641 _, fileName, fileLine, _ := runtime.Caller(1) 1642 e.getLogger().WithFields(logrus.Fields{ 1643 logfields.EndpointState + ".from": fromState, 1644 logfields.EndpointState + ".to": toState, 1645 "file": fileName, 1646 "line": fileLine, 1647 }).Info("Invalid state transition skipped") 1648 } 1649 e.logStatusLocked(Other, Warning, fmt.Sprintf("Skipped invalid state transition to %s due to: %s", toState, reason)) 1650 return false 1651 1652 OKState: 1653 e.state = toState 1654 e.logStatusLocked(Other, OK, reason) 1655 1656 if fromState != "" { 1657 metrics.EndpointStateCount. 1658 WithLabelValues(fromState).Dec() 1659 } 1660 1661 // Since StateDisconnected and StateInvalid are final states, after which 1662 // the endpoint is gone or doesn't exist, we should not increment metrics 1663 // for these states. 1664 if toState != "" && toState != StateDisconnected && toState != StateInvalid { 1665 metrics.EndpointStateCount. 1666 WithLabelValues(toState).Inc() 1667 } 1668 return true 1669 } 1670 1671 // BuilderSetStateLocked modifies the endpoint's state 1672 // endpoint.Mutex must be held 1673 // endpoint BuildMutex must be held! 1674 func (e *Endpoint) BuilderSetStateLocked(toState, reason string) bool { 1675 // Validate the state transition. 1676 fromState := e.state 1677 switch fromState { // From state 1678 case StateCreating, StateWaitingForIdentity, StateReady, StateDisconnecting, StateDisconnected, StateInvalid: 1679 // No valid transitions for the builder 1680 case StateWaitingToRegenerate, StateRestoring: 1681 switch toState { 1682 // Builder transitions the endpoint from 1683 // waiting-to-regenerate state to regenerating state 1684 // right after acquiring the endpoint lock, and while 1685 // endpoint's build mutex is held. All changes to 1686 // cilium and endpoint configuration, policy as well 1687 // as the existing set of security identities will be 1688 // reconsidered after this point, i.e., even if some 1689 // of them are changed regeneration need not be queued 1690 // if the endpoint is already in waiting-to-regenerate 1691 // state. 1692 case StateRegenerating: 1693 goto OKState 1694 // Transition to ReadyState is not supported, but is 1695 // attempted when a regeneration is competed, and another 1696 // regeneration has been queued in the meanwhile. So this 1697 // is expected and will not be logged as an error or warning. 1698 case StateReady: 1699 return false 1700 } 1701 case StateRegenerating: 1702 switch toState { 1703 // While still holding the build mutex, the builder 1704 // tries to transition the endpoint to ready 1705 // state. But since the endpoint mutex was released 1706 // for the duration of the bpf generation, it is 1707 // possible that another build request has been 1708 // queued. In this case the endpoint has been 1709 // transitioned to waiting-to-regenerate state 1710 // already, and the transition to ready state is 1711 // skipped (but not worth logging for, as this is 1712 // normal, see above). 1713 case StateReady: 1714 goto OKState 1715 } 1716 } 1717 e.logStatusLocked(Other, Warning, fmt.Sprintf("Skipped invalid state transition to %s due to: %s", toState, reason)) 1718 return false 1719 1720 OKState: 1721 e.state = toState 1722 e.logStatusLocked(Other, OK, reason) 1723 1724 if fromState != "" { 1725 metrics.EndpointStateCount. 1726 WithLabelValues(fromState).Dec() 1727 } 1728 1729 // Since StateDisconnected and StateInvalid are final states, after which 1730 // the endpoint is gone or doesn't exist, we should not increment metrics 1731 // for these states. 1732 if toState != "" && toState != StateDisconnected && toState != StateInvalid { 1733 metrics.EndpointStateCount. 1734 WithLabelValues(toState).Inc() 1735 } 1736 return true 1737 } 1738 1739 // OnProxyPolicyUpdate is a callback used to update the Endpoint's 1740 // proxyPolicyRevision when the specified revision has been applied in the 1741 // proxy. 1742 func (e *Endpoint) OnProxyPolicyUpdate(revision uint64) { 1743 // NOTE: UnconditionalLock is used here because this callback has no way of reporting an error 1744 e.UnconditionalLock() 1745 if revision > e.proxyPolicyRevision { 1746 e.proxyPolicyRevision = revision 1747 } 1748 e.Unlock() 1749 } 1750 1751 // getProxyStatisticsLocked gets the ProxyStatistics for the flows with the 1752 // given characteristics, or adds a new one and returns it. 1753 // Must be called with e.proxyStatisticsMutex held. 1754 func (e *Endpoint) getProxyStatisticsLocked(key string, l7Protocol string, port uint16, ingress bool) *models.ProxyStatistics { 1755 if e.proxyStatistics == nil { 1756 e.proxyStatistics = make(map[string]*models.ProxyStatistics) 1757 } 1758 1759 proxyStats, ok := e.proxyStatistics[key] 1760 if !ok { 1761 var location string 1762 if ingress { 1763 location = models.ProxyStatisticsLocationIngress 1764 } else { 1765 location = models.ProxyStatisticsLocationEgress 1766 } 1767 proxyStats = &models.ProxyStatistics{ 1768 Location: location, 1769 Port: int64(port), 1770 Protocol: l7Protocol, 1771 Statistics: &models.RequestResponseStatistics{ 1772 Requests: &models.MessageForwardingStatistics{}, 1773 Responses: &models.MessageForwardingStatistics{}, 1774 }, 1775 } 1776 1777 e.proxyStatistics[key] = proxyStats 1778 } 1779 1780 return proxyStats 1781 } 1782 1783 // UpdateProxyStatistics updates the Endpoint's proxy statistics to account 1784 // for a new observed flow with the given characteristics. 1785 func (e *Endpoint) UpdateProxyStatistics(l4Protocol string, port uint16, ingress, request bool, verdict accesslog.FlowVerdict) { 1786 e.proxyStatisticsMutex.Lock() 1787 defer e.proxyStatisticsMutex.Unlock() 1788 1789 key := policy.ProxyID(e.ID, ingress, l4Protocol, port) 1790 proxyStats, ok := e.proxyStatistics[key] 1791 if !ok { 1792 e.getLogger().WithField(logfields.L4PolicyID, key).Warn("Proxy stats not found when updating") 1793 return 1794 } 1795 1796 var stats *models.MessageForwardingStatistics 1797 if request { 1798 stats = proxyStats.Statistics.Requests 1799 } else { 1800 stats = proxyStats.Statistics.Responses 1801 } 1802 1803 stats.Received++ 1804 metrics.ProxyReceived.Inc() 1805 metrics.ProxyPolicyL7Total.WithLabelValues("received").Inc() 1806 1807 switch verdict { 1808 case accesslog.VerdictForwarded: 1809 stats.Forwarded++ 1810 metrics.ProxyForwarded.Inc() 1811 metrics.ProxyPolicyL7Total.WithLabelValues("forwarded").Inc() 1812 case accesslog.VerdictDenied: 1813 stats.Denied++ 1814 metrics.ProxyDenied.Inc() 1815 metrics.ProxyPolicyL7Total.WithLabelValues("denied").Inc() 1816 case accesslog.VerdictError: 1817 stats.Error++ 1818 metrics.ProxyParseErrors.Inc() 1819 metrics.ProxyPolicyL7Total.WithLabelValues("parse_errors").Inc() 1820 } 1821 } 1822 1823 // APICanModify determines whether API requests from a user are allowed to 1824 // modify this endpoint. 1825 func APICanModify(e *Endpoint) error { 1826 if e.IsInit() { 1827 return nil 1828 } 1829 if e.OpLabels.OrchestrationIdentity.IsReserved() { 1830 return fmt.Errorf("endpoint may not be associated reserved labels") 1831 } 1832 return nil 1833 } 1834 1835 func (e *Endpoint) getIDandLabels() string { 1836 e.UnconditionalRLock() 1837 defer e.RUnlock() 1838 1839 labels := "" 1840 if e.SecurityIdentity != nil { 1841 labels = e.SecurityIdentity.Labels.String() 1842 } 1843 1844 return fmt.Sprintf("%d (%s)", e.ID, labels) 1845 } 1846 1847 // MetadataResolverCB provides an implementation for resolving the endpoint 1848 // metadata for an endpoint such as the associated labels and annotations. 1849 type MetadataResolverCB func(*Endpoint) (identityLabels labels.Labels, infoLabels labels.Labels, err error) 1850 1851 // RunMetadataResolver starts a controller associated with the received 1852 // endpoint which will periodically attempt to resolve the metadata for the 1853 // endpoint and update the endpoint with the related. It stops resolving after 1854 // either the first successful metadata resolution or when the endpoint is 1855 // removed. 1856 // 1857 // This assumes that after the initial successful resolution, other mechanisms 1858 // will handle updates (such as pkg/k8s/watchers informers). 1859 func (e *Endpoint) RunMetadataResolver(resolveMetadata MetadataResolverCB) { 1860 done := make(chan struct{}) 1861 controllerName := fmt.Sprintf("resolve-labels-%s/%s", e.GetK8sNamespace(), e.GetK8sPodName()) 1862 go func() { 1863 <-done 1864 e.controllers.RemoveController(controllerName) 1865 }() 1866 1867 e.controllers.UpdateController(controllerName, 1868 controller.ControllerParams{ 1869 DoFunc: func(ctx context.Context) error { 1870 identityLabels, info, err := resolveMetadata(e) 1871 if err != nil { 1872 e.Logger(controllerName).WithError(err).Warning("Unable to fetch kubernetes labels") 1873 return err 1874 } 1875 e.UpdateLabels(ctx, identityLabels, info, true) 1876 close(done) 1877 return nil 1878 }, 1879 RunInterval: 30 * time.Second, 1880 }, 1881 ) 1882 } 1883 1884 // ModifyIdentityLabels changes the custom and orchestration identity labels of an endpoint. 1885 // Labels can be added or deleted. If a label change is performed, the 1886 // endpoint will receive a new identity and will be regenerated. Both of these 1887 // operations will happen in the background. 1888 func (e *Endpoint) ModifyIdentityLabels(addLabels, delLabels pkgLabels.Labels) error { 1889 if err := e.LockAlive(); err != nil { 1890 return err 1891 } 1892 1893 changed, err := e.OpLabels.ModifyIdentityLabels(addLabels, delLabels) 1894 if err != nil { 1895 e.Unlock() 1896 return err 1897 } 1898 1899 var rev int 1900 if changed { 1901 // Mark with StateWaitingForIdentity, it will be set to 1902 // StateWaitingToRegenerate after the identity resolution has been 1903 // completed 1904 e.SetStateLocked(StateWaitingForIdentity, "Triggering identity resolution due to updated identity labels") 1905 1906 e.identityRevision++ 1907 rev = e.identityRevision 1908 } 1909 e.Unlock() 1910 1911 if changed { 1912 e.runIdentityResolver(context.Background(), rev, false) 1913 } 1914 return nil 1915 } 1916 1917 // IsInit returns true if the endpoint still hasn't received identity labels, 1918 // i.e. has the special identity with label reserved:init. 1919 func (e *Endpoint) IsInit() bool { 1920 init, found := e.OpLabels.GetIdentityLabel(pkgLabels.IDNameInit) 1921 return found && init.Source == pkgLabels.LabelSourceReserved 1922 } 1923 1924 // UpdateLabels is called to update the labels of an endpoint. Calls to this 1925 // function do not necessarily mean that the labels actually changed. The 1926 // container runtime layer will periodically synchronize labels. 1927 // 1928 // If a net label changed was performed, the endpoint will receive a new 1929 // identity and will be regenerated. Both of these operations will happen in 1930 // the background. 1931 func (e *Endpoint) UpdateLabels(ctx context.Context, identityLabels, infoLabels pkgLabels.Labels, blocking bool) { 1932 log.WithFields(logrus.Fields{ 1933 logfields.ContainerID: e.GetShortContainerID(), 1934 logfields.EndpointID: e.StringID(), 1935 logfields.IdentityLabels: identityLabels.String(), 1936 logfields.InfoLabels: infoLabels.String(), 1937 }).Debug("Refreshing labels of endpoint") 1938 1939 if err := e.LockAlive(); err != nil { 1940 e.LogDisconnectedMutexAction(err, "when trying to refresh endpoint labels") 1941 return 1942 } 1943 1944 e.replaceInformationLabels(infoLabels) 1945 // replace identity labels and update the identity if labels have changed 1946 rev := e.replaceIdentityLabels(identityLabels) 1947 e.Unlock() 1948 if rev != 0 { 1949 e.runIdentityResolver(ctx, rev, blocking) 1950 } 1951 } 1952 1953 func (e *Endpoint) identityResolutionIsObsolete(myChangeRev int) bool { 1954 // Check if the endpoint has since received a new identity revision, if 1955 // so, abort as a new resolution routine will have been started. 1956 if myChangeRev != e.identityRevision { 1957 return true 1958 } 1959 1960 return false 1961 } 1962 1963 // runIdentityResolver resolves the numeric identity for the set of labels that 1964 // are currently configured on the endpoint. 1965 // 1966 // Must be called with e.Mutex NOT held. 1967 func (e *Endpoint) runIdentityResolver(ctx context.Context, myChangeRev int, blocking bool) { 1968 if err := e.RLockAlive(); err != nil { 1969 // If a labels update and an endpoint delete API request arrive 1970 // in quick succession, this could occur; in that case, there's 1971 // no point updating the controller. 1972 e.getLogger().WithError(err).Info("Cannot run labels resolver") 1973 return 1974 } 1975 newLabels := e.OpLabels.IdentityLabels() 1976 e.RUnlock() 1977 scopedLog := e.getLogger().WithField(logfields.IdentityLabels, newLabels) 1978 1979 // If we are certain we can resolve the identity without accessing the KV 1980 // store, do it first synchronously right now. This can reduce the number 1981 // of regenerations for the endpoint during its initialization. 1982 if blocking || cache.IdentityAllocationIsLocal(newLabels) { 1983 scopedLog.Info("Resolving identity labels (blocking)") 1984 1985 err := e.identityLabelsChanged(ctx, myChangeRev) 1986 switch err { 1987 case ErrNotAlive: 1988 scopedLog.Debug("not changing endpoint identity because endpoint is in process of being removed") 1989 return 1990 default: 1991 if err != nil { 1992 scopedLog.WithError(err).Warn("Error changing endpoint identity") 1993 } 1994 } 1995 } else { 1996 scopedLog.Info("Resolving identity labels (non-blocking)") 1997 } 1998 1999 ctrlName := fmt.Sprintf("resolve-identity-%d", e.ID) 2000 e.controllers.UpdateController(ctrlName, 2001 controller.ControllerParams{ 2002 DoFunc: func(ctx context.Context) error { 2003 err := e.identityLabelsChanged(ctx, myChangeRev) 2004 switch err { 2005 case ErrNotAlive: 2006 e.getLogger().Debug("not changing endpoint identity because endpoint is in process of being removed") 2007 return controller.NewExitReason("Endpoint disappeared") 2008 default: 2009 return err 2010 } 2011 }, 2012 RunInterval: 5 * time.Minute, 2013 }, 2014 ) 2015 } 2016 2017 func (e *Endpoint) identityLabelsChanged(ctx context.Context, myChangeRev int) error { 2018 if err := e.RLockAlive(); err != nil { 2019 return ErrNotAlive 2020 } 2021 newLabels := e.OpLabels.IdentityLabels() 2022 elog := e.getLogger().WithFields(logrus.Fields{ 2023 logfields.EndpointID: e.ID, 2024 logfields.IdentityLabels: newLabels, 2025 }) 2026 2027 // Since we unlocked the endpoint and re-locked, the label update may already be obsolete 2028 if e.identityResolutionIsObsolete(myChangeRev) { 2029 e.RUnlock() 2030 elog.Debug("Endpoint identity has changed, aborting resolution routine in favour of new one") 2031 return nil 2032 } 2033 2034 if e.SecurityIdentity != nil && e.SecurityIdentity.Labels.Equals(newLabels) { 2035 // Sets endpoint state to ready if was waiting for identity 2036 if e.GetStateLocked() == StateWaitingForIdentity { 2037 e.SetStateLocked(StateReady, "Set identity for this endpoint") 2038 } 2039 e.RUnlock() 2040 elog.Debug("Endpoint labels unchanged, skipping resolution of identity") 2041 return nil 2042 } 2043 2044 // Unlock the endpoint mutex for the possibly long lasting kvstore operation 2045 e.RUnlock() 2046 elog.Debug("Resolving identity for labels") 2047 2048 allocateCtx, cancel := context.WithTimeout(context.Background(), option.Config.KVstoreConnectivityTimeout) 2049 defer cancel() 2050 2051 identity, _, err := cache.AllocateIdentity(allocateCtx, e.owner, newLabels) 2052 if err != nil { 2053 err = fmt.Errorf("unable to resolve identity: %s", err) 2054 e.LogStatus(Other, Warning, fmt.Sprintf("%s (will retry)", err.Error())) 2055 return err 2056 } 2057 2058 // When releasing identities after allocation due to either failure of 2059 // allocation or due a no longer used identity we want to operation to 2060 // continue even if the parent has given up. Enforce a timeout of two 2061 // minutes to avoid blocking forever but give plenty of time to release 2062 // the identity. 2063 releaseCtx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) 2064 defer cancel() 2065 2066 releaseNewlyAllocatedIdentity := func() { 2067 _, err := cache.Release(releaseCtx, e.owner, identity) 2068 if err != nil { 2069 // non fatal error as keys will expire after lease expires but log it 2070 elog.WithFields(logrus.Fields{logfields.Identity: identity.ID}). 2071 WithError(err).Warn("Unable to release newly allocated identity again") 2072 } 2073 } 2074 2075 if err := e.LockAlive(); err != nil { 2076 releaseNewlyAllocatedIdentity() 2077 return err 2078 } 2079 2080 // Since we unlocked the endpoint and re-locked, the label update may already be obsolete 2081 if e.identityResolutionIsObsolete(myChangeRev) { 2082 e.Unlock() 2083 2084 releaseNewlyAllocatedIdentity() 2085 2086 return nil 2087 } 2088 2089 // If endpoint has an old identity, defer release of it to the end of 2090 // the function after the endpoint structured has been unlocked again 2091 oldIdentity := e.SecurityIdentity 2092 if oldIdentity != nil { 2093 // The identity of the endpoint is changing, delay the use of 2094 // the identity by a grace period to give all other cluster 2095 // nodes a chance to adjust their policies first. This requires 2096 // to unlock the endpoit and then lock it again. 2097 // 2098 // If the identity change is from init -> *, don't delay the 2099 // use of the identity as we want the init duration to be as 2100 // short as possible. 2101 if identity.ID != oldIdentity.ID && oldIdentity.ID != identityPkg.ReservedIdentityInit { 2102 e.Unlock() 2103 2104 elog.Debugf("Applying grace period before regeneration due to identity change") 2105 time.Sleep(option.Config.IdentityChangeGracePeriod) 2106 2107 if err := e.LockAlive(); err != nil { 2108 releaseNewlyAllocatedIdentity() 2109 return err 2110 } 2111 2112 // Since we unlocked the endpoint and re-locked, the label update may already be obsolete 2113 if e.identityResolutionIsObsolete(myChangeRev) { 2114 e.Unlock() 2115 releaseNewlyAllocatedIdentity() 2116 return nil 2117 } 2118 } 2119 } 2120 2121 elog.WithFields(logrus.Fields{logfields.Identity: identity.StringID()}). 2122 Debug("Assigned new identity to endpoint") 2123 2124 e.SetIdentity(identity, false) 2125 2126 if oldIdentity != nil { 2127 _, err := cache.Release(releaseCtx, e.owner, oldIdentity) 2128 if err != nil { 2129 elog.WithFields(logrus.Fields{logfields.Identity: oldIdentity.ID}). 2130 WithError(err).Warn("Unable to release old endpoint identity") 2131 } 2132 } 2133 2134 readyToRegenerate := false 2135 2136 // Regeneration is only triggered once the endpoint ID has been 2137 // assigned. This ensures that on the initial creation, the endpoint is 2138 // not generated until the endpoint ID has been assigned. If the 2139 // identity is resolved before the endpoint ID is assigned, the 2140 // regeneration is deferred into endpointmanager.AddEndpoint(). If the 2141 // identity is not allocated yet when endpointmanager.AddEndpoint() is 2142 // called, the controller calling identityLabelsChanged() will trigger 2143 // the regeneration as soon as the identity is known. 2144 if e.ID != 0 { 2145 readyToRegenerate = e.SetStateLocked(StateWaitingToRegenerate, "Triggering regeneration due to new identity") 2146 } 2147 2148 // Unconditionally force policy recomputation after a new identity has been 2149 // assigned. 2150 e.ForcePolicyCompute() 2151 2152 e.Unlock() 2153 2154 if readyToRegenerate { 2155 e.Regenerate(®eneration.ExternalRegenerationMetadata{Reason: "updated security labels"}) 2156 } 2157 2158 return nil 2159 } 2160 2161 // SetPolicyRevision sets the endpoint's policy revision with the given 2162 // revision. 2163 func (e *Endpoint) SetPolicyRevision(rev uint64) { 2164 if err := e.LockAlive(); err != nil { 2165 return 2166 } 2167 e.setPolicyRevision(rev) 2168 e.Unlock() 2169 } 2170 2171 // setPolicyRevision sets the endpoint's policy revision with the given 2172 // revision. 2173 func (e *Endpoint) setPolicyRevision(rev uint64) { 2174 if rev <= e.policyRevision { 2175 return 2176 } 2177 2178 now := time.Now() 2179 e.policyRevision = rev 2180 e.UpdateLogger(map[string]interface{}{ 2181 logfields.DatapathPolicyRevision: e.policyRevision, 2182 }) 2183 for ps := range e.policyRevisionSignals { 2184 select { 2185 case <-ps.ctx.Done(): 2186 close(ps.ch) 2187 ps.done(now) 2188 delete(e.policyRevisionSignals, ps) 2189 default: 2190 if rev >= ps.wantedRev { 2191 close(ps.ch) 2192 ps.done(now) 2193 delete(e.policyRevisionSignals, ps) 2194 } 2195 } 2196 } 2197 } 2198 2199 // cleanPolicySignals closes and removes all policy revision signals. 2200 func (e *Endpoint) cleanPolicySignals() { 2201 now := time.Now() 2202 for w := range e.policyRevisionSignals { 2203 w.done(now) 2204 close(w.ch) 2205 } 2206 e.policyRevisionSignals = map[*policySignal]bool{} 2207 } 2208 2209 // policySignal is used to mark when a wanted policy wantedRev is reached 2210 type policySignal struct { 2211 // wantedRev specifies which policy revision the signal wants. 2212 wantedRev uint64 2213 // ch is the channel that signalizes once the policy revision wanted is reached. 2214 ch chan struct{} 2215 // ctx is the context for the policy signal request. 2216 ctx context.Context 2217 // done is a callback to call for this policySignal. It is in addition to the 2218 // ch above. 2219 done func(ts time.Time) 2220 } 2221 2222 // WaitForPolicyRevision returns a channel that is closed when one or more of 2223 // the following conditions have met: 2224 // - the endpoint is disconnected state 2225 // - the endpoint's policy revision reaches the wanted revision 2226 // When the done callback is non-nil it will be called just before the channel is closed. 2227 func (e *Endpoint) WaitForPolicyRevision(ctx context.Context, rev uint64, done func(ts time.Time)) <-chan struct{} { 2228 // NOTE: UnconditionalLock is used here because this method handles endpoint in disconnected state on its own 2229 e.UnconditionalLock() 2230 defer e.Unlock() 2231 2232 if done == nil { 2233 done = func(time.Time) {} 2234 } 2235 2236 ch := make(chan struct{}) 2237 if e.policyRevision >= rev || e.state == StateDisconnected { 2238 close(ch) 2239 done(time.Now()) 2240 return ch 2241 } 2242 ps := &policySignal{ 2243 wantedRev: rev, 2244 ctx: ctx, 2245 ch: ch, 2246 done: done, 2247 } 2248 if e.policyRevisionSignals == nil { 2249 e.policyRevisionSignals = map[*policySignal]bool{} 2250 } 2251 e.policyRevisionSignals[ps] = true 2252 return ch 2253 } 2254 2255 // IPs returns the slice of valid IPs for this endpoint. 2256 func (e *Endpoint) IPs() []net.IP { 2257 ips := []net.IP{} 2258 if e.IPv4.IsSet() { 2259 ips = append(ips, e.IPv4.IP()) 2260 } 2261 if e.IPv6.IsSet() { 2262 ips = append(ips, e.IPv6.IP()) 2263 } 2264 return ips 2265 } 2266 2267 // InsertEvent is called when the endpoint is inserted into the endpoint 2268 // manager. 2269 func (e *Endpoint) InsertEvent() { 2270 e.getLogger().Info("New endpoint") 2271 } 2272 2273 // IsDisconnecting returns true if the endpoint is being disconnected or 2274 // already disconnected 2275 // 2276 // This function must be called after re-acquiring the endpoint mutex to verify 2277 // that the endpoint has not been removed in the meantime. 2278 // 2279 // endpoint.mutex must be held in read mode at least 2280 func (e *Endpoint) IsDisconnecting() bool { 2281 return e.state == StateDisconnected || e.state == StateDisconnecting 2282 } 2283 2284 // PinDatapathMap retrieves a file descriptor from the map ID from the API call 2285 // and pins the corresponding map into the BPF file system. 2286 func (e *Endpoint) PinDatapathMap() error { 2287 if e.DatapathMapID == 0 { 2288 return nil 2289 } 2290 2291 mapFd, err := bpf.MapFdFromID(e.DatapathMapID) 2292 if err != nil { 2293 return err 2294 } 2295 defer unix.Close(mapFd) 2296 2297 err = bpf.ObjPin(mapFd, e.BPFIpvlanMapPath()) 2298 2299 if err == nil { 2300 e.isDatapathMapPinned = true 2301 } 2302 2303 return err 2304 } 2305 2306 func (e *Endpoint) syncEndpointHeaderFile(reasons []string) { 2307 e.BuildMutex.Lock() 2308 defer e.BuildMutex.Unlock() 2309 2310 if err := e.LockAlive(); err != nil { 2311 // endpoint was removed in the meanwhile, return 2312 return 2313 } 2314 defer e.Unlock() 2315 2316 if err := e.writeHeaderfile(e.StateDirectoryPath()); err != nil { 2317 e.getLogger().WithFields(logrus.Fields{ 2318 logfields.Reason: reasons, 2319 }).WithError(err).Warning("could not sync header file") 2320 } 2321 } 2322 2323 // SyncEndpointHeaderFile it bumps the current DNS History information for the 2324 // endpoint in the lxc_config.h file. 2325 func (e *Endpoint) SyncEndpointHeaderFile() error { 2326 if err := e.LockAlive(); err != nil { 2327 // endpoint was removed in the meanwhile, return 2328 return nil 2329 } 2330 defer e.Unlock() 2331 2332 if e.dnsHistoryTrigger == nil { 2333 t, err := trigger.NewTrigger(trigger.Parameters{ 2334 Name: "sync_endpoint_header_file", 2335 MinInterval: 5 * time.Second, 2336 TriggerFunc: func(reasons []string) { e.syncEndpointHeaderFile(reasons) }, 2337 }) 2338 if err != nil { 2339 return fmt.Errorf( 2340 "Sync Endpoint header file trigger for endpoint cannot be activated: %s", 2341 err) 2342 } 2343 e.dnsHistoryTrigger = t 2344 } 2345 e.dnsHistoryTrigger.Trigger() 2346 return nil 2347 }