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