blob: 8c40e9fd4bae7de7b8283ad023aa3a8161c8d5f2 [file] [log] [blame]
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +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 node
18
19import (
Lorenz Brunaa6b7342019-12-12 02:55:02 +010020 "bytes"
Serge Bazanskicdb8c782020-02-17 12:34:02 +010021 "context"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010022 "crypto/ed25519"
23 "crypto/rand"
24 "crypto/sha512"
25 "crypto/tls"
26 "crypto/x509"
27 "crypto/x509/pkix"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010028 "errors"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020029 "flag"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010030 "fmt"
31 "io/ioutil"
32 "math/big"
33 "net"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010034 "os"
Lorenz Brun878f5f92020-05-12 16:15:39 +020035 "strings"
Serge Bazanskicdb8c782020-02-17 12:34:02 +010036 "time"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010037
Lorenz Brun3ff5af32020-06-24 16:34:11 +020038 "github.com/cenkalti/backoff/v4"
39 "github.com/gogo/protobuf/proto"
40 "go.uber.org/zap"
41 "golang.org/x/sys/unix"
42 "google.golang.org/grpc"
43 "google.golang.org/grpc/credentials"
44
Lorenz Brunaa6b7342019-12-12 02:55:02 +010045 apipb "git.monogon.dev/source/nexantic.git/core/generated/api"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020046 "git.monogon.dev/source/nexantic.git/core/internal/api"
47 "git.monogon.dev/source/nexantic.git/core/internal/common"
48 "git.monogon.dev/source/nexantic.git/core/internal/consensus"
Lorenz Brun3ff5af32020-06-24 16:34:11 +020049 "git.monogon.dev/source/nexantic.git/core/internal/containerd"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010050 "git.monogon.dev/source/nexantic.git/core/internal/integrity/tpm2"
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010051 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010052 "git.monogon.dev/source/nexantic.git/core/internal/network"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020053 "git.monogon.dev/source/nexantic.git/core/internal/storage"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020054)
55
Lorenz Brunaa6b7342019-12-12 02:55:02 +010056var (
57 // From RFC 5280 Section 4.1.2.5
58 unknownNotAfter = time.Unix(253402300799, 0)
59)
60
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020061type (
62 SmalltownNode struct {
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010063 Api *api.Server
64 Consensus *consensus.Service
65 Storage *storage.Manager
66 Kubernetes *kubernetes.Service
Lorenz Brun878f5f92020-05-12 16:15:39 +020067 Containerd *containerd.Service
Lorenz Brunaa6b7342019-12-12 02:55:02 +010068 Network *network.Service
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020069
Lorenz Brunaa6b7342019-12-12 02:55:02 +010070 logger *zap.Logger
71 state common.SmalltownState
72 hostname string
73 enrolmentConfig *apipb.EnrolmentConfig
Lorenz Brun878f5f92020-05-12 16:15:39 +020074
75 debugServer *grpc.Server
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020076 }
77)
78
Serge Bazanskicdb8c782020-02-17 12:34:02 +010079func NewSmalltownNode(logger *zap.Logger, ntwk *network.Service, strg *storage.Manager) (*SmalltownNode, error) {
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020080 flag.Parse()
81 logger.Info("Creating Smalltown node")
Serge Bazanskicdb8c782020-02-17 12:34:02 +010082 ctx := context.Background()
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020083
Leopold Schabel68c58752019-11-14 21:00:59 +010084 hostname, err := os.Hostname()
85 if err != nil {
86 panic(err)
87 }
88
Serge Bazanskicdb8c782020-02-17 12:34:02 +010089 // Wait for IP adddress...
90 ctxT, ctxTC := context.WithTimeout(ctx, time.Second*10)
91 defer ctxTC()
92 externalIP, err := ntwk.GetIP(ctxT, true)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010093 if err != nil {
Serge Bazanskicdb8c782020-02-17 12:34:02 +010094 logger.Panic("Could not get IP address", zap.Error(err))
Lorenz Brunaa6b7342019-12-12 02:55:02 +010095 }
96
97 // Important to know if the GetIP above hangs
98 logger.Info("Node has IP", zap.String("ip", externalIP.String()))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020099
100 consensusService, err := consensus.NewConsensusService(consensus.Config{
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100101 Name: hostname,
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100102 ListenHost: "0.0.0.0",
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100103 ExternalHost: externalIP.String(),
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200104 }, logger.With(zap.String("module", "consensus")))
105 if err != nil {
106 return nil, err
107 }
108
Lorenz Brun878f5f92020-05-12 16:15:39 +0200109 containerdService, err := containerd.New()
110 if err != nil {
111 return nil, err
112 }
113
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200114 s := &SmalltownNode{
Lorenz Brun878f5f92020-05-12 16:15:39 +0200115 Consensus: consensusService,
116 Containerd: containerdService,
117 Storage: strg,
118 Network: ntwk,
119 logger: logger,
120 hostname: hostname,
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200121 }
122
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100123 apiService, err := api.NewApiServer(&api.Config{}, logger.With(zap.String("module", "api")), s.Consensus)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200124 if err != nil {
125 return nil, err
126 }
127
128 s.Api = apiService
129
Lorenz Brun0db90ba2020-04-06 14:04:52 +0200130 s.Kubernetes = kubernetes.New(logger.With(zap.String("module", "kubernetes")), consensusService, strg)
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100131
Lorenz Brun878f5f92020-05-12 16:15:39 +0200132 s.debugServer = grpc.NewServer()
133 apipb.RegisterNodeDebugServiceServer(s.debugServer, s)
134
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200135 logger.Info("Created SmalltownNode")
136
137 return s, nil
138}
139
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100140func (s *SmalltownNode) Start(ctx context.Context) error {
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200141 s.logger.Info("Starting Smalltown node")
142
Lorenz Brun878f5f92020-05-12 16:15:39 +0200143 s.startDebugSvc()
144
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100145 // TODO(lorenz): Abstracting enrolment sounds like a good idea, but ends up being painful
146 // because of things like storage access. I'm keeping it this way until the more complex
147 // enrolment procedures are fleshed out. This is also a bit panic()-happy, but there is really
148 // no good way out of an invalid enrolment configuration.
149 enrolmentPath, err := s.Storage.GetPathInPlace(storage.PlaceESP, "enrolment.pb")
150 if err != nil {
151 s.logger.Panic("ESP configuration partition not available", zap.Error(err))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200152 }
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100153 enrolmentConfigRaw, err := ioutil.ReadFile(enrolmentPath)
Lorenz Brun3ff5af32020-06-24 16:34:11 +0200154 if os.IsNotExist(err) {
155 enrolmentConfigRaw, err = ioutil.ReadFile("/sys/firmware/qemu_fw_cfg/by_name/com.nexantic.smalltown/enrolment.pb/raw")
156 }
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100157 if err == nil {
158 // We have an enrolment file, let's check its contents
159 var enrolmentConfig apipb.EnrolmentConfig
160 if err := proto.Unmarshal(enrolmentConfigRaw, &enrolmentConfig); err != nil {
161 s.logger.Panic("Invalid enrolment configuration provided", zap.Error(err))
162 }
163 s.enrolmentConfig = &enrolmentConfig
164 // The enrolment secret is only zeroed after
165 if len(enrolmentConfig.EnrolmentSecret) == 0 {
166 return s.startFull()
167 }
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100168 return s.startEnrolling(ctx)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100169 } else if os.IsNotExist(err) {
170 // This is ok like this, once a new cluster has been set up the initial node also generates
171 // its own enrolment config
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100172 return s.startForSetup(ctx)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100173 }
174 // Unknown error reading enrolment config (disk issues/invalid configuration format/...)
175 s.logger.Panic("Invalid enrolment configuration provided", zap.Error(err))
176 panic("Unreachable")
177}
178
Lorenz Brun878f5f92020-05-12 16:15:39 +0200179func (s *SmalltownNode) startDebugSvc() {
180 debugListenHost := fmt.Sprintf(":%v", common.DebugServicePort)
181 debugListener, err := net.Listen("tcp", debugListenHost)
182 if err != nil {
183 s.logger.Fatal("failed to listen", zap.Error(err))
184 }
185
186 go func() {
187 if err := s.debugServer.Serve(debugListener); err != nil {
188 s.logger.Fatal("failed to serve", zap.Error(err))
189 }
190 }()
191}
192
193func (s *SmalltownNode) initHostname() error {
194 if err := unix.Sethostname([]byte(s.hostname)); err != nil {
195 return err
196 }
197 if err := ioutil.WriteFile("/etc/hosts", []byte(fmt.Sprintf("%v %v", "127.0.0.1", s.hostname)), 0644); err != nil {
198 return err
199 }
200 return ioutil.WriteFile("/etc/machine-id", []byte(strings.TrimPrefix(s.hostname, "smalltown-")), 0644)
201}
202
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100203func (s *SmalltownNode) startEnrolling(ctx context.Context) error {
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100204 s.logger.Info("Initializing subsystems for enrolment")
205 s.state = common.StateEnrollMode
206
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100207 nodeInfo, nodeID, err := s.InitializeNode(ctx)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100208 if err != nil {
209 return err
210 }
211
Lorenz Brun878f5f92020-05-12 16:15:39 +0200212 s.hostname = nodeID
213 if err := s.initHostname(); err != nil {
214 return err
215 }
216
Lorenz Brun52f7f292020-06-24 16:42:02 +0200217 if err := s.initNodeAPI(); err != nil {
218 return err
219 }
220
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100221 // We only support TPM2 at the moment, any abstractions here would be premature
222 trustAgent := tpm2.TPM2Agent{}
223
224 initializeOp := func() error {
225 if err := trustAgent.Initialize(*nodeInfo, *s.enrolmentConfig); err != nil {
226 s.logger.Warn("Failed to initialize integrity backend", zap.Error(err))
227 return err
228 }
229 return nil
230 }
231
232 if err := backoff.Retry(initializeOp, getIntegrityBackoff()); err != nil {
233 panic("invariant violated: integrity initialization retry can never fail")
234 }
235
236 enrolmentPath, err := s.Storage.GetPathInPlace(storage.PlaceESP, "enrolment.pb")
237 if err != nil {
238 panic(err)
239 }
240
241 s.enrolmentConfig.EnrolmentSecret = []byte{}
242 s.enrolmentConfig.NodeId = nodeID
243
244 enrolmentConfigRaw, err := proto.Marshal(s.enrolmentConfig)
245 if err != nil {
246 panic(err)
247 }
248 if err := ioutil.WriteFile(enrolmentPath, enrolmentConfigRaw, 0600); err != nil {
249 return err
250 }
251 s.logger.Info("Node successfully enrolled")
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200252
253 return nil
254}
255
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100256func (s *SmalltownNode) startForSetup(ctx context.Context) error {
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100257 s.logger.Info("Setting up a new cluster")
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100258 initData, nodeID, err := s.InitializeNode(ctx)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200259 if err != nil {
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200260 return err
261 }
Lorenz Brun878f5f92020-05-12 16:15:39 +0200262 s.hostname = nodeID
263 if err := s.initHostname(); err != nil {
264 return err
265 }
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200266
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100267 if err := s.initNodeAPI(); err != nil {
268 return err
269 }
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200270
Lorenz Brun878f5f92020-05-12 16:15:39 +0200271 // TODO: Use supervisor.Run for this
272 go s.Containerd.Run()(context.TODO())
273
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100274 dataPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "etcd")
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200275 if err != nil {
276 return err
277 }
278
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100279 // Spin up etcd
280 config := s.Consensus.GetConfig()
281 config.NewCluster = true
282 config.Name = s.hostname
283 config.DataDir = dataPath
284 s.Consensus.SetConfig(config)
285
286 // Generate the cluster CA and store it to local storage.
Lorenz Brun52f7f292020-06-24 16:42:02 +0200287 extIP, err := s.Network.GetIP(ctx, true)
288 if err != nil {
289 return err
290 }
291 if err := s.Consensus.PrecreateCA(*extIP); err != nil {
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100292 return err
293 }
294
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200295 err = s.Consensus.Start()
296 if err != nil {
297 return err
298 }
299
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100300 // Now that the cluster is up and running, we can persist the CA to the cluster.
301 if err := s.Consensus.InjectCA(); err != nil {
302 return err
303 }
304
305 if err := s.Api.BootstrapNewClusterHook(initData); err != nil {
306 return err
307 }
308
309 if err := s.Kubernetes.NewCluster(); err != nil {
310 return err
311 }
312
313 if err := s.Kubernetes.Start(); err != nil {
314 return err
315 }
316
317 if err := s.Api.Start(); err != nil {
318 s.logger.Error("Failed to start the API service", zap.Error(err))
319 return err
320 }
321
322 enrolmentPath, err := s.Storage.GetPathInPlace(storage.PlaceESP, "enrolment.pb")
323 if err != nil {
324 panic(err)
325 }
326
327 masterCert, err := s.Api.GetMasterCert()
328 if err != nil {
329 return err
330 }
331
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100332 ip, err := s.Network.GetIP(ctx, true)
333 if err != nil {
334 return fmt.Errorf("could not get node IP: %v", err)
335 }
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100336 enrolmentConfig := &apipb.EnrolmentConfig{
337 EnrolmentSecret: []byte{}, // First node is always already enrolled
338 MastersCert: masterCert,
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100339 MasterIps: [][]byte{[]byte(*ip)},
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100340 NodeId: nodeID,
341 }
342 enrolmentConfigRaw, err := proto.Marshal(enrolmentConfig)
343 if err != nil {
344 panic(err)
345 }
346 if err := ioutil.WriteFile(enrolmentPath, enrolmentConfigRaw, 0600); err != nil {
347 return err
348 }
349 masterCertFingerprint := sha512.Sum512_256(masterCert)
350 s.logger.Info("New Smalltown cluster successfully bootstrapped", zap.Binary("fingerprint", masterCertFingerprint[:]))
351
352 return nil
353}
354
355func (s *SmalltownNode) generateNodeID() ([]byte, string, error) {
356 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 127)
357 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
358 if err != nil {
359 return []byte{}, "", fmt.Errorf("Failed to generate serial number: %w", err)
360 }
361
362 pubKey, privKeyRaw, err := ed25519.GenerateKey(rand.Reader)
363 if err != nil {
364 return []byte{}, "", err
365 }
366 privkey, err := x509.MarshalPKCS8PrivateKey(privKeyRaw)
367 if err != nil {
368 return []byte{}, "", err
369 }
370
371 nodeKeyPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "node-key.der")
372 if err != nil {
373 return []byte{}, "", err
374 }
375
376 if err := ioutil.WriteFile(nodeKeyPath, privkey, 0600); err != nil {
377 return []byte{}, "", fmt.Errorf("failed to write node key: %w", err)
378 }
379
Lorenz Brun52f7f292020-06-24 16:42:02 +0200380 name := common.NameFromIDKey(pubKey)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100381
382 // This has no SANs because it authenticates by public key, not by name
383 nodeCert := &x509.Certificate{
384 SerialNumber: serialNumber,
385 Subject: pkix.Name{
386 // We identify nodes by their ID public keys (not hashed since a strong hash is longer and serves no benefit)
387 CommonName: name,
388 },
389 IsCA: false,
390 BasicConstraintsValid: true,
391 NotBefore: time.Now(),
392 NotAfter: unknownNotAfter,
393 // Certificate is used both as server & client
394 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
395 }
396 cert, err := x509.CreateCertificate(rand.Reader, nodeCert, nodeCert, pubKey, privKeyRaw)
397 if err != nil {
398 return []byte{}, "", err
399 }
400
401 nodeCertPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "node.der")
402 if err != nil {
403 return []byte{}, "", err
404 }
405
406 if err := ioutil.WriteFile(nodeCertPath, cert, 0600); err != nil {
407 return []byte{}, "", fmt.Errorf("failed to write node cert: %w", err)
408 }
409 return cert, name, nil
410}
411
412func (s *SmalltownNode) initNodeAPI() error {
413 certPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "node.der")
414 if err != nil {
415 s.logger.Panic("Invariant violated: Data is available once this is called")
416 }
417 keyPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "node-key.der")
418 if err != nil {
419 s.logger.Panic("Invariant violated: Data is available once this is called")
420 }
421
422 certRaw, err := ioutil.ReadFile(certPath)
423 if err != nil {
424 return err
425 }
426 privKeyRaw, err := ioutil.ReadFile(keyPath)
427 if err != nil {
428 return err
429 }
430
431 var nodeID tls.Certificate
432
433 cert, err := x509.ParseCertificate(certRaw)
434 if err != nil {
435 return err
436 }
437
438 privKey, err := x509.ParsePKCS8PrivateKey(privKeyRaw)
439 if err != nil {
440 return err
441 }
442
443 nodeID.Certificate = [][]byte{certRaw}
444 nodeID.PrivateKey = privKey
445 nodeID.Leaf = cert
446
447 secureTransport := &tls.Config{
448 Certificates: []tls.Certificate{nodeID},
Lorenz Brun52f7f292020-06-24 16:42:02 +0200449 ClientAuth: tls.RequestClientCert,
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100450 InsecureSkipVerify: true,
451 // Critical function, please review any changes with care
452 // TODO(lorenz): Actively check that this actually provides the security guarantees that we need
453 VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
454 for _, cert := range rawCerts {
455 // X.509 certificates in DER can be compared like this since DER has a unique representation
456 // for each certificate.
457 if bytes.Equal(cert, s.enrolmentConfig.MastersCert) {
458 return nil
459 }
460 }
Lorenz Brun52f7f292020-06-24 16:42:02 +0200461 s.logger.Warn("Rejecting NodeService connection with no trusted client certificate")
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100462 return errors.New("failed to find authorized NMS certificate")
463 },
464 MinVersion: tls.VersionTLS13,
465 }
466 secureTransportCreds := credentials.NewTLS(secureTransport)
467
468 masterListenHost := fmt.Sprintf(":%d", common.NodeServicePort)
469 lis, err := net.Listen("tcp", masterListenHost)
470 if err != nil {
471 s.logger.Fatal("failed to listen", zap.Error(err))
472 }
473
474 nodeGRPCServer := grpc.NewServer(grpc.Creds(secureTransportCreds))
475 apipb.RegisterNodeServiceServer(nodeGRPCServer, s)
476 go func() {
477 if err := nodeGRPCServer.Serve(lis); err != nil {
478 panic(err) // Can only happen during initialization and is always fatal
479 }
480 }()
Lorenz Brun52f7f292020-06-24 16:42:02 +0200481
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100482 return nil
483}
484
485func getIntegrityBackoff() *backoff.ExponentialBackOff {
486 unlockBackoff := backoff.NewExponentialBackOff()
487 unlockBackoff.MaxElapsedTime = time.Duration(0)
488 unlockBackoff.InitialInterval = 5 * time.Second
489 unlockBackoff.MaxInterval = 5 * time.Minute
490 return unlockBackoff
491}
492
493func (s *SmalltownNode) startFull() error {
494 s.logger.Info("Initializing subsystems for production")
495 s.state = common.StateJoined
496
Lorenz Brun878f5f92020-05-12 16:15:39 +0200497 s.hostname = s.enrolmentConfig.NodeId
498 if err := s.initHostname(); err != nil {
499 return err
500 }
501
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100502 trustAgent := tpm2.TPM2Agent{}
503 unlockOp := func() error {
504 unlockKey, err := trustAgent.Unlock(*s.enrolmentConfig)
505 if err != nil {
506 s.logger.Warn("Failed to unlock", zap.Error(err))
507 return err
508 }
509 if err := s.Storage.MountData(unlockKey); err != nil {
510 s.logger.Panic("Failed to mount storage", zap.Error(err))
511 return err
512 }
513 return nil
514 }
515
516 if err := backoff.Retry(unlockOp, getIntegrityBackoff()); err != nil {
517 s.logger.Panic("Invariant violated: Unlock retry can never fail")
518 }
519
520 s.initNodeAPI()
521
Lorenz Brun878f5f92020-05-12 16:15:39 +0200522 // TODO: Use supervisor.Run for this
523 go s.Containerd.Run()(context.TODO())
524
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100525 err := s.Consensus.Start()
526 if err != nil {
527 return err
528 }
529
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200530 err = s.Api.Start()
531 if err != nil {
532 s.logger.Error("Failed to start the API service", zap.Error(err))
533 return err
534 }
535
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100536 err = s.Kubernetes.Start()
537 if err != nil {
538 s.logger.Error("Failed to start the Kubernetes Service", zap.Error(err))
539 }
540
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200541 return nil
542}
543
544func (s *SmalltownNode) Stop() error {
545 s.logger.Info("Stopping Smalltown node")
546 return nil
547}