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 }