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