2015-03-27 21:00:38 +00:00
package vault
import (
2018-01-19 06:44:44 +00:00
"context"
2015-09-18 16:18:37 +00:00
"crypto/sha256"
2015-03-27 21:00:38 +00:00
"errors"
"fmt"
2015-03-31 22:26:03 +00:00
"strings"
2015-03-27 21:00:38 +00:00
2015-12-16 17:56:20 +00:00
"github.com/hashicorp/go-uuid"
2015-03-27 21:00:38 +00:00
"github.com/hashicorp/vault/audit"
2016-07-06 16:25:40 +00:00
"github.com/hashicorp/vault/helper/jsonutil"
2015-09-18 16:18:37 +00:00
"github.com/hashicorp/vault/helper/salt"
2015-04-01 20:55:07 +00:00
"github.com/hashicorp/vault/logical"
2015-03-27 21:00:38 +00:00
)
const (
// coreAuditConfigPath is used to store the audit configuration.
// Audit configuration is protected within the Vault itself, which means it
// can only be viewed or modified after an unseal.
coreAuditConfigPath = "core/audit"
2015-03-31 22:26:03 +00:00
2017-02-17 01:13:19 +00:00
// coreLocalAuditConfigPath is used to store audit information for local
// (non-replicated) mounts
coreLocalAuditConfigPath = "core/local-audit"
2015-03-31 22:26:03 +00:00
// auditBarrierPrefix is the prefix to the UUID used in the
// barrier view for the audit backends.
auditBarrierPrefix = "audit/"
2016-05-26 17:38:51 +00:00
// auditTableType is the value we expect to find for the audit table and
// corresponding entries
auditTableType = "audit"
2015-03-27 21:00:38 +00:00
)
var (
// loadAuditFailed if loading audit tables encounters an error
2015-09-18 16:18:37 +00:00
errLoadAuditFailed = errors . New ( "failed to setup audit table" )
2015-03-27 21:00:38 +00:00
)
2015-03-31 22:26:03 +00:00
// enableAudit is used to enable a new audit backend
2018-01-19 06:44:44 +00:00
func ( c * Core ) enableAudit ( ctx context . Context , entry * MountEntry ) error {
2015-04-03 21:27:33 +00:00
// Ensure we end the path in a slash
if ! strings . HasSuffix ( entry . Path , "/" ) {
entry . Path += "/"
}
2015-03-31 22:26:03 +00:00
// Ensure there is a name
2015-04-03 21:27:33 +00:00
if entry . Path == "/" {
2015-03-31 22:26:03 +00:00
return fmt . Errorf ( "backend path must be specified" )
}
2015-11-11 16:44:07 +00:00
// Update the audit table
c . auditLock . Lock ( )
defer c . auditLock . Unlock ( )
2015-03-31 22:26:03 +00:00
// Look for matching name
for _ , ent := range c . audit . Entries {
2015-04-03 21:27:33 +00:00
switch {
// Existing is sql/mysql/ new is sql/ or
// existing is sql/ and new is sql/mysql/
case strings . HasPrefix ( ent . Path , entry . Path ) :
fallthrough
case strings . HasPrefix ( entry . Path , ent . Path ) :
2015-03-31 22:26:03 +00:00
return fmt . Errorf ( "path already in use" )
}
}
2015-09-18 16:18:37 +00:00
// Generate a new UUID and view
2017-02-17 01:13:19 +00:00
if entry . UUID == "" {
entryUUID , err := uuid . GenerateUUID ( )
if err != nil {
return err
}
entry . UUID = entryUUID
2016-01-13 18:40:08 +00:00
}
2017-06-26 17:14:36 +00:00
if entry . Accessor == "" {
accessor , err := c . generateMountAccessor ( "audit_" + entry . Type )
if err != nil {
return err
}
entry . Accessor = accessor
}
2017-02-17 01:13:19 +00:00
viewPath := auditBarrierPrefix + entry . UUID + "/"
view := NewBarrierView ( c . barrier , viewPath )
2015-09-18 16:18:37 +00:00
2018-02-09 19:04:25 +00:00
// Mark the view as read-only until the mounting is complete and
// ensure that it is reset after. This ensures that there will be no
// writes during the construction of the backend.
view . setReadOnlyErr ( logical . ErrSetupReadOnly )
defer view . setReadOnlyErr ( nil )
2015-03-31 22:26:03 +00:00
// Lookup the new backend
2018-01-19 06:44:44 +00:00
backend , err := c . newAuditBackend ( ctx , entry , view , entry . Options )
2015-03-31 22:26:03 +00:00
if err != nil {
return err
}
2017-03-04 21:35:41 +00:00
if backend == nil {
return fmt . Errorf ( "nil audit backend of type %q returned from factory" , entry . Type )
}
2015-03-31 22:26:03 +00:00
2016-09-13 15:50:14 +00:00
newTable := c . audit . shallowClone ( )
2015-03-31 22:26:03 +00:00
newTable . Entries = append ( newTable . Entries , entry )
2018-01-19 06:44:44 +00:00
if err := c . persistAudit ( ctx , newTable , entry . Local ) ; err != nil {
2015-03-31 22:26:03 +00:00
return errors . New ( "failed to update audit table" )
}
2015-11-11 16:44:07 +00:00
2015-03-31 22:26:03 +00:00
c . audit = newTable
// Register the backend
c . auditBroker . Register ( entry . Path , backend , view )
2016-08-19 20:45:17 +00:00
if c . logger . IsInfo ( ) {
2018-04-03 00:46:59 +00:00
c . logger . Info ( "enabled audit backend" , "path" , entry . Path , "type" , entry . Type )
2016-08-19 20:45:17 +00:00
}
2015-03-31 22:26:03 +00:00
return nil
}
// disableAudit is used to disable an existing audit backend
2018-01-19 06:44:44 +00:00
func ( c * Core ) disableAudit ( ctx context . Context , path string ) ( bool , error ) {
2015-04-03 21:27:33 +00:00
// Ensure we end the path in a slash
if ! strings . HasSuffix ( path , "/" ) {
path += "/"
}
2015-03-31 22:26:03 +00:00
// Remove the entry from the mount table
2015-11-11 16:44:07 +00:00
c . auditLock . Lock ( )
defer c . auditLock . Unlock ( )
2016-09-13 15:50:14 +00:00
newTable := c . audit . shallowClone ( )
2016-09-30 19:04:50 +00:00
entry := newTable . remove ( path )
2015-03-31 22:26:03 +00:00
// Ensure there was a match
2016-09-30 19:04:50 +00:00
if entry == nil {
2016-09-19 17:02:25 +00:00
return false , fmt . Errorf ( "no matching backend" )
2015-03-31 22:26:03 +00:00
}
2016-09-30 19:04:50 +00:00
c . removeAuditReloadFunc ( entry )
2017-02-17 01:13:19 +00:00
// When unmounting all entries the JSON code will load back up from storage
// as a nil slice, which kills tests...just set it nil explicitly
if len ( newTable . Entries ) == 0 {
newTable . Entries = nil
}
2015-03-31 22:26:03 +00:00
// Update the audit table
2018-01-19 06:44:44 +00:00
if err := c . persistAudit ( ctx , newTable , entry . Local ) ; err != nil {
2016-09-19 17:02:25 +00:00
return true , errors . New ( "failed to update audit table" )
2015-03-31 22:26:03 +00:00
}
2015-11-11 16:44:07 +00:00
2015-03-31 22:26:03 +00:00
c . audit = newTable
// Unmount the backend
c . auditBroker . Deregister ( path )
2016-08-19 20:45:17 +00:00
if c . logger . IsInfo ( ) {
2018-04-03 00:46:59 +00:00
c . logger . Info ( "disabled audit backend" , "path" , path )
2016-08-19 20:45:17 +00:00
}
2017-02-17 01:13:19 +00:00
2016-09-19 17:02:25 +00:00
return true , nil
2015-03-31 22:26:03 +00:00
}
2015-03-27 21:00:38 +00:00
// loadAudits is invoked as part of postUnseal to load the audit table
2018-01-19 06:44:44 +00:00
func ( c * Core ) loadAudits ( ctx context . Context ) error {
2015-11-11 16:44:07 +00:00
auditTable := & MountTable { }
2017-02-17 01:13:19 +00:00
localAuditTable := & MountTable { }
2015-11-11 16:44:07 +00:00
2015-03-27 21:00:38 +00:00
// Load the existing audit table
2018-01-19 06:44:44 +00:00
raw , err := c . barrier . Get ( ctx , coreAuditConfigPath )
2015-03-27 21:00:38 +00:00
if err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to read audit table" , "error" , err )
2015-09-18 16:18:37 +00:00
return errLoadAuditFailed
2015-03-27 21:00:38 +00:00
}
2018-01-19 06:44:44 +00:00
rawLocal , err := c . barrier . Get ( ctx , coreLocalAuditConfigPath )
2017-02-17 01:13:19 +00:00
if err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to read local audit table" , "error" , err )
2017-02-17 01:13:19 +00:00
return errLoadAuditFailed
}
2015-11-11 16:44:07 +00:00
c . auditLock . Lock ( )
defer c . auditLock . Unlock ( )
2015-03-27 21:00:38 +00:00
if raw != nil {
2016-07-06 16:25:40 +00:00
if err := jsonutil . DecodeJSON ( raw . Value , auditTable ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to decode audit table" , "error" , err )
2015-09-18 16:18:37 +00:00
return errLoadAuditFailed
2015-03-27 21:00:38 +00:00
}
2015-11-11 16:44:07 +00:00
c . audit = auditTable
2015-03-27 21:00:38 +00:00
}
2017-11-08 01:30:02 +00:00
var needPersist bool
if c . audit == nil {
c . audit = defaultAuditTable ( )
needPersist = true
}
2017-02-17 01:13:19 +00:00
if rawLocal != nil {
if err := jsonutil . DecodeJSON ( rawLocal . Value , localAuditTable ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to decode local audit table" , "error" , err )
2017-02-17 01:13:19 +00:00
return errLoadAuditFailed
}
2017-11-07 23:04:37 +00:00
if localAuditTable != nil && len ( localAuditTable . Entries ) > 0 {
c . audit . Entries = append ( c . audit . Entries , localAuditTable . Entries ... )
}
2017-02-17 01:13:19 +00:00
}
2015-03-27 21:00:38 +00:00
2017-11-08 01:30:02 +00:00
// Upgrade to typed auth table
if c . audit . Type == "" {
c . audit . Type = auditTableType
needPersist = true
}
2016-05-26 17:38:51 +00:00
2017-11-08 01:30:02 +00:00
// Upgrade to table-scoped entries
for _ , entry := range c . audit . Entries {
if entry . Table == "" {
entry . Table = c . audit . Type
2016-05-26 17:38:51 +00:00
needPersist = true
}
2017-11-08 01:30:02 +00:00
if entry . Accessor == "" {
accessor , err := c . generateMountAccessor ( "audit_" + entry . Type )
if err != nil {
return err
2017-06-26 17:14:36 +00:00
}
2017-11-08 01:30:02 +00:00
entry . Accessor = accessor
needPersist = true
2016-05-26 17:38:51 +00:00
}
2017-11-08 01:30:02 +00:00
}
2016-05-26 17:38:51 +00:00
2017-11-08 01:30:02 +00:00
if ! needPersist {
return nil
2015-03-27 21:00:38 +00:00
}
2018-01-19 06:44:44 +00:00
if err := c . persistAudit ( ctx , c . audit , false ) ; err != nil {
2015-09-18 16:18:37 +00:00
return errLoadAuditFailed
2015-03-27 21:00:38 +00:00
}
return nil
}
// persistAudit is used to persist the audit table after modification
2018-01-19 06:44:44 +00:00
func ( c * Core ) persistAudit ( ctx context . Context , table * MountTable , localOnly bool ) error {
2016-05-26 17:38:51 +00:00
if table . Type != auditTableType {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "given table to persist has wrong type" , "actual_type" , table . Type , "expected_type" , auditTableType )
2016-05-26 17:38:51 +00:00
return fmt . Errorf ( "invalid table type given, not persisting" )
}
for _ , entry := range table . Entries {
if entry . Table != table . Type {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "given entry to persist in audit table has wrong table value" , "path" , entry . Path , "entry_table_type" , entry . Table , "actual_type" , table . Type )
2016-05-26 17:38:51 +00:00
return fmt . Errorf ( "invalid audit entry found, not persisting" )
}
}
2017-02-17 01:13:19 +00:00
nonLocalAudit := & MountTable {
Type : auditTableType ,
}
localAudit := & MountTable {
Type : auditTableType ,
}
for _ , entry := range table . Entries {
if entry . Local {
localAudit . Entries = append ( localAudit . Entries , entry )
} else {
nonLocalAudit . Entries = append ( nonLocalAudit . Entries , entry )
}
}
2017-03-02 19:37:59 +00:00
if ! localOnly {
// Marshal the table
compressedBytes , err := jsonutil . EncodeJSONAndCompress ( nonLocalAudit , nil )
if err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to encode and/or compress audit table" , "error" , err )
2017-03-02 19:37:59 +00:00
return err
}
2015-03-27 21:00:38 +00:00
2017-03-02 19:37:59 +00:00
// Create an entry
entry := & Entry {
Key : coreAuditConfigPath ,
Value : compressedBytes ,
}
2015-03-27 21:00:38 +00:00
2017-03-02 19:37:59 +00:00
// Write to the physical backend
2018-01-19 06:44:44 +00:00
if err := c . barrier . Put ( ctx , entry ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to persist audit table" , "error" , err )
2017-03-02 19:37:59 +00:00
return err
}
2015-03-27 21:00:38 +00:00
}
2017-02-17 01:13:19 +00:00
// Repeat with local audit
2017-03-02 19:37:59 +00:00
compressedBytes , err := jsonutil . EncodeJSONAndCompress ( localAudit , nil )
2017-02-17 01:13:19 +00:00
if err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to encode and/or compress local audit table" , "error" , err )
2017-02-17 01:13:19 +00:00
return err
}
2017-03-02 19:37:59 +00:00
entry := & Entry {
2017-02-17 01:13:19 +00:00
Key : coreLocalAuditConfigPath ,
Value : compressedBytes ,
}
2018-01-19 06:44:44 +00:00
if err := c . barrier . Put ( ctx , entry ) ; err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to persist local audit table" , "error" , err )
2017-02-17 01:13:19 +00:00
return err
}
2015-03-27 21:00:38 +00:00
return nil
}
// setupAudit is invoked after we've loaded the audit able to
// initialize the audit backends
2018-01-19 06:44:44 +00:00
func ( c * Core ) setupAudits ( ctx context . Context ) error {
2018-04-03 00:46:59 +00:00
broker := NewAuditBroker ( c . logger . ResetNamed ( "audit" ) )
2015-11-11 16:44:07 +00:00
c . auditLock . Lock ( )
defer c . auditLock . Unlock ( )
2016-12-02 20:09:01 +00:00
var successCount int
2015-03-27 21:00:38 +00:00
for _ , entry := range c . audit . Entries {
2015-09-18 16:18:37 +00:00
// Create a barrier view using the UUID
2017-02-17 01:13:19 +00:00
viewPath := auditBarrierPrefix + entry . UUID + "/"
view := NewBarrierView ( c . barrier , viewPath )
2015-09-18 16:18:37 +00:00
2018-02-09 19:04:25 +00:00
// Mark the view as read-only until the mounting is complete and
// ensure that it is reset after. This ensures that there will be no
// writes during the construction of the backend.
view . setReadOnlyErr ( logical . ErrSetupReadOnly )
2018-02-09 20:39:27 +00:00
defer view . setReadOnlyErr ( nil )
2018-02-09 19:04:25 +00:00
2015-03-27 21:00:38 +00:00
// Initialize the backend
2018-01-19 06:44:44 +00:00
backend , err := c . newAuditBackend ( ctx , entry , view , entry . Options )
2015-03-27 21:00:38 +00:00
if err != nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "failed to create audit entry" , "path" , entry . Path , "error" , err )
2016-12-02 20:09:01 +00:00
continue
2015-03-27 21:00:38 +00:00
}
2017-03-04 21:35:41 +00:00
if backend == nil {
2018-04-03 00:46:59 +00:00
c . logger . Error ( "created audit entry was nil" , "path" , entry . Path , "type" , entry . Type )
2017-03-04 21:35:41 +00:00
continue
}
2015-03-31 20:22:40 +00:00
2015-03-31 22:26:03 +00:00
// Mount the backend
2017-03-04 21:35:41 +00:00
broker . Register ( entry . Path , backend , view )
2016-12-02 20:09:01 +00:00
successCount += 1
2015-03-31 22:26:03 +00:00
}
2016-12-02 20:09:01 +00:00
if len ( c . audit . Entries ) > 0 && successCount == 0 {
return errLoadAuditFailed
}
2015-03-31 22:26:03 +00:00
c . auditBroker = broker
2015-03-27 21:00:38 +00:00
return nil
}
// teardownAudit is used before we seal the vault to reset the audit
// backends to their unloaded state. This is reversed by loadAudits.
func ( c * Core ) teardownAudits ( ) error {
2015-11-11 16:44:07 +00:00
c . auditLock . Lock ( )
defer c . auditLock . Unlock ( )
2016-10-28 19:32:32 +00:00
if c . audit != nil {
for _ , entry := range c . audit . Entries {
c . removeAuditReloadFunc ( entry )
}
2016-09-30 19:04:50 +00:00
}
2015-03-27 21:00:38 +00:00
c . audit = nil
2015-03-31 20:22:40 +00:00
c . auditBroker = nil
2015-03-27 21:00:38 +00:00
return nil
}
2016-09-30 19:04:50 +00:00
// removeAuditReloadFunc removes the reload func from the working set. The
// audit lock needs to be held before calling this.
func ( c * Core ) removeAuditReloadFunc ( entry * MountEntry ) {
switch entry . Type {
case "file" :
key := "audit_file|" + entry . Path
c . reloadFuncsLock . Lock ( )
if c . logger . IsDebug ( ) {
2018-04-03 00:46:59 +00:00
c . logger . ResetNamed ( "audit" ) . Debug ( "removing reload function" , "path" , entry . Path )
2016-09-30 19:04:50 +00:00
}
delete ( c . reloadFuncs , key )
c . reloadFuncsLock . Unlock ( )
}
}
2015-03-27 21:00:38 +00:00
// newAuditBackend is used to create and configure a new audit backend by name
2018-01-19 06:44:44 +00:00
func ( c * Core ) newAuditBackend ( ctx context . Context , entry * MountEntry , view logical . Storage , conf map [ string ] string ) ( audit . Backend , error ) {
2016-09-30 19:04:50 +00:00
f , ok := c . auditBackends [ entry . Type ]
2015-03-27 21:00:38 +00:00
if ! ok {
2018-04-05 15:49:21 +00:00
return nil , fmt . Errorf ( "unknown backend type: %q" , entry . Type )
2015-03-27 21:00:38 +00:00
}
2017-05-24 00:36:20 +00:00
saltConfig := & salt . Config {
2015-09-18 16:18:37 +00:00
HMAC : sha256 . New ,
2015-09-18 21:36:42 +00:00
HMACType : "hmac-sha256" ,
2017-05-24 00:36:20 +00:00
Location : salt . DefaultLocation ,
2015-09-18 16:18:37 +00:00
}
2016-09-30 19:04:50 +00:00
2018-01-19 06:44:44 +00:00
be , err := f ( ctx , & audit . BackendConfig {
2017-05-24 00:36:20 +00:00
SaltView : view ,
SaltConfig : saltConfig ,
Config : conf ,
2015-09-18 16:18:37 +00:00
} )
2016-09-30 19:04:50 +00:00
if err != nil {
return nil , err
}
2017-03-04 21:35:41 +00:00
if be == nil {
return nil , fmt . Errorf ( "nil backend returned from %q factory function" , entry . Type )
}
2016-09-30 19:04:50 +00:00
2018-04-03 00:46:59 +00:00
auditLogger := c . logger . ResetNamed ( "audit" )
2016-09-30 19:04:50 +00:00
switch entry . Type {
case "file" :
key := "audit_file|" + entry . Path
c . reloadFuncsLock . Lock ( )
2018-04-03 00:46:59 +00:00
if auditLogger . IsDebug ( ) {
auditLogger . Debug ( "adding reload function" , "path" , entry . Path )
2017-11-10 17:08:36 +00:00
if entry . Options != nil {
2018-04-03 00:46:59 +00:00
auditLogger . Debug ( "file backend options" , "path" , entry . Path , "file_path" , entry . Options [ "file_path" ] )
2017-11-10 17:08:36 +00:00
}
2016-09-30 19:04:50 +00:00
}
2017-06-22 19:29:53 +00:00
c . reloadFuncs [ key ] = append ( c . reloadFuncs [ key ] , func ( map [ string ] interface { } ) error {
2018-04-03 00:46:59 +00:00
if auditLogger . IsInfo ( ) {
auditLogger . Info ( "reloading file audit backend" , "path" , entry . Path )
2016-09-30 19:04:50 +00:00
}
2018-01-19 06:44:44 +00:00
return be . Reload ( ctx )
2016-09-30 19:04:50 +00:00
} )
c . reloadFuncsLock . Unlock ( )
2017-11-10 17:08:36 +00:00
case "socket" :
2018-04-03 00:46:59 +00:00
if auditLogger . IsDebug ( ) {
2017-11-10 17:08:36 +00:00
if entry . Options != nil {
2018-04-03 00:46:59 +00:00
auditLogger . Debug ( "socket backend options" , "path" , entry . Path , "address" , entry . Options [ "address" ] , "socket type" , entry . Options [ "socket_type" ] )
2017-11-10 17:08:36 +00:00
}
}
case "syslog" :
2018-04-03 00:46:59 +00:00
if auditLogger . IsDebug ( ) {
2017-11-10 17:08:36 +00:00
if entry . Options != nil {
2018-04-03 00:46:59 +00:00
auditLogger . Debug ( "syslog backend options" , "path" , entry . Path , "facility" , entry . Options [ "facility" ] , "tag" , entry . Options [ "tag" ] )
2017-11-10 17:08:36 +00:00
}
}
2016-09-30 19:04:50 +00:00
}
return be , err
2015-03-27 21:00:38 +00:00
}
// defaultAuditTable creates a default audit table
func defaultAuditTable ( ) * MountTable {
2016-05-26 17:38:51 +00:00
table := & MountTable {
Type : auditTableType ,
}
2015-03-27 21:00:38 +00:00
return table
}