audit

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 12, 2026 License: Apache-2.0 Imports: 23 Imported by: 0

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

View Source
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
)
View Source
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.

View Source
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.

View Source
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.

View Source
const (
	DefaultAuditBytes = 64 << 20
	DefaultAuditFiles = 8
)

Default size of the active audit file and number of files kept.

View Source
const EventSchema = 8

EventSchema is the version of the event envelope below.

View Source
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

View Source
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

func Content(data []byte) []byte

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

func End(file *os.File) (int64, error)

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

func QueueSize(maxInFlight int64) int

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

func RecordRoom(capacity int) int

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

type AddressCount struct {
	Address string `json:"address"`
	Count   uint64 `json:"count"`
}

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

func OpenAudit(path string, s Settings) *AuditLog

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

func (a *AuditLog) Budget() (used, total int64)

Budget reports the bytes held by every audit file and the budget, files × file size, as last stored by the writer.

func (*AuditLog) Close

func (a *AuditLog) Close(ctx context.Context) error

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) Closed

func (a *AuditLog) Closed() bool

Closed reports whether the writer has ended; it closes its file first.

func (*AuditLog) Commit

func (a *AuditLog) Commit(ctx context.Context, e Event) error

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

func (a *AuditLog) Evicts() bool

Evicts reports whether the log deletes its oldest file when full.

func (*AuditLog) Fail

func (a *AuditLog) Fail(err error)

func (*AuditLog) Failed

func (a *AuditLog) Failed() bool

Failed reports whether the log refuses new records: it has failed, or every file is in use.

func (*AuditLog) Lost

func (a *AuditLog) Lost() uint64

Lost is the number of refused events not yet counted in the file.

func (*AuditLog) OpenErr

func (a *AuditLog) OpenErr() error

OpenErr is why the log could not open its file, or nil.

func (*AuditLog) Path

func (a *AuditLog) Path() string

Path is the active audit file's path.

func (*AuditLog) Record

func (a *AuditLog) Record(e Event)

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

func (a *AuditLog) Summarise(reason, address string)

Summarise counts one ingress refusal by its reason and client address.

func (*AuditLog) Unconfirmed

func (a *AuditLog) Unconfirmed() (events, lost uint64)

Unconfirmed returns the number of accepted events not yet written and synced, and the count of refused events not yet in the file.

func (*AuditLog) Usage

func (a *AuditLog) Usage() (activeBytes, activeLimit, files, filesLimit int64)

Usage reports the active file's size and the number of files, each with its limit, as last stored by the writer.

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

func (e *Event) IdentityKnown() bool

IdentityKnown reports whether the request's identity is known.

func (*Event) SetIdentityKnown

func (e *Event) SetIdentityKnown(known bool)

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL