blob: b7d8157e3c1a72c6706da3d2462bb754328d789b [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"
21 "crypto/ed25519"
22 "encoding/json"
23 "encoding/pem"
24 "fmt"
25 "go.uber.org/zap"
26 "io/ioutil"
27 "os"
28 "os/exec"
29
30 "net"
31
32 "git.monogon.dev/source/nexantic.git/core/pkg/fileargs"
33 "go.etcd.io/etcd/clientv3"
34 v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35 "k8s.io/kubelet/config/v1beta1"
36)
37
38type KubeletSpec struct {
39 clusterDNS []net.IP
40}
41
42func bootstrapLocalKubelet(consensusKV clientv3.KV, nodeName string) error {
43 idCA, idKeyRaw, err := getCert(consensusKV, "id-ca")
44 if err != nil {
45 return err
46 }
47 idKey := ed25519.PrivateKey(idKeyRaw)
48 cert, key, err := issueCertificate(clientCertTemplate("system:node:"+nodeName, []string{"system:nodes"}), idCA, idKey)
49 if err != nil {
50 return err
51 }
52 kubeconfig, err := makeLocalKubeconfig(idCA, cert, key)
53 if err != nil {
54 return err
55 }
56
57 serverCert, serverKey, err := issueCertificate(serverCertTemplate([]string{nodeName}, []net.IP{}), idCA, idKey)
58 if err != nil {
59 return err
60 }
61 if err := os.MkdirAll("/data/kubernetes", 0755); err != nil {
62 return err
63 }
64 if err := ioutil.WriteFile("/data/kubernetes/kubelet.kubeconfig", kubeconfig, 0400); err != nil {
65 return err
66 }
67 if err := ioutil.WriteFile("/data/kubernetes/ca.crt", pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: idCA}), 0400); err != nil {
68 return err
69 }
70 if err := ioutil.WriteFile("/data/kubernetes/kubelet.crt", pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: serverCert}), 0400); err != nil {
71 return err
72 }
73 if err := ioutil.WriteFile("/data/kubernetes/kubelet.key", pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: serverKey}), 0400); err != nil {
74 return err
75 }
76
77 return nil
78}
79
80func (s *Service) runKubelet(ctx context.Context, spec *KubeletSpec) error {
81 fargs, err := fileargs.New()
82 if err != nil {
83 return err
84 }
85 var clusterDNS []string
86 for _, dnsIP := range spec.clusterDNS {
87 clusterDNS = append(clusterDNS, dnsIP.String())
88 }
89
90 kubeletConf := &v1beta1.KubeletConfiguration{
91 TypeMeta: v1.TypeMeta{
92 Kind: "KubeletConfiguration",
93 APIVersion: v1beta1.GroupName + "/v1beta1",
94 },
95 TLSCertFile: "/data/kubernetes/kubelet.crt",
96 TLSPrivateKeyFile: "/data/kubernetes/kubelet.key",
97 TLSMinVersion: "VersionTLS13",
98 ClusterDNS: clusterDNS,
99 Authentication: v1beta1.KubeletAuthentication{
100 X509: v1beta1.KubeletX509Authentication{
101 ClientCAFile: "/data/kubernetes/ca.crt",
102 },
103 },
104 ClusterDomain: "cluster.local",
105 EnableControllerAttachDetach: False(),
106 HairpinMode: "none",
107 MakeIPTablesUtilChains: False(), // We don't have iptables
108 FailSwapOn: False(), // Our kernel doesn't have swap enabled which breaks Kubelet's detection
109 KubeReserved: map[string]string{
110 "cpu": "200m",
111 "memory": "300Mi",
112 },
113 // We're not going to use this, but let's make it point to a known-empty directory in case anybody manages to
114 // trigger it.
115 VolumePluginDir: "/kubernetes/conf/flexvolume-plugins",
116 }
117
118 configRaw, err := json.Marshal(kubeletConf)
119 if err != nil {
120 return err
121 }
122 cmd := exec.CommandContext(ctx, "/kubernetes/bin/kube", "kubelet",
123 fargs.FileOpt("--config", "config.json", configRaw),
124 "--container-runtime=remote",
125 "--container-runtime-endpoint=unix:///containerd/run/containerd.sock",
126 "--kubeconfig=/data/kubernetes/kubelet.kubeconfig",
127 "--root-dir=/data/kubernetes/kubelet",
128 )
129 cmd.Env = []string{"PATH=/kubernetes/bin"}
130 cmd.Stdout = s.kubeletLogs
131 cmd.Stderr = s.kubeletLogs
132
133 err = cmd.Run()
134 fmt.Fprintf(s.kubeletLogs, "kubelet stopped: %v\n", err)
135 if ctx.Err() == context.Canceled {
136 s.logger.Info("kubelet stopped", zap.Error(err))
137 } else {
138 s.logger.Warn("kubelet stopped unexpectedly", zap.Error(err))
139 }
140 return err
141}