From 71a738ad7d433b5d0fe38bfa31bf767ef10f97c9 Mon Sep 17 00:00:00 2001 From: Nate Brown Date: Thu, 18 Jun 2015 18:30:18 -0700 Subject: [PATCH] Logging authentication errors and bad token usage --- audit/audit.go | 2 +- audit/format_json.go | 12 ++++++++++-- audit/formatter.go | 2 +- builtin/audit/file/backend.go | 4 ++-- builtin/audit/syslog/backend.go | 4 ++-- vault/audit.go | 4 ++-- vault/core.go | 10 ++++++++-- vault/testing.go | 2 +- vault/token_store.go | 4 ++-- 9 files changed, 29 insertions(+), 15 deletions(-) diff --git a/audit/audit.go b/audit/audit.go index 6043613879..8ffaf8b660 100644 --- a/audit/audit.go +++ b/audit/audit.go @@ -11,7 +11,7 @@ type Backend interface { // request is authorized but before the request is executed. The arguments // MUST not be modified in anyway. They should be deep copied if this is // a possibility. - LogRequest(*logical.Auth, *logical.Request) error + LogRequest(*logical.Auth, *logical.Request, error) error // LogResponse is used to syncronously log a response. This is done after // the request is processed but before the response is sent. The arguments diff --git a/audit/format_json.go b/audit/format_json.go index 1f423f6123..cba7bc10b8 100644 --- a/audit/format_json.go +++ b/audit/format_json.go @@ -8,22 +8,29 @@ import ( "errors" ) -// FormatJSON is a Formatter implementation that structuteres data into +// FormatJSON is a Formatter implementation that structures data into // a JSON format. type FormatJSON struct{} func (f *FormatJSON) FormatRequest( w io.Writer, - auth *logical.Auth, req *logical.Request) error { + auth *logical.Auth, + req *logical.Request, + err error) error { + // If auth is nil, make an empty one if auth == nil { auth = new(logical.Auth) } + if err == nil { + err = errors.New("") + } // Encode! enc := json.NewEncoder(w) return enc.Encode(&JSONRequestEntry{ Type: "request", + Error: err.Error(), Auth: JSONAuth{ DisplayName: auth.DisplayName, @@ -106,6 +113,7 @@ type JSONRequestEntry struct { Type string `json:"type"` Auth JSONAuth `json:"auth"` Request JSONRequest `json:"request"` + Error string `json:"error"` } // JSONResponseEntry is the structure of a response audit log entry in JSON. diff --git a/audit/formatter.go b/audit/formatter.go index 9294d4ed3a..45f665ed4b 100644 --- a/audit/formatter.go +++ b/audit/formatter.go @@ -12,6 +12,6 @@ import ( // // It is recommended that you pass data through Hash prior to formatting it. type Formatter interface { - FormatRequest(io.Writer, *logical.Auth, *logical.Request) error + FormatRequest(io.Writer, *logical.Auth, *logical.Request, error) error FormatResponse(io.Writer, *logical.Auth, *logical.Request, *logical.Response, error) error } diff --git a/builtin/audit/file/backend.go b/builtin/audit/file/backend.go index c917354d9c..d99010daba 100644 --- a/builtin/audit/file/backend.go +++ b/builtin/audit/file/backend.go @@ -48,7 +48,7 @@ type Backend struct { f *os.File } -func (b *Backend) LogRequest(auth *logical.Auth, req *logical.Request) error { +func (b *Backend) LogRequest(auth *logical.Auth, req *logical.Request, outerErr error) error { if err := b.open(); err != nil { return err } @@ -76,7 +76,7 @@ func (b *Backend) LogRequest(auth *logical.Auth, req *logical.Request) error { } var format audit.FormatJSON - return format.FormatRequest(b.f, auth, req) + return format.FormatRequest(b.f, auth, req, outerErr) } func (b *Backend) LogResponse( diff --git a/builtin/audit/syslog/backend.go b/builtin/audit/syslog/backend.go index f0856e8b64..79f6eb740f 100644 --- a/builtin/audit/syslog/backend.go +++ b/builtin/audit/syslog/backend.go @@ -52,7 +52,7 @@ type Backend struct { logRaw bool } -func (b *Backend) LogRequest(auth *logical.Auth, req *logical.Request) error { +func (b *Backend) LogRequest(auth *logical.Auth, req *logical.Request, outerErr error) error { if !b.logRaw { // Copy the structures cp, err := copystructure.Copy(auth) @@ -79,7 +79,7 @@ func (b *Backend) LogRequest(auth *logical.Auth, req *logical.Request) error { // Encode the entry as JSON var buf bytes.Buffer var format audit.FormatJSON - if err := format.FormatRequest(&buf, auth, req); err != nil { + if err := format.FormatRequest(&buf, auth, req, outerErr); err != nil { return err } diff --git a/vault/audit.go b/vault/audit.go index ad1a157558..ac0dc27d62 100644 --- a/vault/audit.go +++ b/vault/audit.go @@ -261,7 +261,7 @@ func (a *AuditBroker) IsRegistered(name string) bool { // LogRequest is used to ensure all the audit backends have an opportunity to // log the given request and that *at least one* succeeds. -func (a *AuditBroker) LogRequest(auth *logical.Auth, req *logical.Request) error { +func (a *AuditBroker) LogRequest(auth *logical.Auth, req *logical.Request, outerErr error) error { defer metrics.MeasureSince([]string{"audit", "log_request"}, time.Now()) a.l.RLock() defer a.l.RUnlock() @@ -270,7 +270,7 @@ func (a *AuditBroker) LogRequest(auth *logical.Auth, req *logical.Request) error anyLogged := false for name, be := range a.backends { start := time.Now() - err := be.backend.LogRequest(auth, req) + err := be.backend.LogRequest(auth, req, outerErr) metrics.MeasureSince([]string{"audit", name, "log_request"}, start) if err != nil { a.logger.Printf("[ERR] audit: backend '%s' failed to log request: %v", name, err) diff --git a/vault/core.go b/vault/core.go index 3c67f63fad..3b0dd3d311 100644 --- a/vault/core.go +++ b/vault/core.go @@ -374,6 +374,7 @@ func (c *Core) HandleRequest(req *logical.Request) (resp *logical.Response, err func (c *Core) handleRequest(req *logical.Request) (*logical.Response, error) { defer metrics.MeasureSince([]string{"core", "handle_request"}, time.Now()) + // Validate the token auth, err := c.checkToken(req.Operation, req.Path, req.ClientToken) if err != nil { @@ -387,6 +388,11 @@ func (c *Core) handleRequest(req *logical.Request) (*logical.Response, error) { errType = logical.ErrInvalidRequest } + if err := c.auditBroker.LogRequest(auth, req, err); err != nil { + c.logger.Printf("[ERR] core: failed to audit request (%#v): %v", + req, err) + } + return logical.ErrorResponse(err.Error()), errType } @@ -394,7 +400,7 @@ func (c *Core) handleRequest(req *logical.Request) (*logical.Response, error) { req.DisplayName = auth.DisplayName // Create an audit trail of the request - if err := c.auditBroker.LogRequest(auth, req); err != nil { + if err := c.auditBroker.LogRequest(auth, req, errors.New("")); err != nil { c.logger.Printf("[ERR] core: failed to audit request (%#v): %v", req, err) return nil, ErrInternalError @@ -473,7 +479,7 @@ func (c *Core) handleLoginRequest(req *logical.Request) (*logical.Response, erro defer metrics.MeasureSince([]string{"core", "handle_login_request"}, time.Now()) // Create an audit trail of the request, auth is not available on login requests - if err := c.auditBroker.LogRequest(nil, req); err != nil { + if err := c.auditBroker.LogRequest(nil, req, errors.New("")); err != nil { c.logger.Printf("[ERR] core: failed to audit request (%#v): %v", req, err) return nil, ErrInternalError diff --git a/vault/testing.go b/vault/testing.go index 5bb4946eab..e1d1c4da7a 100644 --- a/vault/testing.go +++ b/vault/testing.go @@ -86,7 +86,7 @@ func TestKeyCopy(key []byte) []byte { type noopAudit struct{} -func (n *noopAudit) LogRequest(a *logical.Auth, r *logical.Request) error { +func (n *noopAudit) LogRequest(a *logical.Auth, r *logical.Request, e error) error { return nil } diff --git a/vault/token_store.go b/vault/token_store.go index 03f278c83c..af032ca422 100644 --- a/vault/token_store.go +++ b/vault/token_store.go @@ -656,13 +656,13 @@ func (ts *TokenStore) handleLookup( // Lookup the token out, err := ts.Lookup(id) + if err != nil { return logical.ErrorResponse(err.Error()), logical.ErrInvalidRequest } - // Fast-path the not found case if out == nil { - return nil, nil + return logical.ErrorResponse("bad token"), logical.ErrPermissionDenied } // Generate a response. We purposely omit the parent reference otherwise