Documentation
¶
Overview ¶
Package audit is the gateway's durable security log: one JSON record per request decision, administrator action and log event, appended to rotating files and synced to disk.
An admitted request is acknowledged only after its record is on disk; a record that cannot be written fails the log and protected admission stops, and records refused meanwhile are counted, never dropped silently. Records never hold credentials. The writer never deletes a file: when the file limit is reached it fails instead. It imports only fault. The request pipeline, the controller and the operator commands write to it.
Index ¶
- Constants
- Variables
- func Content(data []byte) []byte
- func End(file *os.File) (int64, error)
- func NewRequestID() string
- func QueueSize(maxInFlight int64) int
- func RecordRoom(capacity int) int
- type AddressCount
- type AuditLog
- func (a *AuditLog) Budget() (used, total int64)
- func (a *AuditLog) Close(ctx context.Context) error
- func (a *AuditLog) Closed() bool
- func (a *AuditLog) Commit(ctx context.Context, e Event) error
- func (a *AuditLog) Evicts() bool
- func (a *AuditLog) Fail(err error)
- func (a *AuditLog) Failed() bool
- func (a *AuditLog) Lost() uint64
- func (a *AuditLog) OpenErr() error
- func (a *AuditLog) Path() string
- func (a *AuditLog) Record(e Event)
- func (a *AuditLog) Summarise(reason, address string)
- func (a *AuditLog) Unconfirmed() (events, lost uint64)
- func (a *AuditLog) Usage() (activeBytes, activeLimit, files, filesLimit int64)
- type Event
- type Settings
Constants ¶
const ( // AuditQueueSize is the smallest queue; QueueSize grows it with the // requests a deployment admits at once. AuditQueueSize = 1024 MaxLoggedPath = 256 // MaxRecordBytes bounds one record, so an incomplete last record is // always found within this many bytes of the end of the file. MaxRecordBytes = 64 << 10 // AuditDrainWait bounds how long a graceful stop waits for the writer to // write what was queued before the log closed. AuditDrainWait = 5 * time.Second )
const ( StageIngress = iota StageCapture StageCanonical StageRoute StageAccess StageJWT StageRBAC StageRate StageBody StageValidation StageAudit StageBind StageForward )
Request stages in evaluation order. BODY covers framing, size, media type and JSON decoding; VALIDATION covers the header and body schemas.
const ( StagePass = "PASS" StageReject = "REJECT" StageFail = "FAIL" StageNotRun = "NE" StageNotApplicable = "NA" )
Stage results. A stage without a result was not evaluated; a stage the operation does not require, such as authentication of a public operation, is not applicable.
const ( FullRefuse = "refuse" FullEvict = "evict_oldest" )
What the log does when every file is in use: refuse every record (and so every admission) until an operator moves rotated files away, or delete the oldest rotated file after recording what it held.
const ( DefaultAuditBytes = 64 << 20 DefaultAuditFiles = 8 )
Default size of the active audit file and number of files kept.
const EventSchema = 8
EventSchema is the version of the event envelope below.
const (
SummaryInterval = time.Minute
)
Requests refused at ingress (a target not in origin form, a tunnel, an upgrade, a declared body over the ceiling, overload) reach no policy, so they are counted and written as one audit.ingress_summary record per SummaryInterval, with the addresses that sent the most. A flood of them costs a counter, not a durable record each.
Variables ¶
var StageNames = [stageCount]string{
"INGRESS", "CAPTURE", "CANONICAL", "ROUTE", "ACCESS", "JWT", "RBAC",
"RATE", "BODY", "VALIDATION", "AUDIT", "BIND", "FORWARD",
}
Functions ¶
func Content ¶ added in v0.2.0
Content returns the content of data, the bytes of an audit file: up to the first zero byte after its last newline, or all of data.
func End ¶ added in v0.2.0
End returns the logical end of file, the length of its Content. It reads backwards past the zero tail, then the last record's bytes before it.
func NewRequestID ¶
func NewRequestID() string
func QueueSize ¶ added in v0.2.0
QueueSize is the queue that keeps an acknowledged record of every request in flight from being refused for want of room: records nobody waits for take at most RecordRoom, three quarters, so the last quarter must hold one admission per request in flight.
func RecordRoom ¶ added in v0.2.0
RecordRoom is how many queued events still leave room for an event nobody waits for: the last quarter of a queue of capacity events is kept for acknowledged ones (admissions, administrator intents). Refused requests need no sign-in, and a flood of their records must not refuse those.
Types ¶
type AddressCount ¶ added in v0.2.0
AddressCount is how many refusals one client address had.
type AuditLog ¶
type AuditLog struct {
// contains filtered or unexported fields
}
AuditLog writes events to a local file. Events that gate a decision wait until the batch containing them has been synced to disk; other events are queued without blocking and counted when they cannot be queued.
An event is accepted when enqueue puts it in the queue. Every accepted event is written in order by the one writer, which ends only once close has closed the queue and every event in it has been written; so a close that returns nil means every accepted event, and the count of every refused one, is in the file and synced.
On Linux, unless the settings ask to append, the active file is filled with zero bytes ahead of the writer and written in place; its content ends at End. The active file is rotated to "<path>.<n>" before it would exceed maxBytes. Once maxFiles files exist the log is full: it refuses records, and so protected admission, until an operator moves rotated files away, which it notices at its next rotation; or, when it may evict, it deletes the oldest rotated file after a synced record of what that file held.
func OpenAudit ¶
OpenAudit never fails: an unusable file yields a log in the failed state, which refuses every acknowledgement and therefore every admission.
func (*AuditLog) Budget ¶ added in v0.2.0
Budget reports the bytes held by every audit file and the budget, files × file size, as last stored by the writer.
func (*AuditLog) Close ¶
Close ends the log. From the moment it begins no event can be queued: Record counts it as lost and Commit refuses it. It then waits, until ctx is done, for the writer to write every event queued before, then the pending loss count, and to close the file. It returns nil only when all of that was written and synced; otherwise it reports what is not confirmed written. A writer blocked in the file system cannot be interrupted: when ctx ends first, the writer keeps the file and may still finish. Each later call reports the state it then finds.
func (*AuditLog) Commit ¶
Commit records an event and waits for durable acknowledgement. A failed or closing log, or a full queue, refuses it at once.
func (*AuditLog) Evicts ¶ added in v0.2.0
Evicts reports whether the log deletes its oldest file when full.
func (*AuditLog) Failed ¶
Failed reports whether the log refuses new records: it has failed, or every file is in use.
func (*AuditLog) Record ¶
Record queues an event without waiting. An event that cannot be queued, because the log has failed, its queue has no room for it (see RecordRoom) or it is closing, is counted; the writer writes the count with its next batch, or as it ends.
func (*AuditLog) Summarise ¶ added in v0.2.0
Summarise counts one ingress refusal by its reason and client address.
func (*AuditLog) Unconfirmed ¶
Unconfirmed returns the number of accepted events not yet written and synced, and the count of refused events not yet in the file.
type Event ¶
type Event struct {
Schema int `json:"schema"`
ID string `json:"event_id"`
Time time.Time `json:"time"`
Seq uint64 `json:"seq"`
Kind string `json:"kind"`
RequestID string `json:"request_id,omitempty"`
Stage string `json:"stage,omitempty"` // terminal stage of a request event
Stages stages `json:"stages,omitzero"`
// Modules holds the result of each compiled-in module other than the
// built-ins, keyed by module ID, with the stage result values.
Modules map[string]string `json:"modules,omitempty"`
Decision string `json:"decision,omitempty"`
Status int `json:"status,omitempty"`
Error string `json:"error,omitempty"`
Method string `json:"method,omitempty"`
Path string `json:"path,omitempty"`
Truncated bool `json:"path_truncated,omitempty"` // Path or ForwardedPath was cut to MaxLoggedPath
ForwardedPath string `json:"forwarded_path,omitempty"` // set only when the forward rule changed the path
Operation string `json:"operation,omitempty"`
API string `json:"api,omitempty"`
Subject string `json:"subject,omitempty"`
Roles []string `json:"roles,omitempty"`
IgnoredRoles []string `json:"ignored_roles,omitempty"`
ClientAddress string `json:"client_address,omitempty"`
DurationMS float64 `json:"duration_ms,omitempty"`
Actor string `json:"actor,omitempty"`
Session string `json:"session,omitempty"`
Generation string `json:"generation,omitempty"`
Profile string `json:"profile,omitempty"`
Upstream string `json:"upstream,omitempty"`
// UpstreamAddress names the address dialled, on forwarding failures only.
UpstreamAddress string `json:"upstream_address,omitempty"`
Transition string `json:"transition,omitempty"`
Revision int64 `json:"revision,omitempty"`
Candidate int64 `json:"candidate,omitempty"`
// A provisional base's events name its revision, the confirmed revision
// it returns to on rollback and its deadline.
Provisional int64 `json:"provisional,omitempty"`
RollbackTarget int64 `json:"rollback_target,omitempty"`
Deadline time.Time `json:"deadline,omitzero"`
// A temporary override's events name it, the control and target it
// governs and its expiry time.
Override int64 `json:"override,omitempty"`
Target string `json:"target,omitempty"`
Expires time.Time `json:"expires_at,omitzero"`
Reason string `json:"reason,omitempty"`
Lost uint64 `json:"lost,omitempty"`
File string `json:"file,omitempty"` // audit file named by audit.* events
Offset int64 `json:"offset,omitempty"` // start of an incomplete record
// An evicted file's size, SHA-256 and the times of its first and last
// records; an ingress summary's interval and counts per reason and per
// client address.
Bytes int64 `json:"bytes,omitempty"`
Digest string `json:"sha256,omitempty"`
First time.Time `json:"first_record,omitzero"`
Last time.Time `json:"last_record,omitzero"`
Counts map[string]uint64 `json:"counts,omitempty"`
Addresses []AddressCount `json:"addresses,omitempty"`
// contains filtered or unexported fields
}
Event is one security record. Client-controlled strings are bounded and JSON-encoded, so they cannot forge additional records. Credentials, bodies and query strings are never recorded. Subject is set only once a token has been verified; Actor only once an administrator has been authenticated, and Session is a non-secret reference to that administrator's session.
func (*Event) IdentityKnown ¶
IdentityKnown reports whether the request's identity is known.
func (*Event) SetIdentityKnown ¶
SetIdentityKnown records whether the request's identity is known.
type Settings ¶ added in v0.2.0
type Settings struct {
MaxBytes int64
MaxFiles int
WhenFull string
Queue int
Window time.Duration
Append bool
}
Settings bound the log: the size of each file, how many files it keeps, what it does when all are in use (FullRefuse or FullEvict), how many records its queue holds (QueueSize), its sync window (0: none) and whether it appends to the active file (Append) instead of zero-filling it ahead of the writer on Linux; copy-on-write file systems gain nothing from the fill.