blob: c643e9170d8b0415dbcf0aee5320eace2315d7ca [file] [log] [blame]
Lorenz Brun878f5f92020-05-12 16:15:39 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package kubernetes
18
19import (
20 "context"
Lorenz Brun878f5f92020-05-12 16:15:39 +020021 "encoding/json"
Lorenz Brun878f5f92020-05-12 16:15:39 +020022 "fmt"
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +020023 "io"
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +020024 "net"
Lorenz Brun878f5f92020-05-12 16:15:39 +020025 "os/exec"
26
Lorenz Brun878f5f92020-05-12 16:15:39 +020027 v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +020028 kubeletconfig "k8s.io/kubelet/config/v1beta1"
Serge Bazanski77cb6c52020-12-19 00:09:22 +010029
Serge Bazanski31370b02021-01-07 16:31:14 +010030 "source.monogon.dev/metropolis/node/core/localstorage"
Serge Bazanski31370b02021-01-07 16:31:14 +010031 "source.monogon.dev/metropolis/node/kubernetes/pki"
32 "source.monogon.dev/metropolis/node/kubernetes/reconciler"
33 "source.monogon.dev/metropolis/pkg/fileargs"
Serge Bazanski9411f7c2021-03-10 13:12:53 +010034 opki "source.monogon.dev/metropolis/pkg/pki"
Serge Bazanski31370b02021-01-07 16:31:14 +010035 "source.monogon.dev/metropolis/pkg/supervisor"
Lorenz Brun878f5f92020-05-12 16:15:39 +020036)
37
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020038type kubeletService struct {
39 NodeName string
40 ClusterDNS []net.IP
Lorenz Brun78cefca2022-06-20 12:59:55 +000041 ClusterDomain string
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020042 KubeletDirectory *localstorage.DataKubernetesKubeletDirectory
43 EphemeralDirectory *localstorage.EphemeralDirectory
44 Output io.Writer
Serge Bazanski9411f7c2021-03-10 13:12:53 +010045 KPKI *pki.PKI
46
47 mount *opki.FilesystemCertificate
48 mountKubeconfigPath string
Lorenz Brun878f5f92020-05-12 16:15:39 +020049}
50
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020051func (s *kubeletService) createCertificates(ctx context.Context) error {
Serge Bazanski9411f7c2021-03-10 13:12:53 +010052 server, client, err := s.KPKI.VolatileKubelet(ctx, s.NodeName)
Serge Bazanski71f7a562020-06-22 16:37:28 +020053 if err != nil {
Serge Bazanski9411f7c2021-03-10 13:12:53 +010054 return fmt.Errorf("when generating local kubelet credentials: %w", err)
Serge Bazanski71f7a562020-06-22 16:37:28 +020055 }
56
Serge Bazanski9411f7c2021-03-10 13:12:53 +010057 clientKubeconfig, err := pki.Kubeconfig(ctx, s.KPKI.KV, client)
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 Bazanskidbfc6382020-06-19 20:35:43 +020061
Serge Bazanski9411f7c2021-03-10 13:12:53 +010062 // Use a single fileargs mount for server certificate and client kubeconfig.
63 mounted, err := server.Mount(ctx, s.KPKI.KV)
Serge Bazanskidbfc6382020-06-19 20:35:43 +020064 if err != nil {
Serge Bazanski9411f7c2021-03-10 13:12:53 +010065 return fmt.Errorf("could not mount kubelet cert dir: %w", err)
Serge Bazanski71f7a562020-06-22 16:37:28 +020066 }
Serge Bazanski9411f7c2021-03-10 13:12:53 +010067 // mounted is closed by Run() on process exit.
Serge Bazanskidbfc6382020-06-19 20:35:43 +020068
Serge Bazanski9411f7c2021-03-10 13:12:53 +010069 s.mount = mounted
70 s.mountKubeconfigPath = mounted.ArgPath("kubeconfig", clientKubeconfig)
Serge Bazanski71f7a562020-06-22 16:37:28 +020071
72 return nil
73}
74
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020075func (s *kubeletService) configure() *kubeletconfig.KubeletConfiguration {
76 var clusterDNS []string
77 for _, dnsIP := range s.ClusterDNS {
78 clusterDNS = append(clusterDNS, dnsIP.String())
79 }
Lorenz Brun878f5f92020-05-12 16:15:39 +020080
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020081 return &kubeletconfig.KubeletConfiguration{
82 TypeMeta: v1.TypeMeta{
83 Kind: "KubeletConfiguration",
84 APIVersion: kubeletconfig.GroupName + "/v1beta1",
85 },
Serge Bazanski9411f7c2021-03-10 13:12:53 +010086 TLSCertFile: s.mount.CertPath,
87 TLSPrivateKeyFile: s.mount.KeyPath,
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020088 TLSMinVersion: "VersionTLS13",
89 ClusterDNS: clusterDNS,
90 Authentication: kubeletconfig.KubeletAuthentication{
91 X509: kubeletconfig.KubeletX509Authentication{
Serge Bazanski9411f7c2021-03-10 13:12:53 +010092 ClientCAFile: s.mount.CACertPath,
Lorenz Brun878f5f92020-05-12 16:15:39 +020093 },
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020094 },
95 // TODO(q3k): move reconciler.False to a generic package, fix the following references.
Lorenz Brun78cefca2022-06-20 12:59:55 +000096 ClusterDomain: s.ClusterDomain,
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020097 EnableControllerAttachDetach: reconciler.False(),
98 HairpinMode: "none",
99 MakeIPTablesUtilChains: reconciler.False(), // We don't have iptables
100 FailSwapOn: reconciler.False(), // Our kernel doesn't have swap enabled which breaks Kubelet's detection
Lorenz Brun98206b92022-06-22 16:21:50 +0000101 CgroupRoot: "/",
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200102 KubeReserved: map[string]string{
103 "cpu": "200m",
104 "memory": "300Mi",
105 },
Lorenz Brun0db90ba2020-04-06 14:04:52 +0200106
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200107 // We're not going to use this, but let's make it point to a
108 // known-empty directory in case anybody manages to trigger it.
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200109 VolumePluginDir: s.EphemeralDirectory.FlexvolumePlugins.FullPath(),
110 }
111}
Lorenz Brun878f5f92020-05-12 16:15:39 +0200112
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200113func (s *kubeletService) Run(ctx context.Context) error {
114 if err := s.createCertificates(ctx); err != nil {
115 return fmt.Errorf("when creating certificates: %w", err)
116 }
Serge Bazanski9411f7c2021-03-10 13:12:53 +0100117 defer s.mount.Close()
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200118
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200119 configRaw, err := json.Marshal(s.configure())
120 if err != nil {
121 return fmt.Errorf("when marshaling kubelet configuration: %w", err)
122 }
123
124 fargs, err := fileargs.New()
125 if err != nil {
Lorenz Brun878f5f92020-05-12 16:15:39 +0200126 return err
127 }
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200128 cmd := exec.CommandContext(ctx, "/kubernetes/bin/kube", "kubelet",
129 fargs.FileOpt("--config", "config.json", configRaw),
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200130 fmt.Sprintf("--container-runtime-endpoint=unix://%s", s.EphemeralDirectory.Containerd.ClientSocket.FullPath()),
Serge Bazanski9411f7c2021-03-10 13:12:53 +0100131 fmt.Sprintf("--kubeconfig=%s", s.mountKubeconfigPath),
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200132 fmt.Sprintf("--root-dir=%s", s.KubeletDirectory.FullPath()),
133 )
134 cmd.Env = []string{"PATH=/kubernetes/bin"}
Serge Bazanski05604292021-03-12 17:47:21 +0100135 return supervisor.RunCommand(ctx, cmd, supervisor.ParseKLog())
Lorenz Brun878f5f92020-05-12 16:15:39 +0200136}