Skip to content

About

NIS2 security-event emitter — one standard shape for auth, authorization, proof, egress and secret-handling events shipped to a SIEM

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

go-sec-events

The NIS2-audit (NIS2 security-operations) event emitter for eIDAS signing services. One standard way for every service to emit structured security events — auth failures, authZ/IDOR denials, DPoP/proof failures, egress/NetworkPolicy violations, secret/key access, privileged/admin actions, and "first-awareness" incident detections — to the SIEM / central log management, with high-precision synced timestamps so the NIS2 24 h / 72 h / 1 month reporting clock is defensible.

Scope: this library targets Azugo services — its entrypoints take *azugo.Context by design, and it is versioned in lockstep with the Azugo-based platform kit. DataSubjects values must be pseudonymous internal identity references, never national identifiers, names, or e-mail addresses.

Events are the frozen broker.Envelope tagged security, stamped with a ULID id, a high-precision occurrence time, and the request's correlation/trace ids. A pluggable Sink decides where they go:

Sink Destination
LogSink structured log lines on the request logger → the log pipeline ships them to the SIEM (the common path)
BrokerSink the broker event stream → fans into the SIEM

Events from work with no request

A scheduled sweep, a retention purge or an outbox drainer has no request to borrow a logger or correlation ids from. EmitBackground is the entry point for those: same tagging, sanitizing, stamping and validation, minus the correlation ids there is nothing to take.

// A log sink that serves BOTH paths needs a logger of its own for the background one.
audit := secevents.NewEmitter(secevents.NewLogSinkFor(app.Log()))

// From a scheduled task:
_ = audit.EmitBackground(ctx, &broker.Envelope{
    EventType: "document.retention_swept",
    Operation: broker.OpDelete,
    Outcome:   broker.OutcomeSuccess,
    Attributes: map[string]any{secevents.AttrSeverity: string(secevents.SeverityInfo), "erased": 4},
})

The sink must implement BackgroundSink — LogSink (built with NewLogSinkFor) and BrokerSink both do. It is a separate interface from Sink so adding it broke no existing implementation; one that does not implement it is told so rather than having its event dropped.

Do not hand-roll this. Writing the sink's log line yourself is what the absence of this entry point used to force, and it loses the token stripping Stamp performs — which is the part that keeps a credential out of the security stream.

Install

go get github.com/gmb-lib/go-sec-events

See CHANGELOG.md for what each release changed, and what it means for code that already uses this library, before you bump.

Usage

import "github.com/gmb-lib/go-sec-events/secevents"

// LogSink: events become structured log lines the platform ships to the SIEM.
audit := secevents.NewEmitter(secevents.NewLogSink())

// …or BrokerSink where the SIEM ingests from the broker:
// audit := secevents.NewEmitter(secevents.NewBrokerSink(pub, secevents.DefaultTopic))

Emit with the typed helpers — severity, category and the lean attribute shape are fixed for you, and severity maps to the log level so SIEM alerting works without parsing:

// In an authZ middleware / handler:
audit.AuthZDenied(ctx, secevents.Denial{
    Actor:         broker.Actor{ID: ctx.User().ID(), Type: "user"},
    Resource:      broker.Resource{Type: "document", ID: id},
    RequiredScope: "documents:read",
    Reason:        "object not owned by caller",
    IDOR:          true, // raises severity to high
})

// On a network-policy / egress alarm:
audit.EgressViolation(ctx, secevents.Egress{Target: host, Policy: "default-deny"})

First awareness — the NIS2 clock anchor

FirstAwareness records the moment a possible significant incident is detected and returns the high-precision occurrence time, so the caller can anchor the 24-hour clock and the incident register:

anchor, err := audit.FirstAwareness(ctx, secevents.Detection{
    Severity:    secevents.SeverityCritical,
    Summary:     "anomalous egress to unknown host",
    IncidentRef: "INC-2026-014",
    // DetectedAt: detectorTimestamp, // optional; defaults to now
})
// persist `anchor` as "when we first became aware" in the incident register

Events

Helper event_type Default severity
Authentication auth.login info / warning on failure
AuthZDenied authz.denied warning / high on IDOR
ServiceTokenFailure auth.service_token warning / high on replay·proof
EdgeBlock edge.block warning
EgressViolation egress.violation high
SigningEgressCall egress.signing info / warning on failure
SecretAccess secret.access info / high on failure
DataTierAnomaly data.anomaly high
ConfigChange config.change warning
PrivilegedAccess access.privileged high
FirstAwareness incident.first_awareness high

PII posture. Security events are metadata only — identifiers and bounded operational metadata (string attribute values are truncated to MaxAttrValueLen (256) runes, so Reason/Detail/Summary are ticket-style references, not narratives). The emitter strips free-text/content attribute keys defensively, and the publisher strips bearer-token-shaped keys; pseudonymise actors where possible. A privileged/break-glass access is also a GDPR access — emit the GDPR-audit record via go-gdpr-audit too.

Develop

go build ./...
go test ./...
go vet ./...

Contributing

Bug reports and pull requests are welcome. CONTRIBUTING.md names the gate a change has to pass, what a change to this library needs, and the sign-off every commit carries.

Suspected vulnerabilities go through the private route in SECURITY.md — never a public issue.

License

MIT — see LICENSE.

About

NIS2 security-event emitter — one standard shape for auth, authorization, proof, egress and secret-handling events shipped to a SIEM

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages