342 lines
9.6 KiB
Go
342 lines
9.6 KiB
Go
package security
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
"gopkg.in/natefinch/lumberjack.v2"
|
|
)
|
|
|
|
// AuditEvent represents an audit event
|
|
type AuditEvent struct {
|
|
Timestamp time.Time `json:"timestamp"`
|
|
EventType string `json:"event_type"`
|
|
Username string `json:"username"`
|
|
Provider string `json:"provider"`
|
|
IPAddress string `json:"ip_address"`
|
|
UserAgent string `json:"user_agent"`
|
|
Resource string `json:"resource,omitempty"`
|
|
Action string `json:"action,omitempty"`
|
|
Result string `json:"result,omitempty"`
|
|
Metadata map[string]interface{} `json:"metadata,omitempty"`
|
|
SessionID string `json:"session_id,omitempty"`
|
|
RequestID string `json:"request_id,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// AuditLogger manages audit logging
|
|
type AuditLogger struct {
|
|
config *AuditConfig
|
|
logger *logrus.Logger
|
|
writer *lumberjack.Logger
|
|
}
|
|
|
|
// NewAuditLogger creates a new audit logger
|
|
func NewAuditLogger(config *AuditConfig) (*AuditLogger, error) {
|
|
// Create log directory if it doesn't exist
|
|
if err := os.MkdirAll(filepath.Dir(config.LogFile), 0755); err != nil {
|
|
return nil, fmt.Errorf("failed to create log directory: %w", err)
|
|
}
|
|
|
|
// Configure log rotation
|
|
writer := &lumberjack.Logger{
|
|
Filename: config.LogFile,
|
|
MaxSize: config.MaxSize, // megabytes
|
|
MaxBackups: config.MaxBackups,
|
|
MaxAge: config.MaxAge, // days
|
|
Compress: true,
|
|
}
|
|
|
|
// Create logger
|
|
logger := logrus.New()
|
|
logger.SetOutput(writer)
|
|
logger.SetFormatter(&logrus.JSONFormatter{
|
|
TimestampFormat: time.RFC3339,
|
|
})
|
|
|
|
// Set log level
|
|
level, err := logrus.ParseLevel(config.LogLevel)
|
|
if err != nil {
|
|
level = logrus.InfoLevel
|
|
}
|
|
logger.SetLevel(level)
|
|
|
|
audit := &AuditLogger{
|
|
config: config,
|
|
logger: logger,
|
|
writer: writer,
|
|
}
|
|
|
|
return audit, nil
|
|
}
|
|
|
|
// LogAuthEvent logs an authentication event
|
|
func (al *AuditLogger) LogAuthEvent(eventType, username, provider string, metadata map[string]interface{}) {
|
|
event := &AuditEvent{
|
|
Timestamp: time.Now(),
|
|
EventType: eventType,
|
|
Username: username,
|
|
Provider: provider,
|
|
IPAddress: al.getClientIP(),
|
|
UserAgent: al.getUserAgent(),
|
|
Metadata: metadata,
|
|
}
|
|
|
|
al.logEvent(event)
|
|
}
|
|
|
|
// LogAccessEvent logs an access control event
|
|
func (al *AuditLogger) LogAccessEvent(eventType, username, resource, action, result string, metadata map[string]interface{}) {
|
|
event := &AuditEvent{
|
|
Timestamp: time.Now(),
|
|
EventType: eventType,
|
|
Username: username,
|
|
Resource: resource,
|
|
Action: action,
|
|
Result: result,
|
|
Metadata: metadata,
|
|
IPAddress: al.getClientIP(),
|
|
UserAgent: al.getUserAgent(),
|
|
}
|
|
|
|
al.logEvent(event)
|
|
}
|
|
|
|
// LogSecurityEvent logs a security-related event
|
|
func (al *AuditLogger) LogSecurityEvent(eventType, username, description string, metadata map[string]interface{}) {
|
|
event := &AuditEvent{
|
|
Timestamp: time.Now(),
|
|
EventType: eventType,
|
|
Username: username,
|
|
Metadata: metadata,
|
|
IPAddress: al.getClientIP(),
|
|
UserAgent: al.getUserAgent(),
|
|
}
|
|
|
|
if metadata == nil {
|
|
metadata = make(map[string]interface{})
|
|
}
|
|
metadata["description"] = description
|
|
|
|
al.logEvent(event)
|
|
}
|
|
|
|
// LogErrorEvent logs an error event
|
|
func (al *AuditLogger) LogErrorEvent(eventType, username, errorMsg string, metadata map[string]interface{}) {
|
|
event := &AuditEvent{
|
|
Timestamp: time.Now(),
|
|
EventType: eventType,
|
|
Username: username,
|
|
Error: errorMsg,
|
|
Metadata: metadata,
|
|
IPAddress: al.getClientIP(),
|
|
UserAgent: al.getUserAgent(),
|
|
}
|
|
|
|
al.logEvent(event)
|
|
}
|
|
|
|
// LogComposeEvent logs a compose-related event
|
|
func (al *AuditLogger) LogComposeEvent(eventType, username, composeID string, metadata map[string]interface{}) {
|
|
if metadata == nil {
|
|
metadata = make(map[string]interface{})
|
|
}
|
|
metadata["compose_id"] = composeID
|
|
|
|
event := &AuditEvent{
|
|
Timestamp: time.Now(),
|
|
EventType: eventType,
|
|
Username: username,
|
|
Metadata: metadata,
|
|
IPAddress: al.getClientIP(),
|
|
UserAgent: al.getUserAgent(),
|
|
}
|
|
|
|
al.logEvent(event)
|
|
}
|
|
|
|
// LogVariantEvent logs a variant-related event
|
|
func (al *AuditLogger) LogVariantEvent(eventType, username, variantName string, metadata map[string]interface{}) {
|
|
if metadata == nil {
|
|
metadata = make(map[string]interface{})
|
|
}
|
|
metadata["variant_name"] = variantName
|
|
|
|
event := &AuditEvent{
|
|
Timestamp: time.Now(),
|
|
EventType: eventType,
|
|
Username: username,
|
|
Metadata: metadata,
|
|
IPAddress: al.getClientIP(),
|
|
UserAgent: al.getUserAgent(),
|
|
}
|
|
|
|
al.logEvent(event)
|
|
}
|
|
|
|
// LogPhaseEvent logs a phase execution event
|
|
func (al *AuditLogger) LogPhaseEvent(eventType, username, phaseName, composeID string, metadata map[string]interface{}) {
|
|
if metadata == nil {
|
|
metadata = make(map[string]interface{})
|
|
}
|
|
metadata["phase_name"] = phaseName
|
|
metadata["compose_id"] = composeID
|
|
|
|
event := &AuditEvent{
|
|
Timestamp: time.Now(),
|
|
EventType: eventType,
|
|
Username: username,
|
|
Metadata: metadata,
|
|
IPAddress: al.getClientIP(),
|
|
UserAgent: al.getUserAgent(),
|
|
}
|
|
|
|
al.logEvent(event)
|
|
}
|
|
|
|
// logEvent logs an audit event
|
|
func (al *AuditLogger) logEvent(event *AuditEvent) {
|
|
// Convert to JSON for structured logging
|
|
eventJSON, err := json.Marshal(event)
|
|
if err != nil {
|
|
al.logger.Errorf("Failed to marshal audit event: %v", err)
|
|
return
|
|
}
|
|
|
|
// Log based on event type
|
|
switch event.EventType {
|
|
case "authentication_success", "token_validation":
|
|
al.logger.WithFields(logrus.Fields{
|
|
"event_type": event.EventType,
|
|
"username": event.Username,
|
|
"provider": event.Provider,
|
|
"ip_address": event.IPAddress,
|
|
}).Info("Authentication event")
|
|
case "authentication_failure", "token_invalid":
|
|
al.logger.WithFields(logrus.Fields{
|
|
"event_type": event.EventType,
|
|
"username": event.Username,
|
|
"provider": event.Provider,
|
|
"ip_address": event.IPAddress,
|
|
}).Warn("Authentication failure")
|
|
case "authorization_check":
|
|
al.logger.WithFields(logrus.Fields{
|
|
"event_type": event.EventType,
|
|
"username": event.Username,
|
|
"resource": event.Resource,
|
|
"action": event.Action,
|
|
"result": event.Result,
|
|
"ip_address": event.IPAddress,
|
|
}).Info("Authorization check")
|
|
case "access_denied":
|
|
al.logger.WithFields(logrus.Fields{
|
|
"event_type": event.EventType,
|
|
"username": event.Username,
|
|
"resource": event.Resource,
|
|
"action": event.Action,
|
|
"ip_address": event.IPAddress,
|
|
}).Warn("Access denied")
|
|
case "security_violation", "suspicious_activity":
|
|
al.logger.WithFields(logrus.Fields{
|
|
"event_type": event.EventType,
|
|
"username": event.Username,
|
|
"ip_address": event.IPAddress,
|
|
"metadata": event.Metadata,
|
|
}).Error("Security violation detected")
|
|
case "compose_started", "compose_completed", "compose_failed":
|
|
al.logger.WithFields(logrus.Fields{
|
|
"event_type": event.EventType,
|
|
"username": event.Username,
|
|
"compose_id": event.Metadata["compose_id"],
|
|
"ip_address": event.IPAddress,
|
|
}).Info("Compose event")
|
|
case "phase_started", "phase_completed", "phase_failed":
|
|
al.logger.WithFields(logrus.Fields{
|
|
"event_type": event.EventType,
|
|
"username": event.Username,
|
|
"phase_name": event.Metadata["phase_name"],
|
|
"compose_id": event.Metadata["compose_id"],
|
|
"ip_address": event.IPAddress,
|
|
}).Info("Phase event")
|
|
default:
|
|
al.logger.WithFields(logrus.Fields{
|
|
"event_type": event.EventType,
|
|
"username": event.Username,
|
|
"ip_address": event.IPAddress,
|
|
"metadata": event.Metadata,
|
|
}).Info("Audit event")
|
|
}
|
|
|
|
// Also log the full event as JSON for external processing
|
|
al.logger.WithField("audit_event", string(eventJSON)).Debug("Full audit event")
|
|
}
|
|
|
|
// getClientIP gets the client IP address (placeholder implementation)
|
|
func (al *AuditLogger) getClientIP() string {
|
|
// In a real implementation, this would extract the client IP from the request context
|
|
// For now, return a placeholder
|
|
return "127.0.0.1"
|
|
}
|
|
|
|
// getUserAgent gets the user agent (placeholder implementation)
|
|
func (al *AuditLogger) getUserAgent() string {
|
|
// In a real implementation, this would extract the user agent from the request context
|
|
// For now, return a placeholder
|
|
return "deb-bootc-compose/1.0"
|
|
}
|
|
|
|
// Close closes the audit logger
|
|
func (al *AuditLogger) Close() error {
|
|
if al.writer != nil {
|
|
return al.writer.Close()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Rotate rotates the log file
|
|
func (al *AuditLogger) Rotate() error {
|
|
if al.writer != nil {
|
|
return al.writer.Rotate()
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetLogStats returns log statistics
|
|
func (al *AuditLogger) GetLogStats() map[string]interface{} {
|
|
stats := make(map[string]interface{})
|
|
|
|
if al.writer != nil {
|
|
stats["current_size"] = al.writer.Size()
|
|
stats["max_size"] = al.writer.MaxSize
|
|
stats["max_backups"] = al.writer.MaxBackups
|
|
stats["max_age"] = al.writer.MaxAge
|
|
}
|
|
|
|
return stats
|
|
}
|
|
|
|
// SearchEvents searches audit events (placeholder implementation)
|
|
func (al *AuditLogger) SearchEvents(query map[string]interface{}) ([]*AuditEvent, error) {
|
|
// In a real implementation, this would search through the audit log
|
|
// For now, return an empty result
|
|
return []*AuditEvent{}, nil
|
|
}
|
|
|
|
// ExportEvents exports audit events to various formats
|
|
func (al *AuditLogger) ExportEvents(format string, startTime, endTime time.Time) ([]byte, error) {
|
|
// In a real implementation, this would export events from the specified time range
|
|
// For now, return an empty result
|
|
return []byte{}, nil
|
|
}
|
|
|
|
// CleanupOldEvents cleans up old audit events
|
|
func (al *AuditLogger) CleanupOldEvents(before time.Time) error {
|
|
// In a real implementation, this would clean up events older than the specified time
|
|
// For now, do nothing
|
|
return nil
|
|
}
|