Revert PR #7241 to fix vault renewal (#7259)

- Current implementation was spawning renewer goroutines
without waiting for the lease duration to end. Remove vault renewer
and call vault.RenewToken directly and manage reauthentication if
lease expired.
This commit is contained in:
poornas 2019-02-20 12:23:59 -08:00 committed by kannappanr
parent 1e82c4a7c4
commit e098852a80

View File

@ -16,7 +16,6 @@ package crypto
import ( import (
"bytes" "bytes"
"context"
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt" "fmt"
@ -24,7 +23,6 @@ import (
"time" "time"
vault "github.com/hashicorp/vault/api" vault "github.com/hashicorp/vault/api"
"github.com/minio/minio/cmd/logger"
) )
var ( var (
@ -64,6 +62,7 @@ type VaultConfig struct {
type vaultService struct { type vaultService struct {
config *VaultConfig config *VaultConfig
client *vault.Client client *vault.Client
secret *vault.Secret
leaseDuration time.Duration leaseDuration time.Duration
} }
@ -123,86 +122,81 @@ func NewVault(config VaultConfig) (KMS, error) {
client.SetNamespace(config.Namespace) client.SetNamespace(config.Namespace)
} }
v := &vaultService{client: client, config: &config} v := &vaultService{client: client, config: &config}
if err := v.authenticate(); err != nil { if err := v.authenticate(); err != nil {
return nil, err return nil, err
} }
v.renewToken()
return v, nil return v, nil
} }
// renewSecret tries to renew the given secret. It blocks // renewToken starts a new go-routine which renews
// until it receives either the new secret or encounters an error. // the vault authentication token periodically and re-authenticates
func (v *vaultService) renewSecret(secret *vault.Secret) (*vault.Secret, error) { // if the token renewal fails
renewer, err := v.client.NewRenewer(&vault.RenewerInput{ func (v *vaultService) renewToken() {
Secret: secret, retryDelay := v.leaseDuration / 2
}) go func() {
if err != nil { for {
logger.CriticalIf(context.Background(), fmt.Errorf("crypto: failed to create hashicorp vault renewer: %s", err)) if v.secret == nil {
} if err := v.authenticate(); err != nil {
go renewer.Renew() time.Sleep(retryDelay)
defer renewer.Stop() continue
}
for { }
select { s, err := v.client.Auth().Token().RenewSelf(int(v.leaseDuration))
case err := <-renewer.DoneCh(): if err != nil || s == nil {
v.secret = nil
time.Sleep(retryDelay)
continue
}
if ok, err := s.TokenIsRenewable(); !ok || err != nil {
v.secret = nil
continue
}
ttl, err := s.TokenTTL()
if err != nil { if err != nil {
return nil, err v.secret = nil
continue
} }
case renew := <-renewer.RenewCh(): v.secret = s
if renew.Secret == nil || renew.Secret.Auth == nil { retryDelay = ttl / 2
return nil, ErrKMSAuthLogin time.Sleep(retryDelay)
}
return renew.Secret, nil
} }
} }()
} }
// login tries to authenticate the minio server to // authenticate logs the app to vault, and starts the auto renewer
// the Vault KMS using the approle ID and secret. // before secret expires
func (v *vaultService) login() (*vault.Secret, error) { func (v *vaultService) authenticate() (err error) {
payload := map[string]interface{}{ payload := map[string]interface{}{
"role_id": v.config.Auth.AppRole.ID, "role_id": v.config.Auth.AppRole.ID,
"secret_id": v.config.Auth.AppRole.Secret, "secret_id": v.config.Auth.AppRole.Secret,
} }
secret, err := v.client.Logical().Write("auth/approle/login", payload) var tokenID string
var ttl time.Duration
var secret *vault.Secret
secret, err = v.client.Logical().Write("auth/approle/login", payload)
if err != nil { if err != nil {
return nil, err return
} }
if secret == nil || secret.Auth == nil { if secret == nil {
return nil, ErrKMSAuthLogin err = ErrKMSAuthLogin
return
} }
return secret, nil
}
// authenticate tries to authenticate the minio server tokenID, err = secret.TokenID()
// to the Vault KMS and starts a background job to renew
// the login.
func (v *vaultService) authenticate() error {
secret, err := v.login()
if err != nil { if err != nil {
return err err = ErrKMSAuthLogin
return
} }
v.client.SetToken(secret.Auth.ClientToken) ttl, err = secret.TokenTTL()
v.leaseDuration = time.Duration(secret.Auth.LeaseDuration) if err != nil {
err = ErrKMSAuthLogin
// Start background job trying to renew the token return
// or (if this fails) try to login again with app-ID and app-Secret. }
go func(secret *vault.Secret) { v.client.SetToken(tokenID)
for { v.secret = secret
newSecret, err := v.renewSecret(secret) // try to renew the secret (blocking) v.leaseDuration = ttl
if err != nil { return
// Try to login again with app-ID and app-Secret
if newSecret, err = v.login(); err != nil { // failed -> try again
time.Sleep(1 * time.Minute) // retry delay
continue
}
}
secret = newSecret // Now newSecret contains a valid, non-nil *vault.Secret
v.client.SetToken(secret.Auth.ClientToken)
v.leaseDuration = time.Duration(secret.Auth.LeaseDuration)
}
}(secret)
return nil
} }
// GenerateKey returns a new plaintext key, generated by the KMS, // GenerateKey returns a new plaintext key, generated by the KMS,