mirror of https://github.com/status-im/consul.git
Automatically renew the token used by the Vault CA provider
This commit is contained in:
parent
9b055021cb
commit
f416c1a8bd
|
@ -3,6 +3,7 @@ package ca
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
@ -11,6 +12,8 @@ import (
|
||||||
|
|
||||||
"github.com/hashicorp/consul/agent/connect"
|
"github.com/hashicorp/consul/agent/connect"
|
||||||
"github.com/hashicorp/consul/agent/structs"
|
"github.com/hashicorp/consul/agent/structs"
|
||||||
|
"github.com/hashicorp/consul/logging"
|
||||||
|
"github.com/hashicorp/go-hclog"
|
||||||
vaultapi "github.com/hashicorp/vault/api"
|
vaultapi "github.com/hashicorp/vault/api"
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
)
|
)
|
||||||
|
@ -23,10 +26,13 @@ var ErrBackendNotInitialized = fmt.Errorf("backend not initialized")
|
||||||
type VaultProvider struct {
|
type VaultProvider struct {
|
||||||
config *structs.VaultCAProviderConfig
|
config *structs.VaultCAProviderConfig
|
||||||
client *vaultapi.Client
|
client *vaultapi.Client
|
||||||
|
doneCh chan struct{}
|
||||||
|
|
||||||
isPrimary bool
|
isPrimary bool
|
||||||
clusterID string
|
clusterID string
|
||||||
spiffeID *connect.SpiffeIDSigning
|
spiffeID *connect.SpiffeIDSigning
|
||||||
setupIntermediatePKIPathDone bool
|
setupIntermediatePKIPathDone bool
|
||||||
|
logger hclog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func vaultTLSConfig(config *structs.VaultCAProviderConfig) *vaultapi.TLSConfig {
|
func vaultTLSConfig(config *structs.VaultCAProviderConfig) *vaultapi.TLSConfig {
|
||||||
|
@ -65,10 +71,73 @@ func (v *VaultProvider) Configure(cfg ProviderConfig) error {
|
||||||
v.isPrimary = cfg.IsPrimary
|
v.isPrimary = cfg.IsPrimary
|
||||||
v.clusterID = cfg.ClusterID
|
v.clusterID = cfg.ClusterID
|
||||||
v.spiffeID = connect.SpiffeIDSigningForCluster(&structs.CAConfiguration{ClusterID: v.clusterID})
|
v.spiffeID = connect.SpiffeIDSigningForCluster(&structs.CAConfiguration{ClusterID: v.clusterID})
|
||||||
|
v.doneCh = make(chan struct{}, 0)
|
||||||
|
|
||||||
|
// Look up the token to see if we can auto-renew its lease.
|
||||||
|
secret, err := client.Auth().Token().Lookup(config.Token)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
var renewable bool
|
||||||
|
if v, ok := secret.Data["renewable"]; ok {
|
||||||
|
renewable, _ = v.(bool)
|
||||||
|
}
|
||||||
|
var increment int64
|
||||||
|
if v, ok := secret.Data["ttl"]; ok {
|
||||||
|
if n, ok := v.(json.Number); ok {
|
||||||
|
increment, _ = n.Int64()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up a renewer to renew the token automatically, if supported.
|
||||||
|
if renewable {
|
||||||
|
renewer, err := client.NewRenewer(&vaultapi.RenewerInput{
|
||||||
|
Secret: &vaultapi.Secret{
|
||||||
|
Auth: &vaultapi.SecretAuth{
|
||||||
|
ClientToken: config.Token,
|
||||||
|
Renewable: renewable,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Increment: int(increment),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error beginning Vault provider token renewal: %v", err)
|
||||||
|
}
|
||||||
|
go v.renewToken(renewer)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// renewToken uses a vaultapi.Renewer to repeatedly renew our token's lease.
|
||||||
|
func (v *VaultProvider) renewToken(renewer *vaultapi.Renewer) {
|
||||||
|
go renewer.Renew()
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-v.doneCh:
|
||||||
|
renewer.Stop()
|
||||||
|
return
|
||||||
|
|
||||||
|
case err := <-renewer.DoneCh():
|
||||||
|
if err != nil {
|
||||||
|
v.logger.Error(fmt.Sprint("Error renewing token for Vault provider: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-renewer.RenewCh():
|
||||||
|
v.logger.Error("Successfully renewed token for Vault provider")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLogger implements the NeedsLogger interface so the provider can log important messages.
|
||||||
|
func (v *VaultProvider) SetLogger(logger hclog.Logger) {
|
||||||
|
v.logger = logger.
|
||||||
|
ResetNamed(logging.Connect).
|
||||||
|
Named(logging.CA).
|
||||||
|
Named(logging.Vault)
|
||||||
|
}
|
||||||
|
|
||||||
// State implements Provider. Vault provider needs no state other than the
|
// State implements Provider. Vault provider needs no state other than the
|
||||||
// user-provided config currently.
|
// user-provided config currently.
|
||||||
func (v *VaultProvider) State() (map[string]string, error) {
|
func (v *VaultProvider) State() (map[string]string, error) {
|
||||||
|
@ -431,6 +500,10 @@ func (c *VaultProvider) SupportsCrossSigning() (bool, error) {
|
||||||
// this down and recreate it on small config changes because the intermediate
|
// this down and recreate it on small config changes because the intermediate
|
||||||
// certs get bundled with the leaf certs, so there's no cost to the CA changing.
|
// certs get bundled with the leaf certs, so there's no cost to the CA changing.
|
||||||
func (v *VaultProvider) Cleanup() error {
|
func (v *VaultProvider) Cleanup() error {
|
||||||
|
if v.doneCh != nil {
|
||||||
|
close(v.doneCh)
|
||||||
|
}
|
||||||
|
|
||||||
return v.client.Sys().Unmount(v.config.IntermediatePKIPath)
|
return v.client.Sys().Unmount(v.config.IntermediatePKIPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -53,4 +53,5 @@ const (
|
||||||
Transaction string = "txn"
|
Transaction string = "txn"
|
||||||
WAN string = "wan"
|
WAN string = "wan"
|
||||||
Watch string = "watch"
|
Watch string = "watch"
|
||||||
|
Vault string = "vault"
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue