1package command
2
3import (
4	"context"
5	"encoding/base64"
6	"path"
7	"testing"
8
9	"github.com/hashicorp/go-hclog"
10	wrapping "github.com/hashicorp/go-kms-wrapping"
11	"github.com/hashicorp/vault/api"
12	"github.com/hashicorp/vault/builtin/logical/transit"
13	commandseal "github.com/hashicorp/vault/command/server/seal"
14	"github.com/hashicorp/vault/helper/testhelpers"
15	"github.com/hashicorp/vault/helper/testhelpers/teststorage"
16	vaulthttp "github.com/hashicorp/vault/http"
17	"github.com/hashicorp/vault/sdk/helper/logging"
18	"github.com/hashicorp/vault/sdk/logical"
19	"github.com/hashicorp/vault/vault"
20	"github.com/hashicorp/vault/vault/seal"
21)
22
23type transitSealServer struct {
24	*vault.TestCluster
25}
26
27func newTransitSealServer(t *testing.T) *transitSealServer {
28	conf := &vault.CoreConfig{
29		LogicalBackends: map[string]logical.Factory{
30			"transit": transit.Factory,
31		},
32	}
33	opts := &vault.TestClusterOptions{
34		NumCores:    1,
35		HandlerFunc: vaulthttp.Handler,
36		Logger:      logging.NewVaultLogger(hclog.Trace).Named(t.Name()).Named("transit"),
37	}
38	teststorage.InmemBackendSetup(conf, opts)
39	cluster := vault.NewTestCluster(t, conf, opts)
40	cluster.Start()
41
42	if err := cluster.Cores[0].Client.Sys().Mount("transit", &api.MountInput{
43		Type: "transit",
44	}); err != nil {
45		t.Fatal(err)
46	}
47
48	return &transitSealServer{cluster}
49}
50
51func (tss *transitSealServer) makeKey(t *testing.T, key string) {
52	client := tss.Cores[0].Client
53	// Create default aesgcm key
54	if _, err := client.Logical().Write(path.Join("transit", "keys", key), nil); err != nil {
55		t.Fatal(err)
56	}
57	if _, err := client.Logical().Write(path.Join("transit", "keys", key, "config"), map[string]interface{}{
58		"deletion_allowed": true,
59	}); err != nil {
60		t.Fatal(err)
61	}
62}
63
64func (tss *transitSealServer) makeSeal(t *testing.T, key string) vault.Seal {
65	client := tss.Cores[0].Client
66	wrapperConfig := map[string]string{
67		"address":     client.Address(),
68		"token":       client.Token(),
69		"mount_path":  "transit",
70		"key_name":    key,
71		"tls_ca_cert": tss.CACertPEMFile,
72	}
73	transitSeal, _, err := commandseal.GetTransitKMSFunc(nil, wrapperConfig)
74	if err != nil {
75		t.Fatalf("error setting wrapper config: %v", err)
76	}
77
78	return vault.NewAutoSeal(&seal.Access{
79		Wrapper: transitSeal,
80	})
81}
82
83func verifyBarrierConfig(t *testing.T, cfg *vault.SealConfig, sealType string, shares, threshold, stored int) {
84	t.Helper()
85	if cfg.Type != sealType {
86		t.Fatalf("bad seal config: %#v, expected type=%q", cfg, sealType)
87	}
88	if cfg.SecretShares != shares {
89		t.Fatalf("bad seal config: %#v, expected SecretShares=%d", cfg, shares)
90	}
91	if cfg.SecretThreshold != threshold {
92		t.Fatalf("bad seal config: %#v, expected SecretThreshold=%d", cfg, threshold)
93	}
94	if cfg.StoredShares != stored {
95		t.Fatalf("bad seal config: %#v, expected StoredShares=%d", cfg, stored)
96	}
97}
98
99func TestSealMigration_ShamirToAuto(t *testing.T) {
100	t.Parallel()
101	t.Run("inmem", func(t *testing.T) {
102		t.Parallel()
103		testSealMigrationShamirToAuto(t, teststorage.InmemBackendSetup)
104	})
105
106	t.Run("file", func(t *testing.T) {
107		t.Parallel()
108		testSealMigrationShamirToAuto(t, teststorage.FileBackendSetup)
109	})
110
111	t.Run("consul", func(t *testing.T) {
112		t.Parallel()
113		testSealMigrationShamirToAuto(t, teststorage.ConsulBackendSetup)
114	})
115
116	t.Run("raft", func(t *testing.T) {
117		t.Parallel()
118		testSealMigrationShamirToAuto(t, teststorage.RaftBackendSetup)
119	})
120}
121
122func testSealMigrationShamirToAuto(t *testing.T, setup teststorage.ClusterSetupMutator) {
123	tcluster := newTransitSealServer(t)
124	defer tcluster.Cleanup()
125
126	conf, opts := teststorage.ClusterSetup(&vault.CoreConfig{
127		DisableSealWrap: true,
128	}, &vault.TestClusterOptions{
129		HandlerFunc: vaulthttp.Handler,
130		SkipInit:    true,
131		NumCores:    3,
132	},
133		setup,
134	)
135	opts.SetupFunc = nil
136	cluster := vault.NewTestCluster(t, conf, opts)
137	tcluster.makeKey(t, "key1")
138	autoSeal := tcluster.makeSeal(t, "key1")
139	cluster.Start()
140	defer cluster.Cleanup()
141
142	client := cluster.Cores[0].Client
143	initResp, err := client.Sys().Init(&api.InitRequest{
144		SecretShares:    5,
145		SecretThreshold: 3,
146	})
147	if err != nil {
148		t.Fatal(err)
149	}
150
151	var resp *api.SealStatusResponse
152	for _, key := range initResp.KeysB64 {
153		resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key})
154		if err != nil {
155			t.Fatal(err)
156		}
157		if resp == nil || !resp.Sealed {
158			break
159		}
160	}
161	if resp == nil || resp.Sealed {
162		t.Fatalf("expected unsealed state; got %#v", resp)
163	}
164
165	testhelpers.WaitForActiveNode(t, cluster)
166	rootToken := initResp.RootToken
167	client.SetToken(rootToken)
168	if err := client.Sys().Seal(); err != nil {
169		t.Fatal(err)
170	}
171
172	if err := adjustCoreForSealMigration(cluster.Logger, cluster.Cores[0].Core, autoSeal, nil); err != nil {
173		t.Fatal(err)
174	}
175
176	for _, key := range initResp.KeysB64 {
177		resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key})
178		if err == nil {
179			t.Fatal("expected error due to lack of migrate parameter")
180		}
181		resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key, Migrate: true})
182		if err != nil {
183			t.Fatal(err)
184		}
185		if resp == nil || !resp.Sealed {
186			break
187		}
188	}
189	if resp == nil || resp.Sealed {
190		t.Fatalf("expected unsealed state; got %#v", resp)
191	}
192
193	testhelpers.WaitForActiveNode(t, cluster)
194	// Seal and unseal again to verify that things are working fine
195	if err := client.Sys().Seal(); err != nil {
196		t.Fatal(err)
197	}
198
199	// Now the barrier unseal keys are actually the recovery keys.
200	// Seal the transit cluster; we expect the unseal of our other cluster
201	// to fail as a result.
202	tcluster.EnsureCoresSealed(t)
203	for _, key := range initResp.KeysB64 {
204		resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key})
205		if err != nil {
206			break
207		}
208		if resp == nil || !resp.Sealed {
209			break
210		}
211	}
212	if err == nil || resp != nil {
213		t.Fatalf("expected sealed state; got %#v", resp)
214	}
215
216	tcluster.UnsealCores(t)
217	for _, key := range initResp.KeysB64 {
218		resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key})
219		if err != nil {
220			t.Fatal(err)
221		}
222		if resp == nil || !resp.Sealed {
223			break
224		}
225	}
226	if resp == nil || resp.Sealed {
227		t.Fatalf("expected unsealed state; got %#v", resp)
228	}
229
230	// Make sure the seal configs were updated correctly
231	b, r, err := cluster.Cores[0].Core.PhysicalSealConfigs(context.Background())
232	if err != nil {
233		t.Fatal(err)
234	}
235	verifyBarrierConfig(t, b, wrapping.Transit, 1, 1, 1)
236	verifyBarrierConfig(t, r, wrapping.Shamir, 5, 3, 0)
237}
238
239/*
240func TestSealMigration_AutoToAuto(t *testing.T) {
241	t.Parallel()
242	t.Run("inmem", func(t *testing.T) {
243		t.Parallel()
244		testSealMigrationAutoToAuto(t, teststorage.InmemBackendSetup)
245	})
246
247	t.Run("file", func(t *testing.T) {
248		t.Parallel()
249		testSealMigrationAutoToAuto(t, teststorage.FileBackendSetup)
250	})
251
252	t.Run("consul", func(t *testing.T) {
253		t.Parallel()
254		testSealMigrationAutoToAuto(t, teststorage.ConsulBackendSetup)
255	})
256
257	t.Run("raft", func(t *testing.T) {
258		t.Parallel()
259		testSealMigrationAutoToAuto(t, teststorage.RaftBackendSetup)
260	})
261}
262*/
263
264func testSealMigrationAutoToAuto(t *testing.T, setup teststorage.ClusterSetupMutator) {
265	tcluster := newTransitSealServer(t)
266	defer tcluster.Cleanup()
267	tcluster.makeKey(t, "key1")
268	tcluster.makeKey(t, "key2")
269	var seals []vault.Seal
270
271	conf, opts := teststorage.ClusterSetup(&vault.CoreConfig{
272		DisableSealWrap: true,
273	}, &vault.TestClusterOptions{
274		HandlerFunc: vaulthttp.Handler,
275		SkipInit:    true,
276		NumCores:    3,
277		SealFunc: func() vault.Seal {
278			tseal := tcluster.makeSeal(t, "key1")
279			seals = append(seals, tseal)
280			return tseal
281		},
282	},
283		setup,
284	)
285	opts.SetupFunc = nil
286	cluster := vault.NewTestCluster(t, conf, opts)
287	cluster.Start()
288	defer cluster.Cleanup()
289
290	client := cluster.Cores[0].Client
291	initResp, err := client.Sys().Init(&api.InitRequest{
292		RecoveryShares:    5,
293		RecoveryThreshold: 3,
294	})
295	if err != nil {
296		t.Fatal(err)
297	}
298	rootToken := initResp.RootToken
299	client.SetToken(rootToken)
300	for _, k := range initResp.RecoveryKeysB64 {
301		b, _ := base64.RawStdEncoding.DecodeString(k)
302		cluster.RecoveryKeys = append(cluster.RecoveryKeys, b)
303	}
304
305	testhelpers.WaitForActiveNode(t, cluster)
306
307	if err := client.Sys().Seal(); err != nil {
308		t.Fatal(err)
309	}
310
311	logger := cluster.Logger.Named("shamir")
312	autoSeal2 := tcluster.makeSeal(t, "key2")
313	if err := adjustCoreForSealMigration(logger, cluster.Cores[0].Core, autoSeal2, seals[0]); err != nil {
314		t.Fatal(err)
315	}
316
317	// Although we're unsealing using the recovery keys, this is still an
318	// autounseal; if we stopped the transit cluster this would fail.
319	var resp *api.SealStatusResponse
320	for _, key := range initResp.RecoveryKeysB64 {
321		resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key})
322		if err == nil {
323			t.Fatal("expected error due to lack of migrate parameter")
324		}
325		resp, err = client.Sys().UnsealWithOptions(&api.UnsealOpts{Key: key, Migrate: true})
326		if err != nil {
327			t.Fatal(err)
328		}
329		if resp == nil || !resp.Sealed {
330			break
331		}
332	}
333	if resp == nil || resp.Sealed {
334		t.Fatalf("expected unsealed state; got %#v", resp)
335	}
336
337	testhelpers.WaitForActiveNode(t, cluster)
338
339	// Seal and unseal again to verify that things are working fine
340	if err := client.Sys().Seal(); err != nil {
341		t.Fatal(err)
342	}
343
344	// Delete the original seal's transit key.
345	_, err = tcluster.Cores[0].Client.Logical().Delete(path.Join("transit", "keys", "key1"))
346	if err != nil {
347		t.Fatal(err)
348	}
349
350	err = cluster.Cores[0].Core.UnsealWithStoredKeys(context.Background())
351	if err != nil {
352		t.Fatal(err)
353	}
354}
355