221 lines
4.8 KiB
Go
221 lines
4.8 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package config
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/hashicorp/nomad/helper/pointer"
|
|
"golang.org/x/exp/slices"
|
|
)
|
|
|
|
// AuditConfig is the configuration specific to Audit Logging
|
|
type AuditConfig struct {
|
|
// Enabled controls the Audit Logging mode
|
|
Enabled *bool `hcl:"enabled"`
|
|
|
|
// Sinks configure output sinks for audit logs
|
|
Sinks []*AuditSink `hcl:"sink"`
|
|
|
|
// Filters configure audit event filters to filter out certain eevents
|
|
// from being written to a sink.
|
|
Filters []*AuditFilter `hcl:"filter"`
|
|
|
|
// ExtraKeysHCL is used by hcl to surface unexpected keys
|
|
ExtraKeysHCL []string `hcl:",unusedKeys" json:"-"`
|
|
}
|
|
|
|
type AuditSink struct {
|
|
// Name is a unique name given to the filter
|
|
Name string `hcl:",key"`
|
|
|
|
// DeliveryGuarantee is the level at which delivery of logs must
|
|
// be met in order to successfully make requests
|
|
DeliveryGuarantee string `hcl:"delivery_guarantee"`
|
|
|
|
// Type is the sink type to configure. (file)
|
|
Type string `hcl:"type"`
|
|
|
|
// Format is the sink output format. (json)
|
|
Format string `hcl:"format"`
|
|
|
|
// FileName is the name that the audit log should follow.
|
|
// If rotation is enabled the pattern will be name-timestamp.log
|
|
Path string `hcl:"path"`
|
|
|
|
// RotateDuration is the time period that logs should be rotated in
|
|
RotateDuration time.Duration
|
|
RotateDurationHCL string `hcl:"rotate_duration" json:"-"`
|
|
|
|
// RotateBytes is the max number of bytes that should be written to a file
|
|
RotateBytes int `hcl:"rotate_bytes"`
|
|
|
|
// RotateMaxFiles is the max number of log files to keep
|
|
RotateMaxFiles int `hcl:"rotate_max_files"`
|
|
|
|
// Mode is the octal formatted permissions for the audit log files.
|
|
Mode string `hcl:"mode"`
|
|
}
|
|
|
|
// AuditFilter is the configuration for a Audit Log Filter
|
|
type AuditFilter struct {
|
|
// Name is a unique name given to the filter
|
|
Name string `hcl:",key"`
|
|
|
|
// Type of auditing event to filter, such as HTTPEvent
|
|
Type string `hcl:"type"`
|
|
|
|
// Endpoints is the list of endpoints to include in the filter
|
|
Endpoints []string `hcl:"endpoints"`
|
|
|
|
// State is the auditing request lifecycle stage to filter
|
|
Stages []string `hcl:"stages"`
|
|
|
|
// Operations is the type of operation to filter, such as GET, DELETE
|
|
Operations []string `hcl:"operations"`
|
|
}
|
|
|
|
// Copy returns a new copy of an AuditConfig
|
|
func (a *AuditConfig) Copy() *AuditConfig {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
|
|
nc := new(AuditConfig)
|
|
*nc = *a
|
|
|
|
// Copy bool pointers
|
|
if a.Enabled != nil {
|
|
nc.Enabled = pointer.Of(*a.Enabled)
|
|
}
|
|
|
|
// Copy Sinks and Filters
|
|
nc.Sinks = copySliceAuditSink(nc.Sinks)
|
|
nc.Filters = copySliceAuditFilter(nc.Filters)
|
|
|
|
return nc
|
|
}
|
|
|
|
// Merge is used to merge two Audit Configs together. Settings from the input take precedence.
|
|
func (a *AuditConfig) Merge(b *AuditConfig) *AuditConfig {
|
|
result := a.Copy()
|
|
|
|
if b.Enabled != nil {
|
|
result.Enabled = pointer.Of(*b.Enabled)
|
|
}
|
|
|
|
// Merge Sinks
|
|
if len(a.Sinks) == 0 && len(b.Sinks) != 0 {
|
|
result.Sinks = copySliceAuditSink(b.Sinks)
|
|
} else if len(b.Sinks) != 0 {
|
|
result.Sinks = auditSinkSliceMerge(a.Sinks, b.Sinks)
|
|
}
|
|
|
|
// Merge Filters
|
|
if len(a.Filters) == 0 && len(b.Filters) != 0 {
|
|
result.Filters = copySliceAuditFilter(b.Filters)
|
|
} else if len(b.Filters) != 0 {
|
|
result.Filters = auditFilterSliceMerge(a.Filters, b.Filters)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (a *AuditSink) Copy() *AuditSink {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
|
|
nc := new(AuditSink)
|
|
*nc = *a
|
|
|
|
return nc
|
|
}
|
|
|
|
func (a *AuditFilter) Copy() *AuditFilter {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
|
|
nc := new(AuditFilter)
|
|
*nc = *a
|
|
|
|
// Copy slices
|
|
nc.Endpoints = slices.Clone(nc.Endpoints)
|
|
nc.Stages = slices.Clone(nc.Stages)
|
|
nc.Operations = slices.Clone(nc.Operations)
|
|
|
|
return nc
|
|
}
|
|
|
|
func copySliceAuditFilter(a []*AuditFilter) []*AuditFilter {
|
|
l := len(a)
|
|
if l == 0 {
|
|
return nil
|
|
}
|
|
|
|
ns := make([]*AuditFilter, l)
|
|
for idx, cfg := range a {
|
|
ns[idx] = cfg.Copy()
|
|
}
|
|
|
|
return ns
|
|
}
|
|
|
|
func auditFilterSliceMerge(a, b []*AuditFilter) []*AuditFilter {
|
|
n := make([]*AuditFilter, len(a))
|
|
seenKeys := make(map[string]int, len(a))
|
|
|
|
for i, config := range a {
|
|
n[i] = config.Copy()
|
|
seenKeys[config.Name] = i
|
|
}
|
|
|
|
for _, config := range b {
|
|
if fIndex, ok := seenKeys[config.Name]; ok {
|
|
n[fIndex] = config.Copy()
|
|
continue
|
|
}
|
|
|
|
n = append(n, config.Copy())
|
|
}
|
|
|
|
return n
|
|
}
|
|
|
|
func copySliceAuditSink(a []*AuditSink) []*AuditSink {
|
|
l := len(a)
|
|
if l == 0 {
|
|
return nil
|
|
}
|
|
|
|
ns := make([]*AuditSink, l)
|
|
for idx, cfg := range a {
|
|
ns[idx] = cfg.Copy()
|
|
}
|
|
|
|
return ns
|
|
}
|
|
|
|
func auditSinkSliceMerge(a, b []*AuditSink) []*AuditSink {
|
|
n := make([]*AuditSink, len(a))
|
|
seenKeys := make(map[string]int, len(a))
|
|
|
|
for i, config := range a {
|
|
n[i] = config.Copy()
|
|
seenKeys[config.Name] = i
|
|
}
|
|
|
|
for _, config := range b {
|
|
if fIndex, ok := seenKeys[config.Name]; ok {
|
|
n[fIndex] = config.Copy()
|
|
continue
|
|
}
|
|
|
|
n = append(n, config.Copy())
|
|
}
|
|
|
|
return n
|
|
}
|