mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 02:02:43 +00:00 
			
		
		
		
	 93f18cbd7a
			
		
	
	93f18cbd7a
	
	
	
		
			
			* add escape hatch to use feature flag for reversion of audit behavior * Setup pipeline which ends with a NoopSink * explicitly call out old way of running test * old behavior for audit trail tests * More manual forcing of tests to legacy audit system * Add NOTE: to suggest that the feature flag is temporary
		
			
				
	
	
		
			320 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			320 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) HashiCorp, Inc.
 | |
| // SPDX-License-Identifier: BUSL-1.1
 | |
| 
 | |
| package audit
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"io"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/hashicorp/eventlogger"
 | |
| 	"github.com/hashicorp/vault/sdk/helper/salt"
 | |
| 
 | |
| 	"github.com/hashicorp/vault/sdk/logical"
 | |
| )
 | |
| 
 | |
| // Audit subtypes.
 | |
| const (
 | |
| 	RequestType  subtype = "AuditRequest"
 | |
| 	ResponseType subtype = "AuditResponse"
 | |
| )
 | |
| 
 | |
| // Audit formats.
 | |
| const (
 | |
| 	JSONFormat  format = "json"
 | |
| 	JSONxFormat format = "jsonx"
 | |
| )
 | |
| 
 | |
| // version defines the version of audit events.
 | |
| const version = "v0.1"
 | |
| 
 | |
| // subtype defines the type of audit event.
 | |
| type subtype string
 | |
| 
 | |
| // format defines types of format audit events support.
 | |
| type format string
 | |
| 
 | |
| // auditEvent is the audit event.
 | |
| type auditEvent struct {
 | |
| 	ID        string            `json:"id"`
 | |
| 	Version   string            `json:"version"`
 | |
| 	Subtype   subtype           `json:"subtype"` // the subtype of the audit event.
 | |
| 	Timestamp time.Time         `json:"timestamp"`
 | |
| 	Data      *logical.LogInput `json:"data"`
 | |
| }
 | |
| 
 | |
| // Option is how options are passed as arguments.
 | |
| type Option func(*options) error
 | |
| 
 | |
| // options are used to represent configuration for a audit related nodes.
 | |
| type options struct {
 | |
| 	withID              string
 | |
| 	withNow             time.Time
 | |
| 	withSubtype         subtype
 | |
| 	withFormat          format
 | |
| 	withPrefix          string
 | |
| 	withRaw             bool
 | |
| 	withElision         bool
 | |
| 	withOmitTime        bool
 | |
| 	withHMACAccessor    bool
 | |
| 	withHeaderFormatter HeaderFormatter
 | |
| }
 | |
| 
 | |
| // Salter is an interface that provides a way to obtain a Salt for hashing.
 | |
| type Salter interface {
 | |
| 	// Salt returns a non-nil salt or an error.
 | |
| 	Salt(context.Context) (*salt.Salt, error)
 | |
| }
 | |
| 
 | |
| // Formatter is an interface that is responsible for formatting a request/response into some format.
 | |
| // It is recommended that you pass data through Hash prior to formatting it.
 | |
| type Formatter interface {
 | |
| 	// FormatRequest formats the logical.LogInput into an RequestEntry.
 | |
| 	FormatRequest(context.Context, *logical.LogInput) (*RequestEntry, error)
 | |
| 	// FormatResponse formats the logical.LogInput into an ResponseEntry.
 | |
| 	FormatResponse(context.Context, *logical.LogInput) (*ResponseEntry, error)
 | |
| }
 | |
| 
 | |
| // Writer is an interface that provides a way to write request and response audit entries.
 | |
| // Formatters write their output to an io.Writer.
 | |
| type Writer interface {
 | |
| 	// WriteRequest writes the request entry to the writer or returns an error.
 | |
| 	WriteRequest(io.Writer, *RequestEntry) error
 | |
| 	// WriteResponse writes the response entry to the writer or returns an error.
 | |
| 	WriteResponse(io.Writer, *ResponseEntry) error
 | |
| }
 | |
| 
 | |
| // HeaderFormatter is an interface defining the methods of the
 | |
| // vault.AuditedHeadersConfig structure needed in this package.
 | |
| type HeaderFormatter interface {
 | |
| 	// ApplyConfig returns a map of header values that consists of the
 | |
| 	// intersection of the provided set of header values with a configured
 | |
| 	// set of headers and will hash headers that have been configured as such.
 | |
| 	ApplyConfig(context.Context, map[string][]string, Salter) (map[string][]string, error)
 | |
| }
 | |
