Documentation
¶
Overview ¶
Package ocsfaudit writes SystemForge's security events as OCSF (Open Cybersecurity Schema Framework) events, one JSON object per line, for a security data lake or SIEM that ingests OCSF.
An Emitter is an audit.Emitter. A host passes it wherever an emitter is taken, such as systemauthsvc.Options.AuditEmitter, alone or with audit.NewMultiEmitter next to audit.NewLogEmitter:
f, err := os.OpenFile("/var/log/systemauth/security.ocsf.jsonl",
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o600)
if err != nil {
return err
}
ocsf := ocsfaudit.New(f, ocsfaudit.Options{Deployment: "prod-eu"})
defer ocsf.Close() // before f.Close
opts.AuditEmitter = audit.NewMultiEmitter(audit.NewLogEmitter(logger), ocsf)
Delivery ¶
Emit maps the event and queues the line without blocking or logging; one goroutine writes the queue in order, one Write per line. When the queue (Options.BufferSize) is full, the event is dropped and counted: Dropped returns the count, and a reporter goroutine logs it once a minute and on Close, so the logger is never called on the request path. Write failures, short writes included, are logged at most once a minute and counted by WriteFailures; after a Write that fails part-way the next line starts with a newline, so only the torn line is lost. Close writes what is queued, flushes a writer with Flush (*bufio.Writer) and syncs one with Sync (*os.File); it does not close the writer.
The package does not rotate files. Rotate by renaming, with a writer that reopens its file on a signal, and have the rotation wait for the reopen (with delaycompress when compressing); copytruncate can lose the events written between its copy and its truncation.
Event classes ¶
Every event type SystemForge emits maps to an OCSF 1.10.0-dev class and activity; metadata.event_code keeps the SystemForge event type.
Event type Class (uid) Activity Severity
systemauth.sign_in Authentication (3002) Logon Informational; Low on failure
systemauth.sign_out Authentication (3002) Logoff Informational
systemauth.token.refused Authentication (3002) Logon, OAuth 2.0 Low
systemauth.refresh_token.reuse Authentication (3002) Logon, OAuth 2.0 High
systemauth.authorization_code.reuse Authentication (3002) Logon, OAuth 2.0 High
systemauth.token.revoked Authentication (3002) Logoff, OAuth 2.0 Informational; Low on failure
systemauth.token_family.revoked Authentication (3002) Logoff, OAuth 2.0 Informational
systemauth.email_verification.sent, Authentication (3002) Preauth (purpose verify_email) Informational
systemauth.email.verified User Management (3007) Update (purpose email_change)
systemauth.email_change.requested, User Management (3007) Update Informational
systemauth.email_change.cancelled,
systemauth.email.changed
systemauth.signing_key.loaded Application Lifecycle (6002) Start Informational
systemauth.production.waiver Application Lifecycle (6002) Start Medium
organization.membership Group Management (3006) Add User, Remove User, Update Informational; Medium for ownership_transferred
principal.platform_admin User Management (3007) Assign / Remove Privileges High when granted
service_account User Management (3007) Create, Enable, Disable, Delete, Informational
Add / Remove Programmatic Credentials,
Assign / Remove Roles
keys Entity Management (3004) by action High when compromised; Medium for destroy,
shred, wrap or job failure, guard denial
connections Entity Management (3004) by event Medium for access.denied
scim_token.issued / rotated / revoked Entity Management (3004) Create / Update / Deactivate Informational
scim_token.authentication_failed Authentication (3002) Logon, Failure Medium
signup_code.*, signup_invite.*, Entity Management (3004) Create, Update, Deactivate Informational
signup_waitlist.invited,
signup_allowance.set
managed_principal.created, deleted, User Management (3007) Create, Delete, Update, Informational; Low for deleted
released, email_*, claim_*, manager_added, Add / Remove Programmatic Credentials
manager_removed, login_token_*
managed_principal.manager_invite* Entity Management (3004) Create, Update, Deactivate Informational
managed_principal.session_revoked, Authentication (3002) Logoff Informational
all_revoked
any other event type Base Event (0) Other, named by the action Informational; Low on failure
A failure outcome raises Informational to Low. Severity High and above sets is_alert.
Common attributes ¶
time the event time, in milliseconds metadata.uid the event ID, or a new UUID metadata.correlation_uid the request ID metadata.event_code the SystemForge event type metadata.tenant_uid the organization (Context.TenantID, or the organization_id detail) metadata.product Options.ProductName, VendorName, ProductVersion metadata.version the OCSF schema version (events.SchemaVersion) metadata.reporter.name Options.Deployment actor.user.uid the acting principal's ID; actor.application names an OAuth client by client ID src_endpoint.ip the client address http_request.user_agent the client's User-Agent, sanitized (audit.SanitizeUserAgent) status_id, status_detail the outcome and SystemForge's reason code; status_code the OAuth error code
What is never written ¶
Only IDs and addresses identify people. No event carries a name, an email address, a token or token prefix, a code, a DPoP proof, an assertion, a secret or a request body, in any attribute including unmapped and message. Actor and resource identifiers, which may be email addresses, are left out (an OAuth client ID, a SystemForge component name and a key ID excepted), and so is Context.SessionID, which may hold a session token; a revoked managed-principal session is named by its record UUID in session.uid. A User-Agent holding an email address or a credential-like value is left out. Details are copied only from an allowlist per event type, and only values of the expected shape: a code (no spaces, no @), a UUID, a boolean, a number or a time. A detail of an event type without an allowlist is never copied.
Index ¶
Constants ¶
const ( DefaultProductName = "SystemForge" DefaultVendorName = "PlexusOne" DefaultBufferSize = 1024 )
Defaults of Options.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Emitter ¶
type Emitter struct {
// contains filtered or unexported fields
}
Emitter is an audit.Emitter writing every event as one line of OCSF JSON. Emit never blocks and never logs: it maps the event and queues the line; a single goroutine writes the queue in order. When the queue is full the event is dropped and counted (Dropped), and a reporter goroutine logs the count once a minute. Close drains the queue.
func New ¶
New returns an Emitter writing to w and starts its writer goroutine. The caller closes it with Close, after which it closes nothing of w's: w stays the caller's. Each event is one Write of one line, so an *os.File opened with os.O_APPEND gets whole lines.
func (*Emitter) Close ¶
Close stops accepting events, writes those queued, flushes the writer when it buffers (Flush) and commits it to storage when it can (Sync). It does not close the writer. Events emitted after Close are dropped and counted. Close is safe to call more than once; later calls return the first one's result.
func (*Emitter) Dropped ¶
Dropped returns how many events were dropped because the buffer was full or the emitter was closed.
func (*Emitter) Emit ¶
Emit maps ev to OCSF and queues it for writing. It does not keep ev. It returns nil whatever happens, and it neither blocks nor logs: an event that cannot be encoded or queued is counted, and the reporter goroutine logs the counts, so recording an event never fails or delays the request that caused it.
func (*Emitter) EncodeFailures ¶
EncodeFailures returns how many events could not be encoded as JSON.
func (*Emitter) WriteFailures ¶
WriteFailures returns how many events failed to be written.
type Options ¶
type Options struct {
// ProductName is metadata.product.name, the product that produced
// the events. Default DefaultProductName.
ProductName string
// ProductVersion is metadata.product.version. Default: the version of
// the SystemForge module the binary was built with, when known.
ProductVersion string
// VendorName is metadata.product.vendor_name. Default
// DefaultVendorName.
VendorName string
// Deployment names this deployment, such as "prod-eu", as
// metadata.reporter.name, so the events of several deployments
// collected in one place can be told apart. Optional.
Deployment string
// BufferSize is how many events wait to be written before Emit drops
// new ones. Default DefaultBufferSize.
BufferSize int
// Logger receives write failures and the counts of dropped and
// unencodable events. It is called from the emitter's own goroutines,
// never from Emit. Default slog.Default().
Logger *slog.Logger
}
Options configure an Emitter.