mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-18 21:21:06 +02:00
* Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Adding explicit MPL license for sub-package. This directory and its subdirectories (packages) contain files licensed with the MPLv2 `LICENSE` file in this directory and are intentionally licensed separately from the BSL `LICENSE` file at the root of this repository. * Updating the license from MPL to Business Source License. Going forward, this project will be licensed under the Business Source License v1.1. Please see our blog post for more details at https://hashi.co/bsl-blog, FAQ at www.hashicorp.com/licensing-faq, and details of the license at www.hashicorp.com/bsl. * add missing license headers * Update copyright file headers to BUS-1.1 * Fix test that expected exact offset on hcl file --------- Co-authored-by: hashicorp-copywrite[bot] <110428419+hashicorp-copywrite[bot]@users.noreply.github.com> Co-authored-by: Sarah Thompson <sthompson@hashicorp.com> Co-authored-by: Brian Kassouf <bkassouf@hashicorp.com>
262 lines
6.3 KiB
Go
262 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"
|
|
}
|
|
|
|
format, ok := conf.Config["format"]
|
|
if !ok {
|
|
format = audit.JSONFormat.String()
|
|
}
|
|
switch format {
|
|
case audit.JSONFormat.String(), audit.JSONxFormat.String():
|
|
default:
|
|
return nil, fmt.Errorf("unknown format type %q", format)
|
|
}
|
|
|
|
// Check if hashing of accessor is disabled
|
|
hmacAccessor := true
|
|
if hmacAccessorRaw, ok := conf.Config["hmac_accessor"]; ok {
|
|
value, err := strconv.ParseBool(hmacAccessorRaw)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hmacAccessor = 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
|
|
}
|
|
|
|
elideListResponses := false
|
|
if elideListResponsesRaw, ok := conf.Config["elide_list_responses"]; ok {
|
|
value, err := strconv.ParseBool(elideListResponsesRaw)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
elideListResponses = value
|
|
}
|
|
|
|
// Get the logger
|
|
logger, err := gsyslog.NewLogger(gsyslog.LOG_INFO, facility, tag)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cfg, err := audit.NewFormatterConfig(
|
|
audit.WithElision(elideListResponses),
|
|
audit.WithFormat(format),
|
|
audit.WithHMACAccessor(hmacAccessor),
|
|
audit.WithRaw(logRaw),
|
|
)
|
|
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 format {
|
|
case audit.JSONFormat.String():
|
|
w = &audit.JSONWriter{Prefix: conf.Config["prefix"]}
|
|
case audit.JSONxFormat.String():
|
|
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 {
|
|
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(format, event.WithFacility(facility), event.WithTag(tag))
|
|
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)
|
|
}
|