1package vault 2 3import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "reflect" 9 "sort" 10 "strings" 11 "sync" 12 "sync/atomic" 13 "testing" 14 "time" 15 16 log "github.com/hashicorp/go-hclog" 17 uuid "github.com/hashicorp/go-uuid" 18 "github.com/hashicorp/vault/helper/namespace" 19 "github.com/hashicorp/vault/sdk/framework" 20 "github.com/hashicorp/vault/sdk/helper/logging" 21 "github.com/hashicorp/vault/sdk/logical" 22 "github.com/hashicorp/vault/sdk/physical" 23 "github.com/hashicorp/vault/sdk/physical/inmem" 24) 25 26var ( 27 testImagePull sync.Once 28) 29 30// mockExpiration returns a mock expiration manager 31func mockExpiration(t testing.TB) *ExpirationManager { 32 c, _, _ := TestCoreUnsealed(t) 33 return c.expiration 34} 35 36func mockBackendExpiration(t testing.TB, backend physical.Backend) (*Core, *ExpirationManager) { 37 c, _, _ := TestCoreUnsealedBackend(t, backend) 38 return c, c.expiration 39} 40 41func TestExpiration_Tidy(t *testing.T) { 42 var err error 43 44 // We use this later for tidy testing where we need to check the output 45 logOut := new(bytes.Buffer) 46 logger := log.New(&log.LoggerOptions{ 47 Output: logOut, 48 }) 49 50 testCore := TestCore(t) 51 testCore.baseLogger = logger 52 testCore.logger = logger.Named("core") 53 testCoreUnsealed(t, testCore) 54 55 exp := testCore.expiration 56 57 if err := exp.Restore(nil); err != nil { 58 t.Fatal(err) 59 } 60 61 // Set up a count function to calculate number of leases 62 count := 0 63 countFunc := func(leaseID string) { 64 count++ 65 } 66 67 // Scan the storage with the count func set 68 if err = logical.ScanView(namespace.RootContext(nil), exp.idView, countFunc); err != nil { 69 t.Fatal(err) 70 } 71 72 // Check that there are no leases to begin with 73 if count != 0 { 74 t.Fatalf("bad: lease count; expected:0 actual:%d", count) 75 } 76 77 // Create a lease entry without a client token in it 78 le := &leaseEntry{ 79 LeaseID: "lease/with/no/client/token", 80 Path: "foo/bar", 81 namespace: namespace.RootNamespace, 82 } 83 84 // Persist the invalid lease entry 85 if err = exp.persistEntry(namespace.RootContext(nil), le); err != nil { 86 t.Fatalf("error persisting entry: %v", err) 87 } 88 89 count = 0 90 if err = logical.ScanView(context.Background(), exp.idView, countFunc); err != nil { 91 t.Fatal(err) 92 } 93 94 // Check that the storage was successful and that the count of leases is 95 // now 1 96 if count != 1 { 97 t.Fatalf("bad: lease count; expected:1 actual:%d", count) 98 } 99 100 // Run the tidy operation 101 err = exp.Tidy(namespace.RootContext(nil)) 102 if err != nil { 103 t.Fatal(err) 104 } 105 106 count = 0 107 if err := logical.ScanView(context.Background(), exp.idView, countFunc); err != nil { 108 t.Fatal(err) 109 } 110 111 // Post the tidy operation, the invalid lease entry should have been gone 112 if count != 0 { 113 t.Fatalf("bad: lease count; expected:0 actual:%d", count) 114 } 115 116 // Set a revoked/invalid token in the lease entry 117 le.ClientToken = "invalidtoken" 118 119 // Persist the invalid lease entry 120 if err = exp.persistEntry(namespace.RootContext(nil), le); err != nil { 121 t.Fatalf("error persisting entry: %v", err) 122 } 123 124 count = 0 125 if err = logical.ScanView(context.Background(), exp.idView, countFunc); err != nil { 126 t.Fatal(err) 127 } 128 129 // Check that the storage was successful and that the count of leases is 130 // now 1 131 if count != 1 { 132 t.Fatalf("bad: lease count; expected:1 actual:%d", count) 133 } 134 135 // Run the tidy operation 136 err = exp.Tidy(namespace.RootContext(nil)) 137 if err != nil { 138 t.Fatal(err) 139 } 140 141 count = 0 142 if err = logical.ScanView(context.Background(), exp.idView, countFunc); err != nil { 143 t.Fatal(err) 144 } 145 146 // Post the tidy operation, the invalid lease entry should have been gone 147 if count != 0 { 148 t.Fatalf("bad: lease count; expected:0 actual:%d", count) 149 } 150 151 // Attach an invalid token with 2 leases 152 if err = exp.persistEntry(namespace.RootContext(nil), le); err != nil { 153 t.Fatalf("error persisting entry: %v", err) 154 } 155 156 le.LeaseID = "another/invalid/lease" 157 if err = exp.persistEntry(context.Background(), le); err != nil { 158 t.Fatalf("error persisting entry: %v", err) 159 } 160 161 // Run the tidy operation 162 err = exp.Tidy(namespace.RootContext(nil)) 163 if err != nil { 164 t.Fatal(err) 165 } 166 167 count = 0 168 if err = logical.ScanView(context.Background(), exp.idView, countFunc); err != nil { 169 t.Fatal(err) 170 } 171 172 // Post the tidy operation, the invalid lease entry should have been gone 173 if count != 0 { 174 t.Fatalf("bad: lease count; expected:0 actual:%d", count) 175 } 176 177 for i := 0; i < 1000; i++ { 178 req := &logical.Request{ 179 Operation: logical.ReadOperation, 180 Path: "invalid/lease/" + fmt.Sprintf("%d", i+1), 181 ClientToken: "invalidtoken", 182 } 183 req.SetTokenEntry(&logical.TokenEntry{ID: "invalidtoken", NamespaceID: "root"}) 184 resp := &logical.Response{ 185 Secret: &logical.Secret{ 186 LeaseOptions: logical.LeaseOptions{ 187 TTL: 100 * time.Millisecond, 188 }, 189 }, 190 Data: map[string]interface{}{ 191 "test_key": "test_value", 192 }, 193 } 194 _, err := exp.Register(namespace.RootContext(nil), req, resp) 195 if err != nil { 196 t.Fatalf("err: %v", err) 197 } 198 } 199 200 count = 0 201 if err = logical.ScanView(context.Background(), exp.idView, countFunc); err != nil { 202 t.Fatal(err) 203 } 204 205 // Check that there are 1000 leases now 206 if count != 1000 { 207 t.Fatalf("bad: lease count; expected:1000 actual:%d", count) 208 } 209 210 errCh1 := make(chan error) 211 errCh2 := make(chan error) 212 213 // Initiate tidy of the above 1000 invalid leases in quick succession. Only 214 // one tidy operation can be in flight at any time. One of these requests 215 // should error out. 216 go func() { 217 errCh1 <- exp.Tidy(namespace.RootContext(nil)) 218 }() 219 220 go func() { 221 errCh2 <- exp.Tidy(namespace.RootContext(nil)) 222 }() 223 224 var err1, err2 error 225 226 for i := 0; i < 2; i++ { 227 select { 228 case err1 = <-errCh1: 229 case err2 = <-errCh2: 230 } 231 } 232 233 if err1 != nil || err2 != nil { 234 t.Fatalf("got an error: err1: %v; err2: %v", err1, err2) 235 } 236 if !strings.Contains(logOut.String(), "tidy operation on leases is already in progress") { 237 t.Fatalf("expected to see a warning saying operation in progress, output is %s", logOut.String()) 238 } 239 240 root, err := exp.tokenStore.rootToken(context.Background()) 241 if err != nil { 242 t.Fatal(err) 243 } 244 le.ClientToken = root.ID 245 246 // Attach a valid token with the leases 247 if err = exp.persistEntry(namespace.RootContext(nil), le); err != nil { 248 t.Fatalf("error persisting entry: %v", err) 249 } 250 251 // Run the tidy operation 252 err = exp.Tidy(namespace.RootContext(nil)) 253 if err != nil { 254 t.Fatal(err) 255 } 256 257 count = 0 258 if err = logical.ScanView(context.Background(), exp.idView, countFunc); err != nil { 259 t.Fatal(err) 260 } 261 262 // Post the tidy operation, the valid lease entry should not get affected 263 if count != 1 { 264 t.Fatalf("bad: lease count; expected:1 actual:%d", count) 265 } 266} 267 268// To avoid pulling in deps for all users of the package, don't leave these 269// uncommented in the public tree 270/* 271func BenchmarkExpiration_Restore_Etcd(b *testing.B) { 272 addr := os.Getenv("PHYSICAL_BACKEND_BENCHMARK_ADDR") 273 randPath := fmt.Sprintf("vault-%d/", time.Now().Unix()) 274 275 logger := logging.NewVaultLogger(log.Trace) 276 physicalBackend, err := physEtcd.NewEtcdBackend(map[string]string{ 277 "address": addr, 278 "path": randPath, 279 "max_parallel": "256", 280 }, logger) 281 if err != nil { 282 b.Fatalf("err: %s", err) 283 } 284 285 benchmarkExpirationBackend(b, physicalBackend, 10000) // 10,000 leases 286} 287 288func BenchmarkExpiration_Restore_Consul(b *testing.B) { 289 addr := os.Getenv("PHYSICAL_BACKEND_BENCHMARK_ADDR") 290 randPath := fmt.Sprintf("vault-%d/", time.Now().Unix()) 291 292 logger := logging.NewVaultLogger(log.Trace) 293 physicalBackend, err := physConsul.NewConsulBackend(map[string]string{ 294 "address": addr, 295 "path": randPath, 296 "max_parallel": "256", 297 }, logger) 298 if err != nil { 299 b.Fatalf("err: %s", err) 300 } 301 302 benchmarkExpirationBackend(b, physicalBackend, 10000) // 10,000 leases 303} 304*/ 305 306func BenchmarkExpiration_Restore_InMem(b *testing.B) { 307 logger := logging.NewVaultLogger(log.Trace) 308 inm, err := inmem.NewInmem(nil, logger) 309 if err != nil { 310 b.Fatal(err) 311 } 312 benchmarkExpirationBackend(b, inm, 100000) // 100,000 Leases 313} 314 315func benchmarkExpirationBackend(b *testing.B, physicalBackend physical.Backend, numLeases int) { 316 c, _, _ := TestCoreUnsealedBackend(b, physicalBackend) 317 exp := c.expiration 318 noop := &NoopBackend{} 319 view := NewBarrierView(c.barrier, "logical/") 320 meUUID, err := uuid.GenerateUUID() 321 if err != nil { 322 b.Fatal(err) 323 } 324 err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 325 if err != nil { 326 b.Fatal(err) 327 } 328 329 // Register fake leases 330 for i := 0; i < numLeases; i++ { 331 pathUUID, err := uuid.GenerateUUID() 332 if err != nil { 333 b.Fatal(err) 334 } 335 336 req := &logical.Request{ 337 Operation: logical.ReadOperation, 338 Path: "prod/aws/" + pathUUID, 339 ClientToken: "root", 340 } 341 resp := &logical.Response{ 342 Secret: &logical.Secret{ 343 LeaseOptions: logical.LeaseOptions{ 344 TTL: 400 * time.Second, 345 }, 346 }, 347 Data: map[string]interface{}{ 348 "access_key": "xyz", 349 "secret_key": "abcd", 350 }, 351 } 352 _, err = exp.Register(context.Background(), req, resp) 353 if err != nil { 354 b.Fatalf("err: %v", err) 355 } 356 } 357 358 // Stop everything 359 err = exp.Stop() 360 if err != nil { 361 b.Fatalf("err: %v", err) 362 } 363 364 b.ResetTimer() 365 for i := 0; i < b.N; i++ { 366 err = exp.Restore(nil) 367 // Restore 368 if err != nil { 369 b.Fatalf("err: %v", err) 370 } 371 } 372 b.StopTimer() 373} 374 375func TestExpiration_Restore(t *testing.T) { 376 exp := mockExpiration(t) 377 noop := &NoopBackend{} 378 _, barrier, _ := mockBarrier(t) 379 view := NewBarrierView(barrier, "logical/") 380 meUUID, err := uuid.GenerateUUID() 381 if err != nil { 382 t.Fatal(err) 383 } 384 err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 385 if err != nil { 386 t.Fatal(err) 387 } 388 389 paths := []string{ 390 "prod/aws/foo", 391 "prod/aws/sub/bar", 392 "prod/aws/zip", 393 } 394 for _, path := range paths { 395 req := &logical.Request{ 396 Operation: logical.ReadOperation, 397 Path: path, 398 ClientToken: "foobar", 399 } 400 req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"}) 401 resp := &logical.Response{ 402 Secret: &logical.Secret{ 403 LeaseOptions: logical.LeaseOptions{ 404 TTL: 20 * time.Millisecond, 405 }, 406 }, 407 Data: map[string]interface{}{ 408 "access_key": "xyz", 409 "secret_key": "abcd", 410 }, 411 } 412 _, err := exp.Register(namespace.RootContext(nil), req, resp) 413 if err != nil { 414 t.Fatalf("err: %v", err) 415 } 416 } 417 418 // Stop everything 419 err = exp.Stop() 420 if err != nil { 421 t.Fatalf("err: %v", err) 422 } 423 424 // Restore 425 err = exp.Restore(nil) 426 if err != nil { 427 t.Fatalf("err: %v", err) 428 } 429 430 // Ensure all are reaped 431 start := time.Now() 432 for time.Now().Sub(start) < time.Second { 433 noop.Lock() 434 less := len(noop.Requests) < 3 435 noop.Unlock() 436 437 if less { 438 time.Sleep(5 * time.Millisecond) 439 continue 440 } 441 break 442 } 443 for _, req := range noop.Requests { 444 if req.Operation != logical.RevokeOperation { 445 t.Fatalf("Bad: %v", req) 446 } 447 } 448} 449 450func TestExpiration_Register(t *testing.T) { 451 exp := mockExpiration(t) 452 req := &logical.Request{ 453 Operation: logical.ReadOperation, 454 Path: "prod/aws/foo", 455 ClientToken: "foobar", 456 } 457 req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"}) 458 resp := &logical.Response{ 459 Secret: &logical.Secret{ 460 LeaseOptions: logical.LeaseOptions{ 461 TTL: time.Hour, 462 }, 463 }, 464 Data: map[string]interface{}{ 465 "access_key": "xyz", 466 "secret_key": "abcd", 467 }, 468 } 469 470 id, err := exp.Register(namespace.RootContext(nil), req, resp) 471 if err != nil { 472 t.Fatalf("err: %v", err) 473 } 474 475 if !strings.HasPrefix(id, req.Path) { 476 t.Fatalf("bad: %s", id) 477 } 478 479 if len(id) <= len(req.Path) { 480 t.Fatalf("bad: %s", id) 481 } 482} 483 484func TestExpiration_RegisterAuth(t *testing.T) { 485 exp := mockExpiration(t) 486 root, err := exp.tokenStore.rootToken(context.Background()) 487 if err != nil { 488 t.Fatalf("err: %v", err) 489 } 490 491 auth := &logical.Auth{ 492 ClientToken: root.ID, 493 LeaseOptions: logical.LeaseOptions{ 494 TTL: time.Hour, 495 }, 496 } 497 498 te := &logical.TokenEntry{ 499 Path: "auth/github/login", 500 NamespaceID: namespace.RootNamespaceID, 501 } 502 err = exp.RegisterAuth(namespace.RootContext(nil), te, auth) 503 if err != nil { 504 t.Fatalf("err: %v", err) 505 } 506 507 te = &logical.TokenEntry{ 508 Path: "auth/github/../login", 509 NamespaceID: namespace.RootNamespaceID, 510 } 511 err = exp.RegisterAuth(namespace.RootContext(nil), te, auth) 512 if err == nil { 513 t.Fatal("expected error") 514 } 515} 516 517func TestExpiration_RegisterAuth_NoLease(t *testing.T) { 518 exp := mockExpiration(t) 519 root, err := exp.tokenStore.rootToken(context.Background()) 520 if err != nil { 521 t.Fatalf("err: %v", err) 522 } 523 524 auth := &logical.Auth{ 525 ClientToken: root.ID, 526 } 527 528 te := &logical.TokenEntry{ 529 ID: root.ID, 530 Path: "auth/github/login", 531 NamespaceID: namespace.RootNamespaceID, 532 } 533 err = exp.RegisterAuth(namespace.RootContext(nil), te, auth) 534 if err != nil { 535 t.Fatalf("err: %v", err) 536 } 537 538 // Should not be able to renew, no expiration 539 te = &logical.TokenEntry{ 540 ID: root.ID, 541 Path: "auth/github/login", 542 NamespaceID: namespace.RootNamespaceID, 543 } 544 resp, err := exp.RenewToken(namespace.RootContext(nil), &logical.Request{}, te, 0) 545 if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "lease is not renewable")) { 546 t.Fatalf("bad: err:%v resp:%#v", err, resp) 547 } 548 if resp == nil { 549 t.Fatal("expected a response") 550 } 551 552 // Wait and check token is not invalidated 553 time.Sleep(20 * time.Millisecond) 554 555 // Verify token does not get revoked 556 out, err := exp.tokenStore.Lookup(namespace.RootContext(nil), root.ID) 557 if err != nil { 558 t.Fatalf("err: %v", err) 559 } 560 if out == nil { 561 t.Fatalf("missing token") 562 } 563} 564 565func TestExpiration_Revoke(t *testing.T) { 566 exp := mockExpiration(t) 567 noop := &NoopBackend{} 568 _, barrier, _ := mockBarrier(t) 569 view := NewBarrierView(barrier, "logical/") 570 meUUID, err := uuid.GenerateUUID() 571 if err != nil { 572 t.Fatal(err) 573 } 574 err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 575 if err != nil { 576 t.Fatal(err) 577 } 578 579 req := &logical.Request{ 580 Operation: logical.ReadOperation, 581 Path: "prod/aws/foo", 582 ClientToken: "foobar", 583 } 584 req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"}) 585 resp := &logical.Response{ 586 Secret: &logical.Secret{ 587 LeaseOptions: logical.LeaseOptions{ 588 TTL: time.Hour, 589 }, 590 }, 591 Data: map[string]interface{}{ 592 "access_key": "xyz", 593 "secret_key": "abcd", 594 }, 595 } 596 597 id, err := exp.Register(namespace.RootContext(nil), req, resp) 598 if err != nil { 599 t.Fatalf("err: %v", err) 600 } 601 602 if err := exp.Revoke(namespace.RootContext(nil), id); err != nil { 603 t.Fatalf("err: %v", err) 604 } 605 606 req = noop.Requests[0] 607 if req.Operation != logical.RevokeOperation { 608 t.Fatalf("Bad: %v", req) 609 } 610} 611 612func TestExpiration_RevokeOnExpire(t *testing.T) { 613 exp := mockExpiration(t) 614 noop := &NoopBackend{} 615 _, barrier, _ := mockBarrier(t) 616 view := NewBarrierView(barrier, "logical/") 617 meUUID, err := uuid.GenerateUUID() 618 if err != nil { 619 t.Fatal(err) 620 } 621 err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 622 if err != nil { 623 t.Fatal(err) 624 } 625 626 req := &logical.Request{ 627 Operation: logical.ReadOperation, 628 Path: "prod/aws/foo", 629 ClientToken: "foobar", 630 } 631 req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"}) 632 resp := &logical.Response{ 633 Secret: &logical.Secret{ 634 LeaseOptions: logical.LeaseOptions{ 635 TTL: 20 * time.Millisecond, 636 }, 637 }, 638 Data: map[string]interface{}{ 639 "access_key": "xyz", 640 "secret_key": "abcd", 641 }, 642 } 643 644 _, err = exp.Register(namespace.RootContext(nil), req, resp) 645 if err != nil { 646 t.Fatalf("err: %v", err) 647 } 648 649 start := time.Now() 650 for time.Now().Sub(start) < time.Second { 651 req = nil 652 653 noop.Lock() 654 if len(noop.Requests) > 0 { 655 req = noop.Requests[0] 656 } 657 noop.Unlock() 658 if req == nil { 659 time.Sleep(5 * time.Millisecond) 660 continue 661 } 662 if req.Operation != logical.RevokeOperation { 663 t.Fatalf("Bad: %v", req) 664 } 665 666 break 667 } 668} 669 670func TestExpiration_RevokePrefix(t *testing.T) { 671 exp := mockExpiration(t) 672 noop := &NoopBackend{} 673 _, barrier, _ := mockBarrier(t) 674 view := NewBarrierView(barrier, "logical/") 675 meUUID, err := uuid.GenerateUUID() 676 if err != nil { 677 t.Fatal(err) 678 } 679 err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 680 if err != nil { 681 t.Fatal(err) 682 } 683 684 paths := []string{ 685 "prod/aws/foo", 686 "prod/aws/sub/bar", 687 "prod/aws/zip", 688 } 689 for _, path := range paths { 690 req := &logical.Request{ 691 Operation: logical.ReadOperation, 692 Path: path, 693 ClientToken: "foobar", 694 } 695 req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"}) 696 resp := &logical.Response{ 697 Secret: &logical.Secret{ 698 LeaseOptions: logical.LeaseOptions{ 699 TTL: 20 * time.Millisecond, 700 }, 701 }, 702 Data: map[string]interface{}{ 703 "access_key": "xyz", 704 "secret_key": "abcd", 705 }, 706 } 707 _, err := exp.Register(namespace.RootContext(nil), req, resp) 708 if err != nil { 709 t.Fatalf("err: %v", err) 710 } 711 } 712 713 // Should nuke all the keys 714 if err := exp.RevokePrefix(namespace.RootContext(nil), "prod/aws/", true); err != nil { 715 t.Fatalf("err: %v", err) 716 } 717 718 if len(noop.Requests) != 3 { 719 t.Fatalf("Bad: %v", noop.Requests) 720 } 721 for _, req := range noop.Requests { 722 if req.Operation != logical.RevokeOperation { 723 t.Fatalf("Bad: %v", req) 724 } 725 } 726 727 expect := []string{ 728 "foo", 729 "sub/bar", 730 "zip", 731 } 732 sort.Strings(noop.Paths) 733 sort.Strings(expect) 734 if !reflect.DeepEqual(noop.Paths, expect) { 735 t.Fatalf("bad: %v", noop.Paths) 736 } 737} 738 739func TestExpiration_RevokeByToken(t *testing.T) { 740 exp := mockExpiration(t) 741 noop := &NoopBackend{} 742 _, barrier, _ := mockBarrier(t) 743 view := NewBarrierView(barrier, "logical/") 744 meUUID, err := uuid.GenerateUUID() 745 if err != nil { 746 t.Fatal(err) 747 } 748 err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 749 if err != nil { 750 t.Fatal(err) 751 } 752 753 paths := []string{ 754 "prod/aws/foo", 755 "prod/aws/sub/bar", 756 "prod/aws/zip", 757 } 758 for _, path := range paths { 759 req := &logical.Request{ 760 Operation: logical.ReadOperation, 761 Path: path, 762 ClientToken: "foobarbaz", 763 } 764 req.SetTokenEntry(&logical.TokenEntry{ID: "foobarbaz", NamespaceID: "root"}) 765 resp := &logical.Response{ 766 Secret: &logical.Secret{ 767 LeaseOptions: logical.LeaseOptions{ 768 TTL: 20 * time.Millisecond, 769 }, 770 }, 771 Data: map[string]interface{}{ 772 "access_key": "xyz", 773 "secret_key": "abcd", 774 }, 775 } 776 _, err := exp.Register(namespace.RootContext(nil), req, resp) 777 if err != nil { 778 t.Fatalf("err: %v", err) 779 } 780 } 781 782 // Should nuke all the keys 783 te := &logical.TokenEntry{ 784 ID: "foobarbaz", 785 NamespaceID: namespace.RootNamespaceID, 786 } 787 if err := exp.RevokeByToken(namespace.RootContext(nil), te); err != nil { 788 t.Fatalf("err: %v", err) 789 } 790 791 time.Sleep(300 * time.Millisecond) 792 793 noop.Lock() 794 defer noop.Unlock() 795 796 if len(noop.Requests) != 3 { 797 t.Fatalf("Bad: %v", noop.Requests) 798 } 799 for _, req := range noop.Requests { 800 if req.Operation != logical.RevokeOperation { 801 t.Fatalf("Bad: %v", req) 802 } 803 } 804 805 expect := []string{ 806 "foo", 807 "sub/bar", 808 "zip", 809 } 810 sort.Strings(noop.Paths) 811 sort.Strings(expect) 812 if !reflect.DeepEqual(noop.Paths, expect) { 813 t.Fatalf("bad: %v", noop.Paths) 814 } 815} 816 817func TestExpiration_RevokeByToken_Blocking(t *testing.T) { 818 exp := mockExpiration(t) 819 noop := &NoopBackend{} 820 // Request handle with a timeout context that simulates blocking lease revocation. 821 noop.RequestHandler = func(ctx context.Context, req *logical.Request) (*logical.Response, error) { 822 ctx, cancel := context.WithTimeout(ctx, 200*time.Millisecond) 823 defer cancel() 824 825 select { 826 case <-ctx.Done(): 827 return noop.Response, nil 828 } 829 } 830 831 _, barrier, _ := mockBarrier(t) 832 view := NewBarrierView(barrier, "logical/") 833 meUUID, err := uuid.GenerateUUID() 834 if err != nil { 835 t.Fatal(err) 836 } 837 err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 838 if err != nil { 839 t.Fatal(err) 840 } 841 842 paths := []string{ 843 "prod/aws/foo", 844 "prod/aws/sub/bar", 845 "prod/aws/zip", 846 } 847 for _, path := range paths { 848 req := &logical.Request{ 849 Operation: logical.ReadOperation, 850 Path: path, 851 ClientToken: "foobarbaz", 852 } 853 req.SetTokenEntry(&logical.TokenEntry{ID: "foobarbaz", NamespaceID: "root"}) 854 resp := &logical.Response{ 855 Secret: &logical.Secret{ 856 LeaseOptions: logical.LeaseOptions{ 857 TTL: 1 * time.Minute, 858 }, 859 }, 860 Data: map[string]interface{}{ 861 "access_key": "xyz", 862 "secret_key": "abcd", 863 }, 864 } 865 _, err := exp.Register(namespace.RootContext(nil), req, resp) 866 if err != nil { 867 t.Fatalf("err: %v", err) 868 } 869 } 870 871 // Should nuke all the keys 872 te := &logical.TokenEntry{ 873 ID: "foobarbaz", 874 NamespaceID: namespace.RootNamespaceID, 875 } 876 if err := exp.RevokeByToken(namespace.RootContext(nil), te); err != nil { 877 t.Fatalf("err: %v", err) 878 } 879 880 // Lock and check that no requests has gone through yet 881 noop.Lock() 882 if len(noop.Requests) != 0 { 883 t.Fatalf("Bad: %v", noop.Requests) 884 } 885 noop.Unlock() 886 887 // Wait for a bit for timeouts to trigger and pending revocations to go 888 // through and then we relock 889 time.Sleep(300 * time.Millisecond) 890 891 noop.Lock() 892 defer noop.Unlock() 893 894 // Now make sure that all requests have gone through 895 if len(noop.Requests) != 3 { 896 t.Fatalf("Bad: %v", noop.Requests) 897 } 898 for _, req := range noop.Requests { 899 if req.Operation != logical.RevokeOperation { 900 t.Fatalf("Bad: %v", req) 901 } 902 } 903 904 expect := []string{ 905 "foo", 906 "sub/bar", 907 "zip", 908 } 909 sort.Strings(noop.Paths) 910 sort.Strings(expect) 911 if !reflect.DeepEqual(noop.Paths, expect) { 912 t.Fatalf("bad: %v", noop.Paths) 913 } 914} 915 916func TestExpiration_RenewToken(t *testing.T) { 917 exp := mockExpiration(t) 918 root, err := exp.tokenStore.rootToken(context.Background()) 919 if err != nil { 920 t.Fatalf("err: %v", err) 921 } 922 923 // Register a token 924 auth := &logical.Auth{ 925 ClientToken: root.ID, 926 LeaseOptions: logical.LeaseOptions{ 927 TTL: time.Hour, 928 Renewable: true, 929 }, 930 } 931 932 te := &logical.TokenEntry{ 933 ID: root.ID, 934 Path: "auth/token/login", 935 NamespaceID: namespace.RootNamespaceID, 936 } 937 err = exp.RegisterAuth(namespace.RootContext(nil), te, auth) 938 if err != nil { 939 t.Fatalf("err: %v", err) 940 } 941 942 // Renew the token 943 te = &logical.TokenEntry{ 944 ID: root.ID, 945 Path: "auth/token/login", 946 NamespaceID: namespace.RootNamespaceID, 947 } 948 out, err := exp.RenewToken(namespace.RootContext(nil), &logical.Request{}, te, 0) 949 if err != nil { 950 t.Fatalf("err: %v", err) 951 } 952 953 if auth.ClientToken != out.Auth.ClientToken { 954 t.Fatalf("bad: %#v", out) 955 } 956} 957 958func TestExpiration_RenewToken_period(t *testing.T) { 959 exp := mockExpiration(t) 960 root := &logical.TokenEntry{ 961 Policies: []string{"root"}, 962 Path: "auth/token/root", 963 DisplayName: "root", 964 CreationTime: time.Now().Unix(), 965 Period: time.Minute, 966 NamespaceID: namespace.RootNamespaceID, 967 } 968 if err := exp.tokenStore.create(namespace.RootContext(nil), root); err != nil { 969 t.Fatalf("err: %v", err) 970 } 971 972 // Register a token 973 auth := &logical.Auth{ 974 ClientToken: root.ID, 975 LeaseOptions: logical.LeaseOptions{ 976 TTL: time.Hour, 977 Renewable: true, 978 }, 979 Period: time.Minute, 980 } 981 te := &logical.TokenEntry{ 982 ID: root.ID, 983 Path: "auth/token/login", 984 NamespaceID: namespace.RootNamespaceID, 985 } 986 err := exp.RegisterAuth(namespace.RootContext(nil), te, auth) 987 if err != nil { 988 t.Fatalf("err: %v", err) 989 } 990 991 // Renew the token 992 te = &logical.TokenEntry{ 993 ID: root.ID, 994 Path: "auth/token/login", 995 NamespaceID: namespace.RootNamespaceID, 996 } 997 out, err := exp.RenewToken(namespace.RootContext(nil), &logical.Request{}, te, 0) 998 if err != nil { 999 t.Fatalf("err: %v", err) 1000 } 1001 1002 if auth.ClientToken != out.Auth.ClientToken { 1003 t.Fatalf("bad: %#v", out) 1004 } 1005 1006 if out.Auth.TTL > time.Minute { 1007 t.Fatalf("expected TTL to be less than 1 minute, got: %s", out.Auth.TTL) 1008 } 1009} 1010 1011func TestExpiration_RenewToken_period_backend(t *testing.T) { 1012 exp := mockExpiration(t) 1013 root, err := exp.tokenStore.rootToken(context.Background()) 1014 if err != nil { 1015 t.Fatalf("err: %v", err) 1016 } 1017 1018 // Mount a noop backend 1019 noop := &NoopBackend{ 1020 Response: &logical.Response{ 1021 Auth: &logical.Auth{ 1022 LeaseOptions: logical.LeaseOptions{ 1023 TTL: 10 * time.Second, 1024 Renewable: true, 1025 }, 1026 Period: 5 * time.Second, 1027 }, 1028 }, 1029 DefaultLeaseTTL: 5 * time.Second, 1030 MaxLeaseTTL: 5 * time.Second, 1031 } 1032 1033 _, barrier, _ := mockBarrier(t) 1034 view := NewBarrierView(barrier, credentialBarrierPrefix) 1035 meUUID, err := uuid.GenerateUUID() 1036 if err != nil { 1037 t.Fatal(err) 1038 } 1039 err = exp.router.Mount(noop, "auth/foo/", &MountEntry{Path: "auth/foo/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 1040 if err != nil { 1041 t.Fatal(err) 1042 } 1043 1044 // Register a token 1045 auth := &logical.Auth{ 1046 ClientToken: root.ID, 1047 LeaseOptions: logical.LeaseOptions{ 1048 TTL: 10 * time.Second, 1049 Renewable: true, 1050 }, 1051 Period: 5 * time.Second, 1052 } 1053 te := &logical.TokenEntry{ 1054 ID: root.ID, 1055 Path: "auth/foo/login", 1056 NamespaceID: namespace.RootNamespaceID, 1057 } 1058 1059 err = exp.RegisterAuth(namespace.RootContext(nil), te, auth) 1060 if err != nil { 1061 t.Fatalf("err: %v", err) 1062 } 1063 1064 // Wait 3 seconds 1065 time.Sleep(3 * time.Second) 1066 te = &logical.TokenEntry{ 1067 ID: root.ID, 1068 Path: "auth/foo/login", 1069 NamespaceID: namespace.RootNamespaceID, 1070 } 1071 resp, err := exp.RenewToken(namespace.RootContext(nil), &logical.Request{}, te, 0) 1072 if err != nil { 1073 t.Fatalf("err: %v", err) 1074 } 1075 if resp == nil { 1076 t.Fatal("expected a response") 1077 } 1078 if resp.Auth.TTL == 0 || resp.Auth.TTL > 5*time.Second { 1079 t.Fatalf("expected TTL to be greater than zero and less than or equal to period, got: %s", resp.Auth.TTL) 1080 } 1081 1082 // Wait another 3 seconds. If period works correctly, this should not fail 1083 time.Sleep(3 * time.Second) 1084 resp, err = exp.RenewToken(namespace.RootContext(nil), &logical.Request{}, te, 0) 1085 if err != nil { 1086 t.Fatalf("err: %v", err) 1087 } 1088 if resp == nil { 1089 t.Fatal("expected a response") 1090 } 1091 if resp.Auth.TTL < 4*time.Second || resp.Auth.TTL > 5*time.Second { 1092 t.Fatalf("expected TTL to be around period's value, got: %s", resp.Auth.TTL) 1093 } 1094} 1095 1096func TestExpiration_RenewToken_NotRenewable(t *testing.T) { 1097 exp := mockExpiration(t) 1098 root, err := exp.tokenStore.rootToken(context.Background()) 1099 if err != nil { 1100 t.Fatalf("err: %v", err) 1101 } 1102 1103 // Register a token 1104 auth := &logical.Auth{ 1105 ClientToken: root.ID, 1106 LeaseOptions: logical.LeaseOptions{ 1107 TTL: time.Hour, 1108 Renewable: false, 1109 }, 1110 } 1111 te := &logical.TokenEntry{ 1112 ID: root.ID, 1113 Path: "auth/foo/login", 1114 NamespaceID: namespace.RootNamespaceID, 1115 } 1116 err = exp.RegisterAuth(namespace.RootContext(nil), te, auth) 1117 if err != nil { 1118 t.Fatalf("err: %v", err) 1119 } 1120 1121 // Attempt to renew the token 1122 te = &logical.TokenEntry{ 1123 ID: root.ID, 1124 Path: "auth/github/login", 1125 NamespaceID: namespace.RootNamespaceID, 1126 } 1127 resp, err := exp.RenewToken(namespace.RootContext(nil), &logical.Request{}, te, 0) 1128 if err != nil && (err != logical.ErrInvalidRequest || (resp != nil && resp.IsError() && resp.Error().Error() != "invalid lease ID")) { 1129 t.Fatalf("bad: err:%v resp:%#v", err, resp) 1130 } 1131 if resp == nil { 1132 t.Fatal("expected a response") 1133 } 1134 1135} 1136 1137func TestExpiration_Renew(t *testing.T) { 1138 exp := mockExpiration(t) 1139 noop := &NoopBackend{} 1140 _, barrier, _ := mockBarrier(t) 1141 view := NewBarrierView(barrier, "logical/") 1142 meUUID, err := uuid.GenerateUUID() 1143 if err != nil { 1144 t.Fatal(err) 1145 } 1146 err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 1147 if err != nil { 1148 t.Fatal(err) 1149 } 1150 1151 req := &logical.Request{ 1152 Operation: logical.ReadOperation, 1153 Path: "prod/aws/foo", 1154 ClientToken: "foobar", 1155 } 1156 req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"}) 1157 resp := &logical.Response{ 1158 Secret: &logical.Secret{ 1159 LeaseOptions: logical.LeaseOptions{ 1160 TTL: 20 * time.Millisecond, 1161 Renewable: true, 1162 }, 1163 }, 1164 Data: map[string]interface{}{ 1165 "access_key": "xyz", 1166 "secret_key": "abcd", 1167 }, 1168 } 1169 1170 id, err := exp.Register(namespace.RootContext(nil), req, resp) 1171 if err != nil { 1172 t.Fatalf("err: %v", err) 1173 } 1174 1175 noop.Response = &logical.Response{ 1176 Secret: &logical.Secret{ 1177 LeaseOptions: logical.LeaseOptions{ 1178 TTL: 20 * time.Millisecond, 1179 }, 1180 }, 1181 Data: map[string]interface{}{ 1182 "access_key": "123", 1183 "secret_key": "abcd", 1184 }, 1185 } 1186 1187 out, err := exp.Renew(namespace.RootContext(nil), id, 0) 1188 if err != nil { 1189 t.Fatalf("err: %v", err) 1190 } 1191 1192 noop.Lock() 1193 defer noop.Unlock() 1194 1195 if !reflect.DeepEqual(out, noop.Response) { 1196 t.Fatalf("Bad: %#v", out) 1197 } 1198 1199 if len(noop.Requests) != 1 { 1200 t.Fatalf("Bad: %#v", noop.Requests) 1201 } 1202 req = noop.Requests[0] 1203 if req.Operation != logical.RenewOperation { 1204 t.Fatalf("Bad: %v", req) 1205 } 1206} 1207 1208func TestExpiration_Renew_NotRenewable(t *testing.T) { 1209 exp := mockExpiration(t) 1210 noop := &NoopBackend{} 1211 _, barrier, _ := mockBarrier(t) 1212 view := NewBarrierView(barrier, "logical/") 1213 meUUID, err := uuid.GenerateUUID() 1214 if err != nil { 1215 t.Fatal(err) 1216 } 1217 err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 1218 if err != nil { 1219 t.Fatal(err) 1220 } 1221 1222 req := &logical.Request{ 1223 Operation: logical.ReadOperation, 1224 Path: "prod/aws/foo", 1225 ClientToken: "foobar", 1226 } 1227 req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"}) 1228 resp := &logical.Response{ 1229 Secret: &logical.Secret{ 1230 LeaseOptions: logical.LeaseOptions{ 1231 TTL: 20 * time.Millisecond, 1232 Renewable: false, 1233 }, 1234 }, 1235 Data: map[string]interface{}{ 1236 "access_key": "xyz", 1237 "secret_key": "abcd", 1238 }, 1239 } 1240 1241 id, err := exp.Register(namespace.RootContext(nil), req, resp) 1242 if err != nil { 1243 t.Fatalf("err: %v", err) 1244 } 1245 1246 _, err = exp.Renew(namespace.RootContext(nil), id, 0) 1247 if err.Error() != "lease is not renewable" { 1248 t.Fatalf("err: %v", err) 1249 } 1250 1251 noop.Lock() 1252 defer noop.Unlock() 1253 1254 if len(noop.Requests) != 0 { 1255 t.Fatalf("Bad: %#v", noop.Requests) 1256 } 1257} 1258 1259func TestExpiration_Renew_RevokeOnExpire(t *testing.T) { 1260 exp := mockExpiration(t) 1261 noop := &NoopBackend{} 1262 _, barrier, _ := mockBarrier(t) 1263 view := NewBarrierView(barrier, "logical/") 1264 meUUID, err := uuid.GenerateUUID() 1265 if err != nil { 1266 t.Fatal(err) 1267 } 1268 err = exp.router.Mount(noop, "prod/aws/", &MountEntry{Path: "prod/aws/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 1269 if err != nil { 1270 t.Fatal(err) 1271 } 1272 1273 req := &logical.Request{ 1274 Operation: logical.ReadOperation, 1275 Path: "prod/aws/foo", 1276 ClientToken: "foobar", 1277 } 1278 req.SetTokenEntry(&logical.TokenEntry{ID: "foobar", NamespaceID: "root"}) 1279 resp := &logical.Response{ 1280 Secret: &logical.Secret{ 1281 LeaseOptions: logical.LeaseOptions{ 1282 TTL: 20 * time.Millisecond, 1283 Renewable: true, 1284 }, 1285 }, 1286 Data: map[string]interface{}{ 1287 "access_key": "xyz", 1288 "secret_key": "abcd", 1289 }, 1290 } 1291 1292 id, err := exp.Register(namespace.RootContext(nil), req, resp) 1293 if err != nil { 1294 t.Fatalf("err: %v", err) 1295 } 1296 1297 noop.Response = &logical.Response{ 1298 Secret: &logical.Secret{ 1299 LeaseOptions: logical.LeaseOptions{ 1300 TTL: 20 * time.Millisecond, 1301 }, 1302 }, 1303 Data: map[string]interface{}{ 1304 "access_key": "123", 1305 "secret_key": "abcd", 1306 }, 1307 } 1308 1309 _, err = exp.Renew(namespace.RootContext(nil), id, 0) 1310 if err != nil { 1311 t.Fatalf("err: %v", err) 1312 } 1313 1314 start := time.Now() 1315 for time.Now().Sub(start) < time.Second { 1316 req = nil 1317 1318 noop.Lock() 1319 if len(noop.Requests) >= 2 { 1320 req = noop.Requests[1] 1321 } 1322 noop.Unlock() 1323 1324 if req == nil { 1325 time.Sleep(5 * time.Millisecond) 1326 continue 1327 } 1328 if req.Operation != logical.RevokeOperation { 1329 t.Fatalf("Bad: %v", req) 1330 } 1331 break 1332 } 1333} 1334 1335func TestExpiration_revokeEntry(t *testing.T) { 1336 exp := mockExpiration(t) 1337 1338 noop := &NoopBackend{} 1339 _, barrier, _ := mockBarrier(t) 1340 view := NewBarrierView(barrier, "logical/") 1341 meUUID, err := uuid.GenerateUUID() 1342 if err != nil { 1343 t.Fatal(err) 1344 } 1345 err = exp.router.Mount(noop, "foo/bar/", &MountEntry{Path: "foo/bar/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 1346 if err != nil { 1347 t.Fatal(err) 1348 } 1349 1350 le := &leaseEntry{ 1351 LeaseID: "foo/bar/1234", 1352 Path: "foo/bar", 1353 Data: map[string]interface{}{ 1354 "testing": true, 1355 }, 1356 Secret: &logical.Secret{ 1357 LeaseOptions: logical.LeaseOptions{ 1358 TTL: time.Minute, 1359 }, 1360 }, 1361 IssueTime: time.Now(), 1362 ExpireTime: time.Now(), 1363 namespace: namespace.RootNamespace, 1364 } 1365 1366 err = exp.revokeEntry(namespace.RootContext(nil), le) 1367 if err != nil { 1368 t.Fatalf("err: %v", err) 1369 } 1370 1371 noop.Lock() 1372 defer noop.Unlock() 1373 1374 req := noop.Requests[0] 1375 if req.Operation != logical.RevokeOperation { 1376 t.Fatalf("bad: operation; req: %#v", req) 1377 } 1378 if !reflect.DeepEqual(req.Data, le.Data) { 1379 t.Fatalf("bad: data; req: %#v\n le: %#v\n", req, le) 1380 } 1381} 1382 1383func TestExpiration_revokeEntry_token(t *testing.T) { 1384 exp := mockExpiration(t) 1385 root, err := exp.tokenStore.rootToken(context.Background()) 1386 if err != nil { 1387 t.Fatalf("err: %v", err) 1388 } 1389 1390 // N.B.: Vault doesn't allow both a secret and auth to be returned, but the 1391 // reason for both is that auth needs to be included in order to use the 1392 // token store as it's the only mounted backend, *but* RegisterAuth doesn't 1393 // actually create the index by token, only Register (for a Secret) does. 1394 // So without the Secret we don't do anything when removing the index which 1395 // (at the time of writing) now fails because a bug causing every token 1396 // expiration to do an extra delete to a non-existent key has been fixed, 1397 // and this test relies on this nonstandard behavior. 1398 le := &leaseEntry{ 1399 LeaseID: "foo/bar/1234", 1400 Auth: &logical.Auth{ 1401 ClientToken: root.ID, 1402 LeaseOptions: logical.LeaseOptions{ 1403 TTL: time.Minute, 1404 }, 1405 }, 1406 Secret: &logical.Secret{ 1407 LeaseOptions: logical.LeaseOptions{ 1408 TTL: time.Minute, 1409 }, 1410 }, 1411 ClientToken: root.ID, 1412 Path: "foo/bar", 1413 IssueTime: time.Now(), 1414 ExpireTime: time.Now(), 1415 namespace: namespace.RootNamespace, 1416 } 1417 1418 if err := exp.persistEntry(namespace.RootContext(nil), le); err != nil { 1419 t.Fatalf("error persisting entry: %v", err) 1420 } 1421 if err := exp.createIndexByToken(namespace.RootContext(nil), le, le.ClientToken); err != nil { 1422 t.Fatalf("error creating secondary index: %v", err) 1423 } 1424 exp.updatePending(le, le.Secret.LeaseTotal()) 1425 1426 indexEntry, err := exp.indexByToken(namespace.RootContext(nil), le) 1427 if err != nil { 1428 t.Fatalf("err: %v", err) 1429 } 1430 if indexEntry == nil { 1431 t.Fatalf("err: should have found a secondary index entry") 1432 } 1433 1434 err = exp.revokeEntry(namespace.RootContext(nil), le) 1435 if err != nil { 1436 t.Fatalf("err: %v", err) 1437 } 1438 1439 time.Sleep(300 * time.Millisecond) 1440 1441 out, err := exp.tokenStore.Lookup(namespace.RootContext(nil), le.ClientToken) 1442 if err != nil { 1443 t.Fatalf("err: %v", err) 1444 } 1445 if out != nil { 1446 t.Fatalf("bad: %v", out) 1447 } 1448 1449 indexEntry, err = exp.indexByToken(namespace.RootContext(nil), le) 1450 if err != nil { 1451 t.Fatalf("err: %v", err) 1452 } 1453 if indexEntry != nil { 1454 t.Fatalf("err: should not have found a secondary index entry") 1455 } 1456} 1457 1458func TestExpiration_renewEntry(t *testing.T) { 1459 exp := mockExpiration(t) 1460 1461 noop := &NoopBackend{ 1462 Response: &logical.Response{ 1463 Secret: &logical.Secret{ 1464 LeaseOptions: logical.LeaseOptions{ 1465 Renewable: true, 1466 TTL: time.Hour, 1467 }, 1468 }, 1469 Data: map[string]interface{}{ 1470 "testing": false, 1471 }, 1472 }, 1473 } 1474 _, barrier, _ := mockBarrier(t) 1475 view := NewBarrierView(barrier, "logical/") 1476 meUUID, err := uuid.GenerateUUID() 1477 if err != nil { 1478 t.Fatal(err) 1479 } 1480 err = exp.router.Mount(noop, "foo/bar/", &MountEntry{Path: "foo/bar/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 1481 if err != nil { 1482 t.Fatal(err) 1483 } 1484 1485 le := &leaseEntry{ 1486 LeaseID: "foo/bar/1234", 1487 Path: "foo/bar", 1488 Data: map[string]interface{}{ 1489 "testing": true, 1490 }, 1491 Secret: &logical.Secret{ 1492 LeaseOptions: logical.LeaseOptions{ 1493 TTL: time.Minute, 1494 }, 1495 }, 1496 IssueTime: time.Now(), 1497 ExpireTime: time.Now(), 1498 namespace: namespace.RootNamespace, 1499 } 1500 1501 resp, err := exp.renewEntry(namespace.RootContext(nil), le, 0) 1502 if err != nil { 1503 t.Fatalf("err: %v", err) 1504 } 1505 1506 noop.Lock() 1507 defer noop.Unlock() 1508 1509 if !reflect.DeepEqual(resp, noop.Response) { 1510 t.Fatalf("bad: %#v", resp) 1511 } 1512 1513 req := noop.Requests[0] 1514 if req.Operation != logical.RenewOperation { 1515 t.Fatalf("Bad: %v", req) 1516 } 1517 if !reflect.DeepEqual(req.Data, le.Data) { 1518 t.Fatalf("Bad: %v", req) 1519 } 1520} 1521 1522func TestExpiration_revokeEntry_rejected(t *testing.T) { 1523 core, _, _ := TestCoreUnsealed(t) 1524 exp := core.expiration 1525 1526 rejected := new(uint32) 1527 1528 noop := &NoopBackend{ 1529 RequestHandler: func(ctx context.Context, req *logical.Request) (*logical.Response, error) { 1530 if req.Operation == logical.RevokeOperation { 1531 if atomic.CompareAndSwapUint32(rejected, 0, 1) { 1532 t.Logf("denying revocation") 1533 return nil, errors.New("nope") 1534 } 1535 t.Logf("allowing revocation") 1536 } 1537 return nil, nil 1538 }, 1539 } 1540 _, barrier, _ := mockBarrier(t) 1541 view := NewBarrierView(barrier, "logical/") 1542 meUUID, err := uuid.GenerateUUID() 1543 if err != nil { 1544 t.Fatal(err) 1545 } 1546 err = exp.router.Mount(noop, "foo/bar/", &MountEntry{Path: "foo/bar/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 1547 if err != nil { 1548 t.Fatal(err) 1549 } 1550 1551 le := &leaseEntry{ 1552 LeaseID: "foo/bar/1234", 1553 Path: "foo/bar", 1554 Data: map[string]interface{}{ 1555 "testing": true, 1556 }, 1557 Secret: &logical.Secret{ 1558 LeaseOptions: logical.LeaseOptions{ 1559 TTL: time.Minute, 1560 }, 1561 }, 1562 IssueTime: time.Now(), 1563 ExpireTime: time.Now().Add(time.Minute), 1564 namespace: namespace.RootNamespace, 1565 } 1566 1567 err = exp.persistEntry(namespace.RootContext(nil), le) 1568 if err != nil { 1569 t.Fatal(err) 1570 } 1571 1572 err = exp.LazyRevoke(namespace.RootContext(nil), le.LeaseID) 1573 if err != nil { 1574 t.Fatal(err) 1575 } 1576 1577 // Give time to let the request be handled 1578 time.Sleep(1 * time.Second) 1579 1580 if atomic.LoadUint32(rejected) != 1 { 1581 t.Fatal("unexpected val for rejected") 1582 } 1583 1584 err = exp.Stop() 1585 if err != nil { 1586 t.Fatal(err) 1587 } 1588 1589 err = core.setupExpiration(expireLeaseStrategyRevoke) 1590 if err != nil { 1591 t.Fatal(err) 1592 } 1593 exp = core.expiration 1594 1595 for { 1596 if !exp.inRestoreMode() { 1597 break 1598 } 1599 time.Sleep(100 * time.Millisecond) 1600 } 1601 1602 // Now let the revocation actually process 1603 time.Sleep(1 * time.Second) 1604 1605 le, err = exp.FetchLeaseTimes(namespace.RootContext(nil), le.LeaseID) 1606 if err != nil { 1607 t.Fatal(err) 1608 } 1609 if le != nil { 1610 t.Fatal("lease entry not nil") 1611 } 1612} 1613 1614func TestExpiration_renewAuthEntry(t *testing.T) { 1615 exp := mockExpiration(t) 1616 1617 noop := &NoopBackend{ 1618 Response: &logical.Response{ 1619 Auth: &logical.Auth{ 1620 LeaseOptions: logical.LeaseOptions{ 1621 Renewable: true, 1622 TTL: time.Hour, 1623 }, 1624 }, 1625 }, 1626 } 1627 _, barrier, _ := mockBarrier(t) 1628 view := NewBarrierView(barrier, "auth/") 1629 meUUID, err := uuid.GenerateUUID() 1630 if err != nil { 1631 t.Fatal(err) 1632 } 1633 err = exp.router.Mount(noop, "auth/foo/", &MountEntry{Path: "auth/foo/", Type: "noop", UUID: meUUID, Accessor: "noop-accessor", namespace: namespace.RootNamespace}, view) 1634 if err != nil { 1635 t.Fatal(err) 1636 } 1637 1638 le := &leaseEntry{ 1639 LeaseID: "auth/foo/1234", 1640 Path: "auth/foo/login", 1641 Auth: &logical.Auth{ 1642 LeaseOptions: logical.LeaseOptions{ 1643 Renewable: true, 1644 TTL: time.Minute, 1645 }, 1646 InternalData: map[string]interface{}{ 1647 "MySecret": "secret", 1648 }, 1649 }, 1650 IssueTime: time.Now(), 1651 ExpireTime: time.Now().Add(time.Minute), 1652 namespace: namespace.RootNamespace, 1653 } 1654 1655 resp, err := exp.renewAuthEntry(namespace.RootContext(nil), &logical.Request{}, le, 0) 1656 if err != nil { 1657 t.Fatalf("err: %v", err) 1658 } 1659 1660 noop.Lock() 1661 defer noop.Unlock() 1662 1663 if !reflect.DeepEqual(resp, noop.Response) { 1664 t.Fatalf("bad: %#v", resp) 1665 } 1666 1667 req := noop.Requests[0] 1668 if req.Operation != logical.RenewOperation { 1669 t.Fatalf("Bad: %v", req) 1670 } 1671 if req.Path != "login" { 1672 t.Fatalf("Bad: %v", req) 1673 } 1674 if req.Auth.InternalData["MySecret"] != "secret" { 1675 t.Fatalf("Bad: %v", req) 1676 } 1677} 1678 1679func TestExpiration_PersistLoadDelete(t *testing.T) { 1680 exp := mockExpiration(t) 1681 lastTime := time.Now() 1682 le := &leaseEntry{ 1683 LeaseID: "foo/bar/1234", 1684 Path: "foo/bar", 1685 Data: map[string]interface{}{ 1686 "testing": true, 1687 }, 1688 Secret: &logical.Secret{ 1689 LeaseOptions: logical.LeaseOptions{ 1690 TTL: time.Minute, 1691 }, 1692 }, 1693 IssueTime: lastTime, 1694 ExpireTime: lastTime, 1695 LastRenewalTime: lastTime, 1696 namespace: namespace.RootNamespace, 1697 } 1698 if err := exp.persistEntry(namespace.RootContext(nil), le); err != nil { 1699 t.Fatalf("err: %v", err) 1700 } 1701 1702 out, err := exp.loadEntry(namespace.RootContext(nil), "foo/bar/1234") 1703 if err != nil { 1704 t.Fatalf("err: %v", err) 1705 } 1706 if !le.LastRenewalTime.Equal(out.LastRenewalTime) || 1707 !le.IssueTime.Equal(out.IssueTime) || 1708 !le.ExpireTime.Equal(out.ExpireTime) { 1709 t.Fatalf("bad: expected:\n%#v\nactual:\n%#v", le, out) 1710 } 1711 le.LastRenewalTime = out.LastRenewalTime 1712 le.IssueTime = out.IssueTime 1713 le.ExpireTime = out.ExpireTime 1714 if !reflect.DeepEqual(out, le) { 1715 t.Fatalf("bad: expected:\n%#v\nactual:\n%#v", le, out) 1716 } 1717 1718 err = exp.deleteEntry(namespace.RootContext(nil), le) 1719 if err != nil { 1720 t.Fatalf("err: %v", err) 1721 } 1722 1723 out, err = exp.loadEntry(namespace.RootContext(nil), "foo/bar/1234") 1724 if err != nil { 1725 t.Fatalf("err: %v", err) 1726 } 1727 if out != nil { 1728 t.Fatalf("out: %#v", out) 1729 } 1730} 1731 1732func TestLeaseEntry(t *testing.T) { 1733 le := &leaseEntry{ 1734 LeaseID: "foo/bar/1234", 1735 Path: "foo/bar", 1736 Data: map[string]interface{}{ 1737 "testing": true, 1738 }, 1739 Secret: &logical.Secret{ 1740 LeaseOptions: logical.LeaseOptions{ 1741 TTL: time.Minute, 1742 Renewable: true, 1743 }, 1744 }, 1745 IssueTime: time.Now(), 1746 ExpireTime: time.Now(), 1747 } 1748 1749 enc, err := le.encode() 1750 if err != nil { 1751 t.Fatalf("err: %v", err) 1752 } 1753 1754 out, err := decodeLeaseEntry(enc) 1755 if err != nil { 1756 t.Fatalf("err: %v", err) 1757 } 1758 1759 if !reflect.DeepEqual(out.Data, le.Data) { 1760 t.Fatalf("got: %#v, expect %#v", out, le) 1761 } 1762 1763 // Test renewability 1764 le.ExpireTime = time.Time{} 1765 if r, _ := le.renewable(); r { 1766 t.Fatal("lease with zero expire time is not renewable") 1767 } 1768 le.ExpireTime = time.Now().Add(-1 * time.Hour) 1769 if r, _ := le.renewable(); r { 1770 t.Fatal("lease with expire time in the past is not renewable") 1771 } 1772 le.ExpireTime = time.Now().Add(1 * time.Hour) 1773 if r, err := le.renewable(); !r { 1774 t.Fatalf("lease with future expire time is renewable, err: %v", err) 1775 } 1776 le.Secret.LeaseOptions.Renewable = false 1777 if r, _ := le.renewable(); r { 1778 t.Fatal("secret is set to not be renewable but returns as renewable") 1779 } 1780 le.Secret = nil 1781 le.Auth = &logical.Auth{ 1782 LeaseOptions: logical.LeaseOptions{ 1783 Renewable: true, 1784 }, 1785 } 1786 if r, err := le.renewable(); !r { 1787 t.Fatalf("auth is renewable but is set to not be, err: %v", err) 1788 } 1789 le.Auth.LeaseOptions.Renewable = false 1790 if r, _ := le.renewable(); r { 1791 t.Fatal("auth is set to not be renewable but returns as renewable") 1792 } 1793} 1794 1795func TestExpiration_RevokeForce(t *testing.T) { 1796 core, _, root := TestCoreUnsealed(t) 1797 1798 core.logicalBackends["badrenew"] = badRenewFactory 1799 me := &MountEntry{ 1800 Table: mountTableType, 1801 Path: "badrenew/", 1802 Type: "badrenew", 1803 Accessor: "badrenewaccessor", 1804 } 1805 1806 err := core.mount(namespace.RootContext(nil), me) 1807 if err != nil { 1808 t.Fatal(err) 1809 } 1810 1811 req := &logical.Request{ 1812 Operation: logical.ReadOperation, 1813 Path: "badrenew/creds", 1814 ClientToken: root, 1815 } 1816 req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}}) 1817 1818 resp, err := core.HandleRequest(namespace.RootContext(nil), req) 1819 if err != nil { 1820 t.Fatal(err) 1821 } 1822 if resp == nil { 1823 t.Fatal("response was nil") 1824 } 1825 if resp.Secret == nil { 1826 t.Fatalf("response secret was nil, response was %#v", *resp) 1827 } 1828 1829 req.Operation = logical.UpdateOperation 1830 req.Path = "sys/revoke-prefix/badrenew/creds" 1831 1832 resp, err = core.HandleRequest(namespace.RootContext(nil), req) 1833 if err == nil { 1834 t.Fatal("expected error") 1835 } 1836 1837 req.Path = "sys/revoke-force/badrenew/creds" 1838 resp, err = core.HandleRequest(namespace.RootContext(nil), req) 1839 if err != nil { 1840 t.Fatalf("got error: %s", err) 1841 } 1842} 1843 1844func TestExpiration_RevokeForceSingle(t *testing.T) { 1845 core, _, root := TestCoreUnsealed(t) 1846 1847 core.logicalBackends["badrenew"] = badRenewFactory 1848 me := &MountEntry{ 1849 Table: mountTableType, 1850 Path: "badrenew/", 1851 Type: "badrenew", 1852 Accessor: "badrenewaccessor", 1853 } 1854 1855 err := core.mount(namespace.RootContext(nil), me) 1856 if err != nil { 1857 t.Fatal(err) 1858 } 1859 1860 req := &logical.Request{ 1861 Operation: logical.ReadOperation, 1862 Path: "badrenew/creds", 1863 ClientToken: root, 1864 } 1865 req.SetTokenEntry(&logical.TokenEntry{ID: root, NamespaceID: "root", Policies: []string{"root"}}) 1866 1867 resp, err := core.HandleRequest(namespace.RootContext(nil), req) 1868 if err != nil { 1869 t.Fatal(err) 1870 } 1871 if resp == nil { 1872 t.Fatal("response was nil") 1873 } 1874 if resp.Secret == nil { 1875 t.Fatalf("response secret was nil, response was %#v", *resp) 1876 } 1877 leaseID := resp.Secret.LeaseID 1878 1879 req.Operation = logical.UpdateOperation 1880 req.Path = "sys/leases/lookup" 1881 req.Data = map[string]interface{}{"lease_id": leaseID} 1882 resp, err = core.HandleRequest(namespace.RootContext(nil), req) 1883 if err != nil { 1884 t.Fatal(err) 1885 } 1886 if resp == nil { 1887 t.Fatal("nil response") 1888 } 1889 if resp.Data["id"].(string) != leaseID { 1890 t.Fatalf("expected id %q, got %q", leaseID, resp.Data["id"].(string)) 1891 } 1892 1893 req.Path = "sys/revoke-prefix/" + leaseID 1894 1895 resp, err = core.HandleRequest(namespace.RootContext(nil), req) 1896 if err == nil { 1897 t.Fatal("expected error") 1898 } 1899 1900 req.Path = "sys/revoke-force/" + leaseID 1901 resp, err = core.HandleRequest(namespace.RootContext(nil), req) 1902 if err != nil { 1903 t.Fatalf("got error: %s", err) 1904 } 1905 1906 req.Path = "sys/leases/lookup" 1907 req.Data = map[string]interface{}{"lease_id": leaseID} 1908 resp, err = core.HandleRequest(namespace.RootContext(nil), req) 1909 if err == nil { 1910 t.Fatal("expected error") 1911 } 1912 if !strings.Contains(err.Error(), "invalid request") { 1913 t.Fatalf("bad error: %v", err) 1914 } 1915} 1916 1917func badRenewFactory(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) { 1918 be := &framework.Backend{ 1919 Paths: []*framework.Path{ 1920 { 1921 Pattern: "creds", 1922 Callbacks: map[logical.Operation]framework.OperationFunc{ 1923 logical.ReadOperation: func(context.Context, *logical.Request, *framework.FieldData) (*logical.Response, error) { 1924 resp := &logical.Response{ 1925 Secret: &logical.Secret{ 1926 InternalData: map[string]interface{}{ 1927 "secret_type": "badRenewBackend", 1928 }, 1929 }, 1930 } 1931 resp.Secret.TTL = time.Second * 30 1932 return resp, nil 1933 }, 1934 }, 1935 }, 1936 }, 1937 1938 Secrets: []*framework.Secret{ 1939 &framework.Secret{ 1940 Type: "badRenewBackend", 1941 Revoke: func(context.Context, *logical.Request, *framework.FieldData) (*logical.Response, error) { 1942 return nil, fmt.Errorf("always errors") 1943 }, 1944 }, 1945 }, 1946 BackendType: logical.TypeLogical, 1947 } 1948 1949 err := be.Setup(namespace.RootContext(nil), conf) 1950 if err != nil { 1951 return nil, err 1952 } 1953 1954 return be, nil 1955} 1956