• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender / 8fbc121d225795f8eff22fd8b59bcf6c56fa43ac

pending completion
8fbc121d225795f8eff22fd8b59bcf6c56fa43ac

push

gitlab-ci

GitHub
Merge pull request #1178 from pasinskim/master

9809 of 12349 relevant lines covered (79.43%)

29.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.54
/app/auth.go
1
// Copyright 2021 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
package app
16

17
import (
18
        "runtime"
19
        "strings"
20
        "sync"
21
        "time"
22

23
        "github.com/pkg/errors"
24
        log "github.com/sirupsen/logrus"
25

26
        "github.com/mendersoftware/mender/app/proxy"
27
        "github.com/mendersoftware/mender/client"
28
        "github.com/mendersoftware/mender/conf"
29
        "github.com/mendersoftware/mender/datastore"
30
        "github.com/mendersoftware/mender/dbus"
31
        "github.com/mendersoftware/mender/device"
32
        "github.com/mendersoftware/mender/store"
33
)
34

35
// Constants for auth manager request actions
36
const (
37
        ActionFetchAuthToken = "FETCH_AUTH_TOKEN"
38
        ActionGetAuthToken   = "GET_AUTH_TOKEN"
39
)
40

41
// Constants for auth manager response events
42
const (
43
        EventFetchAuthToken       = "FETCH_AUTH_TOKEN"
44
        EventGetAuthToken         = "GET_AUTH_TOKEN"
45
        EventAuthTokenStateChange = "AUTH_TOKEN_STATE_CHANGE"
46
)
47

48
// Constants for the auth manager DBus interface
49
const (
50
        AuthManagerDBusPath                      = "/io/mender/AuthenticationManager"
51
        AuthManagerDBusObjectName                = "io.mender.AuthenticationManager"
52
        AuthManagerDBusInterfaceName             = "io.mender.Authentication1"
53
        AuthManagerDBusSignalJwtTokenStateChange = "JwtTokenStateChange"
54
        AuthManagerDBusInterface                 = `<node>
55
        <interface name="io.mender.Authentication1">
56
                <method name="GetJwtToken">
57
                        <arg type="s" name="token" direction="out"/>
58
                        <arg type="s" name="server_url" direction="out"/>
59
                </method>
60
                <method name="FetchJwtToken">
61
                        <arg type="b" name="success" direction="out"/>
62
                </method>
63
                <signal name="JwtTokenStateChange">
64
                        <arg type="s" name="token"/>
65
                        <arg type="s" name="server_url"/>
66
                </signal>
67
        </interface>
68
</node>`
69
)
70

71
const (
72
        noAuthToken                  = client.EmptyAuthToken
73
        authManagerInMessageChanSize = 1024
74

75
        // Keep this at 1 for now. At the time of writing it is only used for
76
        // fetchAuthToken, and it doesn't make sense to have more than one
77
        // request in the queue, since additional requests will just accomplish
78
        // the same thing as one request.
79
        authManagerWorkerQueueSize = 1
80
)
81

82
// AuthManagerRequest stores a request to the Mender authorization manager
83
type AuthManagerRequest struct {
84
        Action          string
85
        ResponseChannel chan<- AuthManagerResponse
86
}
87

88
// AuthManagerResponse stores a response from the Mender authorization manager
89
type AuthManagerResponse struct {
90
        AuthToken client.AuthToken
91
        ServerURL client.ServerURL
92
        Event     string
93
        Error     error
94
}
95

96
// AuthManager is the interface of a Mender authorization manager
97
type AuthManager interface {
98
        Bootstrap() menderError
99
        ForceBootstrap()
100
        GetInMessageChan() chan<- AuthManagerRequest
101
        GetBroadcastMessageChan(name string) <-chan AuthManagerResponse
102
        Start()
103
        Stop()
104
        EnableDBus(api dbus.DBusAPI)
105

106
        // check if device key is available
107
        HasKey() bool
108
        // generate device key (will overwrite an already existing key)
109
        GenerateKey() error
110

111
        client.AuthDataMessenger
112
}
113

114
// MenderAuthManager is the Mender authorization manager
115
type MenderAuthManager struct {
116
        // We use this composition so that we can set a finalizer on the outer
117
        // struct and clean up the go routine which is running using the inner
118
        // struct.
119
        *menderAuthManagerService
120
}
121

122
type menderAuthManagerService struct {
123
        hasStarted bool
124
        inChan     chan AuthManagerRequest
125

126
        broadcastChansMutex sync.Mutex
127
        broadcastChans      map[string]chan AuthManagerResponse
128

129
        workerChan chan AuthManagerRequest
130

131
        quitReq  chan bool
132
        quitResp chan bool
133

134
        authReq client.AuthRequester
135
        api     *client.ApiClient
136

137
        forceBootstrap bool
138
        dbus           dbus.DBusAPI
139
        dbusConn       dbus.Handle
140
        config         *conf.MenderConfig
141
        keyStore       *store.Keystore
142
        idSrc          device.IdentityDataGetter
143
        authToken      client.AuthToken
144
        serverURL      client.ServerURL
145
        tenantToken    client.AuthToken
146

147
        localProxy *proxy.ProxyController
148
}
149

150
// AuthManagerConfig holds the configuration of the auth manager
151
type AuthManagerConfig struct {
152
        Config         *conf.MenderConfig        // mender config struct
153
        AuthDataStore  store.Store               // authorization data store
154
        KeyStore       *store.Keystore           // key storage
155
        IdentitySource device.IdentityDataGetter // provider of identity data
156
        TenantToken    []byte                    // tenant token
157
}
158

159
// NewAuthManager returns a new Mender authorization manager instance
160
func NewAuthManager(conf AuthManagerConfig) *MenderAuthManager {
192✔
161
        if conf.KeyStore == nil || conf.IdentitySource == nil ||
192✔
162
                conf.AuthDataStore == nil {
194✔
163
                return nil
2✔
164
        }
2✔
165

166
        httpConfig := client.Config{}
192✔
167
        if conf.Config != nil {
384✔
168
                httpConfig = conf.Config.GetHttpConfig()
192✔
169

192✔
170
        }
192✔
171

172
        api, err := client.NewApiClient(httpConfig)
192✔
173
        if err != nil {
192✔
174
                return nil
×
175
        }
×
176

177
        tenantToken := client.AuthToken(conf.TenantToken)
192✔
178

192✔
179
        wsDialer, err := client.NewWebsocketDialer(httpConfig)
192✔
180
        if err != nil {
192✔
181
                return nil
×
182
        }
×
183

184
        proxy, err := proxy.NewProxyController(api, wsDialer, "", "")
191✔
185
        if err != nil {
191✔
186
                log.Errorf("Error creating local proxy: %s", err.Error())
×
187
        }
×
188

189
        mgr := &MenderAuthManager{
191✔
190
                &menderAuthManagerService{
191✔
191
                        inChan:         make(chan AuthManagerRequest, authManagerInMessageChanSize),
191✔
192
                        broadcastChans: map[string]chan AuthManagerResponse{},
191✔
193
                        quitReq:        make(chan bool),
191✔
194
                        quitResp:       make(chan bool),
191✔
195
                        workerChan:     make(chan AuthManagerRequest, authManagerWorkerQueueSize),
191✔
196
                        api:            api,
191✔
197
                        authReq:        client.NewAuth(),
191✔
198
                        config:         conf.Config,
191✔
199
                        keyStore:       conf.KeyStore,
191✔
200
                        idSrc:          conf.IdentitySource,
191✔
201
                        tenantToken:    tenantToken,
191✔
202
                        localProxy:     proxy,
191✔
203
                },
191✔
204
        }
191✔
205

191✔
206
        if err := mgr.keyStore.Load(); err != nil && !store.IsNoKeys(err) {
193✔
207
                log.Errorf("Failed to load device keys: %v", err)
2✔
208
                // Otherwise ignore error returned from Load() call. It will
2✔
209
                // just result in an empty keyStore which in turn will cause
2✔
210
                // regeneration of keys.
2✔
211
        }
2✔
212

213
        // Clean up keys that we don't use anymore. This is safe to do even if
214
        // rolling back, because the old clients will just fetch a new
215
        // AuthToken. Ignore all errors here; this is just cleanup, and it
216
        // doesn't matter if it fails.
217
        _ = conf.AuthDataStore.WriteTransaction(func(txn store.Transaction) error {
382✔
218
                _ = txn.Remove(datastore.AuthTokenName)
191✔
219
                _ = txn.Remove(datastore.AuthTokenCacheInvalidatorName)
191✔
220
                return nil
191✔
221
        })
191✔
222

223
        return mgr
191✔
224
}
225

