package file import ( "fmt" "os" "path/filepath" "strconv" "sync" "github.com/hashicorp/vault/audit" "github.com/hashicorp/vault/helper/salt" "github.com/hashicorp/vault/logical" "github.com/mitchellh/copystructure" ) func Factory(conf *audit.BackendConfig) (audit.Backend, error) { if conf.Salt == nil { return nil, fmt.Errorf("nil salt") } path, ok := conf.Config["path"] if !ok { return nil, fmt.Errorf("path is required") } // Check if hashing of accessor is disabled hashAccessor := true if hashAccessorRaw, ok := conf.Config["hash_accessor"]; ok { value, err := strconv.ParseBool(hashAccessorRaw) if err != nil { return nil, err } hashAccessor = value } // Check if raw logging is enabled logRaw := false if raw, ok := conf.Config["log_raw"]; ok { b, err := strconv.ParseBool(raw) if err != nil { return nil, err } logRaw = b } b := &Backend{ path: path, logRaw: logRaw, hashAccessor: hashAccessor, salt: conf.Salt, } // Ensure that the file can be successfully opened for writing; // otherwise it will be too late to catch later without problems // (ref: https://github.com/hashicorp/vault/issues/550) if err := b.open(); err != nil { return nil, fmt.Errorf("sanity check failed; unable to open %s for writing", path) } return b, nil } // Backend is the audit backend for the file-based audit store. // // NOTE: This audit backend is currently very simple: it appends to a file. // It doesn't do anything more at the moment to assist with rotation // or reset the write cursor, this should be done in the future. type Backend struct { path string logRaw bool hashAccessor bool salt *salt.Salt once sync.Once f *os.File } func (b *Backend) GetHash(data string) string { return audit.HashString(b.salt, data) } func (b *Backend) LogRequest(auth *logical.Auth, req *logical.Request, outerErr error) error { if err := b.open(); err != nil { return err } if !b.logRaw { // Before we copy the structure we must nil out some data // otherwise we will cause reflection to panic and die if req.Connection != nil && req.Connection.ConnState != nil { origReq := req origState := req.Connection.ConnState req.Connection.ConnState = nil defer func() { origReq.Connection.ConnState = origState }() } // Copy the structures cp, err := copystructure.Copy(auth) if err != nil { return err } auth = cp.(*logical.Auth) cp, err = copystructure.Copy(req) if err != nil { return err } req = cp.(*logical.Request) // Hash any sensitive information if err := audit.Hash(b.salt, auth); err != nil { return err } if err := audit.Hash(b.salt, req); err != nil { return err } } var format audit.FormatJSON return format.FormatRequest(b.f, auth, req, outerErr) } func (b *Backend) LogResponse( auth *logical.Auth, req *logical.Request, resp *logical.Response, err error) error { if err := b.open(); err != nil { return err } if !b.logRaw { // Before we copy the structure we must nil out some data // otherwise we will cause reflection to panic and die if req.Connection != nil && req.Connection.ConnState != nil { origReq := req origState := req.Connection.ConnState req.Connection.ConnState = nil defer func() { origReq.Connection.ConnState = origState }() } // Copy the structure cp, err := copystructure.Copy(auth) if err != nil { return err } auth = cp.(*logical.Auth) cp, err = copystructure.Copy(req) if err != nil { return err } req = cp.(*logical.Request) cp, err = copystructure.Copy(resp) if err != nil { return err } resp = cp.(*logical.Response) // Hash any sensitive information // Cache and restore accessor in the auth var accessor string if !b.hashAccessor && auth != nil && auth.Accessor != "" { accessor = auth.Accessor } if err := audit.Hash(b.salt, auth); err != nil { return err } if accessor != "" { auth.Accessor = accessor } if err := audit.Hash(b.salt, req); err != nil { return err } // Cache and restore accessor in the response accessor = "" if !b.hashAccessor && resp != nil && resp.Auth != nil && resp.Auth.Accessor != "" { accessor = resp.Auth.Accessor } if err := audit.Hash(b.salt, resp); err != nil { return err } if accessor != "" { resp.Auth.Accessor = accessor } } var format audit.FormatJSON return format.FormatResponse(b.f, auth, req, resp, err) } func (b *Backend) open() error { if b.f != nil { return nil } if err := os.MkdirAll(filepath.Dir(b.path), 0600); err != nil { return err } var err error b.f, err = os.OpenFile(b.path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) if err != nil { return err } return nil }