mirror of
https://github.com/minio/minio.git
synced 2025-01-12 07:23:23 -05:00
crypto: fix nil pointer dereference of vault secret (#7241)
This commit fixes a nil pointer dereference issue that can occur when the Vault KMS returns e.g. a 404 with an empty HTTP response. The Vault client SDK does not treat that as error and returns nil for the error and the secret. Further it simplifies the token renewal and re-authentication mechanism by using a single background go-routine. The control-flow of Vault authentications looks like this: 1. `authenticate()`: Initial login and start of background job 2. Background job starts a `vault.Renewer` to renew the token 3. a) If this succeeds the token gets updated b) If this fails the background job tries to login again 4. If the login in 3b. succeeded goto 2. If it fails goto 3b.
This commit is contained in:
parent
df35d7db9d
commit
6f764a8efd
@ -16,6 +16,7 @@ package crypto
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"context"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -64,7 +65,6 @@ 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
|
||||||
@ -130,31 +130,15 @@ func NewVault(config VaultConfig) (KMS, error) {
|
|||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// reauthenticate() tries to login in 1 minute
|
// renewSecret tries to renew the given secret. It blocks
|
||||||
// intervals until successful.
|
// until it receives either the new secret or encounters an error.
|
||||||
func (v *vaultService) reauthenticate() {
|
func (v *vaultService) renewSecret(secret *vault.Secret) (*vault.Secret, error) {
|
||||||
retryDelay := 1 * time.Minute
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
if err := v.authenticate(); err != nil {
|
|
||||||
time.Sleep(retryDelay)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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{
|
renewer, err := v.client.NewRenewer(&vault.RenewerInput{
|
||||||
Secret: secret,
|
Secret: secret,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.FatalIf(err, "crypto: hashicorp vault token renewer could not be started")
|
logger.CriticalIf(context.Background(), fmt.Errorf("crypto: failed to create hashicorp vault renewer: %s", err))
|
||||||
}
|
}
|
||||||
v.tokenRenewer = renewer
|
|
||||||
go renewer.Renew()
|
go renewer.Renew()
|
||||||
defer renewer.Stop()
|
defer renewer.Stop()
|
||||||
|
|
||||||
@ -162,38 +146,63 @@ func (v *vaultService) renewer(secret *vault.Secret) {
|
|||||||
select {
|
select {
|
||||||
case err := <-renewer.DoneCh():
|
case err := <-renewer.DoneCh():
|
||||||
if err != nil {
|
if err != nil {
|
||||||
v.reauthenticate()
|
return nil, err
|
||||||
renewer.Stop()
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
case renew := <-renewer.RenewCh():
|
||||||
// Renewal is now over
|
if renew.Secret == nil || renew.Secret.Auth == nil {
|
||||||
case renewal := <-renewer.RenewCh():
|
return nil, ErrKMSAuthLogin
|
||||||
v.leaseDuration = time.Duration(renewal.Secret.Auth.LeaseDuration)
|
}
|
||||||
|
return renew.Secret, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticate logs the app to vault, and starts the auto renewer
|
// login tries to authenticate the minio server to
|
||||||
// before secret expires
|
// the Vault KMS using the approle ID and secret.
|
||||||
func (v *vaultService) authenticate() (err error) {
|
func (v *vaultService) login() (*vault.Secret, 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,
|
||||||
}
|
}
|
||||||
var secret *vault.Secret
|
secret, err := v.client.Logical().Write("auth/approle/login", payload)
|
||||||
secret, err = v.client.Logical().Write("auth/approle/login", payload)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
if secret.Auth == nil {
|
if secret == nil || secret.Auth == nil {
|
||||||
err = ErrKMSAuthLogin
|
return nil, ErrKMSAuthLogin
|
||||||
return
|
}
|
||||||
|
return secret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// authenticate tries to authenticate the minio server
|
||||||
|
// 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 {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
v.client.SetToken(secret.Auth.ClientToken)
|
v.client.SetToken(secret.Auth.ClientToken)
|
||||||
v.leaseDuration = time.Duration(secret.Auth.LeaseDuration)
|
v.leaseDuration = time.Duration(secret.Auth.LeaseDuration)
|
||||||
go v.renewer(secret)
|
|
||||||
return
|
// Start background job trying to renew the token
|
||||||
|
// or (if this fails) try to login again with app-ID and app-Secret.
|
||||||
|
go func(secret *vault.Secret) {
|
||||||
|
for {
|
||||||
|
newSecret, err := v.renewSecret(secret) // try to renew the secret (blocking)
|
||||||
|
if err != nil {
|
||||||
|
// 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,
|
||||||
|
Loading…
Reference in New Issue
Block a user