mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 19:47:54 +00:00
Support periodic tidy callback and config endpoints.
This commit is contained in:
@@ -43,6 +43,8 @@ func Backend(conf *logical.BackendConfig) (*framework.Backend, error) {
|
|||||||
pathImageTag(&b),
|
pathImageTag(&b),
|
||||||
pathConfigClient(&b),
|
pathConfigClient(&b),
|
||||||
pathConfigCertificate(&b),
|
pathConfigCertificate(&b),
|
||||||
|
pathConfigTidyBlacklistRoleTag(&b),
|
||||||
|
pathConfigTidyWhitelistIdentity(&b),
|
||||||
pathListCertificates(&b),
|
pathListCertificates(&b),
|
||||||
pathBlacklistRoleTag(&b),
|
pathBlacklistRoleTag(&b),
|
||||||
pathListBlacklistRoleTags(&b),
|
pathListBlacklistRoleTags(&b),
|
||||||
@@ -53,6 +55,8 @@ func Backend(conf *logical.BackendConfig) (*framework.Backend, error) {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
AuthRenew: b.pathLoginRenew,
|
AuthRenew: b.pathLoginRenew,
|
||||||
|
|
||||||
|
TidyFunc: b.tidyFunc,
|
||||||
}
|
}
|
||||||
|
|
||||||
b.EC2ClientsMap = make(map[string]*ec2.EC2)
|
b.EC2ClientsMap = make(map[string]*ec2.EC2)
|
||||||
@@ -69,6 +73,45 @@ type backend struct {
|
|||||||
EC2ClientsMap map[string]*ec2.EC2
|
EC2ClientsMap map[string]*ec2.EC2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *backend) tidyFunc(req *logical.Request) error {
|
||||||
|
b.configMutex.Lock()
|
||||||
|
defer b.configMutex.Unlock()
|
||||||
|
// safety_buffer defaults to 72h
|
||||||
|
safety_buffer := 259200
|
||||||
|
tidyBlacklistConfigEntry, err := configTidyBlacklistRoleTag(req.Storage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
skipBlacklistTidy := false
|
||||||
|
if tidyBlacklistConfigEntry != nil {
|
||||||
|
if tidyBlacklistConfigEntry.DisablePeriodicTidy {
|
||||||
|
skipBlacklistTidy = true
|
||||||
|
}
|
||||||
|
safety_buffer = tidyBlacklistConfigEntry.SafetyBuffer
|
||||||
|
}
|
||||||
|
if !skipBlacklistTidy {
|
||||||
|
tidyBlacklistRoleTag(req.Storage, safety_buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the safety_buffer to 72h
|
||||||
|
safety_buffer = 259200
|
||||||
|
tidyWhitelistConfigEntry, err := configTidyWhitelistIdentity(req.Storage)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
skipWhitelistTidy := false
|
||||||
|
if tidyWhitelistConfigEntry != nil {
|
||||||
|
if tidyWhitelistConfigEntry.DisablePeriodicTidy {
|
||||||
|
skipWhitelistTidy = true
|
||||||
|
}
|
||||||
|
safety_buffer = tidyWhitelistConfigEntry.SafetyBuffer
|
||||||
|
}
|
||||||
|
if !skipWhitelistTidy {
|
||||||
|
tidyWhitelistIdentity(req.Storage, safety_buffer)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
const backendHelp = `
|
const backendHelp = `
|
||||||
AWS auth backend takes in a AWS EC2 instance identity document, its PKCS#7 signature
|
AWS auth backend takes in a AWS EC2 instance identity document, its PKCS#7 signature
|
||||||
and a client created nonce to authenticates the instance with Vault.
|
and a client created nonce to authenticates the instance with Vault.
|
||||||
|
|||||||
@@ -171,8 +171,7 @@ func (b *backend) pathBlacklistRoleTagUpdate(
|
|||||||
|
|
||||||
currentTime := time.Now()
|
currentTime := time.Now()
|
||||||
|
|
||||||
var epoch time.Time
|
if blEntry.CreationTime.IsZero() {
|
||||||
if blEntry.CreationTime.Equal(epoch) {
|
|
||||||
// Set the creation time for the blacklist entry.
|
// Set the creation time for the blacklist entry.
|
||||||
// This should not be updated after setting it once.
|
// This should not be updated after setting it once.
|
||||||
// If blacklist operation is invoked more than once, only update the expiration time.
|
// If blacklist operation is invoked more than once, only update the expiration time.
|
||||||
|
|||||||
@@ -29,46 +29,47 @@ expiration, before it is removed from the backend storage.`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pathBlacklistRoleTagTidyUpdate is used to clean-up the entries in the role tag blacklist.
|
// tidyBlacklistRoleTag is used to clean-up the entries in the role tag blacklist.
|
||||||
func (b *backend) pathBlacklistRoleTagTidyUpdate(
|
func tidyBlacklistRoleTag(s logical.Storage, safety_buffer int) error {
|
||||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
|
||||||
|
|
||||||
// safety_buffer is an optional parameter.
|
|
||||||
safety_buffer := data.Get("safety_buffer").(int)
|
|
||||||
bufferDuration := time.Duration(safety_buffer) * time.Second
|
bufferDuration := time.Duration(safety_buffer) * time.Second
|
||||||
|
tags, err := s.List("blacklist/roletag/")
|
||||||
tags, err := req.Storage.List("blacklist/roletag/")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tag := range tags {
|
for _, tag := range tags {
|
||||||
tagEntry, err := req.Storage.Get("blacklist/roletag/" + tag)
|
tagEntry, err := s.Get("blacklist/roletag/" + tag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching tag %s: %s", tag, err)
|
return fmt.Errorf("error fetching tag %s: %s", tag, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tagEntry == nil {
|
if tagEntry == nil {
|
||||||
return nil, fmt.Errorf("tag entry for tag %s is nil", tag)
|
return fmt.Errorf("tag entry for tag %s is nil", tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
if tagEntry.Value == nil || len(tagEntry.Value) == 0 {
|
if tagEntry.Value == nil || len(tagEntry.Value) == 0 {
|
||||||
return nil, fmt.Errorf("found entry for tag %s but actual tag is empty", tag)
|
return fmt.Errorf("found entry for tag %s but actual tag is empty", tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
var result roleTagBlacklistEntry
|
var result roleTagBlacklistEntry
|
||||||
if err := tagEntry.DecodeJSON(&result); err != nil {
|
if err := tagEntry.DecodeJSON(&result); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().After(result.ExpirationTime.Add(bufferDuration)) {
|
if time.Now().After(result.ExpirationTime.Add(bufferDuration)) {
|
||||||
if err := req.Storage.Delete("blacklist/roletag" + tag); err != nil {
|
if err := s.Delete("blacklist/roletag" + tag); err != nil {
|
||||||
return nil, fmt.Errorf("error deleting tag %s from storage: %s", tag, err)
|
return fmt.Errorf("error deleting tag %s from storage: %s", tag, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pathBlacklistRoleTagTidyUpdate is used to clean-up the entries in the role tag blacklist.
|
||||||
|
func (b *backend) pathBlacklistRoleTagTidyUpdate(
|
||||||
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
return nil, tidyBlacklistRoleTag(req.Storage, data.Get("safety_buffer").(int))
|
||||||
}
|
}
|
||||||
|
|
||||||
const pathBlacklistRoleTagTidySyn = `
|
const pathBlacklistRoleTagTidySyn = `
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
|
|
||||||
func pathConfigClient(b *backend) *framework.Path {
|
func pathConfigClient(b *backend) *framework.Path {
|
||||||
return &framework.Path{
|
return &framework.Path{
|
||||||
Pattern: "config/client",
|
Pattern: "config/client$",
|
||||||
Fields: map[string]*framework.FieldSchema{
|
Fields: map[string]*framework.FieldSchema{
|
||||||
"access_key": &framework.FieldSchema{
|
"access_key": &framework.FieldSchema{
|
||||||
Type: framework.TypeString,
|
Type: framework.TypeString,
|
||||||
|
|||||||
141
builtin/credential/aws/path_config_tidy_blacklist_roletag.go
Normal file
141
builtin/credential/aws/path_config_tidy_blacklist_roletag.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fatih/structs"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
"github.com/hashicorp/vault/logical/framework"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pathConfigTidyBlacklistRoleTag(b *backend) *framework.Path {
|
||||||
|
return &framework.Path{
|
||||||
|
Pattern: "config/tidy/blacklist/roletag$",
|
||||||
|
Fields: map[string]*framework.FieldSchema{
|
||||||
|
"safety_buffer": &framework.FieldSchema{
|
||||||
|
Type: framework.TypeDurationSecond,
|
||||||
|
Default: 259200, //72h
|
||||||
|
Description: `The amount of extra time that must have passed beyond the roletag
|
||||||
|
expiration, before it is removed from the backend storage.`,
|
||||||
|
},
|
||||||
|
"disable_periodic_tidy": &framework.FieldSchema{
|
||||||
|
Type: framework.TypeBool,
|
||||||
|
Default: false,
|
||||||
|
Description: "If set to 'true', disables the periodic tidying of the 'blacklist/roletag/<role_tag>' entries and 'whitelist/identity' entries.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
ExistenceCheck: b.pathConfigTidyBlacklistRoleTagExistenceCheck,
|
||||||
|
|
||||||
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
|
logical.CreateOperation: b.pathConfigTidyBlacklistRoleTagCreateUpdate,
|
||||||
|
logical.UpdateOperation: b.pathConfigTidyBlacklistRoleTagCreateUpdate,
|
||||||
|
},
|
||||||
|
|
||||||
|
HelpSynopsis: pathConfigTidyBlacklistRoleTagHelpSyn,
|
||||||
|
HelpDescription: pathConfigTidyBlacklistRoleTagHelpDesc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathConfigTidyBlacklistRoleTagExistenceCheck(req *logical.Request, data *framework.FieldData) (bool, error) {
|
||||||
|
b.configMutex.RLock()
|
||||||
|
defer b.configMutex.RUnlock()
|
||||||
|
|
||||||
|
entry, err := configTidyBlacklistRoleTag(req.Storage)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return entry != nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func configTidyBlacklistRoleTag(s logical.Storage) (*tidyBlacklistRoleTagConfig, error) {
|
||||||
|
entry, err := s.Get("config/tidy/blacklist/roletag")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if entry == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var result tidyBlacklistRoleTagConfig
|
||||||
|
if err := entry.DecodeJSON(&result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathConfigTidyBlacklistRoleTagCreateUpdate(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
b.configMutex.Lock()
|
||||||
|
defer b.configMutex.Unlock()
|
||||||
|
configEntry, err := configTidyBlacklistRoleTag(req.Storage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if configEntry == nil {
|
||||||
|
configEntry = &tidyBlacklistRoleTagConfig{}
|
||||||
|
}
|
||||||
|
safetyBufferInt, ok := data.GetOk("safety_buffer")
|
||||||
|
if ok {
|
||||||
|
configEntry.SafetyBuffer = safetyBufferInt.(int)
|
||||||
|
} else if req.Operation == logical.CreateOperation {
|
||||||
|
configEntry.SafetyBuffer = data.Get("safety_buffer").(int)
|
||||||
|
}
|
||||||
|
disablePeriodicTidyBool, ok := data.GetOk("disable_periodic_tidy")
|
||||||
|
if ok {
|
||||||
|
configEntry.DisablePeriodicTidy = disablePeriodicTidyBool.(bool)
|
||||||
|
} else if req.Operation == logical.CreateOperation {
|
||||||
|
configEntry.DisablePeriodicTidy = data.Get("disable_periodic_tidy").(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry, err := logical.StorageEntryJSON("config/tidy/blacklist/roletag", configEntry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := req.Storage.Put(entry); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathConfigTidyBlacklistRoleTagRead(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
b.configMutex.RLock()
|
||||||
|
defer b.configMutex.RUnlock()
|
||||||
|
|
||||||
|
clientConfig, err := configTidyBlacklistRoleTag(req.Storage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientConfig == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return &logical.Response{
|
||||||
|
Data: structs.New(clientConfig).Map(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathConfigTidyBlacklistRoleTagDelete(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
b.configMutex.Lock()
|
||||||
|
defer b.configMutex.Unlock()
|
||||||
|
|
||||||
|
if err := req.Storage.Delete("config/tidy/blacklist/roletag"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tidyBlacklistRoleTagConfig struct {
|
||||||
|
SafetyBuffer int `json:"safety_buffer" structs:"safety_buffer" mapstructure:"safety_buffer"`
|
||||||
|
DisablePeriodicTidy bool `json:"disable_periodic_tidy" structs:"disable_periodic_tidy" mapstructure:"disable_periodic_tidy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathConfigTidyBlacklistRoleTagHelpSyn = `
|
||||||
|
Configures the periodic tidying operation of the blacklisted role tag entries.
|
||||||
|
`
|
||||||
|
const pathConfigTidyBlacklistRoleTagHelpDesc = `
|
||||||
|
By default, the expired entries in the blacklist will be attempted to be removed
|
||||||
|
periodically. This operation will look for expired items in the list and purge them.
|
||||||
|
However, there is a safety buffer duration (defaults to 72h), which purges the entries,
|
||||||
|
only if they have been persisting this duration, past its expiration time.
|
||||||
|
`
|
||||||
141
builtin/credential/aws/path_config_tidy_whitelist_identity.go
Normal file
141
builtin/credential/aws/path_config_tidy_whitelist_identity.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package aws
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fatih/structs"
|
||||||
|
"github.com/hashicorp/vault/logical"
|
||||||
|
"github.com/hashicorp/vault/logical/framework"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pathConfigTidyWhitelistIdentity(b *backend) *framework.Path {
|
||||||
|
return &framework.Path{
|
||||||
|
Pattern: "config/tidy/whitelist/identity$",
|
||||||
|
Fields: map[string]*framework.FieldSchema{
|
||||||
|
"safety_buffer": &framework.FieldSchema{
|
||||||
|
Type: framework.TypeDurationSecond,
|
||||||
|
Default: 259200, //72h
|
||||||
|
Description: `The amount of extra time that must have passed beyond the identity's
|
||||||
|
expiration, before it is removed from the backend storage.`,
|
||||||
|
},
|
||||||
|
"disable_periodic_tidy": &framework.FieldSchema{
|
||||||
|
Type: framework.TypeBool,
|
||||||
|
Default: false,
|
||||||
|
Description: "If set to 'true', disables the periodic tidying of the 'whitelist/identity/<instance_id>' entries and 'whitelist/identity' entries.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
ExistenceCheck: b.pathConfigTidyWhitelistIdentityExistenceCheck,
|
||||||
|
|
||||||
|
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||||
|
logical.CreateOperation: b.pathConfigTidyWhitelistIdentityCreateUpdate,
|
||||||
|
logical.UpdateOperation: b.pathConfigTidyWhitelistIdentityCreateUpdate,
|
||||||
|
},
|
||||||
|
|
||||||
|
HelpSynopsis: pathConfigTidyWhitelistIdentityHelpSyn,
|
||||||
|
HelpDescription: pathConfigTidyWhitelistIdentityHelpDesc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathConfigTidyWhitelistIdentityExistenceCheck(req *logical.Request, data *framework.FieldData) (bool, error) {
|
||||||
|
b.configMutex.RLock()
|
||||||
|
defer b.configMutex.RUnlock()
|
||||||
|
|
||||||
|
entry, err := configTidyWhitelistIdentity(req.Storage)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return entry != nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func configTidyWhitelistIdentity(s logical.Storage) (*tidyWhitelistIdentityConfig, error) {
|
||||||
|
entry, err := s.Get("config/tidy/whitelist/identity")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if entry == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var result tidyWhitelistIdentityConfig
|
||||||
|
if err := entry.DecodeJSON(&result); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathConfigTidyWhitelistIdentityCreateUpdate(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
b.configMutex.Lock()
|
||||||
|
defer b.configMutex.Unlock()
|
||||||
|
configEntry, err := configTidyWhitelistIdentity(req.Storage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if configEntry == nil {
|
||||||
|
configEntry = &tidyWhitelistIdentityConfig{}
|
||||||
|
}
|
||||||
|
safetyBufferInt, ok := data.GetOk("safety_buffer")
|
||||||
|
if ok {
|
||||||
|
configEntry.SafetyBuffer = safetyBufferInt.(int)
|
||||||
|
} else if req.Operation == logical.CreateOperation {
|
||||||
|
configEntry.SafetyBuffer = data.Get("safety_buffer").(int)
|
||||||
|
}
|
||||||
|
disablePeriodicTidyBool, ok := data.GetOk("disable_periodic_tidy")
|
||||||
|
if ok {
|
||||||
|
configEntry.DisablePeriodicTidy = disablePeriodicTidyBool.(bool)
|
||||||
|
} else if req.Operation == logical.CreateOperation {
|
||||||
|
configEntry.DisablePeriodicTidy = data.Get("disable_periodic_tidy").(bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
entry, err := logical.StorageEntryJSON("config/tidy/whitelist/identity", configEntry)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := req.Storage.Put(entry); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathConfigTidyWhitelistIdentityRead(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
b.configMutex.RLock()
|
||||||
|
defer b.configMutex.RUnlock()
|
||||||
|
|
||||||
|
clientConfig, err := configTidyWhitelistIdentity(req.Storage)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientConfig == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return &logical.Response{
|
||||||
|
Data: structs.New(clientConfig).Map(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *backend) pathConfigTidyWhitelistIdentityDelete(req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
b.configMutex.Lock()
|
||||||
|
defer b.configMutex.Unlock()
|
||||||
|
|
||||||
|
if err := req.Storage.Delete("config/tidy/whitelist/identity"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type tidyWhitelistIdentityConfig struct {
|
||||||
|
SafetyBuffer int `json:"safety_buffer" structs:"safety_buffer" mapstructure:"safety_buffer"`
|
||||||
|
DisablePeriodicTidy bool `json:"disable_periodic_tidy" structs:"disable_periodic_tidy" mapstructure:"disable_periodic_tidy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathConfigTidyWhitelistIdentityHelpSyn = `
|
||||||
|
Configures the periodic tidying operation of the whitelisted identity entries.
|
||||||
|
`
|
||||||
|
const pathConfigTidyWhitelistIdentityHelpDesc = `
|
||||||
|
By default, the expired entries in teb whitelist will be attempted to be removed
|
||||||
|
periodically. This operation will look for expired items in the list and purge them.
|
||||||
|
However, there is a safety buffer duration (defaults to 72h), which purges the entries,
|
||||||
|
only if they have been persisting this duration, past its expiration time.
|
||||||
|
`
|
||||||
@@ -29,46 +29,48 @@ expiration, before it is removed from the backend storage.`,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pathWhitelistIdentityTidyUpdate is used to delete entries in the whitelist that are expired.
|
// tidyWhitelistIdentity is used to delete entries in the whitelist that are expired.
|
||||||
func (b *backend) pathWhitelistIdentityTidyUpdate(
|
func tidyWhitelistIdentity(s logical.Storage, safety_buffer int) error {
|
||||||
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
|
||||||
|
|
||||||
safety_buffer := data.Get("safety_buffer").(int)
|
|
||||||
|
|
||||||
bufferDuration := time.Duration(safety_buffer) * time.Second
|
bufferDuration := time.Duration(safety_buffer) * time.Second
|
||||||
|
|
||||||
identities, err := req.Storage.List("whitelist/identity/")
|
identities, err := s.List("whitelist/identity/")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, instanceID := range identities {
|
for _, instanceID := range identities {
|
||||||
identityEntry, err := req.Storage.Get("whitelist/identity/" + instanceID)
|
identityEntry, err := s.Get("whitelist/identity/" + instanceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error fetching identity of instanceID %s: %s", instanceID, err)
|
return fmt.Errorf("error fetching identity of instanceID %s: %s", instanceID, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if identityEntry == nil {
|
if identityEntry == nil {
|
||||||
return nil, fmt.Errorf("identity entry for instanceID %s is nil", instanceID)
|
return fmt.Errorf("identity entry for instanceID %s is nil", instanceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
if identityEntry.Value == nil || len(identityEntry.Value) == 0 {
|
if identityEntry.Value == nil || len(identityEntry.Value) == 0 {
|
||||||
return nil, fmt.Errorf("found identity entry for instanceID %s but actual identity is empty", instanceID)
|
return fmt.Errorf("found identity entry for instanceID %s but actual identity is empty", instanceID)
|
||||||
}
|
}
|
||||||
|
|
||||||
var result whitelistIdentity
|
var result whitelistIdentity
|
||||||
if err := identityEntry.DecodeJSON(&result); err != nil {
|
if err := identityEntry.DecodeJSON(&result); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if time.Now().After(result.ExpirationTime.Add(bufferDuration)) {
|
if time.Now().After(result.ExpirationTime.Add(bufferDuration)) {
|
||||||
if err := req.Storage.Delete("whitelist/identity" + instanceID); err != nil {
|
if err := s.Delete("whitelist/identity" + instanceID); err != nil {
|
||||||
return nil, fmt.Errorf("error deleting identity of instanceID %s from storage: %s", instanceID, err)
|
return fmt.Errorf("error deleting identity of instanceID %s from storage: %s", instanceID, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// pathWhitelistIdentityTidyUpdate is used to delete entries in the whitelist that are expired.
|
||||||
|
func (b *backend) pathWhitelistIdentityTidyUpdate(
|
||||||
|
req *logical.Request, data *framework.FieldData) (*logical.Response, error) {
|
||||||
|
return nil, tidyWhitelistIdentity(req.Storage, data.Get("safety_buffer").(int))
|
||||||
}
|
}
|
||||||
|
|
||||||
const pathWhitelistIdentityTidySyn = `
|
const pathWhitelistIdentityTidySyn = `
|
||||||
|
|||||||
@@ -35,8 +35,8 @@ func Backend() *framework.Backend {
|
|||||||
secretAccessKeys(&b),
|
secretAccessKeys(&b),
|
||||||
},
|
},
|
||||||
|
|
||||||
Rollback: rollback,
|
WALRollback: walRollback,
|
||||||
RollbackMinAge: 5 * time.Minute,
|
WALRollbackMinAge: 5 * time.Minute,
|
||||||
}
|
}
|
||||||
|
|
||||||
return b.Backend
|
return b.Backend
|
||||||
|
|||||||
@@ -7,12 +7,12 @@ import (
|
|||||||
"github.com/hashicorp/vault/logical/framework"
|
"github.com/hashicorp/vault/logical/framework"
|
||||||
)
|
)
|
||||||
|
|
||||||
var rollbackMap = map[string]framework.RollbackFunc{
|
var walRollbackMap = map[string]framework.WALRollbackFunc{
|
||||||
"user": pathUserRollback,
|
"user": pathUserRollback,
|
||||||
}
|
}
|
||||||
|
|
||||||
func rollback(req *logical.Request, kind string, data interface{}) error {
|
func walRollback(req *logical.Request, kind string, data interface{}) error {
|
||||||
f, ok := rollbackMap[kind]
|
f, ok := walRollbackMap[kind]
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("unknown type to rollback")
|
return fmt.Errorf("unknown type to rollback")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,14 +42,24 @@ type Backend struct {
|
|||||||
// and ease specifying callbacks for revocation, renewal, etc.
|
// and ease specifying callbacks for revocation, renewal, etc.
|
||||||
Secrets []*Secret
|
Secrets []*Secret
|
||||||
|
|
||||||
// Rollback is called when a WAL entry (see wal.go) has to be rolled
|
// TidyFunc is the callback, which if set, will be invoked when the
|
||||||
|
// periodic timer of RollbackManager ticks. This can be used by
|
||||||
|
// backends to do any tidying tasks.
|
||||||
|
//
|
||||||
|
// TidyFunc is different from 'Clean' in the sense that, TidyFunc is
|
||||||
|
// invoked to, say to periodically delete expired/stale entries in backend's
|
||||||
|
// storage, while the backend is still being used. Whereas `Clean` is
|
||||||
|
// invoked just before the backend is unmounted.
|
||||||
|
TidyFunc tidyFunc
|
||||||
|
|
||||||
|
// WALRollback is called when a WAL entry (see wal.go) has to be rolled
|
||||||
// back. It is called with the data from the entry.
|
// back. It is called with the data from the entry.
|
||||||
//
|
//
|
||||||
// RollbackMinAge is the minimum age of a WAL entry before it is attempted
|
// WALRollbackMinAge is the minimum age of a WAL entry before it is attempted
|
||||||
// to be rolled back. This should be longer than the maximum time it takes
|
// to be rolled back. This should be longer than the maximum time it takes
|
||||||
// to successfully create a secret.
|
// to successfully create a secret.
|
||||||
Rollback RollbackFunc
|
WALRollback WALRollbackFunc
|
||||||
RollbackMinAge time.Duration
|
WALRollbackMinAge time.Duration
|
||||||
|
|
||||||
// Clean is called on unload to clean up e.g any existing connections
|
// Clean is called on unload to clean up e.g any existing connections
|
||||||
// to the backend, if required.
|
// to the backend, if required.
|
||||||
@@ -66,11 +76,15 @@ type Backend struct {
|
|||||||
pathsRe []*regexp.Regexp
|
pathsRe []*regexp.Regexp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// tidyFunc is the callback called when the RollbackManager's timer ticks.
|
||||||
|
// This can be utilized by the backends to do tidying tasks.
|
||||||
|
type tidyFunc func(*logical.Request) error
|
||||||
|
|
||||||
// OperationFunc is the callback called for an operation on a path.
|
// OperationFunc is the callback called for an operation on a path.
|
||||||
type OperationFunc func(*logical.Request, *FieldData) (*logical.Response, error)
|
type OperationFunc func(*logical.Request, *FieldData) (*logical.Response, error)
|
||||||
|
|
||||||
// RollbackFunc is the callback for rollbacks.
|
// WALRollbackFunc is the callback for rollbacks.
|
||||||
type RollbackFunc func(*logical.Request, string, interface{}) error
|
type WALRollbackFunc func(*logical.Request, string, interface{}) error
|
||||||
|
|
||||||
// CleanupFunc is the callback for backend unload.
|
// CleanupFunc is the callback for backend unload.
|
||||||
type CleanupFunc func()
|
type CleanupFunc func()
|
||||||
@@ -385,6 +399,19 @@ func (b *Backend) handleRevokeRenew(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// handleRollback invokes the TidyFunc set on the backend. It also does a WAL rollback operation.
|
||||||
|
func (b *Backend) handleRollback(
|
||||||
|
req *logical.Request) (*logical.Response, error) {
|
||||||
|
// Response is not expected from the tidy operation.
|
||||||
|
if b.TidyFunc != nil {
|
||||||
|
if err := b.TidyFunc(req); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.handleWALRollback(req)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Backend) handleAuthRenew(req *logical.Request) (*logical.Response, error) {
|
func (b *Backend) handleAuthRenew(req *logical.Request) (*logical.Response, error) {
|
||||||
if b.AuthRenew == nil {
|
if b.AuthRenew == nil {
|
||||||
return logical.ErrorResponse("this auth type doesn't support renew"), nil
|
return logical.ErrorResponse("this auth type doesn't support renew"), nil
|
||||||
@@ -393,9 +420,9 @@ func (b *Backend) handleAuthRenew(req *logical.Request) (*logical.Response, erro
|
|||||||
return b.AuthRenew(req, nil)
|
return b.AuthRenew(req, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *Backend) handleRollback(
|
func (b *Backend) handleWALRollback(
|
||||||
req *logical.Request) (*logical.Response, error) {
|
req *logical.Request) (*logical.Response, error) {
|
||||||
if b.Rollback == nil {
|
if b.WALRollback == nil {
|
||||||
return nil, logical.ErrUnsupportedOperation
|
return nil, logical.ErrUnsupportedOperation
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -410,7 +437,7 @@ func (b *Backend) handleRollback(
|
|||||||
|
|
||||||
// Calculate the minimum time that the WAL entries could be
|
// Calculate the minimum time that the WAL entries could be
|
||||||
// created in order to be rolled back.
|
// created in order to be rolled back.
|
||||||
age := b.RollbackMinAge
|
age := b.WALRollbackMinAge
|
||||||
if age == 0 {
|
if age == 0 {
|
||||||
age = 10 * time.Minute
|
age = 10 * time.Minute
|
||||||
}
|
}
|
||||||
@@ -434,8 +461,8 @@ func (b *Backend) handleRollback(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt a rollback
|
// Attempt a WAL rollback
|
||||||
err = b.Rollback(req, entry.Kind, entry.Data)
|
err = b.WALRollback(req, entry.Kind, entry.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf(
|
err = fmt.Errorf(
|
||||||
"Error rolling back '%s' entry: %s", entry.Kind, err)
|
"Error rolling back '%s' entry: %s", entry.Kind, err)
|
||||||
|
|||||||
@@ -30,10 +30,10 @@ const (
|
|||||||
type RollbackManager struct {
|
type RollbackManager struct {
|
||||||
logger *log.Logger
|
logger *log.Logger
|
||||||
|
|
||||||
// This gives the current mount table, plus a RWMutex that is
|
// This gives the current mount table of both logical and credential backends,
|
||||||
// locked for reading. It is up to the caller to RUnlock it
|
// plus a RWMutex that is locked for reading. It is up to the caller to RUnlock
|
||||||
// when done with the mount table
|
// it when done with the mount table.
|
||||||
mounts func() []*MountEntry
|
backends func() []*MountEntry
|
||||||
|
|
||||||
router *Router
|
router *Router
|
||||||
period time.Duration
|
period time.Duration
|
||||||
@@ -55,10 +55,10 @@ type rollbackState struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewRollbackManager is used to create a new rollback manager
|
// NewRollbackManager is used to create a new rollback manager
|
||||||
func NewRollbackManager(logger *log.Logger, mounts func() []*MountEntry, router *Router) *RollbackManager {
|
func NewRollbackManager(logger *log.Logger, backendsFunc func() []*MountEntry, router *Router) *RollbackManager {
|
||||||
r := &RollbackManager{
|
r := &RollbackManager{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
mounts: mounts,
|
backends: backendsFunc,
|
||||||
router: router,
|
router: router,
|
||||||
period: rollbackPeriod,
|
period: rollbackPeriod,
|
||||||
inflight: make(map[string]*rollbackState),
|
inflight: make(map[string]*rollbackState),
|
||||||
@@ -109,9 +109,9 @@ func (m *RollbackManager) triggerRollbacks() {
|
|||||||
m.inflightLock.Lock()
|
m.inflightLock.Lock()
|
||||||
defer m.inflightLock.Unlock()
|
defer m.inflightLock.Unlock()
|
||||||
|
|
||||||
mounts := m.mounts()
|
backends := m.backends()
|
||||||
|
|
||||||
for _, e := range mounts {
|
for _, e := range backends {
|
||||||
if _, ok := m.inflight[e.Path]; !ok {
|
if _, ok := m.inflight[e.Path]; !ok {
|
||||||
m.startRollback(e.Path)
|
m.startRollback(e.Path)
|
||||||
}
|
}
|
||||||
@@ -184,16 +184,24 @@ func (m *RollbackManager) Rollback(path string) error {
|
|||||||
|
|
||||||
// startRollback is used to start the rollback manager after unsealing
|
// startRollback is used to start the rollback manager after unsealing
|
||||||
func (c *Core) startRollback() error {
|
func (c *Core) startRollback() error {
|
||||||
mountsFunc := func() []*MountEntry {
|
backendsFunc := func() []*MountEntry {
|
||||||
ret := []*MountEntry{}
|
ret := []*MountEntry{}
|
||||||
c.mountsLock.RLock()
|
c.mountsLock.RLock()
|
||||||
defer c.mountsLock.RUnlock()
|
defer c.mountsLock.RUnlock()
|
||||||
for _, entry := range c.mounts.Entries {
|
for _, entry := range c.mounts.Entries {
|
||||||
ret = append(ret, entry)
|
ret = append(ret, entry)
|
||||||
}
|
}
|
||||||
|
c.authLock.RLock()
|
||||||
|
defer c.authLock.RUnlock()
|
||||||
|
for _, entry := range c.auth.Entries {
|
||||||
|
if !strings.HasPrefix(entry.Path, "auth/") {
|
||||||
|
entry.Path = "auth/" + entry.Path
|
||||||
|
}
|
||||||
|
ret = append(ret, entry)
|
||||||
|
}
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
c.rollback = NewRollbackManager(c.logger, mountsFunc, c.router)
|
c.rollback = NewRollbackManager(c.logger, backendsFunc, c.router)
|
||||||
c.rollback.Start()
|
c.rollback.Start()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user