From 7b646fcc5e99ee5f75e4af232f1930a1c36b2e18 Mon Sep 17 00:00:00 2001 From: Laura Bennett Date: Sat, 23 Jul 2016 21:46:28 -0400 Subject: [PATCH] initial local commit --- logical/request.go | 25 ++++++++++++++----------- logical/response.go | 20 ++++++++++---------- vault/audit.go | 15 ++++++++++++--- vault/audit_test.go | 11 ++++++++++- vault/core.go | 6 ++++++ vault/core_test.go | 6 ++++++ vault/request_handling.go | 8 ++++++++ vault/router.go | 4 ++++ 8 files changed, 70 insertions(+), 25 deletions(-) diff --git a/logical/request.go b/logical/request.go index 1dedb6ac49..5cd7f69a99 100644 --- a/logical/request.go +++ b/logical/request.go @@ -10,53 +10,56 @@ import ( // of a request being made to Vault. It is used to abstract // the details of the higher level request protocol from the handlers. type Request struct { + // Id is the uuid associated with each request + ID string `json:"id" structs:"id" mapstructure:"id"` + // Operation is the requested operation type - Operation Operation + Operation Operation `json:"operation" structs:"operation" mapstructure:"operation"` // Path is the part of the request path not consumed by the // routing. As an example, if the original request path is "prod/aws/foo" // and the AWS logical backend is mounted at "prod/aws/", then the // final path is "foo" since the mount prefix is trimmed. - Path string + Path string `json:"path" structs:"path" mapstructure:"path"` // Request data is an opaque map that must have string keys. - Data map[string]interface{} + Data map[string]interface{} `json:"map" structs:"data" mapstructure:"data"` // Storage can be used to durably store and retrieve state. - Storage Storage + Storage Storage `json:"storage" structs:"storage" mapstructure:"storage"` // Secret will be non-nil only for Revoke and Renew operations // to represent the secret that was returned prior. - Secret *Secret + Secret *Secret `json:"secret" structs:"secret" mapstructure:"secret"` // Auth will be non-nil only for Renew operations // to represent the auth that was returned prior. - Auth *Auth + Auth *Auth `json:"auth" structs:"auth" mapstructure:"auth"` // Connection will be non-nil only for credential providers to // inspect the connection information and potentially use it for // authentication/protection. - Connection *Connection + Connection *Connection `json:"connection" structs:"connection" mapstructure:"connection"` // ClientToken is provided to the core so that the identity // can be verified and ACLs applied. This value is passed // through to the logical backends but after being salted and // hashed. - ClientToken string + ClientToken string `json:"client_token" structs:"client_token" mapstructure:"client_token"` // DisplayName is provided to the logical backend to help associate // dynamic secrets with the source entity. This is not a sensitive // name, but is useful for operators. - DisplayName string + DisplayName string `json:"display_name" structs:"display_name" mapstructure:"display_name"` // MountPoint is provided so that a logical backend can generate // paths relative to itself. The `Path` is effectively the client // request path with the MountPoint trimmed off. - MountPoint string + MountPoint string `json:"mount_point" structs:"mount_point" mapstructure:"mount_point"` // WrapTTL contains the requested TTL of the token used to wrap the // response in a cubbyhole. - WrapTTL time.Duration + WrapTTL time.Duration `json:"wrap_ttl" struct:"wrap_ttl" mapstructure:"wrap_ttl"` } // Get returns a data field and guards for nil Data diff --git a/logical/response.go b/logical/response.go index 02566d99b3..395559dba5 100644 --- a/logical/response.go +++ b/logical/response.go @@ -31,51 +31,51 @@ const ( type WrapInfo struct { // Setting to non-zero specifies that the response should be wrapped. // Specifies the desired TTL of the wrapping token. - TTL time.Duration + TTL time.Duration `json:"ttl" structs:"ttl" mapstructure:"ttl"` // The token containing the wrapped response - Token string + Token string `json:"token" structs:"token" mapstructure:"token"` // The creation time. This can be used with the TTL to figure out an // expected expiration. - CreationTime time.Time + CreationTime time.Time `json:"creation_time" structs:"creation_time" mapstructure:"cration_time"` // If the contained response is the output of a token creation call, the // created token's accessor will be accessible here - WrappedAccessor string + WrappedAccessor string `json:"wrapped_accessor" structs:"wrapped_accessor" mapstructure:"wrapped_accessor"` } // Response is a struct that stores the response of a request. // It is used to abstract the details of the higher level request protocol. type Response struct { // Secret, if not nil, denotes that this response represents a secret. - Secret *Secret + Secret *Secret `json:"secret" structs:"secret" mapstructure:"secret"` // Auth, if not nil, contains the authentication information for // this response. This is only checked and means something for // credential backends. - Auth *Auth + Auth *Auth `json:"auth" structs:"auth" mapstructure:"auth"` // Response data is an opaque map that must have string keys. For // secrets, this data is sent down to the user as-is. To store internal // data that you don't want the user to see, store it in // Secret.InternalData. - Data map[string]interface{} + Data map[string]interface{} `json:"data" structs:"data" mapstructure:"data"` // Redirect is an HTTP URL to redirect to for further authentication. // This is only valid for credential backends. This will be blanked // for any logical backend and ignored. - Redirect string + Redirect string `json:"redirect" structs:"redirect" mapstructure:"redirect"` // Warnings allow operations or backends to return warnings in response // to user actions without failing the action outright. // Making it private helps ensure that it is easy for various parts of // Vault (backend, core, etc.) to add warnings without accidentally // replacing what exists. - warnings []string + warnings []string `json:"warnings" structs:"warnings" mapstructure:"warnings"` // Information for wrapping the response in a cubbyhole - WrapInfo *WrapInfo + WrapInfo *WrapInfo `json:"wrap_info" structs:"wrap_info" mapstructure:"wrap_info"` } func init() { diff --git a/vault/audit.go b/vault/audit.go index 9688b0367d..a2d1158ecc 100644 --- a/vault/audit.go +++ b/vault/audit.go @@ -11,6 +11,7 @@ import ( "time" "github.com/armon/go-metrics" + "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/audit" "github.com/hashicorp/vault/helper/jsonutil" @@ -351,17 +352,24 @@ func (a *AuditBroker) GetHash(name string, input string) (string, error) { // 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, outerErr error) (reterr error) { +func (a *AuditBroker) LogRequest(auth *logical.Auth, req *logical.Request, outerErr error) (retErr error) { defer metrics.MeasureSince([]string{"audit", "log_request"}, time.Now()) a.l.RLock() defer a.l.RUnlock() defer func() { if r := recover(); r != nil { a.logger.Printf("[ERR] audit: panic logging: req path: %s", req.Path) - reterr = fmt.Errorf("panic generating audit log") + retErr = multierror.Append(retErr, fmt.Errorf("panic generating audit log")) } }() + // All logged requests must have an identifier + if req.ID == "" { + a.logger.Printf("[ERR] audit: missing identifier in request object: %s", req.Path) + retErr = multierror.Append(retErr, fmt.Errorf("missing identifier in request object: %s", req.Path)) + return + } + // Ensure at least one backend logs anyLogged := false for name, be := range a.backends { @@ -375,7 +383,8 @@ func (a *AuditBroker) LogRequest(auth *logical.Auth, req *logical.Request, outer } } if !anyLogged && len(a.backends) > 0 { - return fmt.Errorf("no audit backend succeeded in logging the request") + retErr = multierror.Append(retErr, fmt.Errorf("no audit backend succeeded in logging the request")) + return } return nil } diff --git a/vault/audit_test.go b/vault/audit_test.go index f673e0a269..6bd88e77f0 100644 --- a/vault/audit_test.go +++ b/vault/audit_test.go @@ -10,6 +10,7 @@ import ( "errors" + "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/audit" "github.com/hashicorp/vault/logical" ) @@ -223,9 +224,17 @@ func TestAuditBroker_LogRequest(t *testing.T) { Operation: logical.ReadOperation, Path: "sys/mounts", } + + // Create an identifier for the request to verify against + var err error + req.ID, err = uuid.GenerateUUID() + if err != nil { + t.Fatalf("failed to generate identifier for the request: path%s err: %v", req.Path, err) + } + reqErrs := errors.New("errs") - err := b.LogRequest(auth, req, reqErrs) + err = b.LogRequest(auth, req, reqErrs) if err != nil { t.Fatalf("err: %v", err) } diff --git a/vault/core.go b/vault/core.go index 0dc6d6edb7..442719ef5c 100644 --- a/vault/core.go +++ b/vault/core.go @@ -708,6 +708,12 @@ func (c *Core) sealInitCommon(req *logical.Request) (retErr error) { return retErr } + // Create an identifier for the request + var err error + req.ID, err = uuid.GenerateUUID() + if err != nil { + return fmt.Errorf("failed to generate identifier for the request: path: %s err: %v", req.Path, err) + } // Validate the token is a root token acl, te, err := c.fetchACLandTokenEntry(req) if err != nil { diff --git a/vault/core_test.go b/vault/core_test.go index 4692f1c833..2363ba51b2 100644 --- a/vault/core_test.go +++ b/vault/core_test.go @@ -1153,6 +1153,12 @@ func TestCore_StepDown(t *testing.T) { Path: "sys/step-down", } + // Create an identifier for the request + req.ID, err = uuid.GenerateUUID() + if err != nil { + t.Fatalf("failed to generate identifier for the request: path: %s err: %v", req.Path, err) + } + // Step down core err = core.StepDown(req) if err != nil { diff --git a/vault/request_handling.go b/vault/request_handling.go index ae2021e031..6d18b0d436 100644 --- a/vault/request_handling.go +++ b/vault/request_handling.go @@ -2,12 +2,14 @@ package vault import ( "encoding/json" + "fmt" "sort" "strings" "time" "github.com/armon/go-metrics" "github.com/hashicorp/go-multierror" + "github.com/hashicorp/go-uuid" "github.com/hashicorp/vault/helper/strutil" "github.com/hashicorp/vault/logical" ) @@ -35,6 +37,12 @@ func (c *Core) HandleRequest(req *logical.Request) (resp *logical.Response, err return logical.ErrorResponse("cannot write to a path ending in '/'"), nil } + // Create an identifier for the request + req.ID, err = uuid.GenerateUUID() + if err != nil { + return nil, fmt.Errorf("failed to generate identifier for the request: path %s err %v", req.Path, err) + } + var auth *logical.Auth if c.router.LoginPath(req.Path) { resp, auth, err = c.handleLoginRequest(req) diff --git a/vault/router.go b/vault/router.go index 64f63bc3fd..d1c0746762 100644 --- a/vault/router.go +++ b/vault/router.go @@ -248,11 +248,15 @@ func (r *Router) routeCommon(req *logical.Request, existenceCheck bool) (*logica // Cache the pointer to the original connection object originalConn := req.Connection + // Cache the identifier of the request + originalReqID := req.ID + // Reset the request before returning defer func() { req.Path = original req.MountPoint = "" req.Connection = originalConn + req.ID = originalReqID req.Storage = nil req.ClientToken = clientToken }()