Fix vault client to autorenew or reauthenticate (#7161)

Switch to Vault API's Renewer for token renewal.If
token can no longer be renewed, reauthenticate to
get a fresh token.
This commit is contained in:
poornas 2019-01-29 03:27:23 -08:00 committed by Nitish Tiwari
parent 64b5701971
commit 3467460456

View File

@ -23,6 +23,7 @@ import (
"time" "time"
vault "github.com/hashicorp/vault/api" vault "github.com/hashicorp/vault/api"
"github.com/minio/minio/cmd/logger"
) )
var ( var (
@ -63,6 +64,7 @@ type vaultService struct {
config *VaultConfig config *VaultConfig
client *vault.Client client *vault.Client
leaseDuration time.Duration leaseDuration time.Duration
tokenRenewer *vault.Renewer
} }
var _ KMS = (*vaultService)(nil) // compiler check that *vaultService implements KMS var _ KMS = (*vaultService)(nil) // compiler check that *vaultService implements KMS
@ -120,42 +122,80 @@ func NewVault(config VaultConfig) (KMS, error) {
if config.Namespace != "" { if config.Namespace != "" {
client.SetNamespace(config.Namespace) client.SetNamespace(config.Namespace)
} }
v := &vaultService{client: client, config: &config}
payload := map[string]interface{}{ if err := v.authenticate(); err != nil {
"role_id": config.Auth.AppRole.ID,
"secret_id": config.Auth.AppRole.Secret,
}
resp, err := client.Logical().Write("auth/approle/login", payload)
if err != nil {
return nil, err return nil, err
} }
if resp.Auth == nil {
return nil, ErrKMSAuthLogin
}
client.SetToken(resp.Auth.ClientToken)
v := &vaultService{client: client, config: &config, leaseDuration: time.Duration(resp.Auth.LeaseDuration)}
v.renewToken()
return v, nil return v, nil
} }
// renewToken starts a new go-routine which renews // reauthenticate() tries to login in 1 minute
// the vault authentication token periodically. // intervals until successful.
func (v *vaultService) renewToken() { func (v *vaultService) reauthenticate() {
retryDelay := 1 * time.Minute retryDelay := 1 * time.Minute
go func() { go func() {
for { for {
s, err := v.client.Auth().Token().RenewSelf(int(v.leaseDuration)) if err := v.authenticate(); err != nil {
if err != nil {
time.Sleep(retryDelay) time.Sleep(retryDelay)
continue continue
} }
nextRenew := s.Auth.LeaseDuration / 2 return
time.Sleep(time.Duration(nextRenew) * time.Second)
} }
}() }()
} }
// renewer calls vault client's renewer that automatically
// renews secret periodically
func (v *vaultService) renewer(secret *vault.Secret) {
renewer, err := v.client.NewRenewer(&vault.RenewerInput{
Secret: secret,
})
if err != nil {
logger.FatalIf(err, "crypto: hashicorp vault token renewer could not be started")
}
v.tokenRenewer = renewer
go renewer.Renew()
defer renewer.Stop()
for {
select {
case err := <-renewer.DoneCh():
if err != nil {
v.reauthenticate()
renewer.Stop()
return
}
// Renewal is now over
case renewal := <-renewer.RenewCh():
v.leaseDuration = time.Duration(renewal.Secret.Auth.LeaseDuration)
}
}
}
// authenticate logs the app to vault, and starts the auto renewer
// before secret expires
func (v *vaultService) authenticate() (err error) {
payload := map[string]interface{}{
"role_id": v.config.Auth.AppRole.ID,
"secret_id": v.config.Auth.AppRole.Secret,
}
var secret *vault.Secret
secret, err = v.client.Logical().Write("auth/approle/login", payload)
if err != nil {
return
}
if secret.Auth == nil {
err = ErrKMSAuthLogin
return
}
v.client.SetToken(secret.Auth.ClientToken)
v.leaseDuration = time.Duration(secret.Auth.LeaseDuration)
go v.renewer(secret)
return
}
// GenerateKey returns a new plaintext key, generated by the KMS, // GenerateKey returns a new plaintext key, generated by the KMS,
// and a sealed version of this plaintext key encrypted using the // and a sealed version of this plaintext key encrypted using the
// named key referenced by keyID. It also binds the generated key // named key referenced by keyID. It also binds the generated key