226
func (m *MenderAuthManager) EnableDBus(api dbus.DBusAPI) {
191✔
227
        if m.hasStarted {
191✔
228
                panic("Calling WithDBus() after the service has started is a programming mistake.")
×
229
        }
230
        m.dbus = api
191✔
231
}
232

233
// GetInMessageChan returns the channel to send requests to the auth manager
234
func (m *MenderAuthManager) GetInMessageChan() chan<- AuthManagerRequest {
33✔
235
        // Auto-start the service if it hasn't been started already.
33✔
236
        m.Start()
33✔
237
        return m.inChan
33✔
238
}
33✔
239

240
// GetBroadcastMessageChan returns the channel to get responses from the auth manager
241
func (m *MenderAuthManager) GetBroadcastMessageChan(name string) <-chan AuthManagerResponse {
33✔
242
        // Auto-start the service if it hasn't been started already.
33✔
243
        m.Start()
33✔
244

33✔
245
        m.broadcastChansMutex.Lock()
33✔
246
        defer m.broadcastChansMutex.Unlock()
33✔
247

33✔
248
        if m.broadcastChans[name] == nil {
66✔
249
                m.broadcastChans[name] = make(chan AuthManagerResponse, 1)
33✔
250
        }
33✔
251
        return m.broadcastChans[name]
33✔
252
}
253

254
func (m *menderAuthManagerService) registerDBusCallbacks() (unregisterFunc func()) {
33✔
255
        // GetJwtToken
33✔
256
        m.dbus.RegisterMethodCallCallback(
33✔
257
                AuthManagerDBusPath,
33✔
258
                AuthManagerDBusInterfaceName,
33✔
259
                "GetJwtToken",
33✔
260
                func(objectPath, interfaceName, methodName string, parameters string) (interface{}, error) {
61✔
261
                        respChan := make(chan AuthManagerResponse)
28✔
262
                        m.inChan <- AuthManagerRequest{
28✔
263
                                Action:          ActionGetAuthToken,
28✔
264
                                ResponseChannel: respChan,
28✔
265
                        }
28✔
266
                        select {
28✔
267
                        case message := <-respChan:
28✔
268
                                tokenAndServerURL := dbus.TokenAndServerURL{
28✔
269
                                        Token:     string(message.AuthToken),
28✔
270
                                        ServerURL: m.localProxy.GetServerUrl(),
28✔
271
                                }
28✔
272
                                return tokenAndServerURL, message.Error
28✔
273
                        case <-time.After(5 * time.Second):
×
274
                        }
275
                        return string(noAuthToken), errors.New("timeout when calling GetJwtToken")
×
276
                },
277
        )
278
        // FetchJwtToken
279
        m.dbus.RegisterMethodCallCallback(
33✔
280
                AuthManagerDBusPath,
33✔
281
                AuthManagerDBusInterfaceName,
33✔
282
                "FetchJwtToken",
33✔
283
                func(objectPath, interfaceName, methodName string, parameters string) (interface{}, error) {
34✔
284
                        respChan := make(chan AuthManagerResponse)
1✔
285
                        m.inChan <- AuthManagerRequest{
1✔
286
                                Action:          ActionFetchAuthToken,
1✔
287
                                ResponseChannel: respChan,
1✔
288
                        }
1✔
289
                        select {
1✔
290
                        case message := <-respChan:
1✔
291
                                return message.Event == EventFetchAuthToken, message.Error
1✔
292
                        case <-time.After(5 * time.Second):
×
293
                        }
294
                        return false, errors.New("timeout when calling FetchJwtToken")
×
295
                },
296
        )
297

298
        return func() {
38✔
299
                m.dbus.UnregisterMethodCallCallback(
5✔
300
                        AuthManagerDBusPath,
5✔
301
                        AuthManagerDBusInterfaceName,
5✔
302
                        "FetchJwtToken",
5✔
303
                )
5✔
304
                m.dbus.UnregisterMethodCallCallback(
5✔
305
                        AuthManagerDBusPath,
5✔
306
                        AuthManagerDBusInterfaceName,
5✔
307
                        "GetJwtToken",
5✔
308
                )
5✔
309
        }
5✔
310
}
311

