forked from hashicorp/vault
-
Notifications
You must be signed in to change notification settings - Fork 0
/
core.go
1119 lines (960 loc) · 30.5 KB
/
core.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package vault
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"log"
"net/url"
"os"
"strings"
"sync"
"time"
"github.com/armon/go-metrics"
"github.com/hashicorp/vault/audit"
"github.com/hashicorp/vault/helper/mlock"
"github.com/hashicorp/vault/logical"
"github.com/hashicorp/vault/physical"
"github.com/hashicorp/vault/shamir"
)
const (
// coreSealConfigPath is the path used to store our seal configuration.
// This value is stored in plaintext, since we must be able to read
// it even with the Vault sealed. This is required so that we know
// how many secret parts must be used to reconstruct the master key.
coreSealConfigPath = "core/seal-config"
// coreLockPath is the path used to acquire a coordinating lock
// for a highly-available deploy.
coreLockPath = "core/lock"
// coreLeaderPrefix is the prefix used for the UUID that contains
// the currently elected leader.
coreLeaderPrefix = "core/leader/"
// lockRetryInterval is the interval we re-attempt to acquire the
// HA lock if an error is encountered
lockRetryInterval = 10 * time.Second
)
var (
// ErrSealed is returned if an operation is performed on
// a sealed barrier. No operation is expected to succeed before unsealing
ErrSealed = errors.New("Vault is sealed")
// ErrStandby is returned if an operation is performed on
// a standby Vault. No operation is expected to succeed until active.
ErrStandby = errors.New("Vault is in standby mode")
// ErrAlreadyInit is returned if the core is already
// initialized. This prevents a re-initialization.
ErrAlreadyInit = errors.New("Vault is already initialized")
// ErrNotInit is returned if a non-initialized barrier
// is attempted to be unsealed.
ErrNotInit = errors.New("Vault is not initialized")
// ErrInternalError is returned when we don't want to leak
// any information about an internal error
ErrInternalError = errors.New("internal error")
// ErrHANotEnabled is returned if the operation only makes sense
// in an HA setting
ErrHANotEnabled = errors.New("Vault is not configured for highly-available mode")
)
// SealConfig is used to describe the seal configuration
type SealConfig struct {
// SecretShares is the number of shares the secret is
// split into. This is the N value of Shamir
SecretShares int `json:"secret_shares"`
// SecretThreshold is the number of parts required
// to open the vault. This is the T value of Shamir
SecretThreshold int `json:"secret_threshold"`
}
// Validate is used to sanity check the seal configuration
func (s *SealConfig) Validate() error {
if s.SecretShares < 1 {
return fmt.Errorf("secret shares must be at least one")
}
if s.SecretThreshold < 1 {
return fmt.Errorf("secret threshold must be at least one")
}
if s.SecretShares > 255 {
return fmt.Errorf("secret shares must be less than 256")
}
if s.SecretThreshold > 255 {
return fmt.Errorf("secret threshold must be less than 256")
}
if s.SecretThreshold > s.SecretShares {
return fmt.Errorf("secret threshold cannot be larger than secret shares")
}
return nil
}
// InitResult is used to provide the key parts back after
// they are generated as part of the initialization.
type InitResult struct {
SecretShares [][]byte
RootToken string
}
// ErrInvalidKey is returned if there is an error with a
// provided unseal key.
type ErrInvalidKey struct {
Reason string
}
func (e *ErrInvalidKey) Error() string {
return fmt.Sprintf("invalid key: %v", e.Reason)
}
// Core is used as the central manager of Vault activity. It is the primary point of
// interface for API handlers and is responsible for managing the logical and physical
// backends, router, security barrier, and audit trails.
type Core struct {
// HABackend may be available depending on the physical backend
ha physical.HABackend
// AdvertiseAddr is the address we advertise as leader if held
advertiseAddr string
// physical backend is the un-trusted backend with durable data
physical physical.Backend
// barrier is the security barrier wrapping the physical backend
barrier SecurityBarrier
// router is responsible for managing the mount points for logical backends.
router *Router
// logicalBackends is the mapping of backends to use for this core
logicalBackends map[string]logical.Factory
// credentialBackends is the mapping of backends to use for this core
credentialBackends map[string]logical.Factory
// auditBackends is the mapping of backends to use for this core
auditBackends map[string]audit.Factory
// stateLock protects mutable state
stateLock sync.RWMutex
sealed bool
standby bool
standbyDoneCh chan struct{}
standbyStopCh chan struct{}
// unlockParts has the keys provided to Unseal until
// the threshold number of parts is available.
unlockParts [][]byte
// mounts is loaded after unseal since it is a protected
// configuration
mounts *MountTable
// auth is loaded after unseal since it is a protected
// configuration
auth *MountTable
// audit is loaded after unseal since it is a protected
// configuration
audit *MountTable
// auditBroker is used to ingest the audit events and fan
// out into the configured audit backends
auditBroker *AuditBroker
// systemView is the barrier view for the system backend
systemView *BarrierView
// expiration manager is used for managing LeaseIDs,
// renewal, expiration and revocation
expiration *ExpirationManager
// rollback manager is used to run rollbacks periodically
rollback *RollbackManager
// policy store is used to manage named ACL policies
policy *PolicyStore
// token store is used to manage authentication tokens
tokenStore *TokenStore
// metricsCh is used to stop the metrics streaming
metricsCh chan struct{}
logger *log.Logger
}
// CoreConfig is used to parameterize a core
type CoreConfig struct {
LogicalBackends map[string]logical.Factory
CredentialBackends map[string]logical.Factory
AuditBackends map[string]audit.Factory
Physical physical.Backend
Logger *log.Logger
DisableCache bool // Disables the LRU cache on the physical backend
DisableMlock bool // Disables mlock syscall
CacheSize int // Custom cache size of zero for default
AdvertiseAddr string // Set as the leader address for HA
}
// NewCore isk used to construct a new core
func NewCore(conf *CoreConfig) (*Core, error) {
// Check if this backend supports an HA configuraiton
var haBackend physical.HABackend
if ha, ok := conf.Physical.(physical.HABackend); ok {
haBackend = ha
}
if haBackend != nil && conf.AdvertiseAddr == "" {
return nil, fmt.Errorf("missing advertisement address")
}
// Validate the advertise addr if its given to us
if conf.AdvertiseAddr != "" {
u, err := url.Parse(conf.AdvertiseAddr)
if err != nil {
return nil, fmt.Errorf("advertisement address is not valid url: %s", err)
}
if u.Scheme == "" {
return nil, fmt.Errorf("advertisement address must include scheme (ex. 'http')")
}
}
// Wrap the backend in a cache unless disabled
if !conf.DisableCache {
_, isCache := conf.Physical.(*physical.Cache)
_, isInmem := conf.Physical.(*physical.InmemBackend)
if !isCache && !isInmem {
cache := physical.NewCache(conf.Physical, conf.CacheSize)
conf.Physical = cache
}
}
if !conf.DisableMlock {
// Ensure our memory usage is locked into physical RAM
if err := mlock.LockMemory(); err != nil {
return nil, fmt.Errorf(
"Failed to lock memory: %v\n\n"+
"This usually means that the mlock syscall is not available.\n"+
"Vault uses mlock to prevent memory from being swapped to\n"+
"disk. This requires root privileges as well as a machine\n"+
"that supports mlock. Please enable mlock on your system or\n"+
"disable Vault from using it. To disable Vault from using it,\n"+
"set the `disable_mlock` configuration option in your configuration\n"+
"file.",
err)
}
}
// Construct a new AES-GCM barrier
barrier, err := NewAESGCMBarrier(conf.Physical)
if err != nil {
return nil, fmt.Errorf("barrier setup failed: %v", err)
}
// Make a default logger if not provided
if conf.Logger == nil {
conf.Logger = log.New(os.Stderr, "", log.LstdFlags)
}
// Setup the core
c := &Core{
ha: haBackend,
advertiseAddr: conf.AdvertiseAddr,
physical: conf.Physical,
barrier: barrier,
router: NewRouter(),
sealed: true,
standby: true,
logger: conf.Logger,
}
// Setup the backends
logicalBackends := make(map[string]logical.Factory)
for k, f := range conf.LogicalBackends {
logicalBackends[k] = f
}
logicalBackends["generic"] = PassthroughBackendFactory
logicalBackends["system"] = func(map[string]string) (logical.Backend, error) {
return NewSystemBackend(c), nil
}
c.logicalBackends = logicalBackends
credentialBackends := make(map[string]logical.Factory)
for k, f := range conf.CredentialBackends {
credentialBackends[k] = f
}
credentialBackends["token"] = func(map[string]string) (logical.Backend, error) {
return NewTokenStore(c)
}
c.credentialBackends = credentialBackends
auditBackends := make(map[string]audit.Factory)
for k, f := range conf.AuditBackends {
auditBackends[k] = f
}
c.auditBackends = auditBackends
return c, nil
}
// HandleRequest is used to handle a new incoming request
func (c *Core) HandleRequest(req *logical.Request) (*logical.Response, error) {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
if c.sealed {
return nil, ErrSealed
}
if c.standby {
return nil, ErrStandby
}
if c.router.LoginPath(req.Path) {
return c.handleLoginRequest(req)
} else {
return c.handleRequest(req)
}
}
func (c *Core) handleRequest(req *logical.Request) (*logical.Response, error) {
defer metrics.MeasureSince([]string{"core", "handle_request"}, time.Now())
// Validate the token
auth, err := c.checkToken(req.Operation, req.Path, req.ClientToken)
if err != nil {
// If it is an internal error we return that, otherwise we
// return invalid request so that the status codes can be correct
var errType error
switch err {
case ErrInternalError, logical.ErrPermissionDenied:
errType = err
default:
errType = logical.ErrInvalidRequest
}
return logical.ErrorResponse(err.Error()), errType
}
// Attach the display name
req.DisplayName = auth.DisplayName
// Create an audit trail of the request
if err := c.auditBroker.LogRequest(auth, req); err != nil {
c.logger.Printf("[ERR] core: failed to audit request (%#v): %v",
req, err)
return nil, ErrInternalError
}
// Route the request
resp, err := c.router.Route(req)
// If there is a secret, we must register it with the expiration manager.
if resp != nil && resp.Secret != nil {
// Apply the default lease if none given
if resp.Secret.Lease == 0 {
resp.Secret.Lease = defaultLeaseDuration
}
// Limit the lease duration
if resp.Secret.Lease > maxLeaseDuration {
resp.Secret.Lease = maxLeaseDuration
}
// Register the lease
leaseID, err := c.expiration.Register(req, resp)
if err != nil {
c.logger.Printf(
"[ERR] core: failed to register lease "+
"(request: %#v, response: %#v): %v", req, resp, err)
return nil, ErrInternalError
}
resp.Secret.LeaseID = leaseID
}
// Only the token store is allowed to return an auth block, for any
// other request this is an internal error
if resp != nil && resp.Auth != nil {
if !strings.HasPrefix(req.Path, "auth/token/") {
c.logger.Printf(
"[ERR] core: unexpected Auth response for non-token backend "+
"(request: %#v, response: %#v)", req, resp)
return nil, ErrInternalError
}
// Set the default lease if non-provided, root tokens are exempt
if resp.Auth.Lease == 0 && !strListContains(resp.Auth.Policies, "root") {
resp.Auth.Lease = defaultLeaseDuration
}
// Limit the lease duration
if resp.Auth.Lease > maxLeaseDuration {
resp.Auth.Lease = maxLeaseDuration
}
// Register with the expiration manager
if err := c.expiration.RegisterAuth(req.Path, resp.Auth); err != nil {
c.logger.Printf("[ERR] core: failed to register token lease "+
"(request: %#v, response: %#v): %v", req, resp, err)
return nil, ErrInternalError
}
}
// Create an audit trail of the response
if err := c.auditBroker.LogResponse(auth, req, resp, err); err != nil {
c.logger.Printf("[ERR] core: failed to audit response (request: %#v, response: %#v): %v",
req, resp, err)
return nil, ErrInternalError
}
// Return the response and error
return resp, err
}
// handleLoginRequest is used to handle a login request, which is an
// unauthenticated request to the backend.
func (c *Core) handleLoginRequest(req *logical.Request) (*logical.Response, error) {
defer metrics.MeasureSince([]string{"core", "handle_login_request"}, time.Now())
// Create an audit trail of the request, auth is not available on login requests
if err := c.auditBroker.LogRequest(nil, req); err != nil {
c.logger.Printf("[ERR] core: failed to audit request (%#v): %v",
req, err)
return nil, ErrInternalError
}
// Route the request
resp, err := c.router.Route(req)
// If the response generated an authentication, then generate the token
var auth *logical.Auth
if resp != nil && resp.Auth != nil {
auth = resp.Auth
// Determine the source of the login
source := c.router.MatchingMount(req.Path)
source = strings.TrimPrefix(source, credentialRoutePrefix)
source = strings.Replace(source, "/", "-", -1)
// Prepend the source to the display name
auth.DisplayName = strings.TrimSuffix(source+auth.DisplayName, "-")
// Generate a token
te := TokenEntry{
Path: req.Path,
Policies: auth.Policies,
Meta: auth.Metadata,
DisplayName: auth.DisplayName,
}
if err := c.tokenStore.Create(&te); err != nil {
c.logger.Printf("[ERR] core: failed to create token: %v", err)
return nil, ErrInternalError
}
// Populate the client token
resp.Auth.ClientToken = te.ID
// Set the default lease if non-provided, root tokens are exempt
if auth.Lease == 0 && !strListContains(auth.Policies, "root") {
auth.Lease = defaultLeaseDuration
}
// Limit the lease duration
if resp.Auth.Lease > maxLeaseDuration {
resp.Auth.Lease = maxLeaseDuration
}
// Register with the expiration manager
if err := c.expiration.RegisterAuth(req.Path, auth); err != nil {
c.logger.Printf("[ERR] core: failed to register token lease "+
"(request: %#v, response: %#v): %v", req, resp, err)
return nil, ErrInternalError
}
// Attach the display name, might be used by audit backends
req.DisplayName = auth.DisplayName
}
// Create an audit trail of the response
if err := c.auditBroker.LogResponse(auth, req, resp, err); err != nil {
c.logger.Printf("[ERR] core: failed to audit response (request: %#v, response: %#v): %v",
req, resp, err)
return nil, ErrInternalError
}
return resp, err
}
func (c *Core) checkToken(
op logical.Operation, path string, token string) (*logical.Auth, error) {
defer metrics.MeasureSince([]string{"core", "check_token"}, time.Now())
// Ensure there is a client token
if token == "" {
return nil, fmt.Errorf("missing client token")
}
// Resolve the token policy
te, err := c.tokenStore.Lookup(token)
if err != nil {
c.logger.Printf("[ERR] core: failed to lookup token: %v", err)
return nil, ErrInternalError
}
// Ensure the token is valid
if te == nil {
return nil, logical.ErrPermissionDenied
}
// Attempt to use the token
if err := c.tokenStore.UseToken(te); err != nil {
c.logger.Printf("[ERR] core: failed to use token: %v", err)
return nil, ErrInternalError
}
// Construct the corresponding ACL object
acl, err := c.policy.ACL(te.Policies...)
if err != nil {
c.logger.Printf("[ERR] core: failed to construct ACL: %v", err)
return nil, ErrInternalError
}
// Check if this is a root protected path
if c.router.RootPath(path) && !acl.RootPrivilege(path) {
return nil, logical.ErrPermissionDenied
}
// Check the standard non-root ACLs
if !acl.AllowOperation(op, path) {
return nil, logical.ErrPermissionDenied
}
// Create the auth response
auth := &logical.Auth{
ClientToken: token,
Policies: te.Policies,
Metadata: te.Meta,
DisplayName: te.DisplayName,
}
return auth, nil
}
// Initialized checks if the Vault is already initialized
func (c *Core) Initialized() (bool, error) {
// Check the barrier first
init, err := c.barrier.Initialized()
if err != nil {
c.logger.Printf("[ERR] core: barrier init check failed: %v", err)
return false, err
}
if !init {
return false, nil
}
if !init {
c.logger.Printf("[INFO] core: security barrier not initialized")
return false, nil
}
// Verify the seal configuration
sealConf, err := c.SealConfig()
if err != nil {
return false, err
}
if sealConf == nil {
return false, nil
}
return true, nil
}
// Initialize is used to initialize the Vault with the given
// configurations.
func (c *Core) Initialize(config *SealConfig) (*InitResult, error) {
// Check if the seal configuraiton is valid
if err := config.Validate(); err != nil {
c.logger.Printf("[ERR] core: invalid seal configuration: %v", err)
return nil, fmt.Errorf("invalid seal configuration: %v", err)
}
// Avoid an initialization race
c.stateLock.Lock()
defer c.stateLock.Unlock()
// Check if we are initialized
init, err := c.Initialized()
if err != nil {
return nil, err
}
if init {
return nil, ErrAlreadyInit
}
// Encode the seal configuration
buf, err := json.Marshal(config)
if err != nil {
return nil, fmt.Errorf("failed to encode seal configuration: %v", err)
}
// Store the seal configuration
pe := &physical.Entry{
Key: coreSealConfigPath,
Value: buf,
}
if err := c.physical.Put(pe); err != nil {
c.logger.Printf("[ERR] core: failed to read seal configuration: %v", err)
return nil, fmt.Errorf("failed to check seal configuration: %v", err)
}
// Generate a master key
masterKey, err := c.barrier.GenerateKey()
if err != nil {
c.logger.Printf("[ERR] core: failed to generate master key: %v", err)
return nil, fmt.Errorf("master key generation failed: %v", err)
}
// Initialize the barrier
if err := c.barrier.Initialize(masterKey); err != nil {
c.logger.Printf("[ERR] core: failed to initialize barrier: %v", err)
return nil, fmt.Errorf("failed to initialize barrier: %v", err)
}
// Return the master key if only a single key part is used
results := new(InitResult)
if config.SecretShares == 1 {
results.SecretShares = append(results.SecretShares, masterKey)
} else {
// Split the master key using the Shamir algorithm
shares, err := shamir.Split(masterKey, config.SecretShares, config.SecretThreshold)
if err != nil {
c.logger.Printf("[ERR] core: failed to generate shares: %v", err)
return nil, fmt.Errorf("failed to generate shares: %v", err)
}
results.SecretShares = shares
}
c.logger.Printf("[INFO] core: security barrier initialized")
// Unseal the barrier
if err := c.barrier.Unseal(masterKey); err != nil {
c.logger.Printf("[ERR] core: failed to unseal barrier: %v", err)
return nil, fmt.Errorf("failed to unseal barrier: %v", err)
}
// Ensure the barrier is re-sealed
defer func() {
if err := c.barrier.Seal(); err != nil {
c.logger.Printf("[ERR] core: failed to seal barrier: %v", err)
}
}()
// Perform initial setup
if err := c.postUnseal(); err != nil {
c.logger.Printf("[ERR] core: post-unseal setup failed: %v", err)
return nil, err
}
// Generate a new root token
rootToken, err := c.tokenStore.RootToken()
if err != nil {
c.logger.Printf("[ERR] core: root token generation failed: %v", err)
return nil, err
}
results.RootToken = rootToken.ID
c.logger.Printf("[INFO] core: root token generated")
// Prepare to re-seal
if err := c.preSeal(); err != nil {
c.logger.Printf("[ERR] core: pre-seal teardown failed: %v", err)
return nil, err
}
return results, nil
}
// Sealed checks if the Vault is current sealed
func (c *Core) Sealed() (bool, error) {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
return c.sealed, nil
}
// Standby checks if the Vault is in standby mode
func (c *Core) Standby() (bool, error) {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
return c.standby, nil
}
// Leader is used to get the current active leader
func (c *Core) Leader() (bool, string, error) {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
// Check if HA enabled
if c.ha == nil {
return false, "", ErrHANotEnabled
}
// Check if sealed
if c.sealed {
return false, "", ErrSealed
}
// Check if we are the leader
if !c.standby {
return true, c.advertiseAddr, nil
}
// Initialize a lock
lock, err := c.ha.LockWith(coreLockPath, "read")
if err != nil {
return false, "", err
}
// Read the value
held, value, err := lock.Value()
if err != nil {
return false, "", err
}
if !held {
return false, "", nil
}
// Value is the UUID of the leader, fetch the key
key := coreLeaderPrefix + value
entry, err := c.barrier.Get(key)
if err != nil {
return false, "", err
}
if entry == nil {
return false, "", nil
}
// Leader address is in the entry
return false, string(entry.Value), nil
}
// SealConfiguration is used to return information
// about the configuration of the Vault and it's current
// status.
func (c *Core) SealConfig() (*SealConfig, error) {
// Fetch the core configuration
pe, err := c.physical.Get(coreSealConfigPath)
if err != nil {
c.logger.Printf("[ERR] core: failed to read seal configuration: %v", err)
return nil, fmt.Errorf("failed to check seal configuration: %v", err)
}
// If the seal configuration is missing, we are not initialized
if pe == nil {
c.logger.Printf("[INFO] core: seal configuration missing, not initialized")
return nil, nil
}
// Decode the barrier entry
var conf SealConfig
if err := json.Unmarshal(pe.Value, &conf); err != nil {
c.logger.Printf("[ERR] core: failed to decode seal configuration: %v", err)
return nil, fmt.Errorf("failed to decode seal configuration: %v", err)
}
// Check for a valid seal configuration
if err := conf.Validate(); err != nil {
c.logger.Printf("[ERR] core: invalid seal configuration: %v", err)
return nil, fmt.Errorf("seal validation failed: %v", err)
}
return &conf, nil
}
// SecretProgress returns the number of keys provided so far
func (c *Core) SecretProgress() int {
c.stateLock.RLock()
defer c.stateLock.RUnlock()
return len(c.unlockParts)
}
// Unseal is used to provide one of the key parts to unseal the Vault.
//
// They key given as a parameter will automatically be zerod after
// this method is done with it. If you want to keep the key around, a copy
// should be made.
func (c *Core) Unseal(key []byte) (bool, error) {
defer metrics.MeasureSince([]string{"core", "unseal"}, time.Now())
// Verify the key length
min, max := c.barrier.KeyLength()
max += shamir.ShareOverhead
if len(key) < min {
return false, &ErrInvalidKey{fmt.Sprintf("key is shorter than minimum %d bytes", min)}
}
if len(key) > max {
return false, &ErrInvalidKey{fmt.Sprintf("key is longer than maximum %d bytes", max)}
}
// Get the seal configuration
config, err := c.SealConfig()
if err != nil {
return false, err
}
// Ensure the barrier is initialized
if config == nil {
return false, ErrNotInit
}
c.stateLock.Lock()
defer c.stateLock.Unlock()
// Check if already unsealed
if !c.sealed {
return true, nil
}
// Check if we already have this piece
for _, existing := range c.unlockParts {
if bytes.Equal(existing, key) {
return false, nil
}
}
// Store this key
c.unlockParts = append(c.unlockParts, key)
// Check if we don't have enough keys to unlock
if len(c.unlockParts) < config.SecretThreshold {
c.logger.Printf("[DEBUG] core: cannot unseal, have %d of %d keys",
len(c.unlockParts), config.SecretThreshold)
return false, nil
}
// Recover the master key
var masterKey []byte
if config.SecretThreshold == 1 {
masterKey = c.unlockParts[0]
c.unlockParts = nil
} else {
masterKey, err = shamir.Combine(c.unlockParts)
c.unlockParts = nil
if err != nil {
return false, fmt.Errorf("failed to compute master key: %v", err)
}
}
defer memzero(masterKey)
// Attempt to unlock
if err := c.barrier.Unseal(masterKey); err != nil {
return false, err
}
c.logger.Printf("[INFO] core: vault is unsealed")
// Do post-unseal setup if HA is not enabled
if c.ha == nil {
c.standby = false
if err := c.postUnseal(); err != nil {
c.logger.Printf("[ERR] core: post-unseal setup failed: %v", err)
c.barrier.Seal()
c.logger.Printf("[WARN] core: vault is sealed")
return false, err
}
} else {
// Go to standby mode, wait until we are active to unseal
c.standbyDoneCh = make(chan struct{})
c.standbyStopCh = make(chan struct{})
go c.runStandby(c.standbyDoneCh, c.standbyStopCh)
}
// Success!
c.sealed = false
return true, nil
}
// Seal is used to re-seal the Vault. This requires the Vault to
// be unsealed again to perform any further operations.
func (c *Core) Seal(token string) error {
defer metrics.MeasureSince([]string{"core", "seal"}, time.Now())
c.stateLock.Lock()
defer c.stateLock.Unlock()
if c.sealed {
return nil
}
// Validate the token is a root token
_, err := c.checkToken(logical.WriteOperation, "sys/seal", token)
if err != nil {
return err
}
// Enable that we are sealed to prevent furthur transactions
c.sealed = true
// Do pre-seal teardown if HA is not enabled
if c.ha == nil {
if err := c.preSeal(); err != nil {
c.logger.Printf("[ERR] core: pre-seal teardown failed: %v", err)
return fmt.Errorf("internal error")
}
} else {
// Signal the standby goroutine to shutdown, wait for completion
close(c.standbyStopCh)
// Release the lock while we wait to avoid deadlocking
c.stateLock.Unlock()
<-c.standbyDoneCh
c.stateLock.Lock()
}
if err := c.barrier.Seal(); err != nil {
return err
}
c.logger.Printf("[INFO] core: vault is sealed")
return nil
}
// postUnseal is invoked after the barrier is unsealed, but before
// allowing any user operations. This allows us to setup any state that
// requires the Vault to be unsealed such as mount tables, logical backends,
// credential stores, etc.
func (c *Core) postUnseal() error {
defer metrics.MeasureSince([]string{"core", "post_unseal"}, time.Now())
c.logger.Printf("[INFO] core: post-unseal setup starting")
if cache, ok := c.physical.(*physical.Cache); ok {
cache.Purge()
}
if err := c.loadMounts(); err != nil {
return err
}
if err := c.setupMounts(); err != nil {
return err
}
if err := c.startRollback(); err != nil {
return err
}
if err := c.setupPolicyStore(); err != nil {
return nil
}
if err := c.loadCredentials(); err != nil {
return nil
}
if err := c.setupCredentials(); err != nil {
return nil
}
if err := c.setupExpiration(); err != nil {
return err
}
if err := c.loadAudits(); err != nil {
return err
}
if err := c.setupAudits(); err != nil {
return err
}
c.metricsCh = make(chan struct{})
go c.emitMetrics(c.metricsCh)
c.logger.Printf("[INFO] core: post-unseal setup complete")
return nil
}
// preSeal is invoked before the barrier is sealed, allowing
// for any state teardown required.
func (c *Core) preSeal() error {
defer metrics.MeasureSince([]string{"core", "pre_seal"}, time.Now())
c.logger.Printf("[INFO] core: pre-seal teardown starting")
if c.metricsCh != nil {
close(c.metricsCh)
c.metricsCh = nil
}
if err := c.teardownAudits(); err != nil {
return err
}
if err := c.stopExpiration(); err != nil {
return err
}
if err := c.teardownCredentials(); err != nil {
return err
}
if err := c.teardownPolicyStore(); err != nil {
return err
}
if err := c.stopRollback(); err != nil {
return err
}
if err := c.unloadMounts(); err != nil {
return err
}
if cache, ok := c.physical.(*physical.Cache); ok {
cache.Purge()
}
c.logger.Printf("[INFO] core: pre-seal teardown complete")
return nil
}
// runStandby is a long running routine that is used when an HA backend
// is enabled. It waits until we are leader and switches this Vault to
// active.
func (c *Core) runStandby(doneCh, stopCh chan struct{}) {
defer close(doneCh)
c.logger.Printf("[INFO] core: entering standby mode")
for {
// Check for a shutdown
select {