@@ -10,7 +10,9 @@ import (
1010 "strings"
1111 "testing"
1212
13+ "github.com/hashicorp/vault/sdk/helper/testhelpers/snapshots"
1314 "github.com/hashicorp/vault/sdk/logical"
15+ "github.com/stretchr/testify/require"
1416)
1517
1618func TestSSH_ConfigCAStorageUpgrade (t * testing.T ) {
@@ -39,7 +41,7 @@ func TestSSH_ConfigCAStorageUpgrade(t *testing.T) {
3941 }
4042
4143 // Reading it should return the key as well as upgrade the storage path
42- privateKeyEntry , err := readStoredKey (context .Background (), config .StorageView , caPrivateKey )
44+ privateKeyEntry , err := readStoredKey (context .Background (), config .StorageView , caPrivateKey , true )
4345 if err != nil {
4446 t .Fatal (err )
4547 }
@@ -73,7 +75,7 @@ func TestSSH_ConfigCAStorageUpgrade(t *testing.T) {
7375 }
7476
7577 // Reading it should return the key as well as upgrade the storage path
76- publicKeyEntry , err := readStoredKey (context .Background (), config .StorageView , caPublicKey )
78+ publicKeyEntry , err := readStoredKey (context .Background (), config .StorageView , caPublicKey , true )
7779 if err != nil {
7880 t .Fatal (err )
7981 }
@@ -180,6 +182,28 @@ func TestSSH_ConfigCAUpdateDelete(t *testing.T) {
180182 if err != nil || (resp != nil && resp .IsError ()) {
181183 t .Fatalf ("bad: err: %v, resp:%v" , err , resp )
182184 }
185+
186+ // verify deletion of keys on deprecated path
187+ err = config .StorageView .Put (context .Background (), & logical.StorageEntry {
188+ Key : caPublicKeyStoragePathDeprecated ,
189+ Value : []byte (testCAPublicKey ),
190+ })
191+ require .NoError (t , err )
192+ err = config .StorageView .Put (context .Background (), & logical.StorageEntry {
193+ Key : caPrivateKeyStoragePathDeprecated ,
194+ Value : []byte (testCAPrivateKey ),
195+ })
196+ require .NoError (t , err )
197+ caReq .Operation = logical .DeleteOperation
198+ resp , err = b .HandleRequest (context .Background (), caReq )
199+ if err != nil || (resp != nil && resp .IsError ()) {
200+ t .Fatalf ("bad: err: %v, resp:%v" , err , resp )
201+ }
202+ // ensure it was deleted
203+ caReq .Operation = logical .ReadOperation
204+ resp , err = b .HandleRequest (context .Background (), caReq )
205+ require .NoError (t , err )
206+ require .Error (t , resp .Error ())
183207}
184208
185209func createDeleteHelper (t * testing.T , b logical.Backend , config * logical.BackendConfig , index int , keyType string , keyBits int ) {
@@ -340,7 +364,7 @@ func TestReadStoredKey(t *testing.T) {
340364 t .Fatalf ("error writing public key: %s" , err )
341365 }
342366
343- publicKeyEntry , err := readStoredKey (context .Background (), storage , caPublicKey )
367+ publicKeyEntry , err := readStoredKey (context .Background (), storage , caPublicKey , true )
344368 if err != nil {
345369 t .Fatalf ("error reading public key: %s" , err )
346370 }
@@ -349,7 +373,7 @@ func TestReadStoredKey(t *testing.T) {
349373 t .Fatalf ("returned key does not match: expected %s, got %s" , tt .publicKey , publicKeyEntry .Key )
350374 }
351375
352- privateKeyEntry , err := readStoredKey (context .Background (), storage , caPrivateKey )
376+ privateKeyEntry , err := readStoredKey (context .Background (), storage , caPrivateKey , true )
353377 if err != nil {
354378 t .Fatalf ("error reading private key: %s" , err )
355379 }
@@ -387,7 +411,7 @@ func TestGetCAPublicKey(t *testing.T) {
387411 t .Fatalf ("error writing key: %s" , err )
388412 }
389413
390- key , err := getCAPublicKey (ctx , storage )
414+ key , err := getCAPublicKey (ctx , storage , true )
391415 if err != nil {
392416 t .Fatalf ("error retrieving public key: %s" , err )
393417 }
@@ -586,3 +610,106 @@ func readKey(ctx context.Context, s logical.Storage, path string) error {
586610
587611 return nil
588612}
613+
614+ // TestCARecover verifies secret recovery of the SSH CA
615+ func TestCARecover (t * testing.T ) {
616+ var err error
617+ config := logical .TestBackendConfig ()
618+ config .StorageView = & logical.InmemStorage {}
619+
620+ b , err := Factory (context .Background (), config )
621+ if err != nil {
622+ t .Fatalf ("Cannot create backend: %s" , err )
623+ }
624+ tc := snapshots .NewSnapshotTestCase (t , b )
625+
626+ // generate CA keys on the snapshot storage
627+ _ , err = b .HandleRequest (context .Background (), & logical.Request {
628+ Operation : logical .UpdateOperation ,
629+ Path : "config/ca" ,
630+ Storage : tc .SnapshotStorage (),
631+ Data : map [string ]interface {}{
632+ "public_key" : testCAPublicKey ,
633+ "private_key" : testCAPrivateKey ,
634+ },
635+ })
636+ require .NoError (t , err )
637+
638+ // write different CA to the regular storage
639+ _ , err = b .HandleRequest (context .Background (), & logical.Request {
640+ Operation : logical .UpdateOperation ,
641+ Path : "config/ca" ,
642+ Storage : tc .RegularStorage (),
643+ Data : map [string ]interface {}{
644+ "generate_signing_key" : true ,
645+ },
646+ })
647+ require .NoError (t , err )
648+ t .Run ("read no side effects" , func (t * testing.T ) {
649+ tc .RunRead (t , "config/ca" )
650+ })
651+
652+ t .Run ("recover succeeds" , func (t * testing.T ) {
653+ tc .DoRecover (t , "config/ca" )
654+ data , err := b .HandleRequest (context .Background (), & logical.Request {
655+ Operation : logical .ReadOperation ,
656+ Path : "config/ca" ,
657+ Storage : tc .SnapshotStorage (),
658+ Data : map [string ]interface {}{
659+ "public_key" : "should be the actual public key but the SSH CA recovery doesn't really care as it reads directly from snapshot storage" ,
660+ },
661+ })
662+ require .NoError (t , err )
663+ require .NotNil (t , data )
664+ require .Equal (t , testCAPublicKey , data .Data ["public_key" ])
665+ })
666+ }
667+
668+ // TestCARecoverMigration is the same as TestCARecover, but recovering from a snapshot with the data in the deprecated
669+ // storage paths, which ensures that the migration logic is skipped during recovery.
670+ func TestCARecoverMigration (t * testing.T ) {
671+ var err error
672+ config := logical .TestBackendConfig ()
673+ config .StorageView = & logical.InmemStorage {}
674+
675+ b , err := Factory (context .Background (), config )
676+ if err != nil {
677+ t .Fatalf ("Cannot create backend: %s" , err )
678+ }
679+ tc := snapshots .NewSnapshotTestCase (t , b )
680+ err = tc .SnapshotStorage ().Put (context .Background (), & logical.StorageEntry {
681+ Key : caPublicKeyStoragePathDeprecated ,
682+ Value : []byte (testCAPublicKey ),
683+ })
684+ require .NoError (t , err )
685+ err = tc .SnapshotStorage ().Put (context .Background (), & logical.StorageEntry {
686+ Key : caPrivateKeyStoragePathDeprecated ,
687+ Value : []byte (testCAPrivateKey ),
688+ })
689+ require .NoError (t , err )
690+
691+ require .NoError (t , err )
692+ t .Run ("read no side effects" , func (t * testing.T ) {
693+ tc .RunRead (t , "config/ca" )
694+ })
695+
696+ t .Run ("recover succeeds" , func (t * testing.T ) {
697+ tc .DoRecover (t , "config/ca" )
698+ // ensure that even though the migration on read is disabled, by reading from the old path in the snapshot and
699+ // creating a entry in the regular storage, that the entry ends up in the new path
700+ entry , err := tc .RegularStorage ().Get (context .Background (), caPublicKeyStoragePathDeprecated )
701+ require .NoError (t , err )
702+ require .Nil (t , entry )
703+ data , err := b .HandleRequest (context .Background (), & logical.Request {
704+ Operation : logical .ReadOperation ,
705+ Path : "config/ca" ,
706+ Storage : tc .SnapshotStorage (),
707+ Data : map [string ]interface {}{
708+ "public_key" : "should be the actual public key but the SSH CA recovery doesn't really care as it reads directly from snapshot storage" ,
709+ },
710+ })
711+ require .NoError (t , err )
712+ require .NotNil (t , data )
713+ require .Equal (t , testCAPublicKey , data .Data ["public_key" ])
714+ })
715+ }
0 commit comments