1package config
2
3import (
4	"time"
5
6	"github.com/hashicorp/nomad/helper"
7)
8
9// AuditConfig is the configuration specific to Audit Logging
10type AuditConfig struct {
11	// Enabled controls the Audit Logging mode
12	Enabled *bool `hcl:"enabled"`
13
14	// Sinks configure output sinks for audit logs
15	Sinks []*AuditSink `hcl:"sink"`
16
17	// Filters configure audit event filters to filter out certain eevents
18	// from being written to a sink.
19	Filters []*AuditFilter `hcl:"filter"`
20
21	// ExtraKeysHCL is used by hcl to surface unexpected keys
22	ExtraKeysHCL []string `hcl:",unusedKeys" json:"-"`
23}
24
25type AuditSink struct {
26	// Name is a unique name given to the filter
27	Name string `hcl:",key"`
28
29	// DeliveryGuarantee is the level at which delivery of logs must
30	// be met in order to successfully make requests
31	DeliveryGuarantee string `hcl:"delivery_guarantee"`
32
33	// Type is the sink type to configure. (file)
34	Type string `hcl:"type"`
35
36	// Format is the sink output format. (json)
37	Format string `hcl:"format"`
38
39	// FileName is the name that the audit log should follow.
40	// If rotation is enabled the pattern will be name-timestamp.log
41	Path string `hcl:"path"`
42
43	// RotateDuration is the time period that logs should be rotated in
44	RotateDuration    time.Duration
45	RotateDurationHCL string `hcl:"rotate_duration" json:"-"`
46
47	// RotateBytes is the max number of bytes that should be written to a file
48	RotateBytes int `hcl:"rotate_bytes"`
49
50	// RotateMaxFiles is the max number of log files to keep
51	RotateMaxFiles int `hcl:"rotate_max_files"`
52}
53
54// AuditFilter is the configuration for a Audit Log Filter
55type AuditFilter struct {
56	// Name is a unique name given to the filter
57	Name string `hcl:",key"`
58
59	// Type of auditing event to filter, such as HTTPEvent
60	Type string `hcl:"type"`
61
62	// Endpoints is the list of endpoints to include in the filter
63	Endpoints []string `hcl:"endpoints"`
64
65	// State is the auditing request lifecycle stage to filter
66	Stages []string `hcl:"stages"`
67
68	// Operations is the type of operation to filter, such as GET, DELETE
69	Operations []string `hcl:"operations"`
70}
71
72// Copy returns a new copy of an AuditConfig
73func (a *AuditConfig) Copy() *AuditConfig {
74	if a == nil {
75		return nil
76	}
77
78	nc := new(AuditConfig)
79	*nc = *a
80
81	// Copy bool pointers
82	if a.Enabled != nil {
83		nc.Enabled = helper.BoolToPtr(*a.Enabled)
84	}
85
86	// Copy Sinks and Filters
87	nc.Sinks = copySliceAuditSink(nc.Sinks)
88	nc.Filters = copySliceAuditFilter(nc.Filters)
89
90	return nc
91}
92
93// Merge is used to merge two Audit Configs together. Settings from the input take precedence.
94func (a *AuditConfig) Merge(b *AuditConfig) *AuditConfig {
95	result := a.Copy()
96
97	if b.Enabled != nil {
98		result.Enabled = helper.BoolToPtr(*b.Enabled)
99	}
100
101	// Merge Sinks
102	if len(a.Sinks) == 0 && len(b.Sinks) != 0 {
103		result.Sinks = copySliceAuditSink(b.Sinks)
104	} else if len(b.Sinks) != 0 {
105		result.Sinks = auditSinkSliceMerge(a.Sinks, b.Sinks)
106	}
107
108	// Merge Filters
109	if len(a.Filters) == 0 && len(b.Filters) != 0 {
110		result.Filters = copySliceAuditFilter(b.Filters)
111	} else if len(b.Filters) != 0 {
112		result.Filters = auditFilterSliceMerge(a.Filters, b.Filters)
113	}
114
115	return result
116}
117
118func (a *AuditSink) Copy() *AuditSink {
119	if a == nil {
120		return nil
121	}
122
123	nc := new(AuditSink)
124	*nc = *a
125
126	return nc
127}
128
129func (a *AuditFilter) Copy() *AuditFilter {
130	if a == nil {
131		return nil
132	}
133
134	nc := new(AuditFilter)
135	*nc = *a
136
137	// Copy slices
138	nc.Endpoints = helper.CopySliceString(nc.Endpoints)
139	nc.Stages = helper.CopySliceString(nc.Stages)
140	nc.Operations = helper.CopySliceString(nc.Operations)
141
142	return nc
143}
144
145func copySliceAuditFilter(a []*AuditFilter) []*AuditFilter {
146	l := len(a)
147	if l == 0 {
148		return nil
149	}
150
151	ns := make([]*AuditFilter, l)
152	for idx, cfg := range a {
153		ns[idx] = cfg.Copy()
154	}
155
156	return ns
157}
158
159func auditFilterSliceMerge(a, b []*AuditFilter) []*AuditFilter {
160	n := make([]*AuditFilter, len(a))
161	seenKeys := make(map[string]int, len(a))
162
163	for i, config := range a {
164		n[i] = config.Copy()
165		seenKeys[config.Name] = i
166	}
167
168	for _, config := range b {
169		if fIndex, ok := seenKeys[config.Name]; ok {
170			n[fIndex] = config.Copy()
171			continue
172		}
173
174		n = append(n, config.Copy())
175	}
176
177	return n
178}
179
180func copySliceAuditSink(a []*AuditSink) []*AuditSink {
181	l := len(a)
182	if l == 0 {
183		return nil
184	}
185
186	ns := make([]*AuditSink, l)
187	for idx, cfg := range a {
188		ns[idx] = cfg.Copy()
189	}
190
191	return ns
192}
193
194func auditSinkSliceMerge(a, b []*AuditSink) []*AuditSink {
195	n := make([]*AuditSink, len(a))
196	seenKeys := make(map[string]int, len(a))
197
198	for i, config := range a {
199		n[i] = config.Copy()
200		seenKeys[config.Name] = i
201	}
202
203	for _, config := range b {
204		if fIndex, ok := seenKeys[config.Name]; ok {
205			n[fIndex] = config.Copy()
206			continue
207		}
208
209		n = append(n, config.Copy())
210	}
211
212	return n
213}
214