Fix race condition with Deriving vault tokens

This PR fixes a race condition in which the client was not locked while
deriving Vault tokens. This allowed the token to be set which would
cause subsequent Vault requests to fail with permission denied because
the incorrect Vault token was being used.

Further this PR makes the unsetting and unlocking of the client atomic
to avoid an even harder to hit race condition (not sure it was ever hit
but was still incorrect).
This commit is contained in:
Alex Dadgar 2017-02-01 16:25:59 -08:00
parent a26482a57a
commit 9987a235a5
1 changed files with 17 additions and 7 deletions

View File

@ -224,6 +224,13 @@ func (c *vaultClient) Stop() {
close(c.stopCh)
}
// unlockAndUnset is used to unset the vault token on the client and release the
// lock. Helper method for deferring a call that does both.
func (c *vaultClient) unlockAndUnset() {
c.client.SetToken("")
c.lock.Unlock()
}
// DeriveToken takes in an allocation and a set of tasks and for each of the
// task, it derives a vault token from nomad server and unwraps it using vault.
// The return value is a map containing all the unwrapped tokens indexed by the
@ -236,6 +243,12 @@ func (c *vaultClient) DeriveToken(alloc *structs.Allocation, taskNames []string)
return nil, fmt.Errorf("vault client is not running")
}
c.lock.Lock()
defer c.unlockAndUnset()
// Use the token supplied to interact with vault
c.client.SetToken("")
return c.tokenDeriver(alloc, taskNames, c.client)
}
@ -253,14 +266,11 @@ func (c *vaultClient) GetConsulACL(token, path string) (*vaultapi.Secret, error)
}
c.lock.Lock()
defer c.lock.Unlock()
defer c.unlockAndUnset()
// Use the token supplied to interact with vault
c.client.SetToken(token)
// Reset the token before returning
defer c.client.SetToken("")
// Read the consul ACL token and return the secret directly
return c.client.Logical().Read(path)
}
@ -377,9 +387,6 @@ func (c *vaultClient) renew(req *vaultClientRenewalRequest) error {
var renewalErr error
leaseDuration := req.increment
if req.isToken {
// Reset the token in the API client before returning
defer c.client.SetToken("")
// Set the token in the API client to the one that needs
// renewal
c.client.SetToken(req.id)
@ -394,6 +401,9 @@ func (c *vaultClient) renew(req *vaultClientRenewalRequest) error {
// Don't set this if renewal fails
leaseDuration = renewResp.Auth.LeaseDuration
}
// Reset the token in the API client before returning
c.client.SetToken("")
} else {
// Renew the secret
renewResp, err := c.client.Sys().Renew(req.id, req.increment)