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  }