312
// This is idempotent, the service will only start once.
313
func (m *MenderAuthManager) Start() {
33✔
314
        if m.menderAuthManagerService.hasStarted {
66✔
315
                return
33✔
316
        }
33✔
317

318
        m.menderAuthManagerService.hasStarted = true
33✔
319

33✔
320
        initDone := make(chan struct{}, 1)
33✔
321
        go m.menderAuthManagerService.run(initDone)
33✔
322

33✔
323
        // Wait for initialization to finish.
33✔
324
        <-initDone
33✔
325

33✔
326
        runtime.SetFinalizer(m, func(m *MenderAuthManager) {
35✔
327
                m.Stop()
2✔
328
        })
2✔
329
}
330

331
// Run is the main routine of the Mender authorization manager
332
func (m *menderAuthManagerService) run(initDone chan struct{}) {
33✔
333
        // When we are being stopped, make sure they know that this happened.
33✔
334
        defer func() {
38✔
335
                // Checking for panic here is just to avoid deadlocking if we
5✔
336
                // get an unexpected panic: Let it propagate instead of blocking
5✔
337
                // on the channel. If the program is correct, this should never
5✔
338
                // be non-nil.
5✔
339
                if recover() == nil {
10✔
340
                        m.quitResp <- true
5✔
341
                }
5✔
342
        }()
343

344
        // run the DBus interface, if available
345
        if m.dbus != nil {
66✔
346
                if dbusConn, err := m.dbus.BusGet(dbus.GBusTypeSystem); err == nil {
66✔
347
                        m.dbusConn = dbusConn
33✔
348

33✔
349
                        nameGid, err := m.dbus.BusOwnNameOnConnection(dbusConn, AuthManagerDBusObjectName,
33✔
350
                                dbus.DBusNameOwnerFlagsAllowReplacement|dbus.DBusNameOwnerFlagsReplace)
33✔
351
                        if err != nil {
33✔
352
                                log.Errorf(
×
353
                                        "Could not own DBus name '%s': %s",
×
354
                                        AuthManagerDBusObjectName,
×
355
                                        err.Error(),
×
356
                                )
×
357
                                goto mainloop
×
358
                        }
359
                        defer m.dbus.BusUnownName(nameGid)
33✔
360

33✔
361
                        intGid, err := m.dbus.BusRegisterInterface(
33✔
362
                                dbusConn,
33✔
363
                                AuthManagerDBusPath,
33✔
364
                                AuthManagerDBusInterface,
33✔
365
                        )
33✔
366
                        if err != nil {
33✔
367
                                log.Errorf("Could register DBus interface name '%s' at path '%s': %s",
×
368
                                        AuthManagerDBusInterface, AuthManagerDBusPath, err.Error())
×
369
                                goto mainloop
×
370
                        }
371
                        defer m.dbus.BusUnregisterInterface(dbusConn, intGid)
33✔
372

33✔
373
                        unregisterFunc := m.registerDBusCallbacks()
33✔
374
                        defer unregisterFunc()
33✔
375

33✔
376
                        dbusLoop := m.dbus.MainLoopNew()
33✔
377
                        go m.dbus.MainLoopRun(dbusLoop)
33✔
378
                        defer m.dbus.MainLoopQuit(dbusLoop)
33✔
379
                }
380
        }
381

382
mainloop:
383
        initDone <- struct{}{}
33✔
384

33✔
385
        go m.longRunningWorkerLoop()
33✔
386

33✔
387
        // run the auth manager main loop
33✔
388
        running := true
33✔
389
        for running {
66✔
390
                select {
33✔
391
                case msg := <-m.inChan:
33✔
392
                        switch msg.Action {
33✔
393
                        case ActionGetAuthToken:
30✔
394
                                log.Debug("received the GET_AUTH_TOKEN action")
30✔
395
                                m.getAuthToken(msg.ResponseChannel)
30✔
396
                        case ActionFetchAuthToken:
33✔
397
                                log.Debug("received the FETCH_AUTH_TOKEN action")
33✔
398

33✔
399
                                // notify the sender we'll fetch the token
33✔
400
                                resp := AuthManagerResponse{Event: EventFetchAuthToken}
33✔
401
                                msg.ResponseChannel <- resp
33✔
402

33✔
403
                                // Potentially long running operation, use worker.
33✔
404
                                select {
33✔
405
                                case m.workerChan <- msg:
33✔
406
                                default:
2✔
407
                                        // Already a request in the queue, nothing to do.
408
                                }
409
                        }
410
                case <-m.quitReq:
5✔
411
                        running = false
5✔
412
                        m.workerChan <- AuthManagerRequest{}
5✔
413
                }
414
        }
415
}
416

