@@ -107,6 +107,9 @@ func pathConfigCA(b *backend) *framework.Path {
107
107
OperationSuffix : "ca-configuration" ,
108
108
},
109
109
},
110
+ logical .RecoverOperation : & framework.PathOperation {
111
+ Callback : b .pathConfigCARecover ,
112
+ },
110
113
},
111
114
112
115
HelpSynopsis : `Set the SSH private key used for signing certificates.` ,
@@ -120,7 +123,9 @@ Read operations will return the public key, if already stored/generated.`,
120
123
}
121
124
122
125
func (b * backend ) pathConfigCARead (ctx context.Context , req * logical.Request , data * framework.FieldData ) (* logical.Response , error ) {
123
- publicKey , err := getCAPublicKey (ctx , req .Storage )
126
+ // prevent migration from deprecated paths on snapshot read as writes to a loaded snapshot storage are forbidden
127
+ allowMigration := ! req .IsSnapshotReadOrList ()
128
+ publicKey , err := getCAPublicKey (ctx , req .Storage , allowMigration )
124
129
if err != nil {
125
130
return nil , fmt .Errorf ("failed to read CA public key: %w" , err )
126
131
}
@@ -142,16 +147,22 @@ func (b *backend) pathConfigCADelete(ctx context.Context, req *logical.Request,
142
147
if err := req .Storage .Delete (ctx , caPrivateKeyStoragePath ); err != nil {
143
148
return nil , err
144
149
}
150
+ if err := req .Storage .Delete (ctx , caPrivateKeyStoragePathDeprecated ); err != nil {
151
+ return nil , err
152
+ }
145
153
if err := req .Storage .Delete (ctx , caPublicKeyStoragePath ); err != nil {
146
154
return nil , err
147
155
}
156
+ if err := req .Storage .Delete (ctx , caPublicKeyStoragePathDeprecated ); err != nil {
157
+ return nil , err
158
+ }
148
159
if err := req .Storage .Delete (ctx , caManagedKeyStoragePath ); err != nil {
149
160
return nil , err
150
161
}
151
162
return nil , nil
152
163
}
153
164
154
- func readStoredKey (ctx context.Context , storage logical.Storage , keyType string ) (* keyStorageEntry , error ) {
165
+ func readStoredKeyEntry (ctx context.Context , storage logical.Storage , keyType string , allowMigration bool ) (* logical. StorageEntry , error ) {
155
166
var path , deprecatedPath string
156
167
switch keyType {
157
168
case caPrivateKey :
@@ -176,25 +187,39 @@ func readStoredKey(ctx context.Context, storage logical.Storage, keyType string)
176
187
if err != nil {
177
188
return nil , err
178
189
}
190
+
179
191
if entry != nil {
192
+ // modify entry variable, both for possible migration and also to comply with the expected JSON entry for the caller
180
193
entry , err = logical .StorageEntryJSON (path , keyStorageEntry {
181
194
Key : string (entry .Value ),
182
195
})
183
196
if err != nil {
184
197
return nil , err
185
198
}
186
- if err := storage .Put (ctx , entry ); err != nil {
187
- return nil , err
188
- }
189
- if err = storage .Delete (ctx , deprecatedPath ); err != nil {
190
- return nil , err
199
+ // migrations are disable on recover, as we can't write to the loaded snapshot storage
200
+ if allowMigration {
201
+ if err := storage .Put (ctx , entry ); err != nil {
202
+ return nil , err
203
+ }
204
+ if err = storage .Delete (ctx , deprecatedPath ); err != nil {
205
+ return nil , err
206
+ }
191
207
}
192
208
}
193
209
}
210
+ return entry , nil
211
+ }
212
+
213
+ // readStoredKey reads a key from storage, returning nil if not found.
214
+ // ignore-nil-nil-function-check
215
+ func readStoredKey (ctx context.Context , storage logical.Storage , keyType string , allowMigration bool ) (* keyStorageEntry , error ) {
216
+ entry , err := readStoredKeyEntry (ctx , storage , keyType , allowMigration )
217
+ if err != nil {
218
+ return nil , err
219
+ }
194
220
if entry == nil {
195
221
return nil , nil
196
222
}
197
-
198
223
var keyEntry keyStorageEntry
199
224
if err := entry .DecodeJSON (& keyEntry ); err != nil {
200
225
return nil , err
@@ -450,10 +475,10 @@ func (b *backend) createManagedKey(ctx context.Context, s logical.Storage, manag
450
475
return nil
451
476
}
452
477
453
- func getCAPublicKey (ctx context.Context , storage logical.Storage ) (string , error ) {
478
+ func getCAPublicKey (ctx context.Context , storage logical.Storage , allowMigration bool ) (string , error ) {
454
479
var publicKey string
455
480
456
- storedKeyEntry , err := readStoredKey (ctx , storage , caPublicKey )
481
+ storedKeyEntry , err := readStoredKey (ctx , storage , caPublicKey , allowMigration )
457
482
if err != nil {
458
483
return "" , err
459
484
}
@@ -495,12 +520,13 @@ func readManagedKey(ctx context.Context, storage logical.Storage) (*managedKeySt
495
520
}
496
521
497
522
func caKeysConfigured (ctx context.Context , s logical.Storage ) (bool , error ) {
498
- publicKeyEntry , err := readStoredKey (ctx , s , caPublicKey )
523
+ const allowMigration = false // no need to allow migration when just checking for existence, we can do that later
524
+ publicKeyEntry , err := readStoredKey (ctx , s , caPublicKey , allowMigration )
499
525
if err != nil {
500
526
return false , fmt .Errorf ("failed to read CA public key: %w" , err )
501
527
}
502
528
503
- privateKeyEntry , err := readStoredKey (ctx , s , caPrivateKey )
529
+ privateKeyEntry , err := readStoredKey (ctx , s , caPrivateKey , allowMigration )
504
530
if err != nil {
505
531
return false , fmt .Errorf ("failed to read CA private key: %w" , err )
506
532
}
@@ -520,3 +546,64 @@ func caKeysConfigured(ctx context.Context, s logical.Storage) (bool, error) {
520
546
521
547
return false , nil
522
548
}
549
+
550
+ // pathConfigCARecover recovers the CA from the target snapshot back to the live storage.
551
+ // ignore-nil-nil-function-check
552
+ func (b * backend ) pathConfigCARecover (ctx context.Context , req * logical.Request , data * framework.FieldData ) (* logical.Response , error ) {
553
+ // check live storage for existing keys. Disallow recovery if CA is already configured for consistency with create operation
554
+ found , err := caKeysConfigured (ctx , req .Storage )
555
+ if err != nil {
556
+ return nil , err
557
+ }
558
+ if found {
559
+ return logical .ErrorResponse ("keys are already configured; delete them before recovering the CA" ), nil
560
+ }
561
+
562
+ // fetch directly from the snapshot storage instead of following the usual restore procedure of getting the values
563
+ // from the req.Data, since those came from a previous CARead operation on the loaded snapshot, which only contains
564
+ // the public key.
565
+ snapshotStorage , err := logical .NewSnapshotStorageView (req )
566
+ if err != nil {
567
+ return nil , err
568
+ }
569
+ const allowMigration = false // prevent migration from deprecated paths as we can't allow writes on the snapshot storage
570
+ publicKeyEntry , err := readStoredKeyEntry (ctx , snapshotStorage , caPublicKey , allowMigration )
571
+ if err != nil {
572
+ return nil , fmt .Errorf ("failed to read CA public key for restore: %w" , err )
573
+ }
574
+ privateKeyEntry , err := readStoredKeyEntry (ctx , snapshotStorage , caPrivateKey , allowMigration )
575
+ if err != nil {
576
+ return nil , fmt .Errorf ("failed to read CA private key for restore: %w" , err )
577
+ }
578
+ managedKey , err := readManagedKey (ctx , snapshotStorage )
579
+ if err != nil {
580
+ return nil , fmt .Errorf ("failed to read CA managed key for restore: %w" , err )
581
+ }
582
+
583
+ if publicKeyEntry == nil && privateKeyEntry == nil && managedKey == nil {
584
+ return logical .ErrorResponse ("no CA keys found in snapshot storage to restore" ), nil
585
+ }
586
+
587
+ // it's possible that we've read the keys from a deprecated path in the snapshot, but it should be automatically
588
+ // upgraded to the new path anyway, so we don't care about restoring it back to the deprecated path
589
+ if publicKeyEntry != nil {
590
+ err = req .Storage .Put (ctx , publicKeyEntry )
591
+ if err != nil {
592
+ return nil , fmt .Errorf ("failed to restore public key entry in storage: %w" , err )
593
+ }
594
+ }
595
+ if privateKeyEntry != nil {
596
+ err = req .Storage .Put (ctx , privateKeyEntry )
597
+ if err != nil {
598
+ return nil , fmt .Errorf ("failed to restore private key entry in storage: %w" , err )
599
+ }
600
+ }
601
+ if managedKey != nil {
602
+ err = b .createManagedKey (ctx , req .Storage , managedKey .KeyName .String (), managedKey .KeyId .String ())
603
+ if err != nil {
604
+ return nil , fmt .Errorf ("failed to restore managed key entry in storage: %w" , err )
605
+ }
606
+ }
607
+
608
+ return nil , nil
609
+ }
0 commit comments