mirror of
https://github.com/hashicorp/vault.git
synced 2025-09-01 03:51:08 +02:00
vault: Adding enable/disable audit methods
This commit is contained in:
parent
e8a692898c
commit
2f9c64dbf9
138
vault/audit.go
138
vault/audit.go
@ -4,6 +4,8 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/vault/audit"
|
||||
)
|
||||
@ -13,6 +15,10 @@ const (
|
||||
// Audit configuration is protected within the Vault itself, which means it
|
||||
// can only be viewed or modified after an unseal.
|
||||
coreAuditConfigPath = "core/audit"
|
||||
|
||||
// auditBarrierPrefix is the prefix to the UUID used in the
|
||||
// barrier view for the audit backends.
|
||||
auditBarrierPrefix = "audit/"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -20,6 +26,86 @@ var (
|
||||
loadAuditFailed = errors.New("failed to setup audit table")
|
||||
)
|
||||
|
||||
// enableAudit is used to enable a new audit backend
|
||||
func (c *Core) enableAudit(entry *MountEntry) error {
|
||||
c.audit.Lock()
|
||||
defer c.audit.Unlock()
|
||||
|
||||
// Ensure there is a name
|
||||
if entry.Path == "" {
|
||||
return fmt.Errorf("backend path must be specified")
|
||||
}
|
||||
if strings.Contains(entry.Path, "/") {
|
||||
return fmt.Errorf("backend path cannot have a forward slash")
|
||||
}
|
||||
|
||||
// Look for matching name
|
||||
for _, ent := range c.audit.Entries {
|
||||
if ent.Path == entry.Path {
|
||||
return fmt.Errorf("path already in use")
|
||||
}
|
||||
}
|
||||
|
||||
// Lookup the new backend
|
||||
backend, err := c.newAuditBackend(entry.Type, entry.Options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Generate a new UUID and view
|
||||
entry.UUID = generateUUID()
|
||||
view := NewBarrierView(c.barrier, auditBarrierPrefix+entry.UUID+"/")
|
||||
|
||||
// Update the audit table
|
||||
newTable := c.audit.Clone()
|
||||
newTable.Entries = append(newTable.Entries, entry)
|
||||
if err := c.persistAudit(newTable); err != nil {
|
||||
return errors.New("failed to update audit table")
|
||||
}
|
||||
c.audit = newTable
|
||||
|
||||
// Register the backend
|
||||
c.auditBroker.Register(entry.Path, backend, view)
|
||||
c.logger.Printf("[INFO] core: enabled audit backend '%s' type: %s",
|
||||
entry.Path, entry.Type)
|
||||
return nil
|
||||
}
|
||||
|
||||
// disableAudit is used to disable an existing audit backend
|
||||
func (c *Core) disableAudit(path string) error {
|
||||
c.audit.Lock()
|
||||
defer c.audit.Unlock()
|
||||
|
||||
// Remove the entry from the mount table
|
||||
found := false
|
||||
newTable := c.audit.Clone()
|
||||
n := len(newTable.Entries)
|
||||
for i := 0; i < n; i++ {
|
||||
if newTable.Entries[i].Path == path {
|
||||
newTable.Entries[i], newTable.Entries[n-1] = newTable.Entries[n-1], nil
|
||||
newTable.Entries = newTable.Entries[:n-1]
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure there was a match
|
||||
if !found {
|
||||
return fmt.Errorf("no matching backend")
|
||||
}
|
||||
|
||||
// Update the audit table
|
||||
if err := c.persistAudit(newTable); err != nil {
|
||||
return errors.New("failed to update audit table")
|
||||
}
|
||||
c.audit = newTable
|
||||
|
||||
// Unmount the backend
|
||||
c.auditBroker.Deregister(path)
|
||||
c.logger.Printf("[INFO] core: disabled audit backend '%s'", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadAudits is invoked as part of postUnseal to load the audit table
|
||||
func (c *Core) loadAudits() error {
|
||||
// Load the existing audit table
|
||||
@ -75,7 +161,7 @@ func (c *Core) persistAudit(table *MountTable) error {
|
||||
// setupAudit is invoked after we've loaded the audit able to
|
||||
// initialize the audit backends
|
||||
func (c *Core) setupAudits() error {
|
||||
var backends []audit.Backend
|
||||
broker := NewAuditBroker()
|
||||
for _, entry := range c.audit.Entries {
|
||||
// Initialize the backend
|
||||
audit, err := c.newAuditBackend(entry.Type, entry.Options)
|
||||
@ -86,12 +172,13 @@ func (c *Core) setupAudits() error {
|
||||
return loadAuditFailed
|
||||
}
|
||||
|
||||
// Append to the audit entry to the list of backends
|
||||
backends = append(backends, audit)
|
||||
}
|
||||
// Create a barrier view using the UUID
|
||||
view := NewBarrierView(c.barrier, auditBarrierPrefix+entry.UUID+"/")
|
||||
|
||||
// Setup the audit broker
|
||||
c.auditBroker = NewAuditBroker(backends)
|
||||
// Mount the backend
|
||||
broker.Register(entry.Path, audit, view)
|
||||
}
|
||||
c.auditBroker = broker
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -118,16 +205,47 @@ func defaultAuditTable() *MountTable {
|
||||
return table
|
||||
}
|
||||
|
||||
type backendEntry struct {
|
||||
backend audit.Backend
|
||||
view *BarrierView
|
||||
}
|
||||
|
||||
// AuditBroker is used to provide a single ingest interface to auditable
|
||||
// events given that multiple backends may be configured.
|
||||
type AuditBroker struct {
|
||||
backends []audit.Backend
|
||||
l sync.RWMutex
|
||||
backends map[string]backendEntry
|
||||
}
|
||||
|
||||
// NewAuditBroker creates a new broker given the list of backends
|
||||
func NewAuditBroker(backends []audit.Backend) *AuditBroker {
|
||||
// NewAuditBroker creates a new audit broker
|
||||
func NewAuditBroker() *AuditBroker {
|
||||
b := &AuditBroker{
|
||||
backends: backends,
|
||||
backends: make(map[string]backendEntry),
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// Register is used to add new audit backend to the broker
|
||||
func (a *AuditBroker) Register(name string, b audit.Backend, v *BarrierView) {
|
||||
a.l.Lock()
|
||||
defer a.l.Unlock()
|
||||
a.backends[name] = backendEntry{
|
||||
backend: b,
|
||||
view: v,
|
||||
}
|
||||
}
|
||||
|
||||
// Deregister is used to remove an audit backend from the broker
|
||||
func (a *AuditBroker) Deregister(name string) {
|
||||
a.l.Lock()
|
||||
defer a.l.Unlock()
|
||||
delete(a.backends, name)
|
||||
}
|
||||
|
||||
// IsRegistered is used to check if a given audit backend is registered
|
||||
func (a *AuditBroker) IsRegistered(name string) bool {
|
||||
a.l.RLock()
|
||||
defer a.l.RUnlock()
|
||||
_, ok := a.backends[name]
|
||||
return ok
|
||||
}
|
||||
|
@ -3,8 +3,110 @@ package vault
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/vault/audit"
|
||||
)
|
||||
|
||||
type NoopAudit struct{}
|
||||
|
||||
func TestCore_EnableAudit(t *testing.T) {
|
||||
c, key, _ := TestCoreUnsealed(t)
|
||||
c.auditBackends["noop"] = func(map[string]string) (audit.Backend, error) {
|
||||
return &NoopAudit{}, nil
|
||||
}
|
||||
|
||||
me := &MountEntry{
|
||||
Path: "foo",
|
||||
Type: "noop",
|
||||
}
|
||||
err := c.enableAudit(me)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if !c.auditBroker.IsRegistered("foo") {
|
||||
t.Fatalf("missing audit backend")
|
||||
}
|
||||
|
||||
conf := &CoreConfig{
|
||||
Physical: c.physical,
|
||||
AuditBackends: make(map[string]audit.Factory),
|
||||
}
|
||||
conf.AuditBackends["noop"] = func(map[string]string) (audit.Backend, error) {
|
||||
return &NoopAudit{}, nil
|
||||
}
|
||||
c2, err := NewCore(conf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
unseal, err := c2.Unseal(key)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if !unseal {
|
||||
t.Fatalf("should be unsealed")
|
||||
}
|
||||
|
||||
// Verify matching audit tables
|
||||
if !reflect.DeepEqual(c.audit, c2.audit) {
|
||||
t.Fatalf("mismatch: %v %v", c.audit, c2.audit)
|
||||
}
|
||||
|
||||
// Check for registration
|
||||
if !c2.auditBroker.IsRegistered("foo") {
|
||||
t.Fatalf("missing audit backend")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCore_DisableAudit(t *testing.T) {
|
||||
c, key, _ := TestCoreUnsealed(t)
|
||||
c.auditBackends["noop"] = func(map[string]string) (audit.Backend, error) {
|
||||
return &NoopAudit{}, nil
|
||||
}
|
||||
|
||||
err := c.disableAudit("foo")
|
||||
if err.Error() != "no matching backend" {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
me := &MountEntry{
|
||||
Path: "foo",
|
||||
Type: "noop",
|
||||
}
|
||||
err = c.enableAudit(me)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
err = c.disableAudit("foo")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
// Check for registration
|
||||
if c.auditBroker.IsRegistered("foo") {
|
||||
t.Fatalf("audit backend present")
|
||||
}
|
||||
|
||||
conf := &CoreConfig{Physical: c.physical}
|
||||
c2, err := NewCore(conf)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
unseal, err := c2.Unseal(key)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if !unseal {
|
||||
t.Fatalf("should be unsealed")
|
||||
}
|
||||
|
||||
// Verify matching mount tables
|
||||
if !reflect.DeepEqual(c.audit, c2.audit) {
|
||||
t.Fatalf("mismatch: %v %v", c.audit, c2.audit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCore_DefaultAuditTable(t *testing.T) {
|
||||
c, key, _ := TestCoreUnsealed(t)
|
||||
verifyDefaultAuditTable(t, c.audit)
|
||||
|
Loading…
x
Reference in New Issue
Block a user