blob: 2cf88f4345277cdc4ff05a7b7bc32c3f8aab6f57 [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 Brun878f5f92020-05-12 16:15:39 +020028 "encoding/hex"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010029 "errors"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020030 "flag"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010031 "fmt"
32 "io/ioutil"
33 "math/big"
34 "net"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010035 "os"
Lorenz Brun878f5f92020-05-12 16:15:39 +020036 "strings"
Serge Bazanskicdb8c782020-02-17 12:34:02 +010037 "time"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010038
Lorenz Brun3ff5af32020-06-24 16:34:11 +020039 "github.com/cenkalti/backoff/v4"
40 "github.com/gogo/protobuf/proto"
41 "go.uber.org/zap"
42 "golang.org/x/sys/unix"
43 "google.golang.org/grpc"
44 "google.golang.org/grpc/credentials"
45
Lorenz Brunaa6b7342019-12-12 02:55:02 +010046 apipb "git.monogon.dev/source/nexantic.git/core/generated/api"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020047 "git.monogon.dev/source/nexantic.git/core/internal/api"
48 "git.monogon.dev/source/nexantic.git/core/internal/common"
49 "git.monogon.dev/source/nexantic.git/core/internal/consensus"
Lorenz Brun3ff5af32020-06-24 16:34:11 +020050 "git.monogon.dev/source/nexantic.git/core/internal/containerd"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010051 "git.monogon.dev/source/nexantic.git/core/internal/integrity/tpm2"
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010052 "git.monogon.dev/source/nexantic.git/core/internal/kubernetes"
Lorenz Brunaa6b7342019-12-12 02:55:02 +010053 "git.monogon.dev/source/nexantic.git/core/internal/network"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020054 "git.monogon.dev/source/nexantic.git/core/internal/storage"
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020055)
56
Lorenz Brunaa6b7342019-12-12 02:55:02 +010057var (
58 // From RFC 5280 Section 4.1.2.5
59 unknownNotAfter = time.Unix(253402300799, 0)
60)
61
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020062type (
63 SmalltownNode struct {
Lorenz Brun6e8f69c2019-11-18 10:44:24 +010064 Api *api.Server
65 Consensus *consensus.Service
66 Storage *storage.Manager
67 Kubernetes *kubernetes.Service
Lorenz Brun878f5f92020-05-12 16:15:39 +020068 Containerd *containerd.Service
Lorenz Brunaa6b7342019-12-12 02:55:02 +010069 Network *network.Service
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020070
Lorenz Brunaa6b7342019-12-12 02:55:02 +010071 logger *zap.Logger
72 state common.SmalltownState
73 hostname string
74 enrolmentConfig *apipb.EnrolmentConfig
Lorenz Brun878f5f92020-05-12 16:15:39 +020075
76 debugServer *grpc.Server
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020077 }
78)
79
Serge Bazanskicdb8c782020-02-17 12:34:02 +010080func NewSmalltownNode(logger *zap.Logger, ntwk *network.Service, strg *storage.Manager) (*SmalltownNode, error) {
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020081 flag.Parse()
82 logger.Info("Creating Smalltown node")
Serge Bazanskicdb8c782020-02-17 12:34:02 +010083 ctx := context.Background()
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +020084
Leopold Schabel68c58752019-11-14 21:00:59 +010085 hostname, err := os.Hostname()
86 if err != nil {
87 panic(err)
88 }
89
Serge Bazanskicdb8c782020-02-17 12:34:02 +010090 // Wait for IP adddress...
91 ctxT, ctxTC := context.WithTimeout(ctx, time.Second*10)
92 defer ctxTC()
93 externalIP, err := ntwk.GetIP(ctxT, true)
Lorenz Brunaa6b7342019-12-12 02:55:02 +010094 if err != nil {
Serge Bazanskicdb8c782020-02-17 12:34:02 +010095 logger.Panic("Could not get IP address", zap.Error(err))
Lorenz Brunaa6b7342019-12-12 02:55:02 +010096 }
97
98 // Important to know if the GetIP above hangs
99 logger.Info("Node has IP", zap.String("ip", externalIP.String()))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200100
101 consensusService, err := consensus.NewConsensusService(consensus.Config{
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100102 Name: hostname,
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100103 ListenHost: "0.0.0.0",
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100104 ExternalHost: externalIP.String(),
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200105 }, logger.With(zap.String("module", "consensus")))
106 if err != nil {
107 return nil, err
108 }
109
Lorenz Brun878f5f92020-05-12 16:15:39 +0200110 containerdService, err := containerd.New()
111 if err != nil {
112 return nil, err
113 }
114
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200115 s := &SmalltownNode{
Lorenz Brun878f5f92020-05-12 16:15:39 +0200116 Consensus: consensusService,
117 Containerd: containerdService,
118 Storage: strg,
119 Network: ntwk,
120 logger: logger,
121 hostname: hostname,
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200122 }
123
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100124 apiService, err := api.NewApiServer(&api.Config{}, logger.With(zap.String("module", "api")), s.Consensus)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200125 if err != nil {
126 return nil, err
127 }
128
129 s.Api = apiService
130
Lorenz Brun0db90ba2020-04-06 14:04:52 +0200131 s.Kubernetes = kubernetes.New(logger.With(zap.String("module", "kubernetes")), consensusService, strg)
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100132
Lorenz Brun878f5f92020-05-12 16:15:39 +0200133 s.debugServer = grpc.NewServer()
134 apipb.RegisterNodeDebugServiceServer(s.debugServer, s)
135
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200136 logger.Info("Created SmalltownNode")
137
138 return s, nil
139}
140
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100141func (s *SmalltownNode) Start(ctx context.Context) error {
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200142 s.logger.Info("Starting Smalltown node")
143
Lorenz Brun878f5f92020-05-12 16:15:39 +0200144 s.startDebugSvc()
145
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100146 // TODO(lorenz): Abstracting enrolment sounds like a good idea, but ends up being painful
147 // because of things like storage access. I'm keeping it this way until the more complex
148 // enrolment procedures are fleshed out. This is also a bit panic()-happy, but there is really
149 // no good way out of an invalid enrolment configuration.
150 enrolmentPath, err := s.Storage.GetPathInPlace(storage.PlaceESP, "enrolment.pb")
151 if err != nil {
152 s.logger.Panic("ESP configuration partition not available", zap.Error(err))
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200153 }
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100154 enrolmentConfigRaw, err := ioutil.ReadFile(enrolmentPath)
Lorenz Brun3ff5af32020-06-24 16:34:11 +0200155 if os.IsNotExist(err) {
156 enrolmentConfigRaw, err = ioutil.ReadFile("/sys/firmware/qemu_fw_cfg/by_name/com.nexantic.smalltown/enrolment.pb/raw")
157 }
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100158 if err == nil {
159 // We have an enrolment file, let's check its contents
160 var enrolmentConfig apipb.EnrolmentConfig
161 if err := proto.Unmarshal(enrolmentConfigRaw, &enrolmentConfig); err != nil {
162 s.logger.Panic("Invalid enrolment configuration provided", zap.Error(err))
163 }
164 s.enrolmentConfig = &enrolmentConfig
165 // The enrolment secret is only zeroed after
166 if len(enrolmentConfig.EnrolmentSecret) == 0 {
167 return s.startFull()
168 }
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100169 return s.startEnrolling(ctx)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100170 } else if os.IsNotExist(err) {
171 // This is ok like this, once a new cluster has been set up the initial node also generates
172 // its own enrolment config
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100173 return s.startForSetup(ctx)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100174 }
175 // Unknown error reading enrolment config (disk issues/invalid configuration format/...)
176 s.logger.Panic("Invalid enrolment configuration provided", zap.Error(err))
177 panic("Unreachable")
178}
179
Lorenz Brun878f5f92020-05-12 16:15:39 +0200180func (s *SmalltownNode) startDebugSvc() {
181 debugListenHost := fmt.Sprintf(":%v", common.DebugServicePort)
182 debugListener, err := net.Listen("tcp", debugListenHost)
183 if err != nil {
184 s.logger.Fatal("failed to listen", zap.Error(err))
185 }
186
187 go func() {
188 if err := s.debugServer.Serve(debugListener); err != nil {
189 s.logger.Fatal("failed to serve", zap.Error(err))
190 }
191 }()
192}
193
194func (s *SmalltownNode) initHostname() error {
195 if err := unix.Sethostname([]byte(s.hostname)); err != nil {
196 return err
197 }
198 if err := ioutil.WriteFile("/etc/hosts", []byte(fmt.Sprintf("%v %v", "127.0.0.1", s.hostname)), 0644); err != nil {
199 return err
200 }
201 return ioutil.WriteFile("/etc/machine-id", []byte(strings.TrimPrefix(s.hostname, "smalltown-")), 0644)
202}
203
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100204func (s *SmalltownNode) startEnrolling(ctx context.Context) error {
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100205 s.logger.Info("Initializing subsystems for enrolment")
206 s.state = common.StateEnrollMode
207
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100208 nodeInfo, nodeID, err := s.InitializeNode(ctx)
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100209 if err != nil {
210 return err
211 }
212
Lorenz Brun878f5f92020-05-12 16:15:39 +0200213 s.hostname = nodeID
214 if err := s.initHostname(); err != nil {
215 return err
216 }
217
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100218 // We only support TPM2 at the moment, any abstractions here would be premature
219 trustAgent := tpm2.TPM2Agent{}
220
221 initializeOp := func() error {
222 if err := trustAgent.Initialize(*nodeInfo, *s.enrolmentConfig); err != nil {
223 s.logger.Warn("Failed to initialize integrity backend", zap.Error(err))
224 return err
225 }
226 return nil
227 }
228
229 if err := backoff.Retry(initializeOp, getIntegrityBackoff()); err != nil {
230 panic("invariant violated: integrity initialization retry can never fail")
231 }
232
233 enrolmentPath, err := s.Storage.GetPathInPlace(storage.PlaceESP, "enrolment.pb")
234 if err != nil {
235 panic(err)
236 }
237
238 s.enrolmentConfig.EnrolmentSecret = []byte{}
239 s.enrolmentConfig.NodeId = nodeID
240
241 enrolmentConfigRaw, err := proto.Marshal(s.enrolmentConfig)
242 if err != nil {
243 panic(err)
244 }
245 if err := ioutil.WriteFile(enrolmentPath, enrolmentConfigRaw, 0600); err != nil {
246 return err
247 }
248 s.logger.Info("Node successfully enrolled")
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200249
250 return nil
251}
252
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100253func (s *SmalltownNode) startForSetup(ctx context.Context) error {
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100254 s.logger.Info("Setting up a new cluster")
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100255 initData, nodeID, err := s.InitializeNode(ctx)
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200256 if err != nil {
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200257 return err
258 }
Lorenz Brun878f5f92020-05-12 16:15:39 +0200259 s.hostname = nodeID
260 if err := s.initHostname(); err != nil {
261 return err
262 }
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200263
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100264 if err := s.initNodeAPI(); err != nil {
265 return err
266 }
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200267
Lorenz Brun878f5f92020-05-12 16:15:39 +0200268 // TODO: Use supervisor.Run for this
269 go s.Containerd.Run()(context.TODO())
270
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100271 dataPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "etcd")
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200272 if err != nil {
273 return err
274 }
275
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100276 // Spin up etcd
277 config := s.Consensus.GetConfig()
278 config.NewCluster = true
279 config.Name = s.hostname
280 config.DataDir = dataPath
281 s.Consensus.SetConfig(config)
282
283 // Generate the cluster CA and store it to local storage.
284 if err := s.Consensus.PrecreateCA(); err != nil {
285 return err
286 }
287
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200288 err = s.Consensus.Start()
289 if err != nil {
290 return err
291 }
292
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100293 // Now that the cluster is up and running, we can persist the CA to the cluster.
294 if err := s.Consensus.InjectCA(); err != nil {
295 return err
296 }
297
298 if err := s.Api.BootstrapNewClusterHook(initData); err != nil {
299 return err
300 }
301
302 if err := s.Kubernetes.NewCluster(); err != nil {
303 return err
304 }
305
306 if err := s.Kubernetes.Start(); err != nil {
307 return err
308 }
309
310 if err := s.Api.Start(); err != nil {
311 s.logger.Error("Failed to start the API service", zap.Error(err))
312 return err
313 }
314
315 enrolmentPath, err := s.Storage.GetPathInPlace(storage.PlaceESP, "enrolment.pb")
316 if err != nil {
317 panic(err)
318 }
319
320 masterCert, err := s.Api.GetMasterCert()
321 if err != nil {
322 return err
323 }
324
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100325 ip, err := s.Network.GetIP(ctx, true)
326 if err != nil {
327 return fmt.Errorf("could not get node IP: %v", err)
328 }
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100329 enrolmentConfig := &apipb.EnrolmentConfig{
330 EnrolmentSecret: []byte{}, // First node is always already enrolled
331 MastersCert: masterCert,
Serge Bazanskicdb8c782020-02-17 12:34:02 +0100332 MasterIps: [][]byte{[]byte(*ip)},
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100333 NodeId: nodeID,
334 }
335 enrolmentConfigRaw, err := proto.Marshal(enrolmentConfig)
336 if err != nil {
337 panic(err)
338 }
339 if err := ioutil.WriteFile(enrolmentPath, enrolmentConfigRaw, 0600); err != nil {
340 return err
341 }
342 masterCertFingerprint := sha512.Sum512_256(masterCert)
343 s.logger.Info("New Smalltown cluster successfully bootstrapped", zap.Binary("fingerprint", masterCertFingerprint[:]))
344
345 return nil
346}
347
348func (s *SmalltownNode) generateNodeID() ([]byte, string, error) {
349 serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 127)
350 serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
351 if err != nil {
352 return []byte{}, "", fmt.Errorf("Failed to generate serial number: %w", err)
353 }
354
355 pubKey, privKeyRaw, err := ed25519.GenerateKey(rand.Reader)
356 if err != nil {
357 return []byte{}, "", err
358 }
359 privkey, err := x509.MarshalPKCS8PrivateKey(privKeyRaw)
360 if err != nil {
361 return []byte{}, "", err
362 }
363
364 nodeKeyPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "node-key.der")
365 if err != nil {
366 return []byte{}, "", err
367 }
368
369 if err := ioutil.WriteFile(nodeKeyPath, privkey, 0600); err != nil {
370 return []byte{}, "", fmt.Errorf("failed to write node key: %w", err)
371 }
372
Lorenz Brun878f5f92020-05-12 16:15:39 +0200373 name := "smalltown-" + hex.EncodeToString([]byte(pubKey[:16]))
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100374
375 // This has no SANs because it authenticates by public key, not by name
376 nodeCert := &x509.Certificate{
377 SerialNumber: serialNumber,
378 Subject: pkix.Name{
379 // We identify nodes by their ID public keys (not hashed since a strong hash is longer and serves no benefit)
380 CommonName: name,
381 },
382 IsCA: false,
383 BasicConstraintsValid: true,
384 NotBefore: time.Now(),
385 NotAfter: unknownNotAfter,
386 // Certificate is used both as server & client
387 ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
388 }
389 cert, err := x509.CreateCertificate(rand.Reader, nodeCert, nodeCert, pubKey, privKeyRaw)
390 if err != nil {
391 return []byte{}, "", err
392 }
393
394 nodeCertPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "node.der")
395 if err != nil {
396 return []byte{}, "", err
397 }
398
399 if err := ioutil.WriteFile(nodeCertPath, cert, 0600); err != nil {
400 return []byte{}, "", fmt.Errorf("failed to write node cert: %w", err)
401 }
402 return cert, name, nil
403}
404
405func (s *SmalltownNode) initNodeAPI() error {
406 certPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "node.der")
407 if err != nil {
408 s.logger.Panic("Invariant violated: Data is available once this is called")
409 }
410 keyPath, err := s.Storage.GetPathInPlace(storage.PlaceData, "node-key.der")
411 if err != nil {
412 s.logger.Panic("Invariant violated: Data is available once this is called")
413 }
414
415 certRaw, err := ioutil.ReadFile(certPath)
416 if err != nil {
417 return err
418 }
419 privKeyRaw, err := ioutil.ReadFile(keyPath)
420 if err != nil {
421 return err
422 }
423
424 var nodeID tls.Certificate
425
426 cert, err := x509.ParseCertificate(certRaw)
427 if err != nil {
428 return err
429 }
430
431 privKey, err := x509.ParsePKCS8PrivateKey(privKeyRaw)
432 if err != nil {
433 return err
434 }
435
436 nodeID.Certificate = [][]byte{certRaw}
437 nodeID.PrivateKey = privKey
438 nodeID.Leaf = cert
439
440 secureTransport := &tls.Config{
441 Certificates: []tls.Certificate{nodeID},
442 ClientAuth: tls.RequireAndVerifyClientCert,
443 InsecureSkipVerify: true,
444 // Critical function, please review any changes with care
445 // TODO(lorenz): Actively check that this actually provides the security guarantees that we need
446 VerifyPeerCertificate: func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
447 for _, cert := range rawCerts {
448 // X.509 certificates in DER can be compared like this since DER has a unique representation
449 // for each certificate.
450 if bytes.Equal(cert, s.enrolmentConfig.MastersCert) {
451 return nil
452 }
453 }
454 return errors.New("failed to find authorized NMS certificate")
455 },
456 MinVersion: tls.VersionTLS13,
457 }
458 secureTransportCreds := credentials.NewTLS(secureTransport)
459
460 masterListenHost := fmt.Sprintf(":%d", common.NodeServicePort)
461 lis, err := net.Listen("tcp", masterListenHost)
462 if err != nil {
463 s.logger.Fatal("failed to listen", zap.Error(err))
464 }
465
466 nodeGRPCServer := grpc.NewServer(grpc.Creds(secureTransportCreds))
467 apipb.RegisterNodeServiceServer(nodeGRPCServer, s)
468 go func() {
469 if err := nodeGRPCServer.Serve(lis); err != nil {
470 panic(err) // Can only happen during initialization and is always fatal
471 }
472 }()
473 return nil
474}
475
476func getIntegrityBackoff() *backoff.ExponentialBackOff {
477 unlockBackoff := backoff.NewExponentialBackOff()
478 unlockBackoff.MaxElapsedTime = time.Duration(0)
479 unlockBackoff.InitialInterval = 5 * time.Second
480 unlockBackoff.MaxInterval = 5 * time.Minute
481 return unlockBackoff
482}
483
484func (s *SmalltownNode) startFull() error {
485 s.logger.Info("Initializing subsystems for production")
486 s.state = common.StateJoined
487
Lorenz Brun878f5f92020-05-12 16:15:39 +0200488 s.hostname = s.enrolmentConfig.NodeId
489 if err := s.initHostname(); err != nil {
490 return err
491 }
492
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100493 trustAgent := tpm2.TPM2Agent{}
494 unlockOp := func() error {
495 unlockKey, err := trustAgent.Unlock(*s.enrolmentConfig)
496 if err != nil {
497 s.logger.Warn("Failed to unlock", zap.Error(err))
498 return err
499 }
500 if err := s.Storage.MountData(unlockKey); err != nil {
501 s.logger.Panic("Failed to mount storage", zap.Error(err))
502 return err
503 }
504 return nil
505 }
506
507 if err := backoff.Retry(unlockOp, getIntegrityBackoff()); err != nil {
508 s.logger.Panic("Invariant violated: Unlock retry can never fail")
509 }
510
511 s.initNodeAPI()
512
Lorenz Brun878f5f92020-05-12 16:15:39 +0200513 // TODO: Use supervisor.Run for this
514 go s.Containerd.Run()(context.TODO())
515
Lorenz Brunaa6b7342019-12-12 02:55:02 +0100516 err := s.Consensus.Start()
517 if err != nil {
518 return err
519 }
520
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200521 err = s.Api.Start()
522 if err != nil {
523 s.logger.Error("Failed to start the API service", zap.Error(err))
524 return err
525 }
526
Lorenz Brun6e8f69c2019-11-18 10:44:24 +0100527 err = s.Kubernetes.Start()
528 if err != nil {
529 s.logger.Error("Failed to start the Kubernetes Service", zap.Error(err))
530 }
531
Hendrik Hofstadt0d7c91e2019-10-23 21:44:47 +0200532 return nil
533}
534
535func (s *SmalltownNode) Stop() error {
536 s.logger.Info("Stopping Smalltown node")
537 return nil
538}