1package cache
2
3import (
4	"context"
5	"fmt"
6	"io"
7	"math/rand"
8	"net"
9	"net/http"
10	"os"
11	"sync"
12	"testing"
13	"time"
14
15	"github.com/go-test/deep"
16	hclog "github.com/hashicorp/go-hclog"
17	kv "github.com/hashicorp/vault-plugin-secrets-kv"
18	"github.com/hashicorp/vault/api"
19	"github.com/hashicorp/vault/builtin/credential/userpass"
20	"github.com/hashicorp/vault/command/agent/cache/cachememdb"
21	"github.com/hashicorp/vault/command/agent/sink/mock"
22	"github.com/hashicorp/vault/helper/namespace"
23	vaulthttp "github.com/hashicorp/vault/http"
24	"github.com/hashicorp/vault/sdk/helper/consts"
25	"github.com/hashicorp/vault/sdk/helper/logging"
26	"github.com/hashicorp/vault/sdk/logical"
27	"github.com/hashicorp/vault/vault"
28)
29
30const policyAdmin = `
31path "*" {
32	capabilities = ["sudo", "create", "read", "update", "delete", "list"]
33}
34`
35
36// setupClusterAndAgent is a helper func used to set up a test cluster and
37// caching agent against the active node. It returns a cleanup func that should
38// be deferred immediately along with two clients, one for direct cluster
39// communication and another to talk to the caching agent.
40func setupClusterAndAgent(ctx context.Context, t *testing.T, coreConfig *vault.CoreConfig) (func(), *api.Client, *api.Client, *LeaseCache) {
41	return setupClusterAndAgentCommon(ctx, t, coreConfig, false)
42}
43
44// setupClusterAndAgentOnStandby is a helper func used to set up a test cluster
45// and caching agent against a standby node. It returns a cleanup func that
46// should be deferred immediately along with two clients, one for direct cluster
47// communication and another to talk to the caching agent.
48func setupClusterAndAgentOnStandby(ctx context.Context, t *testing.T, coreConfig *vault.CoreConfig) (func(), *api.Client, *api.Client, *LeaseCache) {
49	return setupClusterAndAgentCommon(ctx, t, coreConfig, true)
50}
51
52func setupClusterAndAgentCommon(ctx context.Context, t *testing.T, coreConfig *vault.CoreConfig, onStandby bool) (func(), *api.Client, *api.Client, *LeaseCache) {
53	t.Helper()
54
55	if ctx == nil {
56		ctx = context.Background()
57	}
58
59	// Handle sane defaults
60	if coreConfig == nil {
61		coreConfig = &vault.CoreConfig{
62			DisableMlock: true,
63			DisableCache: true,
64			Logger:       logging.NewVaultLogger(hclog.Trace),
65		}
66	}
67
68	// Always set up the userpass backend since we use that to generate an admin
69	// token for the client that will make proxied requests to through the agent.
70	if coreConfig.CredentialBackends == nil || coreConfig.CredentialBackends["userpass"] == nil {
71		coreConfig.CredentialBackends = map[string]logical.Factory{
72			"userpass": userpass.Factory,
73		}
74	}
75
76	// Init new test cluster
77	cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
78		HandlerFunc: vaulthttp.Handler,
79	})
80	cluster.Start()
81
82	cores := cluster.Cores
83	vault.TestWaitActive(t, cores[0].Core)
84
85	activeClient := cores[0].Client
86	standbyClient := cores[1].Client
87
88	// clienToUse is the client for the agent to point to.
89	clienToUse := activeClient
90	if onStandby {
91		clienToUse = standbyClient
92	}
93
94	// Add an admin policy
95	if err := activeClient.Sys().PutPolicy("admin", policyAdmin); err != nil {
96		t.Fatal(err)
97	}
98
99	// Set up the userpass auth backend and an admin user. Used for getting a token
100	// for the agent later down in this func.
101	err := activeClient.Sys().EnableAuthWithOptions("userpass", &api.EnableAuthOptions{
102		Type: "userpass",
103	})
104	if err != nil {
105		t.Fatal(err)
106	}
107
108	_, err = activeClient.Logical().Write("auth/userpass/users/foo", map[string]interface{}{
109		"password": "bar",
110		"policies": []string{"admin"},
111	})
112	if err != nil {
113		t.Fatal(err)
114	}
115
116	// Set up env vars for agent consumption
117	origEnvVaultAddress := os.Getenv(api.EnvVaultAddress)
118	os.Setenv(api.EnvVaultAddress, clienToUse.Address())
119
120	origEnvVaultCACert := os.Getenv(api.EnvVaultCACert)
121	os.Setenv(api.EnvVaultCACert, fmt.Sprintf("%s/ca_cert.pem", cluster.TempDir))
122
123	cacheLogger := logging.NewVaultLogger(hclog.Trace).Named("cache")
124
125	listener, err := net.Listen("tcp", "127.0.0.1:0")
126	if err != nil {
127		t.Fatal(err)
128	}
129
130	// Create the API proxier
131	apiProxy, err := NewAPIProxy(&APIProxyConfig{
132		Client: clienToUse,
133		Logger: cacheLogger.Named("apiproxy"),
134	})
135	if err != nil {
136		t.Fatal(err)
137	}
138
139	// Create the lease cache proxier and set its underlying proxier to
140	// the API proxier.
141	leaseCache, err := NewLeaseCache(&LeaseCacheConfig{
142		Client:      clienToUse,
143		BaseContext: ctx,
144		Proxier:     apiProxy,
145		Logger:      cacheLogger.Named("leasecache"),
146	})
147	if err != nil {
148		t.Fatal(err)
149	}
150
151	// Create a muxer and add paths relevant for the lease cache layer
152	mux := http.NewServeMux()
153	mux.Handle("/agent/v1/cache-clear", leaseCache.HandleCacheClear(ctx))
154
155	mux.Handle("/", Handler(ctx, cacheLogger, leaseCache, nil))
156	server := &http.Server{
157		Handler:           mux,
158		ReadHeaderTimeout: 10 * time.Second,
159		ReadTimeout:       30 * time.Second,
160		IdleTimeout:       5 * time.Minute,
161		ErrorLog:          cacheLogger.StandardLogger(nil),
162	}
163	go server.Serve(listener)
164
165	// testClient is the client that is used to talk to the agent for proxying/caching behavior.
166	testClient, err := activeClient.Clone()
167	if err != nil {
168		t.Fatal(err)
169	}
170
171	if err := testClient.SetAddress("http://" + listener.Addr().String()); err != nil {
172		t.Fatal(err)
173	}
174
175	// Login via userpass method to derive a managed token. Set that token as the
176	// testClient's token
177	resp, err := testClient.Logical().Write("auth/userpass/login/foo", map[string]interface{}{
178		"password": "bar",
179	})
180	if err != nil {
181		t.Fatal(err)
182	}
183	testClient.SetToken(resp.Auth.ClientToken)
184
185	cleanup := func() {
186		// We wait for a tiny bit for things such as agent renewal to exit properly
187		time.Sleep(50 * time.Millisecond)
188
189		cluster.Cleanup()
190		os.Setenv(api.EnvVaultAddress, origEnvVaultAddress)
191		os.Setenv(api.EnvVaultCACert, origEnvVaultCACert)
192		listener.Close()
193	}
194
195	return cleanup, clienToUse, testClient, leaseCache
196}
197
198func tokenRevocationValidation(t *testing.T, sampleSpace map[string]string, expected map[string]string, leaseCache *LeaseCache) {
199	t.Helper()
200	for val, valType := range sampleSpace {
201		index, err := leaseCache.db.Get(valType, val)
202		if err != nil {
203			t.Fatal(err)
204		}
205		if expected[val] == "" && index != nil {
206			t.Fatalf("failed to evict index from the cache: type: %q, value: %q", valType, val)
207		}
208		if expected[val] != "" && index == nil {
209			t.Fatalf("evicted an undesired index from cache: type: %q, value: %q", valType, val)
210		}
211	}
212}
213
214func TestCache_AutoAuthTokenStripping(t *testing.T) {
215	response1 := `{"data": {"id": "testid", "accessor": "testaccessor", "request": "lookup-self"}}`
216	response2 := `{"data": {"id": "testid", "accessor": "testaccessor", "request": "lookup"}}`
217	responses := []*SendResponse{
218		newTestSendResponse(http.StatusOK, response1),
219		newTestSendResponse(http.StatusOK, response2),
220	}
221
222	leaseCache := testNewLeaseCache(t, responses)
223
224	cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
225		HandlerFunc: vaulthttp.Handler,
226	})
227	cluster.Start()
228	defer cluster.Cleanup()
229
230	cores := cluster.Cores
231	vault.TestWaitActive(t, cores[0].Core)
232	client := cores[0].Client
233
234	cacheLogger := logging.NewVaultLogger(hclog.Trace).Named("cache")
235	listener, err := net.Listen("tcp", "127.0.0.1:0")
236	if err != nil {
237		t.Fatal(err)
238	}
239
240	ctx := namespace.RootContext(nil)
241
242	// Create a muxer and add paths relevant for the lease cache layer
243	mux := http.NewServeMux()
244	mux.Handle(consts.AgentPathCacheClear, leaseCache.HandleCacheClear(ctx))
245
246	mux.Handle("/", Handler(ctx, cacheLogger, leaseCache, mock.NewSink("testid")))
247	server := &http.Server{
248		Handler:           mux,
249		ReadHeaderTimeout: 10 * time.Second,
250		ReadTimeout:       30 * time.Second,
251		IdleTimeout:       5 * time.Minute,
252		ErrorLog:          cacheLogger.StandardLogger(nil),
253	}
254	go server.Serve(listener)
255
256	testClient, err := client.Clone()
257	if err != nil {
258		t.Fatal(err)
259	}
260
261	if err := testClient.SetAddress("http://" + listener.Addr().String()); err != nil {
262		t.Fatal(err)
263	}
264
265	// Empty the token in the client. Auto-auth token should be put to use.
266	testClient.SetToken("")
267	secret, err := testClient.Auth().Token().LookupSelf()
268	if err != nil {
269		t.Fatal(err)
270	}
271	if secret.Data["id"] != nil || secret.Data["accessor"] != nil || secret.Data["request"].(string) != "lookup-self" {
272		t.Fatalf("failed to strip off auto-auth token on lookup-self")
273	}
274
275	secret, err = testClient.Auth().Token().Lookup("")
276	if err != nil {
277		t.Fatal(err)
278	}
279	if secret.Data["id"] != nil || secret.Data["accessor"] != nil || secret.Data["request"].(string) != "lookup" {
280		t.Fatalf("failed to strip off auto-auth token on lookup")
281	}
282}
283
284func TestCache_ConcurrentRequests(t *testing.T) {
285	coreConfig := &vault.CoreConfig{
286		DisableMlock: true,
287		DisableCache: true,
288		Logger:       hclog.NewNullLogger(),
289		LogicalBackends: map[string]logical.Factory{
290			"kv": vault.LeasedPassthroughBackendFactory,
291		},
292	}
293
294	cleanup, _, testClient, _ := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig)
295	defer cleanup()
296
297	err := testClient.Sys().Mount("kv", &api.MountInput{
298		Type: "kv",
299	})
300	if err != nil {
301		t.Fatal(err)
302	}
303
304	wg := &sync.WaitGroup{}
305	for i := 0; i < 100; i++ {
306		wg.Add(1)
307		go func(i int) {
308			defer wg.Done()
309			key := fmt.Sprintf("kv/foo/%d_%d", i, rand.Int())
310			_, err := testClient.Logical().Write(key, map[string]interface{}{
311				"key": key,
312			})
313			if err != nil {
314				t.Fatal(err)
315			}
316			secret, err := testClient.Logical().Read(key)
317			if err != nil {
318				t.Fatal(err)
319			}
320			if secret == nil || secret.Data["key"].(string) != key {
321				t.Fatal(fmt.Sprintf("failed to read value for key: %q", key))
322			}
323		}(i)
324
325	}
326	wg.Wait()
327}
328
329func TestCache_TokenRevocations_RevokeOrphan(t *testing.T) {
330	coreConfig := &vault.CoreConfig{
331		DisableMlock: true,
332		DisableCache: true,
333		Logger:       hclog.NewNullLogger(),
334		LogicalBackends: map[string]logical.Factory{
335			"kv": vault.LeasedPassthroughBackendFactory,
336		},
337	}
338
339	sampleSpace := make(map[string]string)
340
341	cleanup, _, testClient, leaseCache := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig)
342	defer cleanup()
343
344	token1 := testClient.Token()
345	sampleSpace[token1] = "token"
346
347	// Mount the kv backend
348	err := testClient.Sys().Mount("kv", &api.MountInput{
349		Type: "kv",
350	})
351	if err != nil {
352		t.Fatal(err)
353	}
354
355	// Create a secret in the backend
356	_, err = testClient.Logical().Write("kv/foo", map[string]interface{}{
357		"value": "bar",
358		"ttl":   "1h",
359	})
360	if err != nil {
361		t.Fatal(err)
362	}
363
364	// Read the secret and create a lease
365	leaseResp, err := testClient.Logical().Read("kv/foo")
366	if err != nil {
367		t.Fatal(err)
368	}
369	lease1 := leaseResp.LeaseID
370	sampleSpace[lease1] = "lease"
371
372	resp, err := testClient.Logical().Write("auth/token/create", nil)
373	if err != nil {
374		t.Fatal(err)
375	}
376	token2 := resp.Auth.ClientToken
377	sampleSpace[token2] = "token"
378
379	testClient.SetToken(token2)
380
381	leaseResp, err = testClient.Logical().Read("kv/foo")
382	if err != nil {
383		t.Fatal(err)
384	}
385	lease2 := leaseResp.LeaseID
386	sampleSpace[lease2] = "lease"
387
388	resp, err = testClient.Logical().Write("auth/token/create", nil)
389	if err != nil {
390		t.Fatal(err)
391	}
392	token3 := resp.Auth.ClientToken
393	sampleSpace[token3] = "token"
394
395	testClient.SetToken(token3)
396
397	leaseResp, err = testClient.Logical().Read("kv/foo")
398	if err != nil {
399		t.Fatal(err)
400	}
401	lease3 := leaseResp.LeaseID
402	sampleSpace[lease3] = "lease"
403
404	expected := make(map[string]string)
405	for k, v := range sampleSpace {
406		expected[k] = v
407	}
408	tokenRevocationValidation(t, sampleSpace, expected, leaseCache)
409
410	// Revoke-orphan the intermediate token. This should result in its own
411	// eviction and evictions of the revoked token's leases. All other things
412	// including the child tokens and leases of the child tokens should be
413	// untouched.
414	testClient.SetToken(token2)
415	err = testClient.Auth().Token().RevokeOrphan(token2)
416	if err != nil {
417		t.Fatal(err)
418	}
419	time.Sleep(1 * time.Second)
420
421	expected = map[string]string{
422		token1: "token",
423		lease1: "lease",
424		token3: "token",
425		lease3: "lease",
426	}
427	tokenRevocationValidation(t, sampleSpace, expected, leaseCache)
428}
429
430func TestCache_TokenRevocations_LeafLevelToken(t *testing.T) {
431	coreConfig := &vault.CoreConfig{
432		DisableMlock: true,
433		DisableCache: true,
434		Logger:       hclog.NewNullLogger(),
435		LogicalBackends: map[string]logical.Factory{
436			"kv": vault.LeasedPassthroughBackendFactory,
437		},
438	}
439
440	sampleSpace := make(map[string]string)
441
442	cleanup, _, testClient, leaseCache := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig)
443	defer cleanup()
444
445	token1 := testClient.Token()
446	sampleSpace[token1] = "token"
447
448	// Mount the kv backend
449	err := testClient.Sys().Mount("kv", &api.MountInput{
450		Type: "kv",
451	})
452	if err != nil {
453		t.Fatal(err)
454	}
455
456	// Create a secret in the backend
457	_, err = testClient.Logical().Write("kv/foo", map[string]interface{}{
458		"value": "bar",
459		"ttl":   "1h",
460	})
461	if err != nil {
462		t.Fatal(err)
463	}
464
465	// Read the secret and create a lease
466	leaseResp, err := testClient.Logical().Read("kv/foo")
467	if err != nil {
468		t.Fatal(err)
469	}
470	lease1 := leaseResp.LeaseID
471	sampleSpace[lease1] = "lease"
472
473	resp, err := testClient.Logical().Write("auth/token/create", nil)
474	if err != nil {
475		t.Fatal(err)
476	}
477	token2 := resp.Auth.ClientToken
478	sampleSpace[token2] = "token"
479
480	testClient.SetToken(token2)
481
482	leaseResp, err = testClient.Logical().Read("kv/foo")
483	if err != nil {
484		t.Fatal(err)
485	}
486	lease2 := leaseResp.LeaseID
487	sampleSpace[lease2] = "lease"
488
489	resp, err = testClient.Logical().Write("auth/token/create", nil)
490	if err != nil {
491		t.Fatal(err)
492	}
493	token3 := resp.Auth.ClientToken
494	sampleSpace[token3] = "token"
495
496	testClient.SetToken(token3)
497
498	leaseResp, err = testClient.Logical().Read("kv/foo")
499	if err != nil {
500		t.Fatal(err)
501	}
502	lease3 := leaseResp.LeaseID
503	sampleSpace[lease3] = "lease"
504
505	expected := make(map[string]string)
506	for k, v := range sampleSpace {
507		expected[k] = v
508	}
509	tokenRevocationValidation(t, sampleSpace, expected, leaseCache)
510
511	// Revoke the lef token. This should evict all the leases belonging to this
512	// token, evict entries for all the child tokens and their respective
513	// leases.
514	testClient.SetToken(token3)
515	err = testClient.Auth().Token().RevokeSelf("")
516	if err != nil {
517		t.Fatal(err)
518	}
519	time.Sleep(1 * time.Second)
520
521	expected = map[string]string{
522		token1: "token",
523		lease1: "lease",
524		token2: "token",
525		lease2: "lease",
526	}
527	tokenRevocationValidation(t, sampleSpace, expected, leaseCache)
528}
529
530func TestCache_TokenRevocations_IntermediateLevelToken(t *testing.T) {
531	coreConfig := &vault.CoreConfig{
532		DisableMlock: true,
533		DisableCache: true,
534		Logger:       hclog.NewNullLogger(),
535		LogicalBackends: map[string]logical.Factory{
536			"kv": vault.LeasedPassthroughBackendFactory,
537		},
538	}
539
540	sampleSpace := make(map[string]string)
541
542	cleanup, _, testClient, leaseCache := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig)
543	defer cleanup()
544
545	token1 := testClient.Token()
546	sampleSpace[token1] = "token"
547
548	// Mount the kv backend
549	err := testClient.Sys().Mount("kv", &api.MountInput{
550		Type: "kv",
551	})
552	if err != nil {
553		t.Fatal(err)
554	}
555
556	// Create a secret in the backend
557	_, err = testClient.Logical().Write("kv/foo", map[string]interface{}{
558		"value": "bar",
559		"ttl":   "1h",
560	})
561	if err != nil {
562		t.Fatal(err)
563	}
564
565	// Read the secret and create a lease
566	leaseResp, err := testClient.Logical().Read("kv/foo")
567	if err != nil {
568		t.Fatal(err)
569	}
570	lease1 := leaseResp.LeaseID
571	sampleSpace[lease1] = "lease"
572
573	resp, err := testClient.Logical().Write("auth/token/create", nil)
574	if err != nil {
575		t.Fatal(err)
576	}
577	token2 := resp.Auth.ClientToken
578	sampleSpace[token2] = "token"
579
580	testClient.SetToken(token2)
581
582	leaseResp, err = testClient.Logical().Read("kv/foo")
583	if err != nil {
584		t.Fatal(err)
585	}
586	lease2 := leaseResp.LeaseID
587	sampleSpace[lease2] = "lease"
588
589	resp, err = testClient.Logical().Write("auth/token/create", nil)
590	if err != nil {
591		t.Fatal(err)
592	}
593	token3 := resp.Auth.ClientToken
594	sampleSpace[token3] = "token"
595
596	testClient.SetToken(token3)
597
598	leaseResp, err = testClient.Logical().Read("kv/foo")
599	if err != nil {
600		t.Fatal(err)
601	}
602	lease3 := leaseResp.LeaseID
603	sampleSpace[lease3] = "lease"
604
605	expected := make(map[string]string)
606	for k, v := range sampleSpace {
607		expected[k] = v
608	}
609	tokenRevocationValidation(t, sampleSpace, expected, leaseCache)
610
611	// Revoke the second level token. This should evict all the leases
612	// belonging to this token, evict entries for all the child tokens and
613	// their respective leases.
614	testClient.SetToken(token2)
615	err = testClient.Auth().Token().RevokeSelf("")
616	if err != nil {
617		t.Fatal(err)
618	}
619	time.Sleep(1 * time.Second)
620
621	expected = map[string]string{
622		token1: "token",
623		lease1: "lease",
624	}
625	tokenRevocationValidation(t, sampleSpace, expected, leaseCache)
626}
627
628func TestCache_TokenRevocations_TopLevelToken(t *testing.T) {
629	coreConfig := &vault.CoreConfig{
630		DisableMlock: true,
631		DisableCache: true,
632		Logger:       hclog.NewNullLogger(),
633		LogicalBackends: map[string]logical.Factory{
634			"kv": vault.LeasedPassthroughBackendFactory,
635		},
636	}
637
638	sampleSpace := make(map[string]string)
639
640	cleanup, _, testClient, leaseCache := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig)
641	defer cleanup()
642
643	token1 := testClient.Token()
644	sampleSpace[token1] = "token"
645
646	// Mount the kv backend
647	err := testClient.Sys().Mount("kv", &api.MountInput{
648		Type: "kv",
649	})
650	if err != nil {
651		t.Fatal(err)
652	}
653
654	// Create a secret in the backend
655	_, err = testClient.Logical().Write("kv/foo", map[string]interface{}{
656		"value": "bar",
657		"ttl":   "1h",
658	})
659	if err != nil {
660		t.Fatal(err)
661	}
662
663	// Read the secret and create a lease
664	leaseResp, err := testClient.Logical().Read("kv/foo")
665	if err != nil {
666		t.Fatal(err)
667	}
668	lease1 := leaseResp.LeaseID
669	sampleSpace[lease1] = "lease"
670
671	resp, err := testClient.Logical().Write("auth/token/create", nil)
672	if err != nil {
673		t.Fatal(err)
674	}
675	token2 := resp.Auth.ClientToken
676	sampleSpace[token2] = "token"
677
678	testClient.SetToken(token2)
679
680	leaseResp, err = testClient.Logical().Read("kv/foo")
681	if err != nil {
682		t.Fatal(err)
683	}
684	lease2 := leaseResp.LeaseID
685	sampleSpace[lease2] = "lease"
686
687	resp, err = testClient.Logical().Write("auth/token/create", nil)
688	if err != nil {
689		t.Fatal(err)
690	}
691	token3 := resp.Auth.ClientToken
692	sampleSpace[token3] = "token"
693
694	testClient.SetToken(token3)
695
696	leaseResp, err = testClient.Logical().Read("kv/foo")
697	if err != nil {
698		t.Fatal(err)
699	}
700	lease3 := leaseResp.LeaseID
701	sampleSpace[lease3] = "lease"
702
703	expected := make(map[string]string)
704	for k, v := range sampleSpace {
705		expected[k] = v
706	}
707	tokenRevocationValidation(t, sampleSpace, expected, leaseCache)
708
709	// Revoke the top level token. This should evict all the leases belonging
710	// to this token, evict entries for all the child tokens and their
711	// respective leases.
712	testClient.SetToken(token1)
713	err = testClient.Auth().Token().RevokeSelf("")
714	if err != nil {
715		t.Fatal(err)
716	}
717	time.Sleep(1 * time.Second)
718
719	expected = make(map[string]string)
720	tokenRevocationValidation(t, sampleSpace, expected, leaseCache)
721}
722
723func TestCache_TokenRevocations_Shutdown(t *testing.T) {
724	coreConfig := &vault.CoreConfig{
725		DisableMlock: true,
726		DisableCache: true,
727		Logger:       hclog.NewNullLogger(),
728		LogicalBackends: map[string]logical.Factory{
729			"kv": vault.LeasedPassthroughBackendFactory,
730		},
731	}
732
733	sampleSpace := make(map[string]string)
734
735	ctx, rootCancelFunc := context.WithCancel(namespace.RootContext(nil))
736	cleanup, _, testClient, leaseCache := setupClusterAndAgent(ctx, t, coreConfig)
737	defer cleanup()
738
739	token1 := testClient.Token()
740	sampleSpace[token1] = "token"
741
742	// Mount the kv backend
743	err := testClient.Sys().Mount("kv", &api.MountInput{
744		Type: "kv",
745	})
746	if err != nil {
747		t.Fatal(err)
748	}
749
750	// Create a secret in the backend
751	_, err = testClient.Logical().Write("kv/foo", map[string]interface{}{
752		"value": "bar",
753		"ttl":   "1h",
754	})
755	if err != nil {
756		t.Fatal(err)
757	}
758
759	// Read the secret and create a lease
760	leaseResp, err := testClient.Logical().Read("kv/foo")
761	if err != nil {
762		t.Fatal(err)
763	}
764	lease1 := leaseResp.LeaseID
765	sampleSpace[lease1] = "lease"
766
767	resp, err := testClient.Logical().Write("auth/token/create", nil)
768	if err != nil {
769		t.Fatal(err)
770	}
771	token2 := resp.Auth.ClientToken
772	sampleSpace[token2] = "token"
773
774	testClient.SetToken(token2)
775
776	leaseResp, err = testClient.Logical().Read("kv/foo")
777	if err != nil {
778		t.Fatal(err)
779	}
780	lease2 := leaseResp.LeaseID
781	sampleSpace[lease2] = "lease"
782
783	resp, err = testClient.Logical().Write("auth/token/create", nil)
784	if err != nil {
785		t.Fatal(err)
786	}
787	token3 := resp.Auth.ClientToken
788	sampleSpace[token3] = "token"
789
790	testClient.SetToken(token3)
791
792	leaseResp, err = testClient.Logical().Read("kv/foo")
793	if err != nil {
794		t.Fatal(err)
795	}
796	lease3 := leaseResp.LeaseID
797	sampleSpace[lease3] = "lease"
798
799	expected := make(map[string]string)
800	for k, v := range sampleSpace {
801		expected[k] = v
802	}
803	tokenRevocationValidation(t, sampleSpace, expected, leaseCache)
804
805	rootCancelFunc()
806	time.Sleep(1 * time.Second)
807
808	// Ensure that all the entries are now gone
809	expected = make(map[string]string)
810	tokenRevocationValidation(t, sampleSpace, expected, leaseCache)
811}
812
813func TestCache_TokenRevocations_BaseContextCancellation(t *testing.T) {
814	coreConfig := &vault.CoreConfig{
815		DisableMlock: true,
816		DisableCache: true,
817		Logger:       hclog.NewNullLogger(),
818		LogicalBackends: map[string]logical.Factory{
819			"kv": vault.LeasedPassthroughBackendFactory,
820		},
821	}
822
823	sampleSpace := make(map[string]string)
824
825	cleanup, _, testClient, leaseCache := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig)
826	defer cleanup()
827
828	token1 := testClient.Token()
829	sampleSpace[token1] = "token"
830
831	// Mount the kv backend
832	err := testClient.Sys().Mount("kv", &api.MountInput{
833		Type: "kv",
834	})
835	if err != nil {
836		t.Fatal(err)
837	}
838
839	// Create a secret in the backend
840	_, err = testClient.Logical().Write("kv/foo", map[string]interface{}{
841		"value": "bar",
842		"ttl":   "1h",
843	})
844	if err != nil {
845		t.Fatal(err)
846	}
847
848	// Read the secret and create a lease
849	leaseResp, err := testClient.Logical().Read("kv/foo")
850	if err != nil {
851		t.Fatal(err)
852	}
853	lease1 := leaseResp.LeaseID
854	sampleSpace[lease1] = "lease"
855
856	resp, err := testClient.Logical().Write("auth/token/create", nil)
857	if err != nil {
858		t.Fatal(err)
859	}
860	token2 := resp.Auth.ClientToken
861	sampleSpace[token2] = "token"
862
863	testClient.SetToken(token2)
864
865	leaseResp, err = testClient.Logical().Read("kv/foo")
866	if err != nil {
867		t.Fatal(err)
868	}
869	lease2 := leaseResp.LeaseID
870	sampleSpace[lease2] = "lease"
871
872	resp, err = testClient.Logical().Write("auth/token/create", nil)
873	if err != nil {
874		t.Fatal(err)
875	}
876	token3 := resp.Auth.ClientToken
877	sampleSpace[token3] = "token"
878
879	testClient.SetToken(token3)
880
881	leaseResp, err = testClient.Logical().Read("kv/foo")
882	if err != nil {
883		t.Fatal(err)
884	}
885	lease3 := leaseResp.LeaseID
886	sampleSpace[lease3] = "lease"
887
888	expected := make(map[string]string)
889	for k, v := range sampleSpace {
890		expected[k] = v
891	}
892	tokenRevocationValidation(t, sampleSpace, expected, leaseCache)
893
894	// Cancel the base context of the lease cache. This should trigger
895	// evictions of all the entries from the cache.
896	leaseCache.baseCtxInfo.CancelFunc()
897	time.Sleep(1 * time.Second)
898
899	// Ensure that all the entries are now gone
900	expected = make(map[string]string)
901	tokenRevocationValidation(t, sampleSpace, expected, leaseCache)
902}
903
904func TestCache_NonCacheable(t *testing.T) {
905	coreConfig := &vault.CoreConfig{
906		DisableMlock: true,
907		DisableCache: true,
908		Logger:       hclog.NewNullLogger(),
909		LogicalBackends: map[string]logical.Factory{
910			"kv": kv.Factory,
911		},
912	}
913
914	cleanup, _, testClient, _ := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig)
915	defer cleanup()
916
917	// Query mounts first
918	origMounts, err := testClient.Sys().ListMounts()
919	if err != nil {
920		t.Fatal(err)
921	}
922
923	// Mount a kv backend
924	if err := testClient.Sys().Mount("kv", &api.MountInput{
925		Type: "kv",
926		Options: map[string]string{
927			"version": "2",
928		},
929	}); err != nil {
930		t.Fatal(err)
931	}
932
933	// Query mounts again
934	newMounts, err := testClient.Sys().ListMounts()
935	if err != nil {
936		t.Fatal(err)
937	}
938
939	if diff := deep.Equal(origMounts, newMounts); diff == nil {
940		t.Logf("response #1: %#v", origMounts)
941		t.Logf("response #2: %#v", newMounts)
942		t.Fatal("expected requests to be not cached")
943	}
944
945	// Query a non-existing mount, expect an error from api.Response
946	ctx, cancelFunc := context.WithCancel(context.Background())
947	defer cancelFunc()
948	r := testClient.NewRequest("GET", "/v1/kv-invalid")
949
950	apiResp, err := testClient.RawRequestWithContext(ctx, r)
951	if apiResp != nil {
952		defer apiResp.Body.Close()
953	}
954	if apiResp.Error() == nil || (apiResp != nil && apiResp.StatusCode != 404) {
955		t.Fatalf("expected an error response and a 404 from requesting an invalid path, got: %#v", apiResp)
956	}
957	if err == nil {
958		t.Fatal("expected an error from requesting an invalid path")
959	}
960}
961
962func TestCache_Caching_AuthResponse(t *testing.T) {
963	cleanup, _, testClient, _ := setupClusterAndAgent(namespace.RootContext(nil), t, nil)
964	defer cleanup()
965
966	resp, err := testClient.Logical().Write("auth/token/create", nil)
967	if err != nil {
968		t.Fatal(err)
969	}
970	token := resp.Auth.ClientToken
971	testClient.SetToken(token)
972
973	authTokeCreateReq := func(t *testing.T, policies map[string]interface{}) *api.Secret {
974		resp, err := testClient.Logical().Write("auth/token/create", policies)
975		if err != nil {
976			t.Fatal(err)
977		}
978		if resp.Auth == nil || resp.Auth.ClientToken == "" {
979			t.Fatalf("expected a valid client token in the response, got = %#v", resp)
980		}
981
982		return resp
983	}
984
985	// Test on auth response by creating a child token
986	{
987		proxiedResp := authTokeCreateReq(t, map[string]interface{}{
988			"policies": "default",
989		})
990
991		cachedResp := authTokeCreateReq(t, map[string]interface{}{
992			"policies": "default",
993		})
994
995		if diff := deep.Equal(proxiedResp.Auth.ClientToken, cachedResp.Auth.ClientToken); diff != nil {
996			t.Fatal(diff)
997		}
998	}
999
1000	// Test on *non-renewable* auth response by creating a child root token
1001	{
1002		proxiedResp := authTokeCreateReq(t, nil)
1003
1004		cachedResp := authTokeCreateReq(t, nil)
1005
1006		if diff := deep.Equal(proxiedResp.Auth.ClientToken, cachedResp.Auth.ClientToken); diff != nil {
1007			t.Fatal(diff)
1008		}
1009	}
1010}
1011
1012func TestCache_Caching_LeaseResponse(t *testing.T) {
1013	coreConfig := &vault.CoreConfig{
1014		DisableMlock: true,
1015		DisableCache: true,
1016		Logger:       hclog.NewNullLogger(),
1017		LogicalBackends: map[string]logical.Factory{
1018			"kv": vault.LeasedPassthroughBackendFactory,
1019		},
1020	}
1021
1022	cleanup, client, testClient, _ := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig)
1023	defer cleanup()
1024
1025	err := client.Sys().Mount("kv", &api.MountInput{
1026		Type: "kv",
1027	})
1028	if err != nil {
1029		t.Fatal(err)
1030	}
1031
1032	// Test proxy by issuing two different requests
1033	{
1034		// Write data to the lease-kv backend
1035		_, err := testClient.Logical().Write("kv/foo", map[string]interface{}{
1036			"value": "bar",
1037			"ttl":   "1h",
1038		})
1039		if err != nil {
1040			t.Fatal(err)
1041		}
1042		_, err = testClient.Logical().Write("kv/foobar", map[string]interface{}{
1043			"value": "bar",
1044			"ttl":   "1h",
1045		})
1046		if err != nil {
1047			t.Fatal(err)
1048		}
1049
1050		firstResp, err := testClient.Logical().Read("kv/foo")
1051		if err != nil {
1052			t.Fatal(err)
1053		}
1054
1055		secondResp, err := testClient.Logical().Read("kv/foobar")
1056		if err != nil {
1057			t.Fatal(err)
1058		}
1059
1060		if diff := deep.Equal(firstResp, secondResp); diff == nil {
1061			t.Logf("response: %#v", firstResp)
1062			t.Fatal("expected proxied responses, got cached response on second request")
1063		}
1064	}
1065
1066	// Test caching behavior by issue the same request twice
1067	{
1068		_, err := testClient.Logical().Write("kv/baz", map[string]interface{}{
1069			"value": "foo",
1070			"ttl":   "1h",
1071		})
1072		if err != nil {
1073			t.Fatal(err)
1074		}
1075
1076		proxiedResp, err := testClient.Logical().Read("kv/baz")
1077		if err != nil {
1078			t.Fatal(err)
1079		}
1080
1081		cachedResp, err := testClient.Logical().Read("kv/baz")
1082		if err != nil {
1083			t.Fatal(err)
1084		}
1085
1086		if diff := deep.Equal(proxiedResp, cachedResp); diff != nil {
1087			t.Fatal(diff)
1088		}
1089	}
1090}
1091
1092func TestCache_Caching_CacheClear(t *testing.T) {
1093	t.Run("request_path", func(t *testing.T) {
1094		testCachingCacheClearCommon(t, "request_path")
1095	})
1096
1097	t.Run("lease", func(t *testing.T) {
1098		testCachingCacheClearCommon(t, "lease")
1099	})
1100
1101	t.Run("token", func(t *testing.T) {
1102		testCachingCacheClearCommon(t, "token")
1103	})
1104
1105	t.Run("token_accessor", func(t *testing.T) {
1106		testCachingCacheClearCommon(t, "token_accessor")
1107	})
1108
1109	t.Run("all", func(t *testing.T) {
1110		testCachingCacheClearCommon(t, "all")
1111	})
1112}
1113
1114func testCachingCacheClearCommon(t *testing.T, clearType string) {
1115	coreConfig := &vault.CoreConfig{
1116		DisableMlock: true,
1117		DisableCache: true,
1118		Logger:       hclog.NewNullLogger(),
1119		LogicalBackends: map[string]logical.Factory{
1120			"kv": vault.LeasedPassthroughBackendFactory,
1121		},
1122	}
1123
1124	cleanup, client, testClient, leaseCache := setupClusterAndAgent(namespace.RootContext(nil), t, coreConfig)
1125	defer cleanup()
1126
1127	err := client.Sys().Mount("kv", &api.MountInput{
1128		Type: "kv",
1129	})
1130	if err != nil {
1131		t.Fatal(err)
1132	}
1133
1134	// Write data to the lease-kv backend
1135	_, err = testClient.Logical().Write("kv/foo", map[string]interface{}{
1136		"value": "bar",
1137		"ttl":   "1h",
1138	})
1139	if err != nil {
1140		t.Fatal(err)
1141	}
1142
1143	// Proxy this request, agent should cache the response
1144	resp, err := testClient.Logical().Read("kv/foo")
1145	if err != nil {
1146		t.Fatal(err)
1147	}
1148	gotLeaseID := resp.LeaseID
1149
1150	// Verify the entry exists
1151	idx, err := leaseCache.db.Get(cachememdb.IndexNameLease, gotLeaseID)
1152	if err != nil {
1153		t.Fatal(err)
1154	}
1155
1156	if idx == nil {
1157		t.Fatalf("expected cached entry, got: %v", idx)
1158	}
1159
1160	data := map[string]interface{}{
1161		"type": clearType,
1162	}
1163
1164	// We need to set the value here depending on what we're trying to test.
1165	// Some values are be static, but others are dynamically generated at runtime.
1166	switch clearType {
1167	case "request_path":
1168		data["value"] = "/v1/kv/foo"
1169	case "lease":
1170		data["value"] = resp.LeaseID
1171	case "token":
1172		data["value"] = testClient.Token()
1173	case "token_accessor":
1174		lookupResp, err := client.Auth().Token().Lookup(testClient.Token())
1175		if err != nil {
1176			t.Fatal(err)
1177		}
1178		data["value"] = lookupResp.Data["accessor"]
1179	case "all":
1180	default:
1181		t.Fatalf("invalid type provided: %v", clearType)
1182	}
1183
1184	r := testClient.NewRequest("PUT", consts.AgentPathCacheClear)
1185	if err := r.SetJSONBody(data); err != nil {
1186		t.Fatal(err)
1187	}
1188
1189	ctx, cancelFunc := context.WithCancel(context.Background())
1190	defer cancelFunc()
1191	apiResp, err := testClient.RawRequestWithContext(ctx, r)
1192	if apiResp != nil {
1193		defer apiResp.Body.Close()
1194	}
1195	if apiResp != nil && apiResp.StatusCode == 404 {
1196		_, parseErr := api.ParseSecret(apiResp.Body)
1197		switch parseErr {
1198		case nil:
1199		case io.EOF:
1200		default:
1201			t.Fatal(err)
1202		}
1203	}
1204	if err != nil {
1205		t.Fatal(err)
1206	}
1207
1208	time.Sleep(100 * time.Millisecond)
1209
1210	// Verify the entry is cleared
1211	idx, err = leaseCache.db.Get(cachememdb.IndexNameLease, gotLeaseID)
1212	if err != nil {
1213		t.Fatal(err)
1214	}
1215
1216	if idx != nil {
1217		t.Fatalf("expected entry to be nil, got: %v", idx)
1218	}
1219}
1220
1221func TestCache_AuthTokenCreateOrphan(t *testing.T) {
1222	t.Run("create", func(t *testing.T) {
1223		t.Run("managed", func(t *testing.T) {
1224			cleanup, _, testClient, leaseCache := setupClusterAndAgent(namespace.RootContext(nil), t, nil)
1225			defer cleanup()
1226
1227			reqOpts := &api.TokenCreateRequest{
1228				Policies: []string{"default"},
1229				NoParent: true,
1230			}
1231			resp, err := testClient.Auth().Token().Create(reqOpts)
1232			if err != nil {
1233				t.Fatal(err)
1234			}
1235			token := resp.Auth.ClientToken
1236
1237			idx, err := leaseCache.db.Get(cachememdb.IndexNameToken, token)
1238			if err != nil {
1239				t.Fatal(err)
1240			}
1241			if idx == nil {
1242				t.Fatalf("expected entry to be non-nil, got: %#v", idx)
1243			}
1244		})
1245
1246		t.Run("non-managed", func(t *testing.T) {
1247			cleanup, clusterClient, testClient, leaseCache := setupClusterAndAgent(namespace.RootContext(nil), t, nil)
1248			defer cleanup()
1249
1250			reqOpts := &api.TokenCreateRequest{
1251				Policies: []string{"default"},
1252				NoParent: true,
1253			}
1254
1255			// Use the test client but set the token to one that's not managed by agent
1256			testClient.SetToken(clusterClient.Token())
1257
1258			resp, err := testClient.Auth().Token().Create(reqOpts)
1259			if err != nil {
1260				t.Fatal(err)
1261			}
1262			token := resp.Auth.ClientToken
1263
1264			idx, err := leaseCache.db.Get(cachememdb.IndexNameToken, token)
1265			if err != nil {
1266				t.Fatal(err)
1267			}
1268			if idx == nil {
1269				t.Fatalf("expected entry to be non-nil, got: %#v", idx)
1270			}
1271		})
1272	})
1273
1274	t.Run("create-orphan", func(t *testing.T) {
1275		t.Run("managed", func(t *testing.T) {
1276			cleanup, _, testClient, leaseCache := setupClusterAndAgent(namespace.RootContext(nil), t, nil)
1277			defer cleanup()
1278
1279			reqOpts := &api.TokenCreateRequest{
1280				Policies: []string{"default"},
1281			}
1282			resp, err := testClient.Auth().Token().CreateOrphan(reqOpts)
1283			if err != nil {
1284				t.Fatal(err)
1285			}
1286			token := resp.Auth.ClientToken
1287
1288			idx, err := leaseCache.db.Get(cachememdb.IndexNameToken, token)
1289			if err != nil {
1290				t.Fatal(err)
1291			}
1292			if idx == nil {
1293				t.Fatalf("expected entry to be non-nil, got: %#v", idx)
1294			}
1295		})
1296
1297		t.Run("non-managed", func(t *testing.T) {
1298			cleanup, clusterClient, testClient, leaseCache := setupClusterAndAgent(namespace.RootContext(nil), t, nil)
1299			defer cleanup()
1300
1301			reqOpts := &api.TokenCreateRequest{
1302				Policies: []string{"default"},
1303			}
1304
1305			// Use the test client but set the token to one that's not managed by agent
1306			testClient.SetToken(clusterClient.Token())
1307
1308			resp, err := testClient.Auth().Token().CreateOrphan(reqOpts)
1309			if err != nil {
1310				t.Fatal(err)
1311			}
1312			token := resp.Auth.ClientToken
1313
1314			idx, err := leaseCache.db.Get(cachememdb.IndexNameToken, token)
1315			if err != nil {
1316				t.Fatal(err)
1317			}
1318			if idx == nil {
1319				t.Fatalf("expected entry to be non-nil, got: %#v", idx)
1320			}
1321		})
1322	})
1323}
1324