1package misc
2
3import (
4	"path"
5	"testing"
6
7	"github.com/go-test/deep"
8	"github.com/hashicorp/go-hclog"
9	"github.com/hashicorp/vault/helper/testhelpers"
10	"github.com/hashicorp/vault/http"
11	"github.com/hashicorp/vault/sdk/helper/logging"
12	"github.com/hashicorp/vault/sdk/physical/inmem"
13	"github.com/hashicorp/vault/vault"
14	"go.uber.org/atomic"
15)
16
17func TestRecovery(t *testing.T) {
18	logger := logging.NewVaultLogger(hclog.Debug).Named(t.Name())
19	inm, err := inmem.NewInmemHA(nil, logger)
20	if err != nil {
21		t.Fatal(err)
22	}
23
24	var keys [][]byte
25	var secretUUID string
26	var rootToken string
27	{
28		conf := vault.CoreConfig{
29			Physical: inm,
30			Logger:   logger,
31		}
32		opts := vault.TestClusterOptions{
33			HandlerFunc: http.Handler,
34			NumCores:    1,
35		}
36
37		cluster := vault.NewTestCluster(t, &conf, &opts)
38		cluster.Start()
39		defer cluster.Cleanup()
40
41		client := cluster.Cores[0].Client
42		rootToken = client.Token()
43		var fooVal = map[string]interface{}{"bar": 1.0}
44		_, err = client.Logical().Write("secret/foo", fooVal)
45		if err != nil {
46			t.Fatal(err)
47		}
48		secret, err := client.Logical().List("secret/")
49		if err != nil {
50			t.Fatal(err)
51		}
52		if diff := deep.Equal(secret.Data["keys"], []interface{}{"foo"}); len(diff) > 0 {
53			t.Fatalf("got=%v, want=%v, diff: %v", secret.Data["keys"], []string{"foo"}, diff)
54		}
55		mounts, err := cluster.Cores[0].Client.Sys().ListMounts()
56		if err != nil {
57			t.Fatal(err)
58		}
59		secretMount := mounts["secret/"]
60		if secretMount == nil {
61			t.Fatalf("secret mount not found, mounts: %v", mounts)
62		}
63		secretUUID = secretMount.UUID
64		cluster.EnsureCoresSealed(t)
65		keys = cluster.BarrierKeys
66	}
67
68	{
69		// Now bring it up in recovery mode.
70		var tokenRef atomic.String
71		conf := vault.CoreConfig{
72			Physical:     inm,
73			Logger:       logger,
74			RecoveryMode: true,
75		}
76		opts := vault.TestClusterOptions{
77			HandlerFunc: http.Handler,
78			NumCores:    1,
79			SkipInit:    true,
80			DefaultHandlerProperties: vault.HandlerProperties{
81				RecoveryMode:  true,
82				RecoveryToken: &tokenRef,
83			},
84		}
85		cluster := vault.NewTestCluster(t, &conf, &opts)
86		cluster.BarrierKeys = keys
87		cluster.Start()
88		defer cluster.Cleanup()
89
90		client := cluster.Cores[0].Client
91		recoveryToken := testhelpers.GenerateRoot(t, cluster, testhelpers.GenerateRecovery)
92		_, err = testhelpers.GenerateRootWithError(t, cluster, testhelpers.GenerateRecovery)
93		if err == nil {
94			t.Fatal("expected second generate-root to fail")
95		}
96		client.SetToken(recoveryToken)
97
98		secret, err := client.Logical().List(path.Join("sys/raw/logical", secretUUID))
99		if err != nil {
100			t.Fatal(err)
101		}
102		if diff := deep.Equal(secret.Data["keys"], []interface{}{"foo"}); len(diff) > 0 {
103			t.Fatalf("got=%v, want=%v, diff: %v", secret.Data, []string{"foo"}, diff)
104		}
105
106		_, err = client.Logical().Delete(path.Join("sys/raw/logical", secretUUID, "foo"))
107		if err != nil {
108			t.Fatal(err)
109		}
110		cluster.EnsureCoresSealed(t)
111	}
112
113	{
114		// Now go back to regular mode and verify that our changes are present
115		conf := vault.CoreConfig{
116			Physical: inm,
117			Logger:   logger,
118		}
119		opts := vault.TestClusterOptions{
120			HandlerFunc: http.Handler,
121			NumCores:    1,
122			SkipInit:    true,
123		}
124		cluster := vault.NewTestCluster(t, &conf, &opts)
125		cluster.BarrierKeys = keys
126		cluster.Start()
127		testhelpers.EnsureCoresUnsealed(t, cluster)
128		defer cluster.Cleanup()
129
130		client := cluster.Cores[0].Client
131		client.SetToken(rootToken)
132		secret, err := client.Logical().List("secret/")
133		if err != nil {
134			t.Fatal(err)
135		}
136		if secret != nil {
137			t.Fatal("expected no data in secret mount")
138		}
139	}
140}
141