mirror of
https://github.com/hashicorp/vault.git
synced 2026-05-28 04:10:44 -04:00
vault: wire tokens into expiration manager
This commit is contained in:
parent
32ef2c4a32
commit
3ccd20cb58
3 changed files with 128 additions and 10 deletions
|
|
@ -282,18 +282,10 @@ func (c *Core) HandleLogin(req *credential.Request) (*credential.Response, error
|
|||
|
||||
// Register with the expiration manager if there is a lease
|
||||
if resp.Secret.Lease > 0 {
|
||||
lReq := &logical.Request{
|
||||
Path: req.Path,
|
||||
Data: req.Data,
|
||||
}
|
||||
lResp := &logical.Response{
|
||||
Secret: resp.Secret,
|
||||
Data: resp.Data,
|
||||
}
|
||||
vaultID, err := c.expiration.Register(lReq, lResp)
|
||||
vaultID, err := c.expiration.RegisterLogin(te.ID, req, resp)
|
||||
if err != nil {
|
||||
c.logger.Printf(
|
||||
"[ERR] core: failed to register lease "+
|
||||
"[ERR] core: failed to register login token lease "+
|
||||
"(request: %#v, response: %#v): %v", req, resp, err)
|
||||
return nil, ErrInternalError
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/credential"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
)
|
||||
|
||||
|
|
@ -300,6 +301,50 @@ func (m *ExpirationManager) Register(req *logical.Request, resp *logical.Respons
|
|||
return le.VaultID, nil
|
||||
}
|
||||
|
||||
// RegisterLogin is used to take a credential request and response with
|
||||
// an associated lease. The secret gets assigned a vaultId and the management of
|
||||
// of lease is assumed by the expiration manager. This is distinct from Register
|
||||
// as the behavior of renew and revocation differs a bit.
|
||||
func (m *ExpirationManager) RegisterLogin(token string, req *credential.Request, resp *credential.Response) (string, error) {
|
||||
// Ignore if there is no leased secret
|
||||
if resp == nil || resp.Secret == nil || resp.Secret.Lease == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Validate the secret
|
||||
if err := resp.Secret.Validate(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Create a lease entry
|
||||
now := time.Now().UTC()
|
||||
leaseTotal := resp.Secret.Lease + resp.Secret.LeaseGracePeriod
|
||||
le := leaseEntry{
|
||||
VaultID: path.Join(req.Path, generateUUID()),
|
||||
LoginToken: token,
|
||||
Path: req.Path,
|
||||
Data: resp.Data,
|
||||
Secret: resp.Secret,
|
||||
IssueTime: now,
|
||||
ExpireTime: now.Add(leaseTotal),
|
||||
}
|
||||
|
||||
// Encode the entry
|
||||
if err := m.persistEntry(&le); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// Setup revocation timer
|
||||
m.pendingLock.Lock()
|
||||
m.pending[le.VaultID] = time.AfterFunc(leaseTotal, func() {
|
||||
m.expireID(le.VaultID)
|
||||
})
|
||||
m.pendingLock.Unlock()
|
||||
|
||||
// Done
|
||||
return le.VaultID, nil
|
||||
}
|
||||
|
||||
// expireID is invoked when a given ID is expired
|
||||
func (m *ExpirationManager) expireID(vaultID string) {
|
||||
// Clear from the pending expiration
|
||||
|
|
@ -321,6 +366,16 @@ func (m *ExpirationManager) expireID(vaultID string) {
|
|||
|
||||
// revokeEntry is used to attempt revocation of an internal entry
|
||||
func (m *ExpirationManager) revokeEntry(le *leaseEntry) error {
|
||||
// Revocation of login tokens is special since we can by-pass the
|
||||
// backend and directly interact with the token store
|
||||
if le.LoginToken != "" {
|
||||
if err := m.tokenStore.RevokeTree(le.LoginToken); err != nil {
|
||||
return fmt.Errorf("failed to revoke token: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Handle standard revocation via backends
|
||||
_, err := m.router.Route(logical.RevokeRequest(
|
||||
le.Path, le.Secret, le.Data))
|
||||
if err != nil {
|
||||
|
|
@ -390,6 +445,7 @@ func (m *ExpirationManager) deleteEntry(vaultID string) error {
|
|||
// manager stores. This is used to handle renew and revocation.
|
||||
type leaseEntry struct {
|
||||
VaultID string `json:"vault_id"`
|
||||
LoginToken string `json:"login_token"`
|
||||
Path string `json:"path"`
|
||||
Data map[string]interface{} `json:"data"`
|
||||
Secret *logical.Secret `json:"secret"`
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/vault/credential"
|
||||
"github.com/hashicorp/vault/logical"
|
||||
"github.com/hashicorp/vault/physical"
|
||||
)
|
||||
|
|
@ -126,6 +127,40 @@ func TestExpiration_Register(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestExpiration_RegisterLogin(t *testing.T) {
|
||||
exp := mockExpiration(t)
|
||||
root, err := exp.tokenStore.RootToken()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
req := &credential.Request{
|
||||
Path: "auth/user/login",
|
||||
}
|
||||
resp := &credential.Response{
|
||||
Secret: &logical.Secret{
|
||||
Lease: time.Hour,
|
||||
},
|
||||
Data: map[string]interface{}{
|
||||
"access_key": "xyz",
|
||||
"secret_key": "abcd",
|
||||
},
|
||||
}
|
||||
|
||||
id, err := exp.RegisterLogin(root.ID, req, resp)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(id, req.Path) {
|
||||
t.Fatalf("bad: %s", id)
|
||||
}
|
||||
|
||||
if len(id) <= len(req.Path) {
|
||||
t.Fatalf("bad: %s", id)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpiration_Revoke(t *testing.T) {
|
||||
exp := mockExpiration(t)
|
||||
noop := &NoopBackend{}
|
||||
|
|
@ -407,6 +442,41 @@ func TestExpiration_revokeEntry(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestExpiration_revokeEntry_token(t *testing.T) {
|
||||
exp := mockExpiration(t)
|
||||
root, err := exp.tokenStore.RootToken()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
le := &leaseEntry{
|
||||
VaultID: "foo/bar/1234",
|
||||
LoginToken: root.ID,
|
||||
Path: "foo/bar",
|
||||
Data: map[string]interface{}{
|
||||
"testing": true,
|
||||
},
|
||||
Secret: &logical.Secret{
|
||||
Lease: time.Minute,
|
||||
},
|
||||
IssueTime: time.Now(),
|
||||
ExpireTime: time.Now(),
|
||||
}
|
||||
|
||||
err = exp.revokeEntry(le)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
out, err := exp.tokenStore.Lookup(root.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if out != nil {
|
||||
t.Fatalf("bad: %v", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpiration_renewEntry(t *testing.T) {
|
||||
exp := mockExpiration(t)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue