blob: 0cacaef20704fc03903801a5d238bee7a6855fde [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
41 KubeletDirectory *localstorage.DataKubernetesKubeletDirectory
42 EphemeralDirectory *localstorage.EphemeralDirectory
43 Output io.Writer
Serge Bazanski9411f7c2021-03-10 13:12:53 +010044 KPKI *pki.PKI
45
46 mount *opki.FilesystemCertificate
47 mountKubeconfigPath string
Lorenz Brun878f5f92020-05-12 16:15:39 +020048}
49
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020050func (s *kubeletService) createCertificates(ctx context.Context) error {
Serge Bazanski9411f7c2021-03-10 13:12:53 +010051 server, client, err := s.KPKI.VolatileKubelet(ctx, s.NodeName)
Serge Bazanski71f7a562020-06-22 16:37:28 +020052 if err != nil {
Serge Bazanski9411f7c2021-03-10 13:12:53 +010053 return fmt.Errorf("when generating local kubelet credentials: %w", err)
Serge Bazanski71f7a562020-06-22 16:37:28 +020054 }
55
Serge Bazanski9411f7c2021-03-10 13:12:53 +010056 clientKubeconfig, err := pki.Kubeconfig(ctx, s.KPKI.KV, client)
Serge Bazanski71f7a562020-06-22 16:37:28 +020057 if err != nil {
Serge Bazanski9411f7c2021-03-10 13:12:53 +010058 return fmt.Errorf("when generating kubeconfig: %w", err)
Serge Bazanski71f7a562020-06-22 16:37:28 +020059 }
Serge Bazanskidbfc6382020-06-19 20:35:43 +020060
Serge Bazanski9411f7c2021-03-10 13:12:53 +010061 // Use a single fileargs mount for server certificate and client kubeconfig.
62 mounted, err := server.Mount(ctx, s.KPKI.KV)
Serge Bazanskidbfc6382020-06-19 20:35:43 +020063 if err != nil {
Serge Bazanski9411f7c2021-03-10 13:12:53 +010064 return fmt.Errorf("could not mount kubelet cert dir: %w", err)
Serge Bazanski71f7a562020-06-22 16:37:28 +020065 }
Serge Bazanski9411f7c2021-03-10 13:12:53 +010066 // mounted is closed by Run() on process exit.
Serge Bazanskidbfc6382020-06-19 20:35:43 +020067
Serge Bazanski9411f7c2021-03-10 13:12:53 +010068 s.mount = mounted
69 s.mountKubeconfigPath = mounted.ArgPath("kubeconfig", clientKubeconfig)
Serge Bazanski71f7a562020-06-22 16:37:28 +020070
71 return nil
72}
73
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020074func (s *kubeletService) configure() *kubeletconfig.KubeletConfiguration {
75 var clusterDNS []string
76 for _, dnsIP := range s.ClusterDNS {
77 clusterDNS = append(clusterDNS, dnsIP.String())
78 }
Lorenz Brun878f5f92020-05-12 16:15:39 +020079
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020080 return &kubeletconfig.KubeletConfiguration{
81 TypeMeta: v1.TypeMeta{
82 Kind: "KubeletConfiguration",
83 APIVersion: kubeletconfig.GroupName + "/v1beta1",
84 },
Serge Bazanski9411f7c2021-03-10 13:12:53 +010085 TLSCertFile: s.mount.CertPath,
86 TLSPrivateKeyFile: s.mount.KeyPath,
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020087 TLSMinVersion: "VersionTLS13",
88 ClusterDNS: clusterDNS,
89 Authentication: kubeletconfig.KubeletAuthentication{
90 X509: kubeletconfig.KubeletX509Authentication{
Serge Bazanski9411f7c2021-03-10 13:12:53 +010091 ClientCAFile: s.mount.CACertPath,
Lorenz Brun878f5f92020-05-12 16:15:39 +020092 },
Serge Bazanskic2c7ad92020-07-13 17:20:09 +020093 },
94 // TODO(q3k): move reconciler.False to a generic package, fix the following references.
95 ClusterDomain: "cluster.local", // cluster.local is hardcoded in the certificate too currently
96 EnableControllerAttachDetach: reconciler.False(),
97 HairpinMode: "none",
98 MakeIPTablesUtilChains: reconciler.False(), // We don't have iptables
99 FailSwapOn: reconciler.False(), // Our kernel doesn't have swap enabled which breaks Kubelet's detection
100 KubeReserved: map[string]string{
101 "cpu": "200m",
102 "memory": "300Mi",
103 },
Lorenz Brun0db90ba2020-04-06 14:04:52 +0200104
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200105 // We're not going to use this, but let's make it point to a known-empty directory in case anybody manages to
106 // trigger it.
107 VolumePluginDir: s.EphemeralDirectory.FlexvolumePlugins.FullPath(),
108 }
109}
Lorenz Brun878f5f92020-05-12 16:15:39 +0200110
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200111func (s *kubeletService) Run(ctx context.Context) error {
112 if err := s.createCertificates(ctx); err != nil {
113 return fmt.Errorf("when creating certificates: %w", err)
114 }
Serge Bazanski9411f7c2021-03-10 13:12:53 +0100115 defer s.mount.Close()
Lorenz Brun8e3b8fc2020-05-19 14:29:40 +0200116
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200117 configRaw, err := json.Marshal(s.configure())
118 if err != nil {
119 return fmt.Errorf("when marshaling kubelet configuration: %w", err)
120 }
121
122 fargs, err := fileargs.New()
123 if err != nil {
Lorenz Brun878f5f92020-05-12 16:15:39 +0200124 return err
125 }
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200126 cmd := exec.CommandContext(ctx, "/kubernetes/bin/kube", "kubelet",
127 fargs.FileOpt("--config", "config.json", configRaw),
128 "--container-runtime=remote",
129 fmt.Sprintf("--container-runtime-endpoint=unix://%s", s.EphemeralDirectory.Containerd.ClientSocket.FullPath()),
Serge Bazanski9411f7c2021-03-10 13:12:53 +0100130 fmt.Sprintf("--kubeconfig=%s", s.mountKubeconfigPath),
Serge Bazanskic2c7ad92020-07-13 17:20:09 +0200131 fmt.Sprintf("--root-dir=%s", s.KubeletDirectory.FullPath()),
132 )
133 cmd.Env = []string{"PATH=/kubernetes/bin"}
Serge Bazanski967be212020-11-02 11:26:59 +0100134 return supervisor.RunCommand(ctx, cmd)
Lorenz Brun878f5f92020-05-12 16:15:39 +0200135}