| 
 | |
| // EntryFormatter should be used to format audit requests and responses.
 | |
| type EntryFormatter struct {
 | |
| 	salter          Salter
 | |
| 	headerFormatter HeaderFormatter
 | |
| 	config          FormatterConfig
 | |
| 	prefix          string
 | |
| }
 | |
| 
 | |
| // EntryFormatterWriter should be used to format and write out audit requests and responses.
 | |
| type EntryFormatterWriter struct {
 | |
| 	Formatter
 | |
| 	Writer
 | |
| 	config FormatterConfig
 | |
| }
 | |
| 
 | |
| // FormatterConfig is used to provide basic configuration to a formatter.
 | |
| // Use NewFormatterConfig to initialize the FormatterConfig struct.
 | |
| type FormatterConfig struct {
 | |
| 	Raw          bool
 | |
| 	HMACAccessor bool
 | |
| 
 | |
| 	// Vault lacks pagination in its APIs. As a result, certain list operations can return **very** large responses.
 | |
| 	// The user's chosen audit sinks may experience difficulty consuming audit records that swell to tens of megabytes
 | |
| 	// of JSON. The responses of list operations are typically not very interesting, as they are mostly lists of keys,
 | |
| 	// or, even when they include a "key_info" field, are not returning confidential information. They become even less
 | |
| 	// interesting once HMAC-ed by the audit system.
 | |
| 	//
 | |
| 	// Some example Vault "list" operations that are prone to becoming very large in an active Vault installation are:
 | |
| 	//   auth/token/accessors/
 | |
| 	//   identity/entity/id/
 | |
| 	//   identity/entity-alias/id/
 | |
| 	//   pki/certs/
 | |
| 	//
 | |
| 	// This option exists to provide such users with the option to have response data elided from audit logs, only when
 | |
| 	// the operation type is "list". For added safety, the elision only applies to the "keys" and "key_info" fields
 | |
| 	// within the response data - these are conventionally the only fields present in a list response - see
 | |
| 	// logical.ListResponse, and logical.ListResponseWithInfo. However, other fields are technically possible if a
 | |
| 	// plugin author writes unusual code, and these will be preserved in the audit log even with this option enabled.
 | |
| 	// The elision replaces the values of the "keys" and "key_info" fields with an integer count of the number of
 | |
| 	// entries. This allows even the elided audit logs to still be useful for answering questions like
 | |
| 	// "Was any data returned?" or "How many records were listed?".
 | |
| 	ElideListResponses bool
 | |
| 
 | |
| 	// This should only ever be used in a testing context
 | |
| 	OmitTime bool
 | |
| 
 | |
| 	// The required/target format for the event (supported: JSONFormat and JSONxFormat).
 | |
| 	RequiredFormat format
 | |
| }
 | |
| 
 | |
| // RequestEntry is the structure of a request audit log entry.
 | |
| type RequestEntry struct {
 | |
| 	Time          string   `json:"time,omitempty"`
 | |
| 	Type          string   `json:"type,omitempty"`
 | |
| 	Auth          *Auth    `json:"auth,omitempty"`
 | |
| 	Request       *Request `json:"request,omitempty"`
 | |
| 	Error         string   `json:"error,omitempty"`
 | |
| 	ForwardedFrom string   `json:"forwarded_from,omitempty"` // Populated in Enterprise when a request is forwarded
 | |
| }
 | |
| 
 | |
| // ResponseEntry is the structure of a response audit log entry.
 | |
| type ResponseEntry struct {
 | |
| 	Time      string    `json:"time,omitempty"`
 | |
| 	Type      string    `json:"type,omitempty"`
 | |
| 	Auth      *Auth     `json:"auth,omitempty"`
 | |
| 	Request   *Request  `json:"request,omitempty"`
 | |
| 	Response  *Response `json:"response,omitempty"`
 | |
| 	Error     string    `json:"error,omitempty"`
 | |
| 	Forwarded bool      `json:"forwarded,omitempty"`
 | |
| }
 | |
| 
 | |
| type Request struct {
 | |
| 	ID                            string                 `json:"id,omitempty"`
 | |
| 	ClientID                      string                 `json:"client_id,omitempty"`
 | |
| 	ReplicationCluster            string                 `json:"replication_cluster,omitempty"`
 | |
| 	Operation                     logical.Operation      `json:"operation,omitempty"`
 | |
| 	MountPoint                    string                 `json:"mount_point,omitempty"`
 | |
| 	MountType                     string                 `json:"mount_type,omitempty"`
 | |
| 	MountAccessor                 string                 `json:"mount_accessor,omitempty"`
 | |
| 	MountRunningVersion           string                 `json:"mount_running_version,omitempty"`
 | |
| 	MountRunningSha256            string                 `json:"mount_running_sha256,omitempty"`
 | |
| 	MountClass                    string                 `json:"mount_class,omitempty"`
 | |
| 	MountIsExternalPlugin         bool                   `json:"mount_is_external_plugin,omitempty"`
 | |
| 	ClientToken                   string                 `json:"client_token,omitempty"`
 | |
| 	ClientTokenAccessor           string                 `json:"client_token_accessor,omitempty"`
 | |
| 	Namespace                     *Namespace             `json:"namespace,omitempty"`
 | |
| 	Path                          string                 `json:"path,omitempty"`
 | |
| 	Data                          map[string]interface{} `json:"data,omitempty"`
 | |
| 	PolicyOverride                bool                   `json:"policy_override,omitempty"`
 | |
| 	RemoteAddr                    string                 `json:"remote_address,omitempty"`
 | |
| 	RemotePort                    int                    `json:"remote_port,omitempty"`
 | |
| 	WrapTTL                       int                    `json:"wrap_ttl,omitempty"`
 | |
| 	Headers                       map[string][]string    `json:"headers,omitempty"`
 | |
| 	ClientCertificateSerialNumber string                 `json:"client_certificate_serial_number,omitempty"`
 | |
| }
 | |
| 
 | |
| type Response struct {
 | |
| 	Auth                  *Auth                  `json:"auth,omitempty"`
 | |
| 	MountPoint            string                 `json:"mount_point,omitempty"`
 | |
| 	MountType             string                 `json:"mount_type,omitempty"`
 | |
| 	MountAccessor         string                 `json:"mount_accessor,omitempty"`
 | |
| 	MountRunningVersion   string                 `json:"mount_running_plugin_version,omitempty"`
 | |
| 	MountRunningSha256    string                 `json:"mount_running_sha256,omitempty"`
 | |
| 	MountClass            string                 `json:"mount_class,omitempty"`
 | |
| 	MountIsExternalPlugin bool                   `json:"mount_is_external_plugin,omitempty"`
 | |
| 	Secret                *Secret                `json:"secret,omitempty"`
 | |
| 	Data                  map[string]interface{} `json:"data,omitempty"`
 | |
| 	Warnings              []string               `json:"warnings,omitempty"`
 | |
| 	Redirect              string                 `json:"redirect,omitempty"`
 | |
| 	WrapInfo              *ResponseWrapInfo      `json:"wrap_info,omitempty"`
 | |
| 	Headers               map[string][]string    `json:"headers,omitempty"`
 | |
| }
 | |
| 
 | |
| type Auth struct {
 | |
| 	ClientToken               string              `json:"client_token,omitempty"`
 | |
| 	Accessor                  string              `json:"accessor,omitempty"`
 | |
| 	DisplayName               string              `json:"display_name,omitempty"`
 | |
| 	Policies                  []string            `json:"policies,omitempty"`
 | |
| 	TokenPolicies             []string            `json:"token_policies,omitempty"`
 | |
| 	IdentityPolicies          []string            `json:"identity_policies,omitempty"`
 | |
| 	ExternalNamespacePolicies map[string][]string `json:"external_namespace_policies,omitempty"`
 | |
| 	NoDefaultPolicy           bool                `json:"no_default_policy,omitempty"`
 | |
| 	PolicyResults             *PolicyResults      `json:"policy_results,omitempty"`
 | |
| 	Metadata                  map[string]string   `json:"metadata,omitempty"`
 | |
| 	NumUses                   int                 `json:"num_uses,omitempty"`
 | |
| 	RemainingUses             int                 `json:"remaining_uses,omitempty"`
 | |
| 	EntityID                  string              `json:"entity_id,omitempty"`
 | |
| 	EntityCreated             bool                `json:"entity_created,omitempty"`
 | |
| 	TokenType                 string              `json:"token_type,omitempty"`
 | |
| 	TokenTTL                  int64               `json:"token_ttl,omitempty"`
 | |
| 	TokenIssueTime            string              `json:"token_issue_time,omitempty"`
 | |
| }
 | |
| 
 | |
| type PolicyResults struct {
 | |
| 	Allowed          bool         `json:"allowed"`
 | |
| 	GrantingPolicies []PolicyInfo `json:"granting_policies,omitempty"`
 | |
| }
 | |
| 
 | |
| type PolicyInfo struct {
 | |
| 	Name          string `json:"name,omitempty"`
 | |
| 	NamespaceId   string `json:"namespace_id,omitempty"`
 | |
| 	NamespacePath string `json:"namespace_path,omitempty"`
 | |
| 	Type          string `json:"type"`
 | |
| }
 | |
| 
 | |
| type Secret struct {
 | |
| 	LeaseID string `json:"lease_id,omitempty"`
 | |
| }
 | |
| 
 | |
| type ResponseWrapInfo struct {
 | |
| 	TTL             int    `json:"ttl,omitempty"`
 | |
| 	Token           string `json:"token,omitempty"`
 | |
| 	Accessor        string `json:"accessor,omitempty"`
 | |
| 	CreationTime    string `json:"creation_time,omitempty"`
 | |
| 	CreationPath    string `json:"creation_path,omitempty"`
 | |
| 	WrappedAccessor string `json:"wrapped_accessor,omitempty"`
 | |
| }
 | |
| 
 | |
| type Namespace struct {
 | |
| 	ID   string `json:"id,omitempty"`
 | |
| 	Path string `json:"path,omitempty"`
 | |
| }
 | |
| 
 | |
| // nonPersistentSalt is used for obtaining a salt that is not persisted.
 | |
| type nonPersistentSalt struct{}
 | |
| 
 | |
| // Backend interface must be implemented for an audit
 | |
| // mechanism to be made available. Audit backends can be enabled to
 | |
| // sink information to different backends such as logs, file, databases,
 | |
| // or other external services.
 | |
| type Backend interface {
 | |
| 	// Salter interface must be implemented by anything implementing Backend.
 | |
| 	Salter
 | |
| 
 | |
| 	// LogRequest is used to synchronously log a request. This is done after the
 | |
| 	// request is authorized but before the request is executed. The arguments
 | |
| 	// MUST not be modified in any way. They should be deep copied if this is
 | |
| 	// a possibility.
 | |
| 	LogRequest(context.Context, *logical.LogInput) error
 | |
| 
 | |
| 	// LogResponse is used to synchronously log a response. This is done after
 | |
| 	// the request is processed but before the response is sent. The arguments
 | |
| 	// MUST not be modified in any way. They should be deep copied if this is
 | |
| 	// a possibility.
 | |
| 	LogResponse(context.Context, *logical.LogInput) error
 | |
| 
 | |
| 	// LogTestMessage is used to check an audit backend before adding it
 | |
| 	// permanently. It should attempt to synchronously log the given test
 | |
| 	// message, WITHOUT using the normal Salt (which would require a storage
 | |
| 	// operation on creation, which is currently disallowed.)
 | |
| 	LogTestMessage(context.Context, *logical.LogInput, map[string]string) error
 | |
| 
 | |
| 	// Reload is called on SIGHUP for supporting backends.
 | |
| 	Reload(context.Context) error
 | |
| 
 | |
| 	// Invalidate is called for path invalidation
 | |
| 	Invalidate(context.Context)
 | |
| 
 | |
| 	// RegisterNodesAndPipeline provides an eventlogger.Broker pointer so that
 | |
| 	// the Backend can call its RegisterNode and RegisterPipeline methods with
 | |
| 	// the nodes and the pipeline that were created in the corresponding
 | |
| 	// Factory function.
 | |
| 	RegisterNodesAndPipeline(*eventlogger.Broker, string) error
 | |
| }
 | |
| 
 | |
| // BackendConfig contains configuration parameters used in the factory func to
 | |
| // instantiate audit backends
 | |
| type BackendConfig struct {
 | |
| 	// The view to store the salt
 | |
| 	SaltView logical.Storage
 | |
| 
 | |
| 	// The salt config that should be used for any secret obfuscation
 | |
| 	SaltConfig *salt.Config
 | |
| 
 | |
| 	// Config is the opaque user configuration provided when mounting
 | |
| 	Config map[string]string
 | |
| 
 | |
| 	// MountPath is the path where this Backend is mounted
 | |
| 	MountPath string
 | |
| }
 | |
| 
 | |
| // Factory is the factory function to create an audit backend.
 | |
| type Factory func(context.Context, *BackendConfig, bool, HeaderFormatter) (Backend, error)
 |