github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/monitor/internal/kubernetes/handler.go (about)

     1  // +build !windows
     2  
     3  package kubernetesmonitor
     4  
     5  import (
     6  	"context"
     7  	"fmt"
     8  
     9  	"go.aporeto.io/trireme-lib/common"
    10  	"go.aporeto.io/trireme-lib/monitor/constants"
    11  	"go.aporeto.io/trireme-lib/policy"
    12  	"go.uber.org/zap"
    13  	api "k8s.io/api/core/v1"
    14  )
    15  
    16  // General logic for handling logic fron the DockerMonitor ss the following:
    17  // The only interesting event is the Start and Die event. All the other events are ignored
    18  
    19  // Those events are then put together with the Pod events received from the Kubernetes API.
    20  // Once both are received and are consistent, the Pod get activated.
    21  
    22  // HandlePUEvent is called by all monitors when a PU event is generated. The implementer
    23  // is responsible to update all components by explicitly adding a new PU.
    24  // Specifically for Kubernetes, The monitor handles the downstream events from Docker.
    25  func (m *KubernetesMonitor) HandlePUEvent(ctx context.Context, puID string, event common.Event, dockerRuntime policy.RuntimeReader) error {
    26  	zap.L().Debug("dockermonitor event", zap.String("puID", puID), zap.String("eventType", string(event)))
    27  
    28  	var kubernetesRuntime policy.RuntimeReader
    29  
    30  	// If the event coming from DockerMonitor is start or create, we will get a meaningful PURuntime from
    31  	// DockerMonitor. We can use it and combine it with the pod information on Kubernetes API.
    32  	if event == common.EventStart || event == common.EventCreate {
    33  
    34  		// We check first if this is a Kubernetes managed container
    35  		podNamespace, podName, err := getKubernetesInformation(dockerRuntime)
    36  		if err != nil {
    37  			return err
    38  		}
    39  
    40  		// We get the information for that specific POD from Kubernetes API
    41  		pod, err := m.getPod(podNamespace, podName)
    42  		if err != nil {
    43  			return err
    44  		}
    45  		// The KubernetesMetadataExtractor combines the information coming from Docker (runtime)
    46  		// and from Kube (pod) in order to create a KubernetesRuntime.
    47  		// The managedContainer parameters define if this container should be ignored.
    48  		var managedContainer bool
    49  		kubernetesRuntime, managedContainer, err = m.kubernetesExtractor(dockerRuntime, pod)
    50  		if err != nil {
    51  
    52  			return fmt.Errorf("error while processing Kubernetes pod %s/%s for container %s %s", podNamespace, podName, puID, err)
    53  		}
    54  
    55  		// UnmanagedContainers are simply ignored. No policy is associated.
    56  		if !managedContainer {
    57  			// for Unmanaged container check if host network and add the process to cgroup.
    58  			zap.L().Debug("unmanaged Kubernetes container on create or start", zap.String("puID", puID), zap.String("podNamespace", podNamespace), zap.String("podName", podName))
    59  			return m.decorateRuntime(puID, dockerRuntime, event, podName, podNamespace)
    60  		}
    61  
    62  		// We keep the cache uptoDate for future queries
    63  		m.cache.updatePUIDCache(podNamespace, podName, puID, dockerRuntime, kubernetesRuntime)
    64  	} else {
    65  
    66  		// We check if this PUID was previously managed. We only sent the event upstream to the resolver if it was managed on create or start.
    67  		kubernetesRuntime = m.cache.getKubernetesRuntimeByPUID(puID)
    68  		if kubernetesRuntime == nil {
    69  			zap.L().Debug("unmanaged Kubernetes container", zap.String("puID", puID), zap.String("event", string(event)))
    70  			return nil
    71  		}
    72  	}
    73  
    74  	if event == common.EventDestroy {
    75  		// Time to kill the cache entry
    76  		m.cache.deletePUIDCache(puID)
    77  	}
    78  
    79  	// The event is then sent to the upstream policyResolver
    80  	if err := m.handlers.Policy.HandlePUEvent(ctx, puID, event, kubernetesRuntime); err != nil {
    81  		zap.L().Error("Unable to resolve policy for puid", zap.String("puID", puID), zap.Error(err))
    82  		return fmt.Errorf("Unable to resolve policy for puid:%s", puID)
    83  	}
    84  
    85  	if dockerRuntime.PUType() == common.LinuxProcessPU {
    86  		return m.decorateRuntime(puID, dockerRuntime, event, "", "")
    87  	}
    88  
    89  	return nil
    90  }
    91  
    92  // RefreshPUs is used to resend an update event to the Upstream Policy Resolver in case of an update is needed.
    93  func (m *KubernetesMonitor) RefreshPUs(ctx context.Context, pod *api.Pod) error {
    94  	if pod == nil {
    95  		return fmt.Errorf("pod is nil")
    96  	}
    97  
    98  	podNamespace := pod.GetNamespace()
    99  	podName := pod.GetName()
   100  
   101  	puIDs := m.cache.getPUIDsbyPod(podNamespace, podName)
   102  
   103  	for _, puid := range puIDs {
   104  		dockerRuntime := m.cache.getDockerRuntimeByPUID(puid)
   105  		if dockerRuntime == nil {
   106  			continue
   107  		}
   108  
   109  		kubernetesRuntime, managedContainer, err := m.kubernetesExtractor(dockerRuntime, pod)
   110  		if err != nil {
   111  			return fmt.Errorf("error while processing Kubernetes pod %s/%s for container %s %s", podNamespace, podName, puid, err)
   112  		}
   113  
   114  		// UnmanagedContainers are simply ignored. It should not come this far if it is a non managed container anyways.
   115  		if !managedContainer {
   116  			zap.L().Debug("unmanaged Kubernetes container", zap.String("puID", puid), zap.String("podNamespace", podNamespace), zap.String("podName", podName))
   117  			continue
   118  		}
   119  
   120  		// We keep the cache uptoDate for future queries
   121  		m.cache.updatePUIDCache(podNamespace, podName, puid, dockerRuntime, kubernetesRuntime)
   122  
   123  		if err := m.handlers.Policy.HandlePUEvent(ctx, puid, common.EventUpdate, kubernetesRuntime); err != nil {
   124  			return err
   125  		}
   126  	}
   127  
   128  	return nil
   129  }
   130  
   131  // getKubernetesInformation returns the name and namespace from a standard Docker runtime, if the docker container is associated at all with Kubernetes
   132  func getKubernetesInformation(runtime policy.RuntimeReader) (string, string, error) {
   133  	podNamespace, ok := runtime.Tag(KubernetesPodNamespaceIdentifier)
   134  	if !ok {
   135  		return "", "", fmt.Errorf("Error getting Kubernetes Pod namespace")
   136  	}
   137  	podName, ok := runtime.Tag(KubernetesPodNameIdentifier)
   138  	if !ok {
   139  		return "", "", fmt.Errorf("Error getting Kubernetes Pod name")
   140  	}
   141  
   142  	return podNamespace, podName, nil
   143  }
   144  
   145  // decorateRuntime decorates the docker runtime with puid of the pause container.
   146  func (m *KubernetesMonitor) decorateRuntime(puID string, runtimeInfo policy.RuntimeReader, event common.Event,
   147  	podName, podNamespace string) (err error) {
   148  
   149  	// Do nothing on other events apart from start event.
   150  	if event != common.EventStart {
   151  		return nil
   152  	}
   153  
   154  	puRuntime, ok := runtimeInfo.(*policy.PURuntime)
   155  	if !ok {
   156  		zap.L().Error("Found invalid runtime for puid", zap.String("puid", puID))
   157  		return fmt.Errorf("invalid runtime for puid:%s", puID)
   158  	}
   159  
   160  	extensions := policy.ExtendedMap{}
   161  
   162  	// pause container with host net set to true.
   163  	if runtimeInfo.PUType() == common.LinuxProcessPU {
   164  		extensions[constants.DockerHostMode] = "true"
   165  		extensions[constants.DockerHostPUID] = puID
   166  		options := puRuntime.Options()
   167  		options.PolicyExtensions = extensions
   168  		options.AutoPort = true
   169  
   170  		// set Options on docker runtime.
   171  		puRuntime.SetOptions(options)
   172  		return nil
   173  	}
   174  
   175  	pausePUID := ""
   176  	puIDs := m.cache.getPUIDsbyPod(podNamespace, podName)
   177  	// get the puid of the pause container.
   178  	for _, id := range puIDs {
   179  		rtm := m.cache.getDockerRuntimeByPUID(id)
   180  		if rtm == nil {
   181  			continue
   182  		}
   183  
   184  		if isPodInfraContainer(rtm) && rtm.PUType() == common.LinuxProcessPU {
   185  			pausePUID = id
   186  			break
   187  		}
   188  
   189  		// if the pause container is not host net container, nothing to do.
   190  		if isPodInfraContainer(rtm) {
   191  			return nil
   192  		}
   193  	}
   194  
   195  	extensions[constants.DockerHostPUID] = pausePUID
   196  	options := puRuntime.Options()
   197  	options.PolicyExtensions = extensions
   198  	// set Options on docker runtime.
   199  	puRuntime.SetOptions(options)
   200  
   201  	return nil
   202  }
   203  
   204  // isPodInfraContainer returns true if the runtime represents the infra container for the POD
   205  func isPodInfraContainer(runtime policy.RuntimeReader) bool {
   206  	// The Infra container can be found by checking env. variable.
   207  	tagContent, ok := runtime.Tag(KubernetesContainerNameIdentifier)
   208  
   209  	return ok && tagContent == KubernetesInfraContainerName
   210  }