open-nomad/nomad/structs/config/audit.go
2023-04-10 15:36:59 +00:00

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
}