417
// This is a helper to the main loop, for tasks that may take a long time. It's
418
// running in a separate Go routine.
419
func (m *menderAuthManagerService) longRunningWorkerLoop() {
33✔
420
        for msg := range m.workerChan {
66✔
421
                switch msg.Action {
33✔
422
                case ActionFetchAuthToken:
33✔
423
                        m.fetchAuthToken()
33✔
424
                case "":
5✔
425
                        // Quit loop.
5✔
426
                        return
5✔
427

428
                }
429
        }
430
}
431

432
// Stop the running MenderAuthManager. Must not be called in the same go
433
// routine as run(). This is idempotent, it is safe to call on a stopped
434
// service.
435
func (m *MenderAuthManager) Stop() {
5✔
436
        if !m.menderAuthManagerService.hasStarted {
5✔
437
                return
×
438
        }
×
439

440
        m.menderAuthManagerService.quitReq <- true
5✔
441
        <-m.menderAuthManagerService.quitResp
5✔
442
        m.menderAuthManagerService.hasStarted = false
5✔
443

5✔
444
        m.localProxy.Stop()
5✔
445

5✔
446
        runtime.SetFinalizer(m, nil)
5✔
447
}
448

449
// getAuthToken returns the cached auth token
450
func (m *menderAuthManagerService) getAuthToken(responseChannel chan<- AuthManagerResponse) {
30✔
451
        msg := AuthManagerResponse{
30✔
452
                AuthToken: m.authToken,
30✔
453
                ServerURL: m.serverURL,
30✔
454
                Event:     EventGetAuthToken,
30✔
455
        }
30✔
456
        responseChannel <- msg
30✔
457
}
30✔
458

459
// broadcast broadcasts the notification to all the subscribers
460
func (m *menderAuthManagerService) broadcast(message AuthManagerResponse) {
33✔
461
        m.broadcastChansMutex.Lock()
33✔
462
        for _, broadcastChan := range m.broadcastChans {
66✔
463
                select {
33✔
464
                case broadcastChan <- message:
33✔
465
                default:
×
466
                }
467
        }
468
        m.broadcastChansMutex.Unlock()
33✔
469

33✔
470
        // emit signal on dbus, if available
33✔
471
        if m.dbus != nil {
66✔
472
                tokenAndServerURL := dbus.TokenAndServerURL{
33✔
473
                        Token:     string(message.AuthToken),
33✔
474
                        ServerURL: string(message.ServerURL),
33✔
475
                }
33✔
476
                _ = m.dbus.EmitSignal(m.dbusConn, "", AuthManagerDBusPath,
33✔
477
                        AuthManagerDBusInterfaceName, AuthManagerDBusSignalJwtTokenStateChange,
33✔
478
                        tokenAndServerURL)
33✔
479
        }
33✔
480
}
481

