// defaultMountTable creates a default mount table func defaultMountTable() *MountTable { table := &MountTable{} genericMount := &MountEntry{ Path: "secret/", Type: "generic", Description: "generic secret storage", UUID: uuid.GenerateUUID(), } cubbyholeMount := &MountEntry{ Path: "cubbyhole/", Type: "cubbyhole", Description: "per-token private secret storage", UUID: uuid.GenerateUUID(), } sysMount := &MountEntry{ Path: "sys/", Type: "system", Description: "system endpoints used for control, policy and debugging", UUID: uuid.GenerateUUID(), } table.Entries = append(table.Entries, genericMount) table.Entries = append(table.Entries, cubbyholeMount) table.Entries = append(table.Entries, sysMount) return table }
func (b *backend) pathCredsCreateRead( req *logical.Request, data *framework.FieldData) (*logical.Response, error) { name := data.Get("name").(string) // Get the role role, err := getRole(req.Storage, name) if err != nil { return nil, err } if role == nil { return logical.ErrorResponse(fmt.Sprintf("Unknown role: %s", name)), nil } displayName := req.DisplayName username := fmt.Sprintf("vault_%s_%s_%s_%d", name, displayName, strings.Replace(uuid.GenerateUUID(), "-", "_", -1), time.Now().Unix()) password := uuid.GenerateUUID() // Get our connection session, err := b.DB(req.Storage) if err != nil { return nil, err } // Execute each query for _, query := range splitSQL(role.CreationCQL) { err = session.Query(substQuery(query, map[string]string{ "username": username, "password": password, })).Exec() if err != nil { for _, query := range splitSQL(role.RollbackCQL) { session.Query(substQuery(query, map[string]string{ "username": username, "password": password, })).Exec() } return nil, err } } // Return the secret resp := b.Secret(SecretCredsType).Response(map[string]interface{}{ "username": username, "password": password, }, map[string]interface{}{ "username": username, "role": name, }) resp.Secret.TTL = role.Lease resp.Secret.GracePeriod = role.LeaseGracePeriod return resp, nil }
func TestRouter_Mount(t *testing.T) { r := NewRouter() _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "logical/") n := &NoopBackend{} err := r.Mount(n, "prod/aws/", &MountEntry{UUID: uuid.GenerateUUID()}, view) if err != nil { t.Fatalf("err: %v", err) } err = r.Mount(n, "prod/aws/", &MountEntry{UUID: uuid.GenerateUUID()}, view) if !strings.Contains(err.Error(), "cannot mount under existing mount") { t.Fatalf("err: %v", err) } if path := r.MatchingMount("prod/aws/foo"); path != "prod/aws/" { t.Fatalf("bad: %s", path) } if v := r.MatchingStorageView("prod/aws/foo"); v != view { t.Fatalf("bad: %s", v) } if path := r.MatchingMount("stage/aws/foo"); path != "" { t.Fatalf("bad: %s", path) } if v := r.MatchingStorageView("stage/aws/foo"); v != nil { t.Fatalf("bad: %s", v) } req := &logical.Request{ Path: "prod/aws/foo", } resp, err := r.Route(req) if err != nil { t.Fatalf("err: %v", err) } if resp != nil { t.Fatalf("bad: %v", resp) } // Verify the path if len(n.Paths) != 1 || n.Paths[0] != "foo" { t.Fatalf("bad: %v", n.Paths) } }
func TestRouter_LoginPath(t *testing.T) { r := NewRouter() _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "auth/") n := &NoopBackend{ Login: []string{ "login", "oauth/*", }, } err := r.Mount(n, "auth/foo/", &MountEntry{UUID: uuid.GenerateUUID()}, view) if err != nil { t.Fatalf("err: %v", err) } type tcase struct { path string expect bool } tcases := []tcase{ {"random", false}, {"auth/foo/bar", false}, {"auth/foo/login", true}, {"auth/foo/oauth", false}, {"auth/foo/oauth/redirect", true}, } for _, tc := range tcases { out := r.LoginPath(tc.path) if out != tc.expect { t.Fatalf("bad: path: %s expect: %v got %v", tc.path, tc.expect, out) } } }
func TestRouter_Untaint(t *testing.T) { r := NewRouter() _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "logical/") n := &NoopBackend{} err := r.Mount(n, "prod/aws/", &MountEntry{UUID: uuid.GenerateUUID()}, view) if err != nil { t.Fatalf("err: %v", err) } err = r.Taint("prod/aws/") if err != nil { t.Fatalf("err: %v", err) } err = r.Untaint("prod/aws/") if err != nil { t.Fatalf("err: %v", err) } req := &logical.Request{ Operation: logical.ReadOperation, Path: "prod/aws/foo", } _, err = r.Route(req) if err != nil { t.Fatalf("err: %v", err) } }
func TestSaltID(t *testing.T) { salt := uuid.GenerateUUID() id := "foobarbaz" sid1 := SaltID(salt, id, SHA1Hash) sid2 := SaltID(salt, id, SHA1Hash) if len(sid1) != sha1.Size*2 { t.Fatalf("Bad len: %d %s", len(sid1), sid1) } if sid1 != sid2 { t.Fatalf("mismatch") } sid1 = SaltID(salt, id, SHA256Hash) sid2 = SaltID(salt, id, SHA256Hash) if len(sid1) != sha256.Size*2 { t.Fatalf("Bad len: %d", len(sid1)) } if sid1 != sid2 { t.Fatalf("mismatch") } }
func TestCubbyholeBackend_Read(t *testing.T) { b := testCubbyholeBackend() req := logical.TestRequest(t, logical.WriteOperation, "foo") req.Data["raw"] = "test" storage := req.Storage clientToken := uuid.GenerateUUID() req.ClientToken = clientToken if _, err := b.HandleRequest(req); err != nil { t.Fatalf("err: %v", err) } req = logical.TestRequest(t, logical.ReadOperation, "foo") req.Storage = storage req.ClientToken = clientToken resp, err := b.HandleRequest(req) if err != nil { t.Fatalf("err: %v", err) } expected := &logical.Response{ Data: map[string]interface{}{ "raw": "test", }, } if !reflect.DeepEqual(resp, expected) { t.Fatalf("bad response.\n\nexpected: %#v\n\nGot: %#v", expected, resp) } }
func TestCubbyholeBackend_Delete(t *testing.T) { b := testCubbyholeBackend() req := logical.TestRequest(t, logical.WriteOperation, "foo") req.Data["raw"] = "test" storage := req.Storage clientToken := uuid.GenerateUUID() req.ClientToken = clientToken if _, err := b.HandleRequest(req); err != nil { t.Fatalf("err: %v", err) } req = logical.TestRequest(t, logical.DeleteOperation, "foo") req.Storage = storage req.ClientToken = clientToken resp, err := b.HandleRequest(req) if err != nil { t.Fatalf("err: %v", err) } if resp != nil { t.Fatalf("bad: %v", resp) } req = logical.TestRequest(t, logical.ReadOperation, "foo") req.Storage = storage req.ClientToken = clientToken resp, err = b.HandleRequest(req) if err != nil { t.Fatalf("err: %v", err) } if resp != nil { t.Fatalf("bad: %v", resp) } }
func mockTokenStore(t *testing.T) (*Core, *TokenStore, string) { c, _, root := TestCoreUnsealed(t) me := &MountEntry{ Path: "token/", Type: "token", Description: "token based credentials", } me.UUID = uuid.GenerateUUID() view := NewBarrierView(c.barrier, credentialBarrierPrefix+me.UUID+"/") tokenstore, _ := c.newCredentialBackend("token", c.mountEntrySysView(me), view, nil) ts := tokenstore.(*TokenStore) router := NewRouter() router.Mount(ts, "auth/token/", &MountEntry{UUID: ""}, ts.view) subview := c.systemBarrierView.SubView(expirationSubPath) logger := log.New(os.Stderr, "", log.LstdFlags) exp := NewExpirationManager(router, subview, ts, logger) ts.SetExpirationManager(exp) return c, ts, root }
func TestExpiration_RevokeByToken(t *testing.T) { exp := mockExpiration(t) noop := &NoopBackend{} _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "logical/") exp.router.Mount(noop, "prod/aws/", uuid.GenerateUUID(), view) paths := []string{ "prod/aws/foo", "prod/aws/sub/bar", "prod/aws/zip", } for _, path := range paths { req := &logical.Request{ Operation: logical.ReadOperation, Path: path, ClientToken: "foobarbaz", } resp := &logical.Response{ Secret: &logical.Secret{ LeaseOptions: logical.LeaseOptions{ Lease: 20 * time.Millisecond, }, }, Data: map[string]interface{}{ "access_key": "xyz", "secret_key": "abcd", }, } _, err := exp.Register(req, resp) if err != nil { t.Fatalf("err: %v", err) } } // Should nuke all the keys if err := exp.RevokeByToken("foobarbaz"); err != nil { t.Fatalf("err: %v", err) } if len(noop.Requests) != 3 { t.Fatalf("Bad: %v", noop.Requests) } for _, req := range noop.Requests { if req.Operation != logical.RevokeOperation { t.Fatalf("Bad: %v", req) } } expect := []string{ "foo", "sub/bar", "zip", } sort.Strings(noop.Paths) sort.Strings(expect) if !reflect.DeepEqual(noop.Paths, expect) { t.Fatalf("bad: %v", noop.Paths) } }
// enableCredential is used to enable a new credential backend func (c *Core) enableCredential(entry *MountEntry) error { c.auth.Lock() defer c.auth.Unlock() // Ensure we end the path in a slash if !strings.HasSuffix(entry.Path, "/") { entry.Path += "/" } // Ensure there is a name if entry.Path == "/" { return fmt.Errorf("backend path must be specified") } // Look for matching name for _, ent := range c.auth.Entries { switch { // Existing is oauth/github/ new is oauth/ or // existing is oauth/ and new is oauth/github/ case strings.HasPrefix(ent.Path, entry.Path): fallthrough case strings.HasPrefix(entry.Path, ent.Path): return logical.CodedError(409, "path is already in use") } } // Ensure the token backend is a singleton if entry.Type == "token" { return fmt.Errorf("token credential backend cannot be instantiated") } // Generate a new UUID and view entry.UUID = uuid.GenerateUUID() view := NewBarrierView(c.barrier, credentialBarrierPrefix+entry.UUID+"/") // Create the new backend backend, err := c.newCredentialBackend(entry.Type, c.mountEntrySysView(entry), view, nil) if err != nil { return err } // Update the auth table newTable := c.auth.ShallowClone() newTable.Entries = append(newTable.Entries, entry) if err := c.persistAuth(newTable); err != nil { return errors.New("failed to update auth table") } c.auth = newTable // Mount the backend path := credentialRoutePrefix + entry.Path if err := c.router.Mount(backend, path, entry, view); err != nil { return err } c.logger.Printf("[INFO] core: enabled credential backend '%s' type: %s", entry.Path, entry.Type) return nil }
// defaultAuthTable creates a default auth table func defaultAuthTable() *MountTable { table := &MountTable{} tokenAuth := &MountEntry{ Path: "token/", Type: "token", Description: "token based credentials", UUID: uuid.GenerateUUID(), } table.Entries = append(table.Entries, tokenAuth) return table }
// defaultMountTable creates a default mount table func defaultMountTable() *MountTable { table := &MountTable{} genericMount := &MountEntry{ Path: "secret/", Type: "generic", Description: "generic secret storage", UUID: uuid.GenerateUUID(), } table.Entries = append(table.Entries, genericMount) table.Entries = append(table.Entries, requiredMountTable().Entries...) return table }
// Mount is used to mount a new backend to the mount table. func (c *Core) mount(me *MountEntry) error { c.mounts.Lock() defer c.mounts.Unlock() // Ensure we end the path in a slash if !strings.HasSuffix(me.Path, "/") { me.Path += "/" } // Prevent protected paths from being mounted for _, p := range protectedMounts { if strings.HasPrefix(me.Path, p) { return logical.CodedError(403, fmt.Sprintf("cannot mount '%s'", me.Path)) } } // Do not allow more than one instance of a singleton mount for _, p := range singletonMounts { if me.Type == p { return logical.CodedError(403, fmt.Sprintf("Cannot mount more than one instance of '%s'", me.Type)) } } // Verify there is no conflicting mount if match := c.router.MatchingMount(me.Path); match != "" { return logical.CodedError(409, fmt.Sprintf("existing mount at %s", match)) } // Generate a new UUID and view me.UUID = uuid.GenerateUUID() view := NewBarrierView(c.barrier, backendBarrierPrefix+me.UUID+"/") backend, err := c.newLogicalBackend(me.Type, c.mountEntrySysView(me), view, nil) if err != nil { return err } // Update the mount table newTable := c.mounts.ShallowClone() newTable.Entries = append(newTable.Entries, me) if err := c.persistMounts(newTable); err != nil { return errors.New("failed to update mount table") } c.mounts = newTable // Mount the backend if err := c.router.Mount(backend, me.Path, me, view); err != nil { return err } c.logger.Printf("[INFO] core: mounted '%s' type: %s", me.Path, me.Type) return nil }
// NewSalt creates a new salt based on the configuration func NewSalt(view logical.Storage, config *Config) (*Salt, error) { // Setup the configuration if config == nil { config = &Config{} } if config.Location == "" { config.Location = DefaultLocation } if config.HashFunc == nil { config.HashFunc = SHA256Hash } // Create the salt s := &Salt{ config: config, } // Look for the salt raw, err := view.Get(config.Location) if err != nil { return nil, fmt.Errorf("failed to read salt: %v", err) } // Restore the salt if it exists if raw != nil { s.salt = string(raw.Value) } // Generate a new salt if necessary if s.salt == "" { s.salt = uuid.GenerateUUID() s.generated = true if view != nil { raw := &logical.StorageEntry{ Key: config.Location, Value: []byte(s.salt), } if err := view.Put(raw); err != nil { return nil, fmt.Errorf("failed to persist salt: %v", err) } } } if config.HMAC != nil { if len(config.HMACType) == 0 { return nil, fmt.Errorf("HMACType must be defined") } s.hmacType = config.HMACType } return s, nil }
// enableAudit is used to enable a new audit backend func (c *Core) enableAudit(entry *MountEntry) error { c.audit.Lock() defer c.audit.Unlock() // Ensure we end the path in a slash if !strings.HasSuffix(entry.Path, "/") { entry.Path += "/" } // Ensure there is a name if entry.Path == "/" { return fmt.Errorf("backend path must be specified") } // Look for matching name for _, ent := range c.audit.Entries { switch { // Existing is sql/mysql/ new is sql/ or // existing is sql/ and new is sql/mysql/ case strings.HasPrefix(ent.Path, entry.Path): fallthrough case strings.HasPrefix(entry.Path, ent.Path): return fmt.Errorf("path already in use") } } // Lookup the new backend backend, err := c.newAuditBackend(entry.Type, entry.Options) if err != nil { return err } // Generate a new UUID and view entry.UUID = uuid.GenerateUUID() view := NewBarrierView(c.barrier, auditBarrierPrefix+entry.UUID+"/") // Update the audit table newTable := c.audit.Clone() newTable.Entries = append(newTable.Entries, entry) if err := c.persistAudit(newTable); err != nil { return errors.New("failed to update audit table") } c.audit = newTable // Register the backend c.auditBroker.Register(entry.Path, backend, view) c.logger.Printf("[INFO] core: enabled audit backend '%s' type: %s", entry.Path, entry.Type) return nil }
func TestExpiration_RevokeOnExpire(t *testing.T) { exp := mockExpiration(t) noop := &NoopBackend{} _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "logical/") exp.router.Mount(noop, "prod/aws/", uuid.GenerateUUID(), view) req := &logical.Request{ Operation: logical.ReadOperation, Path: "prod/aws/foo", } resp := &logical.Response{ Secret: &logical.Secret{ LeaseOptions: logical.LeaseOptions{ Lease: 20 * time.Millisecond, }, }, Data: map[string]interface{}{ "access_key": "xyz", "secret_key": "abcd", }, } _, err := exp.Register(req, resp) if err != nil { t.Fatalf("err: %v", err) } start := time.Now() for time.Now().Sub(start) < time.Second { req = nil noop.Lock() if len(noop.Requests) > 0 { req = noop.Requests[0] } noop.Unlock() if req == nil { time.Sleep(5 * time.Millisecond) continue } if req.Operation != logical.RevokeOperation { t.Fatalf("Bad: %v", req) } break } }
// Create is used to create a new token entry. The entry is assigned // a newly generated ID if not provided. func (ts *TokenStore) Create(entry *TokenEntry) error { defer metrics.MeasureSince([]string{"token", "create"}, time.Now()) // Generate an ID if necessary if entry.ID == "" { entry.ID = uuid.GenerateUUID() } saltedId := ts.SaltID(entry.ID) // Marshal the entry enc, err := json.Marshal(entry) if err != nil { return fmt.Errorf("failed to encode entry: %v", err) } // Write the secondary index if necessary. This is done before the // primary index because we'd rather have a dangling pointer with // a missing primary instead of missing the parent index and potentially // escaping the revocation chain. if entry.Parent != "" { // Ensure the parent exists parent, err := ts.Lookup(entry.Parent) if err != nil { return fmt.Errorf("failed to lookup parent: %v", err) } if parent == nil { return fmt.Errorf("parent token not found") } // Create the index entry path := parentPrefix + ts.SaltID(entry.Parent) + "/" + saltedId le := &logical.StorageEntry{Key: path} if err := ts.view.Put(le); err != nil { return fmt.Errorf("failed to persist entry: %v", err) } } // Write the primary ID path := lookupPrefix + saltedId le := &logical.StorageEntry{Key: path, Value: enc} if err := ts.view.Put(le); err != nil { return fmt.Errorf("failed to persist entry: %v", err) } return nil }
// mockRollback returns a mock rollback manager func mockRollback(t *testing.T) (*RollbackManager, *NoopBackend) { backend := new(NoopBackend) mounts := new(MountTable) router := NewRouter() mounts.Entries = []*MountEntry{ &MountEntry{ Path: "foo", }, } if err := router.Mount(backend, "foo", uuid.GenerateUUID(), nil); err != nil { t.Fatalf("err: %s", err) } logger := log.New(os.Stderr, "", log.LstdFlags) rb := NewRollbackManager(logger, mounts, router) rb.period = 10 * time.Millisecond return rb, backend }
func TestRouter_Remount(t *testing.T) { r := NewRouter() _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "logical/") n := &NoopBackend{} err := r.Mount(n, "prod/aws/", &MountEntry{UUID: uuid.GenerateUUID()}, view) if err != nil { t.Fatalf("err: %v", err) } err = r.Remount("prod/aws/", "stage/aws/") if err != nil { t.Fatalf("err: %v", err) } err = r.Remount("prod/aws/", "stage/aws/") if !strings.Contains(err.Error(), "no mount at") { t.Fatalf("err: %v", err) } req := &logical.Request{ Path: "prod/aws/foo", } _, err = r.Route(req) if !strings.Contains(err.Error(), "unsupported path") { t.Fatalf("err: %v", err) } req = &logical.Request{ Path: "stage/aws/foo", } _, err = r.Route(req) if err != nil { t.Fatalf("err: %v", err) } // Verify the path if len(n.Paths) != 1 || n.Paths[0] != "foo" { t.Fatalf("bad: %v", n.Paths) } }
func TestExpiration_revokeEntry(t *testing.T) { exp := mockExpiration(t) noop := &NoopBackend{} _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "logical/") exp.router.Mount(noop, "", uuid.GenerateUUID(), view) le := &leaseEntry{ LeaseID: "foo/bar/1234", Path: "foo/bar", Data: map[string]interface{}{ "testing": true, }, Secret: &logical.Secret{ LeaseOptions: logical.LeaseOptions{ Lease: time.Minute, }, }, IssueTime: time.Now(), ExpireTime: time.Now(), } err := exp.revokeEntry(le) if err != nil { t.Fatalf("err: %v", err) } noop.Lock() defer noop.Unlock() req := noop.Requests[0] if req.Operation != logical.RevokeOperation { t.Fatalf("Bad: %v", req) } if req.Path != le.Path { t.Fatalf("Bad: %v", req) } if !reflect.DeepEqual(req.Data, le.Data) { t.Fatalf("Bad: %v", req) } }
func TestExpiration_Renew_NotRenewable(t *testing.T) { exp := mockExpiration(t) noop := &NoopBackend{} _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "logical/") exp.router.Mount(noop, "prod/aws/", uuid.GenerateUUID(), view) req := &logical.Request{ Operation: logical.ReadOperation, Path: "prod/aws/foo", } resp := &logical.Response{ Secret: &logical.Secret{ LeaseOptions: logical.LeaseOptions{ Lease: 20 * time.Millisecond, Renewable: false, }, }, Data: map[string]interface{}{ "access_key": "xyz", "secret_key": "abcd", }, } id, err := exp.Register(req, resp) if err != nil { t.Fatalf("err: %v", err) } _, err = exp.Renew(id, 0) if err.Error() != "lease is not renewable" { t.Fatalf("err: %v", err) } noop.Lock() defer noop.Unlock() if len(noop.Requests) != 0 { t.Fatalf("Bad: %#v", noop.Requests) } }
// Register is used to take a request and response with an associated // lease. The secret gets assigned a LeaseID and the management of // of lease is assumed by the expiration manager. func (m *ExpirationManager) Register(req *logical.Request, resp *logical.Response) (string, error) { defer metrics.MeasureSince([]string{"expire", "register"}, time.Now()) // Ignore if there is no leased secret if resp == nil || resp.Secret == nil { return "", nil } // Validate the secret if err := resp.Secret.Validate(); err != nil { return "", err } // Create a lease entry le := leaseEntry{ LeaseID: path.Join(req.Path, uuid.GenerateUUID()), ClientToken: req.ClientToken, Path: req.Path, Data: resp.Data, Secret: resp.Secret, IssueTime: time.Now().UTC(), ExpireTime: resp.Secret.ExpirationTime(), } // Encode the entry if err := m.persistEntry(&le); err != nil { return "", err } // Maintain secondary index by token if err := m.indexByToken(le.ClientToken, le.LeaseID); err != nil { return "", err } // Setup revocation timer if there is a lease m.updatePending(&le, resp.Secret.LeaseTotal()) // Done return le.LeaseID, nil }
func TestRouter_Taint(t *testing.T) { r := NewRouter() _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "logical/") n := &NoopBackend{} err := r.Mount(n, "prod/aws/", &MountEntry{UUID: uuid.GenerateUUID()}, view) if err != nil { t.Fatalf("err: %v", err) } err = r.Taint("prod/aws/") if err != nil { t.Fatalf("err: %v", err) } req := &logical.Request{ Operation: logical.ReadOperation, Path: "prod/aws/foo", } _, err = r.Route(req) if err.Error() != "unsupported path" { t.Fatalf("err: %v", err) } // Rollback and Revoke should work req.Operation = logical.RollbackOperation _, err = r.Route(req) if err != nil { t.Fatalf("err: %v", err) } req.Operation = logical.RevokeOperation _, err = r.Route(req) if err != nil { t.Fatalf("err: %v", err) } }
func TestExpiration_Revoke(t *testing.T) { exp := mockExpiration(t) noop := &NoopBackend{} _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "logical/") exp.router.Mount(noop, "prod/aws/", uuid.GenerateUUID(), view) req := &logical.Request{ Operation: logical.ReadOperation, Path: "prod/aws/foo", } resp := &logical.Response{ Secret: &logical.Secret{ LeaseOptions: logical.LeaseOptions{ Lease: time.Hour, }, }, Data: map[string]interface{}{ "access_key": "xyz", "secret_key": "abcd", }, } id, err := exp.Register(req, resp) if err != nil { t.Fatalf("err: %v", err) } if err := exp.Revoke(id); err != nil { t.Fatalf("err: %v", err) } req = noop.Requests[0] if req.Operation != logical.RevokeOperation { t.Fatalf("Bad: %v", req) } }
func TestRouter_RootPath(t *testing.T) { r := NewRouter() _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "logical/") n := &NoopBackend{ Root: []string{ "root", "policy/*", }, } err := r.Mount(n, "prod/aws/", &MountEntry{UUID: uuid.GenerateUUID()}, view) if err != nil { t.Fatalf("err: %v", err) } type tcase struct { path string expect bool } tcases := []tcase{ {"random", false}, {"prod/aws/foo", false}, {"prod/aws/root", true}, {"prod/aws/root-more", false}, {"prod/aws/policy", false}, {"prod/aws/policy/", true}, {"prod/aws/policy/ops", true}, } for _, tc := range tcases { out := r.RootPath(tc.path) if out != tc.expect { t.Fatalf("bad: path: %s expect: %v got %v", tc.path, tc.expect, out) } } }
func TestCubbyholeBackend_List(t *testing.T) { b := testCubbyholeBackend() req := logical.TestRequest(t, logical.WriteOperation, "foo") clientToken := uuid.GenerateUUID() req.Data["raw"] = "test" req.ClientToken = clientToken storage := req.Storage if _, err := b.HandleRequest(req); err != nil { t.Fatalf("err: %v", err) } req = logical.TestRequest(t, logical.WriteOperation, "bar") req.Data["raw"] = "baz" req.ClientToken = clientToken req.Storage = storage if _, err := b.HandleRequest(req); err != nil { t.Fatalf("err: %v", err) } req = logical.TestRequest(t, logical.ListOperation, "") req.Storage = storage req.ClientToken = clientToken resp, err := b.HandleRequest(req) if err != nil { t.Fatalf("err: %v", err) } expKeys := []string{"foo", "bar"} respKeys := resp.Data["keys"].([]string) sort.Strings(expKeys) sort.Strings(respKeys) if !reflect.DeepEqual(respKeys, expKeys) { t.Fatalf("bad response.\n\nexpected: %#v\n\nGot: %#v", expKeys, respKeys) } }
func TestRouter_Unmount(t *testing.T) { r := NewRouter() _, barrier, _ := mockBarrier(t) view := NewBarrierView(barrier, "logical/") n := &NoopBackend{} err := r.Mount(n, "prod/aws/", &MountEntry{UUID: uuid.GenerateUUID()}, view) if err != nil { t.Fatalf("err: %v", err) } err = r.Unmount("prod/aws/") if err != nil { t.Fatalf("err: %v", err) } req := &logical.Request{ Path: "prod/aws/foo", } _, err = r.Route(req) if !strings.Contains(err.Error(), "unsupported path") { t.Fatalf("err: %v", err) } }
// Generates a UUID OTP and its salted value based on the salt of the backend. func (b *backend) GenerateSaltedOTP() (string, string) { str := uuid.GenerateUUID() return str, b.salt.SaltID(str) }
// runStandby is a long running routine that is used when an HA backend // is enabled. It waits until we are leader and switches this Vault to // active. func (c *Core) runStandby(doneCh, stopCh chan struct{}) { defer close(doneCh) c.logger.Printf("[INFO] core: entering standby mode") // Monitor for key rotation keyRotateDone := make(chan struct{}) keyRotateStop := make(chan struct{}) go c.periodicCheckKeyUpgrade(keyRotateDone, keyRotateStop) defer func() { close(keyRotateStop) <-keyRotateDone }() for { // Check for a shutdown select { case <-stopCh: return default: } // Create a lock uuid := uuid.GenerateUUID() lock, err := c.ha.LockWith(coreLockPath, uuid) if err != nil { c.logger.Printf("[ERR] core: failed to create lock: %v", err) return } // Attempt the acquisition leaderCh := c.acquireLock(lock, stopCh) // Bail if we are being shutdown if leaderCh == nil { return } c.logger.Printf("[INFO] core: acquired lock, enabling active operation") // Advertise ourself as leader if err := c.advertiseLeader(uuid); err != nil { c.logger.Printf("[ERR] core: leader advertisement setup failed: %v", err) lock.Unlock() continue } // Attempt the post-unseal process c.stateLock.Lock() err = c.postUnseal() if err == nil { c.standby = false } c.stateLock.Unlock() // Handle a failure to unseal if err != nil { c.logger.Printf("[ERR] core: post-unseal setup failed: %v", err) lock.Unlock() continue } // Monitor a loss of leadership select { case <-leaderCh: c.logger.Printf("[WARN] core: leadership lost, stopping active operation") case <-stopCh: c.logger.Printf("[WARN] core: stopping active operation") } // Clear ourself as leader if err := c.clearLeader(uuid); err != nil { c.logger.Printf("[ERR] core: clearing leader advertisement failed: %v", err) } // Attempt the pre-seal process c.stateLock.Lock() c.standby = true err = c.preSeal() c.stateLock.Unlock() // Give up leadership lock.Unlock() // Check for a failure to prepare to seal if err := c.preSeal(); err != nil { c.logger.Printf("[ERR] core: pre-seal teardown failed: %v", err) continue } } }