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