Documentation
¶
Overview ¶
Package audit provides a middleware module for tamper-evident audit logging.
The audit module implements types.MiddlewareModule to intercept framework events and log them with cryptographic hash chaining for tamper detection.
Example usage:
auditFile, _ := os.OpenFile("audit.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
auditModule, _ := audit.New(
audit.WithOutput(auditFile),
audit.WithHashChaining(""), // Start new chain
)
framework, _ := mono.NewMonoApplication()
framework.Register(auditModule) // Register as first module
framework.Start(ctx)
Index ¶
- func ComputeEntryHash(entry Entry) string
- func VerifyChain(entries []Entry) error
- type AuditAdapterPort
- type AuditModule
- func (m *AuditModule) Name() string
- func (m *AuditModule) OnConfigurationChange(ctx context.Context, event types.ConfigurationEvent) types.ConfigurationEvent
- func (m *AuditModule) OnEventConsumerRegistration(ctx context.Context, entry types.EventConsumerEntry) types.EventConsumerEntry
- func (m *AuditModule) OnEventStreamConsumerRegistration(ctx context.Context, entry types.EventStreamConsumerEntry) types.EventStreamConsumerEntry
- func (m *AuditModule) OnModuleLifecycle(ctx context.Context, event types.ModuleLifecycleEvent) types.ModuleLifecycleEvent
- func (m *AuditModule) OnOutgoingMessage(octx types.OutgoingMessageContext) types.OutgoingMessageContext
- func (m *AuditModule) OnServiceRegistration(ctx context.Context, reg types.ServiceRegistration) types.ServiceRegistration
- func (m *AuditModule) RegisterServices(container types.ServiceContainer) error
- func (m *AuditModule) Start(_ context.Context) error
- func (m *AuditModule) Stop(_ context.Context) error
- type Entry
- type EventType
- type HashChain
- type Option
- type SaveEntryResponse
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ComputeEntryHash ¶
ComputeEntryHash computes the SHA-256 hash of an audit entry. The hash is computed from a deterministic representation of the entry fields. The EntryHash field itself is excluded from the computation.
func VerifyChain ¶
VerifyChain verifies the integrity of an audit log chain.
Returns an error if:
- The chain is broken (prev_hash doesn't match previous entry_hash)
- Any entry_hash is invalid (doesn't match computed hash)
Example:
entries, _ := parseAuditLogFile("audit.log")
if err := audit.VerifyChain(entries); err != nil {
log.Fatalf("Audit log tampering detected: %v", err)
}
Types ¶
type AuditAdapterPort ¶
type AuditAdapterPort interface {
// SaveAuditTrail saves a list of audit entries synchronously.
// Each entry is sent as a separate message to the channel and waits for confirmation.
// The Timestamp field of each entry will be set by the audit module.
SaveAuditTrail(ctx context.Context, entries []Entry) (int, error)
// AsyncSaveAuditTrail saves a list of audit entries asynchronously (fire-and-forget).
// Each entry is sent as a separate message to the channel.
// It does not wait for confirmation and returns immediately after sending all entries.
// The Timestamp field of each entry will be set by the audit module.
AsyncSaveAuditTrail(ctx context.Context, entries []Entry) error
}
AuditAdapterPort provides a type-safe interface for saving custom audit entries.
Note: The adapter is safe for concurrent use. Multiple goroutines can call SaveAuditTrail and AsyncSaveAuditTrail simultaneously.
func NewAuditAdapter ¶
func NewAuditAdapter(container types.ServiceContainer, consumerModuleName string) (AuditAdapterPort, error)
NewAuditAdapter creates a new audit adapter from a service container.
The adapter wraps the "audit-trail" channel service and provides type-safe methods for saving audit entries. The consumerModuleName parameter identifies the module consuming the audit service and ensures it receives a dedicated response channel.
Returns an error if the container is nil or the channel service is not found.
Example:
func (m *MyModule) SetDependencyServiceContainer(dep string, container types.ServiceContainer) {
if dep == "audit" {
adapter, err := audit.NewAuditAdapter(container, m.Name())
if err != nil {
// Handle error
}
m.auditAdapter = adapter
}
}
type AuditModule ¶
type AuditModule struct {
// contains filtered or unexported fields
}
AuditModule implements types.MiddlewareModule to provide tamper-evident audit logging.
The module:
- Logs all module lifecycle events (registered, started, stopped)
- Logs all service registration events
- Logs configuration change events
- Uses SHA-256 hash chaining for tamper detection
- Writes JSON-formatted entries to configured output
- Provides a channel service for custom audit trail entries
The module is an observer - it doesn't modify events, just logs them.
func New ¶
func New(opts ...Option) (*AuditModule, error)
New creates a new audit module with the given options.
The module requires at least WithOutput to be specified. Hash chaining must be explicitly enabled using WithHashChaining.
Example:
auditModule, err := audit.New(
audit.WithOutput(auditFile),
audit.WithHashChaining(""), // Start new chain, or pass lastHash to resume
audit.WithUserContext(extractUserFromContext),
)
func (*AuditModule) OnConfigurationChange ¶
func (m *AuditModule) OnConfigurationChange(ctx context.Context, event types.ConfigurationEvent) types.ConfigurationEvent
OnConfigurationChange intercepts configuration change events and logs them. The event is passed through unchanged (observer pattern).
func (*AuditModule) OnEventConsumerRegistration ¶
func (m *AuditModule) OnEventConsumerRegistration(ctx context.Context, entry types.EventConsumerEntry) types.EventConsumerEntry
OnEventConsumerRegistration logs event consumer registration (observer pattern). The entry is passed through unchanged.
func (*AuditModule) OnEventStreamConsumerRegistration ¶
func (m *AuditModule) OnEventStreamConsumerRegistration(ctx context.Context, entry types.EventStreamConsumerEntry) types.EventStreamConsumerEntry
OnEventStreamConsumerRegistration logs event stream consumer registration (observer pattern). The entry is passed through unchanged.
func (*AuditModule) OnModuleLifecycle ¶
func (m *AuditModule) OnModuleLifecycle(ctx context.Context, event types.ModuleLifecycleEvent) types.ModuleLifecycleEvent
OnModuleLifecycle intercepts module lifecycle events and logs them. The event is passed through unchanged (observer pattern).
Note: ModuleRegisteredEvent cannot be captured because it occurs before the middleware chain is built (during framework.Register(), which happens before framework.Start()). Only ModuleStartedEvent and ModuleStoppedEvent are captured.
func (*AuditModule) OnOutgoingMessage ¶
func (m *AuditModule) OnOutgoingMessage(octx types.OutgoingMessageContext) types.OutgoingMessageContext
OnOutgoingMessage passes through outgoing messages without modification. Audit module does not intercept outgoing messages.
func (*AuditModule) OnServiceRegistration ¶
func (m *AuditModule) OnServiceRegistration(ctx context.Context, reg types.ServiceRegistration) types.ServiceRegistration
OnServiceRegistration intercepts service registration events and logs them. The registration is passed through unchanged (observer pattern).
func (*AuditModule) RegisterServices ¶
func (m *AuditModule) RegisterServices(container types.ServiceContainer) error
RegisterServices registers the channel-based audit-trail service.
Other modules can use this service to save custom audit entries via the adapter. The service name is "audit-trail".
type Entry ¶
type Entry struct {
// Timestamp is the UTC time when the event occurred.
Timestamp time.Time `json:"timestamp"`
// EventType identifies the type of security event.
EventType EventType `json:"event_type"`
// ModuleName is the name of the module (if applicable).
ModuleName string `json:"module_name,omitempty"`
// ServiceName is the name of the service (if applicable).
ServiceName string `json:"service_name,omitempty"`
// Details contains event-specific structured data.
Details map[string]any `json:"details,omitempty"`
// UserContext contains user/request context information.
UserContext string `json:"user_context,omitempty"`
// PrevHash is the SHA-256 hash of the previous entry (empty for first entry).
PrevHash string `json:"prev_hash"`
// EntryHash is the SHA-256 hash of this entry.
EntryHash string `json:"entry_hash"`
}
Entry represents a single audit log entry with tamper-evident hash chaining.
Each entry contains:
- Timestamp: UTC timestamp in RFC3339 format
- EventType: Type of security event
- ModuleName: Name of the module (if applicable)
- ServiceName: Name of the service (if applicable)
- Details: Event-specific structured data
- UserContext: User/request context information
- PrevHash: SHA-256 hash of previous entry (empty for first entry)
- EntryHash: SHA-256 hash of current entry
Hash chaining ensures that any modification to the audit log can be detected by verifying the chain using VerifyChain().
type EventType ¶
type EventType string
EventType represents the type of security event being logged.
const ( // EventModuleStarted indicates a module was started. EventModuleStarted EventType = "module.started" // EventModuleStopped indicates a module was stopped. EventModuleStopped EventType = "module.stopped" // EventConfigurationUpdate indicates a configuration change. EventConfigurationUpdate EventType = "configuration.updated" // EventServiceRegistered indicates a service was registered. EventServiceRegistered EventType = "service.registered" // EventCustomAuditTrail indicates a custom audit trail entry from another module. EventCustomAuditTrail EventType = "custom.audit_trail" )
type HashChain ¶
type HashChain struct {
// contains filtered or unexported fields
}
HashChain manages SHA-256 hash chaining for tamper-evident audit logging.
Each audit entry contains:
- prev_hash: SHA-256 hash of previous entry
- entry_hash: SHA-256 hash of current entry (includes prev_hash)
This creates a chain where any modification to past entries can be detected by verifying the chain using VerifyChain().
func NewHashChain ¶
NewHashChain creates a new hash chain with an optional initial hash.
The lastSavedHash parameter allows resuming an existing chain:
- Empty string: starts a new chain
- Non-empty string: continues from this hash (used as prev_hash for first entry)
type Option ¶
type Option func(*options) error
Option configures an audit module.
func WithHashChaining ¶
WithHashChaining enables hash chaining for tamper-evidence with an optional initial hash to continue an existing chain.
When enabled, each audit entry contains:
- prev_hash: SHA-256 hash of previous entry
- entry_hash: SHA-256 hash of current entry
This creates a tamper-evident chain that can be verified using audit.VerifyChain().
The lastSavedHash parameter allows resuming an existing audit chain:
- Empty string: starts a new chain (first entry has empty prev_hash)
- Non-empty string: continues from this hash (first entry uses it as prev_hash)
Example:
audit.New(audit.WithHashChaining("")) // Start new chain
audit.New(audit.WithHashChaining(lastHash)) // Continue existing chain
func WithOutput ¶
WithOutput sets the output writer for audit logs.
The writer should support concurrent writes (e.g., *os.File) as the audit module writes from multiple goroutines.
Example:
auditFile, _ := os.OpenFile("audit.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
audit.New(audit.WithOutput(auditFile))
func WithUserContext ¶
WithUserContext sets a function to extract user context from context.Context.
The function is called for each audit event to populate the UserContext field. This is useful for tracking which user triggered each event.
Example:
audit.New(audit.WithUserContext(func(ctx context.Context) string {
if user, ok := ctx.Value(userKey).(string); ok {
return user
}
return "system"
}))
type SaveEntryResponse ¶
type SaveEntryResponse struct {
Success bool `json:"success"`
Error string `json:"error,omitempty"`
}
SaveEntryResponse represents the response after saving a single audit entry.