482
// broadcastAuthTokenStateChange broadcasts the notification to all the subscribers
483
func (m *menderAuthManagerService) broadcastAuthTokenStateChange() {
33✔
484
        m.localProxy.Stop()
33✔
485
        if m.authToken != "" {
64✔
486
                // reconfigure proxy
31✔
487
                err := m.localProxy.Reconfigure(string(m.serverURL), string(m.authToken))
31✔
488
                if err != nil {
31✔
489
                        log.Errorf(
×
490
                                "Could not reconfigure proxy with URL %q and token '%s...'"+
×
491
                                        " Other applications running on the device won't be able"+
×
492
                                        " to reach the Mender server. Error: %s",
×
493
                                m.serverURL,
×
494
                                string(m.authToken)[:7],
×
495
                                err.Error(),
×
496
                        )
×
497
                } else {
31✔
498
                        m.localProxy.Start()
31✔
499

31✔
500
                }
31✔
501
        }
502

503
        m.broadcast(AuthManagerResponse{
33✔
504
                Event:     EventAuthTokenStateChange,
33✔
505
                AuthToken: m.authToken,
33✔
506
                ServerURL: client.ServerURL(m.localProxy.GetServerUrl()),
33✔
507
        })
33✔
508
}
509

510
// fetchAuthToken authenticates with the server and retrieve a new auth token, if needed
511
func (m *menderAuthManagerService) fetchAuthToken() {
33✔
512
        var rsp []byte
33✔
513
        var err error
33✔
514
        var server *client.MenderServer
33✔
515
        resp := AuthManagerResponse{Event: EventFetchAuthToken}
33✔
516

33✔
517
        defer func() {
66✔
518
                m.broadcastAuthTokenStateChange()
33✔
519
        }()
33✔
520

521
        if err := m.Bootstrap(); err != nil {
33✔
522
                log.Errorf("Bootstrap failed: %s", err)
×
523
                resp.Error = err
×
524
                return
×
525
        }
×
526

527
        // Cycle through servers and attempt to authorize.
528
        serverIterator := nextServerIterator(*m.config)
33✔
529
        if serverIterator == nil {
33✔
530
                log.Debug("empty server list in mender.conf, serverIterator is nil")
×
531
                err := NewFatalError(errors.New("empty server list in mender.conf"))
×
532
                resp.Error = err
×
533
                return
×
534
        }
×
535

536
        if server = serverIterator(); server == nil {
33✔
537
                log.Debug("empty server list in mender.conf, server is nil")
×
538
                err := NewFatalError(errors.New("empty server list in mender.conf"))
×
539
                resp.Error = err
×
540
                return
×
541
        }
×
542

543
        var serverURL string
33✔
544
        for {
66✔
545
                serverURL = server.ServerURL
33✔
546
                rsp, err = m.authReq.Request(m.api, serverURL, m)
33✔
547

33✔
548
                if err == nil {
64✔
549
                        // SUCCESS!
31✔
550
                        break
31✔
551
                }
552
                log.Errorf("Failed to authorize with %q: %s",
4✔
553
                        server.ServerURL, err.Error())
4✔
554
                server = serverIterator()
4✔
555
                if server == nil {
8✔
556
                        break
4✔
557
                }
558
                log.Infof("Attempting to authorize with %q.",
2✔
559
                        server.ServerURL)
2✔
560
        }
561
        if err != nil {
37✔
562
                // Generate and report error.
4✔
563
                errCause := errors.Cause(err)
4✔
564
                if errCause == client.AuthErrorUnauthorized {
6✔
565
                        m.authToken = ""
2✔
566
                        m.serverURL = ""
2✔
567
                }
2✔
568
                err := NewTransientError(errors.Wrap(err, "authorization request failed"))
4✔
569
                resp.Error = err
4✔
570
                return
4✔
571
        }
572

573
        if len(rsp) == 0 {
31✔
574
                resp.Error = errors.New("empty auth response data")
×
575
                return
×
576
        }
×
577

578
        m.authToken = client.AuthToken(rsp)
31✔
579
        m.serverURL = client.ServerURL(serverURL)
31✔
580

31✔
581
        log.Infof("successfully received new authorization data from server %s", m.serverURL)
31✔
582
}
583

584
// ForceBootstrap forces the bootstrap
585
func (m *menderAuthManagerService) ForceBootstrap() {
4✔
586
        m.forceBootstrap = true
4✔
587
}
4✔
588

589
// Bootstrap performs the bootstrap, if needed or forced
590
func (m *menderAuthManagerService) Bootstrap() menderError {
33✔
591
        if !m.needsBootstrap() {
56✔
592
                return nil
23✔
593
        }
23✔
594

595
        return m.doBootstrap()
26✔
596
}
597

598
func (m *menderAuthManagerService) needsBootstrap() bool {
33✔
599
        if m.forceBootstrap {
37✔
600
                return true
4✔
601
        }
4✔
602

603
        if !m.HasKey() {
57✔
604
                log.Debugf("Needs keys")
24✔
605
                return true
24✔
606
        }
24✔
607

608
        return false
23✔
609
}
610

611
func (m *menderAuthManagerService) doBootstrap() menderError {
26✔
612
        if !m.HasKey() || m.forceBootstrap {
52✔
613
                log.Infof("Device keys not present or bootstrap forced, generating")
26✔
614

26✔
615
                err := m.GenerateKey()
26✔
616
                if err != nil {
28✔
617
                        if store.IsStaticKey(err) {
2✔
618
                                log.Error("Device key is static, refusing to regenerate.")
×
619
                        } else {
2✔
620
                                return NewFatalError(err)
2✔
621
                        }
2✔
622
                }
623
        }
624

625
        m.forceBootstrap = false
26✔
626
        return nil
26✔
627
}
628

629
// MakeAuthRequest makes an auth request
630
func (m *menderAuthManagerService) MakeAuthRequest() (*client.AuthRequest, error) {
33✔
631

33✔
632
        var err error
33✔
633
        authd := client.AuthReqData{}
33✔
634

33✔
635
        idata, err := m.idSrc.Get()
33✔
636
        if err != nil {
35✔
637
                return nil, errors.Wrapf(err, "failed to obtain identity data")
2✔
638
        }
2✔
639

640
        authd.IdData = idata
33✔
641

33✔
642
        // fill device public key
33✔
643
        authd.Pubkey, err = m.keyStore.PublicPEM()
33✔
644
        if err != nil {
35✔
645
                return nil, errors.Wrapf(err, "failed to obtain device public key")
2✔
646
        }
2✔
647

648
        tentok := strings.TrimSpace(string(m.tenantToken))
33✔
649

33✔
650
        log.Debugf("Tenant token: %s", tentok)
33✔
651

33✔
652
        // fill tenant token
33✔
653
        authd.TenantToken = string(tentok)
33✔
654

33✔
655
        log.Debugf("Authorization data: %v", authd)
33✔
656

33✔
657
        reqdata, err := authd.ToBytes()
33✔
658
        if err != nil {
33✔
659
                return nil, errors.Wrapf(err, "failed to convert auth request data")
×
660
        }
×
661

662
        // generate signature
663
        sig, err := m.keyStore.Sign(reqdata)
33✔
664
        if err != nil {
33✔
665
                return nil, errors.Wrapf(err, "failed to sign auth request")
×
666
        }
×
667

668
        return &client.AuthRequest{
33✔
669
                Data:      reqdata,
33✔
670
                Token:     client.AuthToken(tentok),
33✔
671
                Signature: sig,
33✔
672
        }, nil
33✔
673
}
674

675
// HasKey check if device key is available
676
func (m *menderAuthManagerService) HasKey() bool {
33✔
677
        return m.keyStore.Private() != nil
33✔
678
}
33✔
679

680
// GenerateKey generate device key (will overwrite an already existing key)
681
func (m *menderAuthManagerService) GenerateKey() error {
26✔
682
        if err := m.keyStore.Generate(); err != nil {
28✔
683
                if store.IsStaticKey(err) {
4✔
684
                        return err
2✔
685
                }
2✔
686
                log.Errorf("Failed to generate device key: %v", err)
×
687
                return errors.Wrapf(err, "failed to generate device key")
×
688
        }
689

690
        if err := m.keyStore.Save(); err != nil {
28✔
691
                log.Errorf("Failed to save device key: %s", err)
2✔
692
                return NewFatalError(err)
2✔
693
        }
2✔
694
        return nil
26✔
695
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc