mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 02:02:43 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			261 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			261 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) HashiCorp, Inc.
 | |
| // SPDX-License-Identifier: BUSL-1.1
 | |
| 
 | |
| package syslog
 | |
| 
 | |
| import (
 | |
| 	"bytes"
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"strconv"
 | |
| 	"sync"
 | |
| 
 | |
| 	"github.com/hashicorp/eventlogger"
 | |
| 	gsyslog "github.com/hashicorp/go-syslog"
 | |
| 	"github.com/hashicorp/vault/audit"
 | |
| 	"github.com/hashicorp/vault/internal/observability/event"
 | |
| 	"github.com/hashicorp/vault/sdk/helper/salt"
 | |
| 	"github.com/hashicorp/vault/sdk/logical"
 | |
| )
 | |
| 
 | |
| func Factory(ctx context.Context, conf *audit.BackendConfig, useEventLogger bool, headersConfig audit.HeaderFormatter) (audit.Backend, error) {
 | |
| 	if conf.SaltConfig == nil {
 | |
| 		return nil, fmt.Errorf("nil salt config")
 | |
| 	}
 | |
| 
 | |
| 	if conf.SaltView == nil {
 | |
| 		return nil, fmt.Errorf("nil salt view")
 | |
| 	}
 | |
| 
 | |
| 	// Get facility or default to AUTH
 | |
| 	facility, ok := conf.Config["facility"]
 | |
| 	if !ok {
 | |
| 		facility = "AUTH"
 | |
| 	}
 | |
| 
 | |
| 	// Get tag or default to 'vault'
 | |
| 	tag, ok := conf.Config["tag"]
 | |
| 	if !ok {
 | |
| 		tag = "vault"
 | |
| 	}
 | |
| 
 | |
| 	var cfgOpts []audit.Option
 | |
| 
 | |
| 	if format, ok := conf.Config["format"]; ok {
 | |
| 		cfgOpts = append(cfgOpts, audit.WithFormat(format))
 | |
| 	}
 | |
| 
 | |
| 	// Check if hashing of accessor is disabled
 | |
| 	if hmacAccessorRaw, ok := conf.Config["hmac_accessor"]; ok {
 | |
| 		v, err := strconv.ParseBool(hmacAccessorRaw)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		cfgOpts = append(cfgOpts, audit.WithHMACAccessor(v))
 | |
| 	}
 | |
| 
 | |
| 	// Check if raw logging is enabled
 | |
| 	if raw, ok := conf.Config["log_raw"]; ok {
 | |
| 		v, err := strconv.ParseBool(raw)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		cfgOpts = append(cfgOpts, audit.WithRaw(v))
 | |
| 	}
 | |
| 
 | |
| 	if elideListResponsesRaw, ok := conf.Config["elide_list_responses"]; ok {
 | |
| 		v, err := strconv.ParseBool(elideListResponsesRaw)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 		cfgOpts = append(cfgOpts, audit.WithElision(v))
 | |
| 	}
 | |
| 
 | |
| 	cfg, err := audit.NewFormatterConfig(cfgOpts...)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// Get the logger
 | |
| 	logger, err := gsyslog.NewLogger(gsyslog.LOG_INFO, facility, tag)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	b := &Backend{
 | |
| 		logger:       logger,
 | |
| 		saltConfig:   conf.SaltConfig,
 | |
| 		saltView:     conf.SaltView,
 | |
| 		formatConfig: cfg,
 | |
| 	}
 | |
| 
 | |
| 	// Configure the formatter for either case.
 | |
| 	f, err := audit.NewEntryFormatter(b.formatConfig, b, audit.WithHeaderFormatter(headersConfig), audit.WithPrefix(conf.Config["prefix"]))
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("error creating formatter: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	var w audit.Writer
 | |
| 	switch b.formatConfig.RequiredFormat {
 | |
| 	case audit.JSONFormat:
 | |
| 		w = &audit.JSONWriter{Prefix: conf.Config["prefix"]}
 | |
| 	case audit.JSONxFormat:
 | |
| 		w = &audit.JSONxWriter{Prefix: conf.Config["prefix"]}
 | |
| 	}
 | |
| 
 | |
| 	fw, err := audit.NewEntryFormatterWriter(b.formatConfig, f, w)
 | |
| 	if err != nil {
 | |
| 		return nil, fmt.Errorf("error creating formatter writer: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	b.formatter = fw
 | |
| 
 | |
| 	if useEventLogger {
 | |
| 		var opts []event.Option
 | |
| 
 | |
| 		// Get facility or default to AUTH
 | |
| 		if facility, ok := conf.Config["facility"]; ok {
 | |
| 			opts = append(opts, event.WithFacility(facility))
 | |
| 		}
 | |
| 
 | |
| 		if tag, ok := conf.Config["tag"]; ok {
 | |
| 			opts = append(opts, event.WithTag(tag))
 | |
| 		}
 | |
| 
 | |
| 		b.nodeIDList = make([]eventlogger.NodeID, 2)
 | |
| 		b.nodeMap = make(map[eventlogger.NodeID]eventlogger.Node)
 | |
| 
 | |
| 		formatterNodeID, err := event.GenerateNodeID()
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("error generating random NodeID for formatter node: %w", err)
 | |
| 		}
 | |
| 		b.nodeIDList[0] = formatterNodeID
 | |
| 		b.nodeMap[formatterNodeID] = f
 | |
| 
 | |
| 		n, err := event.NewSyslogSink(b.formatConfig.RequiredFormat.String(), opts...)
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("error creating syslog sink node: %w", err)
 | |
| 		}
 | |
| 		sinkNode := &audit.SinkWrapper{Name: conf.MountPath, Sink: n}
 | |
| 
 | |
| 		sinkNodeID, err := event.GenerateNodeID()
 | |
| 		if err != nil {
 | |
| 			return nil, fmt.Errorf("error generating random NodeID for sink node: %w", err)
 | |
| 		}
 | |
| 		b.nodeIDList[1] = sinkNodeID
 | |
| 		b.nodeMap[sinkNodeID] = sinkNode
 | |
| 	}
 | |
| 	return b, nil
 | |
| }
 | |
| 
 | |
| // Backend is the audit backend for the syslog-based audit store.
 | |
| type Backend struct {
 | |
| 	logger gsyslog.Syslogger
 | |
| 
 | |
| 	formatter    *audit.EntryFormatterWriter
 | |
| 	formatConfig audit.FormatterConfig
 | |
| 
 | |
| 	saltMutex  sync.RWMutex
 | |
| 	salt       *salt.Salt
 | |
| 	saltConfig *salt.Config
 | |
| 	saltView   logical.Storage
 | |
| 
 | |
| 	nodeIDList []eventlogger.NodeID
 | |
| 	nodeMap    map[eventlogger.NodeID]eventlogger.Node
 | |
| }
 | |
| 
 | |
| var _ audit.Backend = (*Backend)(nil)
 | |
| 
 | |
| func (b *Backend) LogRequest(ctx context.Context, in *logical.LogInput) error {
 | |
| 	var buf bytes.Buffer
 | |
| 	if err := b.formatter.FormatAndWriteRequest(ctx, &buf, in); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	// Write out to syslog
 | |
| 	_, err := b.logger.Write(buf.Bytes())
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func (b *Backend) LogResponse(ctx context.Context, in *logical.LogInput) error {
 | |
| 	var buf bytes.Buffer
 | |
| 	if err := b.formatter.FormatAndWriteResponse(ctx, &buf, in); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	// Write out to syslog
 | |
| 	_, err := b.logger.Write(buf.Bytes())
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func (b *Backend) LogTestMessage(ctx context.Context, in *logical.LogInput, config map[string]string) error {
 | |
| 	// Event logger behavior - manually Process each node
 | |
| 	if len(b.nodeIDList) > 0 {
 | |
| 		return audit.ProcessManual(ctx, in, b.nodeIDList, b.nodeMap)
 | |
| 	}
 | |
| 
 | |
| 	// Old behavior
 | |
| 	var buf bytes.Buffer
 | |
| 
 | |
| 	temporaryFormatter, err := audit.NewTemporaryFormatter(config["format"], config["prefix"])
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if err = temporaryFormatter.FormatAndWriteRequest(ctx, &buf, in); err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	// Send to syslog
 | |
| 	_, err = b.logger.Write(buf.Bytes())
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func (b *Backend) Reload(_ context.Context) error {
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (b *Backend) Salt(ctx context.Context) (*salt.Salt, error) {
 | |
| 	b.saltMutex.RLock()
 | |
| 	if b.salt != nil {
 | |
| 		defer b.saltMutex.RUnlock()
 | |
| 		return b.salt, nil
 | |
| 	}
 | |
| 	b.saltMutex.RUnlock()
 | |
| 	b.saltMutex.Lock()
 | |
| 	defer b.saltMutex.Unlock()
 | |
| 	if b.salt != nil {
 | |
| 		return b.salt, nil
 | |
| 	}
 | |
| 	salt, err := salt.NewSalt(ctx, b.saltView, b.saltConfig)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	b.salt = salt
 | |
| 	return salt, nil
 | |
| }
 | |
| 
 | |
| func (b *Backend) Invalidate(_ context.Context) {
 | |
| 	b.saltMutex.Lock()
 | |
| 	defer b.saltMutex.Unlock()
 | |
| 	b.salt = nil
 | |
| }
 | |
| 
 | |
| // RegisterNodesAndPipeline registers the nodes and a pipeline as required by
 | |
| // the audit.Backend interface.
 | |
| func (b *Backend) RegisterNodesAndPipeline(broker *eventlogger.Broker, name string) error {
 | |
| 	for id, node := range b.nodeMap {
 | |
| 		if err := broker.RegisterNode(id, node); err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	pipeline := eventlogger.Pipeline{
 | |
| 		PipelineID: eventlogger.PipelineID(name),
 | |
| 		EventType:  eventlogger.EventType("audit"),
 | |
| 		NodeIDs:    b.nodeIDList,
 | |
| 	}
 | |
| 
 | |
| 	return broker.RegisterPipeline(pipeline)
 | |
| }
 | 
