1// +build !enterprise 2 3package vault 4 5import ( 6 "bytes" 7 "context" 8 "sync" 9 "testing" 10 11 proto "github.com/golang/protobuf/proto" 12 log "github.com/hashicorp/go-hclog" 13 wrapping "github.com/hashicorp/go-kms-wrapping" 14 "github.com/hashicorp/vault/sdk/physical" 15 "github.com/hashicorp/vault/sdk/physical/inmem" 16) 17 18func TestSealUnwrapper(t *testing.T) { 19 logger := log.New(&log.LoggerOptions{ 20 Mutex: &sync.Mutex{}, 21 }) 22 23 // Test without transactions 24 phys, err := inmem.NewInmemHA(nil, logger) 25 if err != nil { 26 t.Fatal(err) 27 } 28 performTestSealUnwrapper(t, phys, logger) 29 30 // Test with transactions 31 tPhys, err := inmem.NewTransactionalInmemHA(nil, logger) 32 if err != nil { 33 t.Fatal(err) 34 } 35 performTestSealUnwrapper(t, tPhys, logger) 36} 37 38func performTestSealUnwrapper(t *testing.T, phys physical.Backend, logger log.Logger) { 39 ctx := context.Background() 40 base := &CoreConfig{ 41 Physical: phys, 42 } 43 cluster := NewTestCluster(t, base, &TestClusterOptions{ 44 Logger: logger, 45 }) 46 cluster.Start() 47 defer cluster.Cleanup() 48 49 // Read a value and then save it back in a proto message 50 entry, err := phys.Get(ctx, "core/master") 51 if err != nil { 52 t.Fatal(err) 53 } 54 if len(entry.Value) == 0 { 55 t.Fatal("got no value for master") 56 } 57 // Save the original for comparison later 58 origBytes := make([]byte, len(entry.Value)) 59 copy(origBytes, entry.Value) 60 se := &wrapping.EncryptedBlobInfo{ 61 Ciphertext: entry.Value, 62 } 63 seb, err := proto.Marshal(se) 64 if err != nil { 65 t.Fatal(err) 66 } 67 // Write the canary 68 entry.Value = append(seb, 's') 69 // Save the protobuf value for comparison later 70 pBytes := make([]byte, len(entry.Value)) 71 copy(pBytes, entry.Value) 72 if err = phys.Put(ctx, entry); err != nil { 73 t.Fatal(err) 74 } 75 76 // At this point we should be able to read through the standby cores, 77 // successfully decode it, but be able to unmarshal it when read back from 78 // the underlying physical store. When we read from active, it should both 79 // successfully decode it and persist it back. 80 checkValue := func(core *Core, wrapped bool) { 81 entry, err := core.physical.Get(ctx, "core/master") 82 if err != nil { 83 t.Fatal(err) 84 } 85 if !bytes.Equal(entry.Value, origBytes) { 86 t.Fatalf("mismatched original bytes and unwrapped entry bytes:\ngot:\n%v\nexpected:\n%v", entry.Value, origBytes) 87 } 88 underlyingEntry, err := phys.Get(ctx, "core/master") 89 if err != nil { 90 t.Fatal(err) 91 } 92 switch wrapped { 93 case true: 94 if !bytes.Equal(underlyingEntry.Value, pBytes) { 95 t.Fatalf("mismatched original bytes and proto entry bytes:\ngot:\n%v\nexpected:\n%v", underlyingEntry.Value, pBytes) 96 } 97 default: 98 if !bytes.Equal(underlyingEntry.Value, origBytes) { 99 t.Fatalf("mismatched original bytes and unwrapped entry bytes:\ngot:\n%v\nexpected:\n%v", underlyingEntry.Value, origBytes) 100 } 101 } 102 } 103 104 TestWaitActive(t, cluster.Cores[0].Core) 105 checkValue(cluster.Cores[2].Core, true) 106 checkValue(cluster.Cores[1].Core, true) 107 checkValue(cluster.Cores[0].Core, false) 108} 109