blob: e35157d6f2b08f1ee5ec0a256f9dc34ba9146607 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
Lorenz Brun878f5f92020-05-12 16:15:39 +02002// SPDX-License-Identifier: Apache-2.0
Lorenz Brun878f5f92020-05-12 16:15:39 +02003
4package kubernetes
5
6import (
7 "context"
Serge Bazanski2cfafc92023-03-21 16:42:47 +01008 "crypto/ed25519"
Lorenz Brun878f5f92020-05-12 16:15:39 +02009 "encoding/json"
Serge Bazanski2cfafc92023-03-21 16:42:47 +010010 "encoding/pem"
Lorenz Brun878f5f92020-05-12 16:15:39 +020011 "fmt"
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +020012 "net"
Lorenz Brun878f5f92020-05-12 16:15:39 +020013 "os/exec"
14
Lorenz Brun878f5f92020-05-12 16:15:39 +020015 v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +020016 kubeletconfig "k8s.io/kubelet/config/v1beta1"
Tim Windelschmidtd77e26e2024-12-02 18:23:10 +010017 "k8s.io/utils/ptr"
Serge Bazanski77cb6c52020-12-19 00:09:22 +010018
Serge Bazanski2cfafc92023-03-21 16:42:47 +010019 ipb "source.monogon.dev/metropolis/node/core/curator/proto/api"
Tim Windelschmidt93020d72024-02-13 18:13:07 +010020
Serge Bazanski31370b02021-01-07 16:31:14 +010021 "source.monogon.dev/metropolis/node/core/localstorage"
Serge Bazanski31370b02021-01-07 16:31:14 +010022 "source.monogon.dev/metropolis/node/kubernetes/pki"
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020023 "source.monogon.dev/osbase/fileargs"
24 "source.monogon.dev/osbase/supervisor"
Lorenz Brun878f5f92020-05-12 16:15:39 +020025)
26
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020027type kubeletService struct {
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020028 ClusterDNS []net.IP
Lorenz Brun78cefca2022-06-20 12:59:55 +000029 ClusterDomain string
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020030 KubeletDirectory *localstorage.DataKubernetesKubeletDirectory
31 EphemeralDirectory *localstorage.EphemeralDirectory
Serge Bazanski9411f7c2021-03-10 13:12:53 +010032
Serge Bazanski2cfafc92023-03-21 16:42:47 +010033 kubeconfig []byte
34 serverCACert []byte
35 serverCert []byte
Lorenz Brun878f5f92020-05-12 16:15:39 +020036}
37
Serge Bazanski2cfafc92023-03-21 16:42:47 +010038func (s *kubeletService) getPubkey(ctx context.Context) (ed25519.PublicKey, error) {
39 // First make sure we have a local ED25519 private key, and generate one if not.
40 if err := s.KubeletDirectory.PKI.GeneratePrivateKey(); err != nil {
41 return nil, fmt.Errorf("failed to generate private key: %w", err)
42 }
43 priv, err := s.KubeletDirectory.PKI.ReadPrivateKey()
Serge Bazanski71f7a562020-06-22 16:37:28 +020044 if err != nil {
Serge Bazanski2cfafc92023-03-21 16:42:47 +010045 return nil, fmt.Errorf("could not read keypair: %w", err)
46 }
47 pubkey := priv.Public().(ed25519.PublicKey)
48 return pubkey, nil
49}
50
51func (s *kubeletService) setCertificates(kw *ipb.IssueCertificateResponse_KubernetesWorker) error {
52 key, err := s.KubeletDirectory.PKI.ReadPrivateKey()
53 if err != nil {
54 return fmt.Errorf("could not read private key from disk: %w", err)
Serge Bazanski71f7a562020-06-22 16:37:28 +020055 }
56
Serge Bazanski2cfafc92023-03-21 16:42:47 +010057 s.kubeconfig, err = pki.KubeconfigRaw(kw.IdentityCaCertificate, kw.KubeletClientCertificate, key, pki.KubernetesAPIEndpointForWorker)
Serge Bazanski71f7a562020-06-22 16:37:28 +020058 if err != nil {
Serge Bazanski9411f7c2021-03-10 13:12:53 +010059 return fmt.Errorf("when generating kubeconfig: %w", err)
Serge Bazanski71f7a562020-06-22 16:37:28 +020060 }
Serge Bazanski2cfafc92023-03-21 16:42:47 +010061 s.serverCACert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: kw.IdentityCaCertificate})
62 s.serverCert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: kw.KubeletServerCertificate})
Serge Bazanski71f7a562020-06-22 16:37:28 +020063 return nil
64}
65
Serge Bazanski2cfafc92023-03-21 16:42:47 +010066func (s *kubeletService) configure(fargs *fileargs.FileArgs) *kubeletconfig.KubeletConfiguration {
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020067 var clusterDNS []string
68 for _, dnsIP := range s.ClusterDNS {
69 clusterDNS = append(clusterDNS, dnsIP.String())
70 }
Lorenz Brun878f5f92020-05-12 16:15:39 +020071
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020072 return &kubeletconfig.KubeletConfiguration{
73 TypeMeta: v1.TypeMeta{
74 Kind: "KubeletConfiguration",
Lorenz Brun6211e4d2023-11-14 19:09:40 +010075 APIVersion: kubeletconfig.SchemeGroupVersion.String(),
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020076 },
Lorenz Brun6211e4d2023-11-14 19:09:40 +010077 ContainerRuntimeEndpoint: "unix://" + s.EphemeralDirectory.Containerd.ClientSocket.FullPath(),
78 TLSCertFile: fargs.ArgPath("server.crt", s.serverCert),
79 TLSPrivateKeyFile: s.KubeletDirectory.PKI.Key.FullPath(),
80 TLSMinVersion: "VersionTLS13",
81 ClusterDNS: clusterDNS,
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020082 Authentication: kubeletconfig.KubeletAuthentication{
83 X509: kubeletconfig.KubeletX509Authentication{
Serge Bazanski2cfafc92023-03-21 16:42:47 +010084 ClientCAFile: fargs.ArgPath("ca.crt", s.serverCACert),
Lorenz Brun878f5f92020-05-12 16:15:39 +020085 },
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020086 },
87 // TODO(q3k): move reconciler.False to a generic package, fix the following references.
Lorenz Brun78cefca2022-06-20 12:59:55 +000088 ClusterDomain: s.ClusterDomain,
Tim Windelschmidtd77e26e2024-12-02 18:23:10 +010089 EnableControllerAttachDetach: ptr.To(false),
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020090 HairpinMode: "none",
Tim Windelschmidtd77e26e2024-12-02 18:23:10 +010091 MakeIPTablesUtilChains: ptr.To(false), // We don't have iptables
92 FailSwapOn: ptr.To(false),
Lorenz Brunfe6b5062024-07-02 16:32:35 +000093 MemorySwap: kubeletconfig.MemorySwapConfiguration{
94 // Only allow burstable pods to use swap
95 SwapBehavior: "LimitedSwap",
96 },
97 CgroupRoot: "/",
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020098 KubeReserved: map[string]string{
99 "cpu": "200m",
100 "memory": "300Mi",
101 },
Lorenz Brun0db90ba2020-04-06 14:04:52 +0200102
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200103 // We're not going to use this, but let's make it point to a
104 // known-empty directory in case anybody manages to trigger it.
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200105 VolumePluginDir: s.EphemeralDirectory.FlexvolumePlugins.FullPath(),
Lorenz Brun2f7e0a22023-06-22 16:56:13 +0200106 // Currently we allocate a /24 per node, so we can have a maximum of
107 // 253 pods per node.
Lorenz Brund58edf42024-11-27 20:38:14 +0000108 MaxPods: 253,
109 PodLogsDir: "/data/kubelet/logs",
110 FeatureGates: extraFeatureGates.AsMap(),
Lorenz Brun837cb8e2024-12-23 13:52:56 +0100111 // Only kill a single process on OOM instead of the whole container.
112 // This is generally the more sane behavior and was default under
113 // cgroups v1.
114 SingleProcessOOMKill: ptr.To(true),
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200115 }
116}
Lorenz Brun878f5f92020-05-12 16:15:39 +0200117
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200118func (s *kubeletService) Run(ctx context.Context) error {
Serge Bazanski2cfafc92023-03-21 16:42:47 +0100119 if len(s.serverCert) == 0 || len(s.serverCACert) == 0 || len(s.kubeconfig) == 0 {
120 return fmt.Errorf("setCertificates was not called")
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200121 }
122
123 fargs, err := fileargs.New()
124 if err != nil {
Lorenz Brun878f5f92020-05-12 16:15:39 +0200125 return err
126 }
Serge Bazanski2cfafc92023-03-21 16:42:47 +0100127 defer fargs.Close()
128
129 configRaw, err := json.Marshal(s.configure(fargs))
130 if err != nil {
131 return fmt.Errorf("when marshaling kubelet configuration: %w", err)
132 }
133
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200134 cmd := exec.CommandContext(ctx, "/kubernetes/bin/kube", "kubelet",
135 fargs.FileOpt("--config", "config.json", configRaw),
Serge Bazanski2cfafc92023-03-21 16:42:47 +0100136 fargs.FileOpt("--kubeconfig", "kubeconfig", s.kubeconfig),
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200137 fmt.Sprintf("--root-dir=%s", s.KubeletDirectory.FullPath()),
138 )
139 cmd.Env = []string{"PATH=/kubernetes/bin"}
Serge Bazanski05604292021-03-12 17:47:21 +0100140 return supervisor.RunCommand(ctx, cmd, supervisor.ParseKLog())
Lorenz Brun878f5f92020-05-12 16:15:39 +0200141}