auditx

package
v0.17.3 Latest Latest
Warning

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

Go to latest
Published: Jul 31, 2026 License: Apache-2.0 Imports: 16 Imported by: 0

Documentation

Overview

Package auditx is the FedRAMP / NIST 800-53 audit logging layer for apic. It provides the building blocks for tamper-evident audit trails; it does not auto-wire those trails into generated servers.

What this package provides today:

  • An event catalog (AllEvents, LookupControls) mapping each audit event code to the NIST 800-53 control IDs it contributes evidence for. The Controls field is a comma-separated list chosen for easy CSV consumption by an external control-mapping report.
  • An emit API (Logger.Emit) that, given an Event whose Code is in the catalog, fills in the mapped controls, redacts sensitive values in Extra (see redact.go), and forwards an Envelope to each configured Sink. Emitting a code not in the catalog is a hard error (ErrUnknownEventCode).
  • A per-Logger hash chain (Envelope.PrevHash / RecordHash) that provides tamper evidence: an external auditor can re-hash each record's JSON-canonical form and verify it links to the prior record's RecordHash. Under FIPS profiles the chain is the integrity anchor for AU-9.
  • A rotating file sink (FileSink, NewFileSink) that writes JSON-Lines records. It is the only Sink implementation shipped today; the config validator rejects the reserved "syslog"/"cef" sink kinds until real sinks exist (GAP-0081).

Not yet implemented (future work):

  • A standalone audit-report CLI that emits the control-mapping CSV from the catalog (no such command exists in cmd/).
  • Generator-emitted middleware that calls Logger.Emit automatically from generated handlers. cmd/apic does not emit any auditx call today, so events — including the AC-2/AC-6 admin.* catalog entries — must be emitted by consumers at their own call sites. Install a process-wide Logger with SetDefault and emit via Default().Emit.

Index

Constants

This section is empty.

Variables

View Source
var ErrUnknownEventCode = errors.New("auditx: unknown event code")

ErrUnknownEventCode is returned by Emit for codes not in the catalog.

Functions

func LookupControls

func LookupControls(code string) (string, bool)

LookupControls returns the 800-53 control list for code, or false when the code is not in the catalog.

func SetDefault

func SetDefault(l *Logger)

SetDefault swaps the package-level Logger.

Types

type CatalogEntry

type CatalogEntry struct {
	Code        string
	Description string
	// Controls is a comma-separated list of NIST 800-53 control IDs.
	// Format chosen for easy CSV consumption.
	Controls string
}

CatalogEntry records one event code and the 800-53 controls it contributes evidence for.

func AllEvents

func AllEvents() []CatalogEntry

AllEvents returns a copy of the catalog. Callers that mutate this slice do not affect the package state.

type Envelope

type Envelope struct {
	Code       string         `json:"code"`
	Controls   string         `json:"controls"`
	Subject    string         `json:"subject"`
	Resource   string         `json:"resource,omitempty"`
	Outcome    string         `json:"outcome,omitempty"`
	AtUnixNano int64          `json:"at_unix_nano"`
	Extra      map[string]any `json:"extra,omitempty"`
	PrevHash   string         `json:"prev_hash"`
	RecordHash string         `json:"record_hash"`
}

Envelope is what a Sink receives — the input Event plus the chain metadata. The hash chain (PrevHash, RecordHash) provides tamper evidence: an external auditor can verify the chain by re-hashing each record's JSON canonical form and comparing against the recorded RecordHash.

type Event

type Event struct {
	Code     string         // catalog event code (must be in AllEvents)
	Subject  string         // user id / EDIPI / sub claim
	Resource string         // HTTP path, file path, key ID, etc.
	Outcome  string         // "success" | "failure" | "denied" | "begin"
	At       time.Time      // wall-clock; defaults to time.Now()
	Extra    map[string]any // additional context (callers should redact secrets)
}

Event is the input to Logger.Emit. Subject and Resource are typed strings to keep the JSON payload stable; everything else lives in Extra. Subject is typically the EDIPI / sub claim / mtlsx.Principal.

type FileSink

type FileSink struct {
	// contains filtered or unexported fields
}

FileSink writes JSON-Lines records to a rotated log file.

func NewFileSink

func NewFileSink(cfg FileSinkConfig) (*FileSink, error)

NewFileSink validates cfg and constructs a file sink. N-07: the godoc always claimed validation, but the body previously only applied defaults -- an empty Path was accepted (lumberjack silently falls back to a process-name-derived file under os.TempDir(), which is not what an AU-2/AU-9 audit-sink operator configured), and there was no symlink/ regular-file guard, unlike the sibling rotating sink at pkg/obsx/logfile.go NewRotatingHandler. Mirrors that guard: an empty path is rejected; an existing path whose final component is a symlink is rejected outright (a log/audit sink is always application-created, never legitimately delivered as a symlink); an existing non-symlink path is canonicalized and must resolve to a regular file via obsx.ResolveSensitivePath.

func (*FileSink) Close

func (s *FileSink) Close() error

Close flushes and rotates the underlying file.

func (*FileSink) Write

func (s *FileSink) Write(_ context.Context, env Envelope) error

Write serializes env as a single JSON line.

type FileSinkConfig

type FileSinkConfig struct {
	Path       string
	MaxSizeMB  int
	MaxBackups int
	MaxAgeDays int
	Compress   bool
}

FileSinkConfig configures a file-backed JSON-Lines sink with rotation via lumberjack. Defaults match FedRAMP AU-11 baseline (100 MiB / 7 backups / 365 days retention).

type Logger

type Logger struct {
	// contains filtered or unexported fields
}

Logger is the public emit API.

func Default

func Default() *Logger

Default returns the current package-level Logger (may be nil).

func NewLogger

func NewLogger(sinks ...Sink) *Logger

NewLogger constructs a Logger forwarding to the given sinks.

func (*Logger) Close

func (l *Logger) Close() error

Close shuts down every sink. Best-effort: returns the first error encountered but continues closing the rest.

func (*Logger) Emit

func (l *Logger) Emit(ctx context.Context, ev Event) error

Emit fills in chain metadata, computes the record hash, and writes to every sink in order. The first sink failure aborts the call.

type Sink

type Sink interface {
	Write(ctx context.Context, env Envelope) error
	Close() error
}

Sink is the audit delivery interface. Implementations MUST be safe for concurrent calls.

Jump to

Keyboard shortcuts

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