istio.io/istio@v0.0.0-20240520182934-d79c90f27776/cni/pkg/nodeagent/server.go (about) 1 // Copyright Istio Authors 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 nodeagent 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "net/netip" 22 "os" 23 "path/filepath" 24 "sync/atomic" 25 26 corev1 "k8s.io/api/core/v1" 27 "k8s.io/client-go/kubernetes" 28 "k8s.io/client-go/rest" 29 30 pconstants "istio.io/istio/cni/pkg/constants" 31 "istio.io/istio/cni/pkg/ipset" 32 "istio.io/istio/cni/pkg/iptables" 33 "istio.io/istio/cni/pkg/util" 34 "istio.io/istio/pkg/kube" 35 ) 36 37 type MeshDataplane interface { 38 // called first, (even before Start()). 39 ConstructInitialSnapshot(ambientPods []*corev1.Pod) error 40 Start(ctx context.Context) 41 42 // IsPodInMesh(ctx context.Context, pod *metav1.ObjectMeta, netNs string) (bool, error) 43 AddPodToMesh(ctx context.Context, pod *corev1.Pod, podIPs []netip.Addr, netNs string) error 44 RemovePodFromMesh(ctx context.Context, pod *corev1.Pod) error 45 DelPodFromMesh(ctx context.Context, pod *corev1.Pod) error 46 47 Stop() 48 } 49 50 type Server struct { 51 ctx context.Context 52 kubeClient kube.Client 53 54 handlers K8sHandlers 55 dataplane MeshDataplane 56 57 isReady *atomic.Value 58 59 cniServerStopFunc func() 60 } 61 62 func NewServer(ctx context.Context, ready *atomic.Value, pluginSocket string, args AmbientArgs) (*Server, error) { 63 client, err := buildKubeClient(args.KubeConfig) 64 if err != nil { 65 return nil, fmt.Errorf("error initializing kube client: %w", err) 66 } 67 68 cfg := &iptables.Config{ 69 RestoreFormat: true, 70 RedirectDNS: args.DNSCapture, 71 EnableIPv6: args.EnableIPv6, 72 } 73 74 log.Debug("creating ipsets in the node netns") 75 set, err := createHostsideProbeIpset(cfg.EnableIPv6) 76 if err != nil { 77 return nil, fmt.Errorf("error initializing hostside probe ipset: %w", err) 78 } 79 80 podNsMap := newPodNetnsCache(openNetnsInRoot(pconstants.HostMountsPath)) 81 ztunnelServer, err := newZtunnelServer(args.ServerSocket, podNsMap) 82 if err != nil { 83 return nil, fmt.Errorf("error initializing the ztunnel server: %w", err) 84 } 85 86 iptablesConfigurator, err := iptables.NewIptablesConfigurator(cfg, realDependencies(), iptables.RealNlDeps()) 87 if err != nil { 88 return nil, fmt.Errorf("error configuring iptables: %w", err) 89 } 90 91 // Create hostprobe rules now, in the host netns 92 // Later we will reuse this same configurator inside the pod netns for adding other rules 93 iptablesConfigurator.DeleteHostRules() 94 95 if err := iptablesConfigurator.CreateHostRulesForHealthChecks(&HostProbeSNATIP, &HostProbeSNATIPV6); err != nil { 96 return nil, fmt.Errorf("error initializing the host rules for health checks: %w", err) 97 } 98 99 podNetns := NewPodNetnsProcFinder(os.DirFS(filepath.Join(pconstants.HostMountsPath, "proc"))) 100 netServer := newNetServer(ztunnelServer, podNsMap, iptablesConfigurator, podNetns, set) 101 102 // Set some defaults 103 s := &Server{ 104 ctx: ctx, 105 kubeClient: client, 106 isReady: ready, 107 dataplane: &meshDataplane{ 108 kubeClient: client.Kube(), 109 netServer: netServer, 110 }, 111 } 112 s.NotReady() 113 s.handlers = setupHandlers(s.ctx, s.kubeClient, s.dataplane, args.SystemNamespace) 114 115 cniServer := startCniPluginServer(ctx, pluginSocket, s.handlers, s.dataplane) 116 err = cniServer.Start() 117 if err != nil { 118 return nil, fmt.Errorf("error starting cni server: %w", err) 119 } 120 s.cniServerStopFunc = cniServer.Stop 121 122 return s, nil 123 } 124 125 func (s *Server) Ready() { 126 s.isReady.Store(true) 127 } 128 129 func (s *Server) NotReady() { 130 s.isReady.Store(false) 131 } 132 133 // buildKubeClient creates the kube client 134 func buildKubeClient(kubeConfig string) (kube.Client, error) { 135 // Used by validation 136 kubeRestConfig, err := kube.DefaultRestConfig(kubeConfig, "", func(config *rest.Config) { 137 config.QPS = 80 138 config.Burst = 160 139 }) 140 if err != nil { 141 return nil, fmt.Errorf("failed creating kube config: %v", err) 142 } 143 144 client, err := kube.NewClient(kube.NewClientConfigForRestConfig(kubeRestConfig), "") 145 if err != nil { 146 return nil, fmt.Errorf("failed creating kube client: %v", err) 147 } 148 149 return client, nil 150 } 151 152 // createHostsideProbeIpset creates an ipset. This is designed to be called from the host netns. 153 // Note that if the ipset already exist by name, Create will not return an error. 154 // 155 // We will unconditionally flush our set before use here, so it shouldn't matter. 156 func createHostsideProbeIpset(isV6 bool) (ipset.IPSet, error) { 157 linDeps := ipset.RealNlDeps() 158 probeSet, err := ipset.NewIPSet(iptables.ProbeIPSet, isV6, linDeps) 159 if err != nil { 160 return probeSet, err 161 } 162 probeSet.Flush() 163 return probeSet, nil 164 } 165 166 func (s *Server) Start() { 167 log.Info("CNI ambient server starting") 168 s.kubeClient.RunAndWait(s.ctx.Done()) 169 log.Info("CNI ambient server kubeclient started") 170 pods := s.handlers.GetAmbientPods() 171 err := s.dataplane.ConstructInitialSnapshot(pods) 172 if err != nil { 173 log.Warnf("failed to construct initial snapshot: %v", err) 174 } 175 176 log.Info("CNI ambient server marking ready") 177 s.Ready() 178 s.dataplane.Start(s.ctx) 179 s.handlers.Start() 180 } 181 182 func (s *Server) Stop() { 183 log.Info("CNI ambient server terminating, cleaning up node net rules") 184 185 s.cniServerStopFunc() 186 s.dataplane.Stop() 187 } 188 189 type meshDataplane struct { 190 kubeClient kubernetes.Interface 191 netServer MeshDataplane 192 } 193 194 func (s *meshDataplane) Start(ctx context.Context) { 195 s.netServer.Start(ctx) 196 } 197 198 func (s *meshDataplane) Stop() { 199 s.netServer.Stop() 200 } 201 202 func (s *meshDataplane) ConstructInitialSnapshot(ambientPods []*corev1.Pod) error { 203 return s.netServer.ConstructInitialSnapshot(ambientPods) 204 } 205 206 func (s *meshDataplane) AddPodToMesh(ctx context.Context, pod *corev1.Pod, podIPs []netip.Addr, netNs string) error { 207 var retErr error 208 err := s.netServer.AddPodToMesh(ctx, pod, podIPs, netNs) 209 if err != nil { 210 log.Errorf("failed to add pod to ztunnel: %v", err) 211 if !errors.Is(err, ErrPartialAdd) { 212 return err 213 } 214 retErr = err 215 } 216 217 log.Debugf("annotating pod %s", pod.Name) 218 if err := util.AnnotateEnrolledPod(s.kubeClient, &pod.ObjectMeta); err != nil { 219 log.Errorf("failed to annotate pod enrollment: %v", err) 220 // don't return error here, as this is purely informational. 221 } 222 return retErr 223 } 224 225 func (s *meshDataplane) RemovePodFromMesh(ctx context.Context, pod *corev1.Pod) error { 226 log := log.WithLabels("ns", pod.Namespace, "name", pod.Name) 227 err := s.netServer.RemovePodFromMesh(ctx, pod) 228 if err != nil { 229 log.Errorf("failed to remove pod from mesh: %v", err) 230 return err 231 } 232 log.Debug("removing annotation from pod") 233 err = util.AnnotateUnenrollPod(s.kubeClient, &pod.ObjectMeta) 234 if err != nil { 235 log.Errorf("failed to annotate pod unenrollment: %v", err) 236 } 237 return err 238 } 239 240 // Delete pod from mesh: pod is deleted. iptables rules will die with it, we just need to update ztunnel 241 func (s *meshDataplane) DelPodFromMesh(ctx context.Context, pod *corev1.Pod) error { 242 log := log.WithLabels("ns", pod.Namespace, "name", pod.Name) 243 err := s.netServer.DelPodFromMesh(ctx, pod) 244 if err != nil { 245 log.Errorf("failed to delete pod from mesh: %v", err) 246 return err 247 } 248 return nil 249 }