processor

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Aug 4, 2025 License: Apache-2.0 Imports: 17 Imported by: 0

README

Processor Package

The processor package provides interfaces and types for processing OPNsense configurations. It enables flexible analysis of OPNsense configurations through an options pattern, allowing features like statistics generation, dead-rule detection, and other analyses to be enabled independently.

Overview

The package defines a Processor interface that implementations can provide to analyze OPNsense configurations and generate comprehensive reports. The design follows Go best practices with:

  • Interface-based design: The Processor interface allows for multiple implementations
  • Options pattern: Flexible configuration using functional options
  • Context support: Proper context handling for cancellation and timeouts
  • Multi-format output: Reports can be exported as JSON, YAML, Markdown, or plain text summaries

Core Processor Implementation

The CoreProcessor implements a comprehensive four-phase processing pipeline:

  1. Normalize: Fill defaults, canonicalize IP/CIDR, sort slices for determinism
  2. Validate: Use go-playground/validator and custom checks leveraging struct tags
  3. Analyze: Dead rule detection, unused interfaces, consistency checks
  4. Transform: Delegate to converter for markdown; marshal to JSON/YAML for other formats
Normalization Features
  • Fill Defaults: Populates missing values (system optimization: "normal", web GUI: "https", timezone: "UTC")
  • Canonicalize Addresses: Standardizes IP addresses and converts single IPs to CIDR notation
  • Sort Slices: Ensures deterministic output by sorting users, groups, rules, and sysctl items
Analysis Capabilities
  • Dead Rule Detection: Identifies unreachable rules after "block all" rules and duplicate rules
  • Unused Interface Analysis: Finds enabled interfaces not used in rules or services
  • Consistency Checks: Validates gateway configurations, DHCP settings, and user-group relationships
  • Security Analysis: Detects insecure protocols, default SNMP community strings, overly permissive rules
  • Performance Analysis: Identifies disabled hardware offloading and excessive rule counts

Core Interface

type Processor interface {
    Process(ctx context.Context, cfg *model.Opnsense, opts ...Option) (*Report, error)
}

The Process method analyzes an OPNsense configuration and returns a comprehensive report containing:

  • Normalized configuration data
  • Analysis findings categorized by severity
  • Configuration statistics
  • Multi-format output capabilities

Features

The processor supports various analysis features that can be enabled through options:

  • Statistics Generation (WithStats()): Generates configuration statistics
  • Dead Rule Detection (WithDeadRuleCheck()): Analyzes for unused/dead firewall rules
  • Security Analysis (WithSecurityAnalysis()): Performs security-related analysis
  • Performance Analysis (WithPerformanceAnalysis()): Analyzes performance aspects
  • Compliance Checking (WithComplianceCheck()): Checks compliance with best practices

Usage Examples

Basic Usage
processor := NewExampleProcessor()
ctx := context.Background()

// Basic processing with default options (statistics enabled)
report, err := processor.Process(ctx, opnsenseConfig)
if err != nil {
    log.Fatal(err)
}

fmt.Println(report.Summary())
Advanced Usage with Options
processor := NewExampleProcessor()
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

// Enable specific analysis features
report, err := processor.Process(ctx, opnsenseConfig,
    WithStats(),
    WithSecurityAnalysis(),
    WithDeadRuleCheck(),
)
if err != nil {
    log.Fatal(err)
}

// Export as Markdown
markdown := report.ToMarkdown()
ioutil.WriteFile("report.md", []byte(markdown), 0644)

// Export as JSON
jsonStr, err := report.ToJSON()
if err != nil {
    log.Fatal(err)
}
ioutil.WriteFile("report.json", []byte(jsonStr), 0644)
Enable All Features
// Enable all available analysis features
report, err := processor.Process(ctx, opnsenseConfig, WithAllFeatures())

Report Structure

The Report struct contains:

type Report struct {
    GeneratedAt      time.Time       // When the report was generated
    ConfigInfo       ConfigInfo      // Basic configuration information
    NormalizedConfig *model.Opnsense // The processed configuration
    Statistics       *Statistics     // Configuration statistics (if enabled)
    Findings         Findings        // Analysis findings by severity
    ProcessorConfig  ProcessorConfig // Configuration used during processing
}
Findings

Findings are categorized by severity:

  • Critical: Issues requiring immediate attention
  • High: High severity issues
  • Medium: Medium severity issues
  • Low: Low severity issues
  • Info: Informational findings

Each finding contains:

  • Type: Category (e.g., "security", "performance", "compliance")
  • Title: Brief description
  • Description: Detailed information
  • Recommendation: Suggested remediation
  • Component: Affected configuration component
  • Reference: Additional documentation links

Processor Workflow

The processor implements a comprehensive four-phase pipeline for analyzing OPNsense configurations:

┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
│   Phase 1:      │    │   Phase 2:      │    │   Phase 3:      │    │   Phase 4:      │
│   NORMALIZE     │───▶│   VALIDATE      │───▶│   ANALYZE       │───▶│   TRANSFORM     │
│                 │    │                 │    │                 │    │                 │
│ • Fill defaults │    │ • Struct tags   │    │ • Dead rules    │    │ • Markdown      │
│ • Canonicalize  │    │ • Custom checks │    │ • Unused ifaces │    │ • JSON/YAML     │
│ • Sort for      │    │ • Cross-field   │    │ • Security scan │    │ • Plain text    │
│   determinism   │    │   validation    │    │ • Performance   │    │ • Export        │
└─────────────────┘    └─────────────────┘    └─────────────────┘    └─────────────────┘
Phase 1: Normalization
  • Fill Defaults: Populates missing values (system optimization: "normal", web GUI: "https", timezone: "UTC")
  • Canonicalize Addresses: Standardizes IP addresses and converts single IPs to CIDR notation
  • Sort Slices: Ensures deterministic output by sorting users, groups, rules, and sysctl items
Phase 2: Validation
  • Struct Tag Validation: Uses go-playground/validator for field-level validation
  • Custom Business Logic: Domain-specific validation rules
  • Cross-field Validation: Validates relationships between configuration elements
Phase 3: Analysis
  • Dead Rule Detection: Identifies unreachable rules after "block all" rules and duplicate rules
  • Unused Interface Analysis: Finds enabled interfaces not used in rules or services
  • Security Analysis: Detects insecure protocols, default SNMP community strings, overly permissive rules
  • Performance Analysis: Identifies disabled hardware offloading and excessive rule counts
  • Compliance Checking: Validates against security and operational best practices
Phase 4: Transform
  • Multi-format Output: Generates Markdown, JSON, YAML, or plain text summaries
  • Structured Reports: Organizes findings by severity (Critical, High, Medium, Low, Info)
  • Export Capabilities: Saves to files or streams to stdout

Configurable Analysis Options

The processor supports flexible configuration through functional options:

// Enable specific analysis features
report, err := processor.Process(ctx, opnsenseConfig,
    WithStats(),
    WithSecurityAnalysis(),
    WithDeadRuleCheck(),
    WithPerformanceAnalysis(),
    WithComplianceCheck(),
)

// Or enable all features
report, err := processor.Process(ctx, opnsenseConfig, WithAllFeatures())

Output Formats

Reports support multiple output formats:

JSON Output
jsonStr, err := report.ToJSON()
Markdown Output
markdown := report.ToMarkdown()
Plain Text Summary
summary := report.Summary()

Implementation

The package includes an ExampleProcessor that provides a reference implementation with basic analysis capabilities:

  • Basic configuration validation
  • Security analysis (SSH, SNMP, web GUI protocol)
  • Dead rule detection (rules without descriptions)
  • Performance analysis (system optimization, hardware offloading)
  • Compliance checking (administrative users, time synchronization)

Extending the Processor

To create a custom processor implementation:

  1. Implement the Processor interface
  2. Handle the provided options in your implementation
  3. Use the Report struct to structure your findings
  4. Leverage the severity levels to categorize findings appropriately
type CustomProcessor struct {
    // Your custom fields
}

func (p *CustomProcessor) Process(ctx context.Context, cfg *model.Opnsense, opts ...Option) (*Report, error) {
    // Apply options
    config := DefaultConfig()
    config.ApplyOptions(opts...)

    // Create report
    report := NewReport(cfg, *config)

    // Perform your custom analysis
    // ...

    return report, nil
}

Testing

The package includes comprehensive tests demonstrating:

  • Interface compliance
  • Option handling
  • Context cancellation
  • Report generation and formatting
  • Finding management

Run tests with:

go test -v ./internal/processor

Documentation

Overview

Package processor provides interfaces and types for processing OPNsense configurations. It enables flexible analysis of OPNsense configurations through an options pattern, allowing features like statistics generation, dead-rule detection, and other analyses to be enabled independently.

Index

Constants

View Source
const (
	// Network constants.
	NetworkAny = "any"

	// Protocol constants.
	ProtocolHTTPS = "https"

	// Rule type constants.
	RuleTypePass = "pass"

	// Finding types.
	FindingTypeSecurity = "security"

	// Theme constants.
	ThemeLight = "light"
	ThemeDark  = "dark"

	// Status display constants.
	StatusNotEnabled = "❌"
	StatusEnabled    = "✅"

	// Configuration availability.
	NoConfigAvailable = "*No configuration available*"
)

Re-export constants from the constants package for backward compatibility. This allows existing code to continue working while avoiding import cycles.

Variables

View Source
var (
	// ErrConfigurationNil is returned when configuration is nil.
	ErrConfigurationNil = errors.New("configuration cannot be nil")

	// ErrNormalizedConfigUnavailable is returned when normalized configuration is not available.
	ErrNormalizedConfigUnavailable = errors.New("no normalized configuration available for markdown conversion")
)

Error constants for the processor package.

Functions

func ExampleUsage

func ExampleUsage(cfg *model.OpnSenseDocument)

ExampleUsage demonstrates various ways to process an OpnSense configuration document using the processor, including basic, security-focused, and comprehensive analyses, as well as handling custom timeouts and reporting in multiple formats.

func NewTestError

func NewTestError(id int, message string) error

NewTestError creates a new test error with goroutine ID.

func ProcessConfigFromFile

func ProcessConfigFromFile(configPath string) error

ProcessConfigFromFile loads an OpnSense configuration from the specified file path, processes it with all analysis features enabled, and prints a summary of the results. Returns an error if processing fails.

Types

type Config

type Config struct {
	// EnableStats controls whether to generate configuration statistics
	EnableStats bool
	// EnableDeadRuleCheck controls whether to analyze for unused/dead rules
	EnableDeadRuleCheck bool
	// EnableSecurityAnalysis controls whether to perform security analysis
	EnableSecurityAnalysis bool
	// EnablePerformanceAnalysis controls whether to analyze performance aspects
	EnablePerformanceAnalysis bool
	// EnableComplianceCheck controls whether to check compliance with best practices
	EnableComplianceCheck bool
}

Config holds the configuration for the processor.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns a Config with default settings.

func (*Config) ApplyOptions

func (c *Config) ApplyOptions(opts ...Option)

ApplyOptions applies the given options to the configuration.

type ConfigInfo

type ConfigInfo struct {
	// Hostname is the configured hostname of the OPNsense system
	Hostname string `json:"hostname"`
	// Domain is the configured domain name
	Domain string `json:"domain"`
	// Version is the OPNsense version (if available)
	Version string `json:"version,omitempty"`
	// Theme is the configured web UI theme
	Theme string `json:"theme,omitempty"`
}

ConfigInfo contains basic information about the processed configuration.

type CoreProcessor

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

CoreProcessor implements the Processor interface with normalize, validate, analyze, and transform capabilities.

func NewCoreProcessor

func NewCoreProcessor() (*CoreProcessor, error)

NewCoreProcessor returns a new CoreProcessor instance with a validator and a markdown generator initialized. Returns an error if the markdown generator cannot be created.

func (*CoreProcessor) Process

func (p *CoreProcessor) Process(ctx context.Context, cfg *model.OpnSenseDocument, opts ...Option) (*Report, error)

Process analyzes the given OPNsense configuration and returns a comprehensive report.

func (*CoreProcessor) Transform

func (p *CoreProcessor) Transform(ctx context.Context, report *Report, format string) (string, error)

Transform converts the report to the specified format.

type CustomCheck

type CustomCheck struct {
	Name        string
	Description string
	CheckFunc   func(*model.OpnSenseDocument) []Finding
}

CustomCheck represents a custom analysis check.

func ExampleCustomCheck

func ExampleCustomCheck() CustomCheck

ExampleCustomCheck returns a custom check that detects if the OpnSense configuration is using the default theme. The check produces a finding if no specific theme is set, recommending that a theme be configured for consistency.

type CustomProcessorExample

type CustomProcessorExample struct {
	*ExampleProcessor
	// contains filtered or unexported fields
}

CustomProcessorExample shows how to create a custom processor implementation that extends or modifies the behavior of the example processor.

func NewCustomProcessor

func NewCustomProcessor(customChecks []CustomCheck) *CustomProcessorExample

NewCustomProcessor returns a CustomProcessorExample that applies the provided custom checks in addition to the standard processing.

func (*CustomProcessorExample) Process

func (p *CustomProcessorExample) Process(
	ctx context.Context,
	cfg *model.OpnSenseDocument,
	opts ...Option,
) (*Report, error)

Process extends the base processor with custom checks.

type DHCPScopeStatistics

type DHCPScopeStatistics struct {
	Interface string `json:"interface"`
	Enabled   bool   `json:"enabled"`
	From      string `json:"from"`
	To        string `json:"to"`
}

DHCPScopeStatistics contains statistics for DHCP scopes.

type ExampleProcessor

type ExampleProcessor struct{}

ExampleProcessor provides a basic implementation of the Processor interface. This serves as a reference implementation and can be extended with more sophisticated analysis.

func NewExampleProcessor

func NewExampleProcessor() *ExampleProcessor

NewExampleProcessor returns a new ExampleProcessor for analyzing OPNsense configurations.

func (*ExampleProcessor) Process

func (p *ExampleProcessor) Process(ctx context.Context, cfg *model.OpnSenseDocument, opts ...Option) (*Report, error)

Process analyzes the given OPNsense configuration and returns a comprehensive report.

type Finding

type Finding struct {
	// Type categorizes the finding (e.g., "security", "performance", "compliance")
	Type string `json:"type"`
	// Title is a brief description of the finding
	Title string `json:"title"`
	// Description provides detailed information about the finding
	Description string `json:"description"`
	// Recommendation suggests how to address the finding
	Recommendation string `json:"recommendation,omitempty"`
	// Component identifies the configuration component involved
	Component string `json:"component,omitempty"`
	// Reference provides additional information or documentation links
	Reference string `json:"reference,omitempty"`
}

Finding represents a single analysis finding.

type Findings

type Findings struct {
	// Critical findings that require immediate attention
	Critical []Finding `json:"critical,omitempty"`
	// High severity findings
	High []Finding `json:"high,omitempty"`
	// Medium severity findings
	Medium []Finding `json:"medium,omitempty"`
	// Low severity findings
	Low []Finding `json:"low,omitempty"`
	// Informational findings
	Info []Finding `json:"info,omitempty"`
}

Findings contains analysis findings categorized by severity and type.

type InterfaceStatistics

type InterfaceStatistics struct {
	Name        string `json:"name"`
	Type        string `json:"type"`
	Enabled     bool   `json:"enabled"`
	HasIPv4     bool   `json:"hasIpv4"`
	HasIPv6     bool   `json:"hasIpv6"`
	HasDHCP     bool   `json:"hasDhcp"`
	BlockPriv   bool   `json:"blockPriv"`
	BlockBogons bool   `json:"blockBogons"`
}

InterfaceStatistics contains detailed statistics for a single interface.

type Option

type Option func(*Config)

Option represents a configuration option for the processor. This follows the functional options pattern to allow flexible configuration.

func WithAllFeatures

func WithAllFeatures() Option

WithAllFeatures enables all available analysis features.

func WithComplianceCheck

func WithComplianceCheck() Option

WithComplianceCheck enables compliance checking in the processor.

func WithDeadRuleCheck

func WithDeadRuleCheck() Option

WithDeadRuleCheck enables dead rule detection in the processor.

func WithPerformanceAnalysis

func WithPerformanceAnalysis() Option

WithPerformanceAnalysis enables performance analysis in the processor.

func WithSecurityAnalysis

func WithSecurityAnalysis() Option

WithSecurityAnalysis enables security analysis in the processor.

func WithStats

func WithStats() Option

WithStats enables statistics generation in the processor.

type OutputFormat

type OutputFormat string

OutputFormat represents the supported output formats.

const (
	// OutputFormatMarkdown outputs the report as Markdown.
	OutputFormatMarkdown OutputFormat = "markdown"
	// OutputFormatJSON outputs the report as JSON.
	OutputFormatJSON OutputFormat = "json"
	// OutputFormatYAML outputs the report as YAML.
	OutputFormatYAML OutputFormat = "yaml"
)

type Processor

type Processor interface {
	// Process analyzes the given OPNsense configuration and returns a comprehensive report.
	// The context allows for cancellation and timeout control.
	// Options can be used to enable specific analysis features.
	Process(ctx context.Context, cfg *model.OpnSenseDocument, opts ...Option) (*Report, error)
}

Processor defines the interface for processing OPNsense configurations. It provides a flexible way to analyze configurations with configurable options.

type Report

type Report struct {
	// GeneratedAt contains the timestamp when the report was generated
	GeneratedAt time.Time `json:"generatedAt"`

	// ConfigInfo contains basic information about the processed configuration
	ConfigInfo ConfigInfo `json:"configInfo"`

	// NormalizedConfig contains the processed and normalized configuration
	NormalizedConfig *model.OpnSenseDocument `json:"normalizedConfig,omitempty"`

	// Statistics contains various statistics about the configuration
	Statistics *Statistics `json:"statistics,omitempty"`

	// Findings contains analysis findings categorized by type
	Findings Findings `json:"findings"`

	// ProcessorConfig contains the configuration used during processing
	ProcessorConfig Config `json:"processorConfig"`
}

Report contains the results of processing an OPNsense configuration. It includes the normalized configuration, analysis findings, and statistics.

func NewReport

func NewReport(cfg *model.OpnSenseDocument, processorConfig Config) *Report

NewReport returns a new Report instance populated with configuration metadata, processor settings, and optionally generated statistics and normalized configuration data.

func (*Report) AddFinding

func (r *Report) AddFinding(severity Severity, finding Finding)

AddFinding adds a finding to the report with the specified severity.

func (*Report) HasCriticalFindings

func (r *Report) HasCriticalFindings() bool

HasCriticalFindings returns true if the report contains critical findings.

func (*Report) Summary

func (r *Report) Summary() string

Summary returns a brief summary of the report.

func (*Report) ToFormat

func (r *Report) ToFormat(format OutputFormat) (string, error)

ToFormat returns the report in the specified format.

func (*Report) ToJSON

func (r *Report) ToJSON() (string, error)

ToJSON returns the report as a JSON string.

func (*Report) ToMarkdown

func (r *Report) ToMarkdown() string

ToMarkdown returns the report formatted as Markdown using the markdown library.

func (*Report) ToYAML

func (r *Report) ToYAML() (string, error)

ToYAML returns the report as a YAML string.

func (*Report) TotalFindings

func (r *Report) TotalFindings() int

TotalFindings returns the total number of findings across all severities.

type ServiceStatistics

type ServiceStatistics struct {
	Name    string            `json:"name"`
	Enabled bool              `json:"enabled"`
	Details map[string]string `json:"details,omitempty"`
}

ServiceStatistics contains statistics for individual services.

type Severity

type Severity string

Severity represents the severity levels for findings.

const (
	// SeverityCritical represents critical findings that require immediate attention.
	SeverityCritical Severity = "critical"
	SeverityHigh     Severity = "high"
	SeverityMedium   Severity = "medium"
	SeverityLow      Severity = "low"
	SeverityInfo     Severity = "info"
)

Severity constants represent the different levels of finding severity.

type Statistics

type Statistics struct {
	// Interface statistics
	TotalInterfaces  int                   `json:"totalInterfaces"`
	InterfacesByType map[string]int        `json:"interfacesByType"`
	InterfaceDetails []InterfaceStatistics `json:"interfaceDetails"`

	// Firewall and NAT statistics
	TotalFirewallRules int            `json:"totalFirewallRules"`
	RulesByInterface   map[string]int `json:"rulesByInterface"`
	RulesByType        map[string]int `json:"rulesByType"`
	NATEntries         int            `json:"natEntries"`
	NATMode            string         `json:"natMode"`

	// DHCP statistics
	DHCPScopes       int                   `json:"dhcpScopes"`
	DHCPScopeDetails []DHCPScopeStatistics `json:"dhcpScopeDetails"`

	// User and group statistics
	TotalUsers    int            `json:"totalUsers"`
	UsersByScope  map[string]int `json:"usersByScope"`
	TotalGroups   int            `json:"totalGroups"`
	GroupsByScope map[string]int `json:"groupsByScope"`

	// Service statistics
	EnabledServices []string            `json:"enabledServices"`
	TotalServices   int                 `json:"totalServices"`
	ServiceDetails  []ServiceStatistics `json:"serviceDetails"`

	// System configuration statistics
	SysctlSettings       int      `json:"sysctlSettings"`
	LoadBalancerMonitors int      `json:"loadBalancerMonitors"`
	SecurityFeatures     []string `json:"securityFeatures"`

	// Summary counts for quick reference
	Summary StatisticsSummary `json:"summary"`
}

Statistics contains various statistics about the configuration.

type StatisticsSummary

type StatisticsSummary struct {
	TotalConfigItems    int  `json:"totalConfigItems"`
	SecurityScore       int  `json:"securityScore"`
	ConfigComplexity    int  `json:"configComplexity"`
	HasSecurityFeatures bool `json:"hasSecurityFeatures"`
}

StatisticsSummary provides high-level summary statistics.

type TestError

type TestError struct {
	GoroutineID int
	Message     string
}

TestError represents error types for consistent error handling in tests.

func (*TestError) Error

func (e *TestError) Error() string

type TestHostnameError

type TestHostnameError struct {
	GoroutineID      int
	ExpectedHostname string
	ActualHostname   string
}

TestHostnameError represents a hostname mismatch error in tests.

func (*TestHostnameError) Error

func (e *TestHostnameError) Error() string

type UnsupportedFormatError

type UnsupportedFormatError struct {
	Format string
}

UnsupportedFormatError represents an error for unsupported output formats.

func (*UnsupportedFormatError) Error

func (e *UnsupportedFormatError) Error() string

type ValidationError

type ValidationError struct {
	Field   string
	Message string
}

ValidationError represents a validation error with field and message information.

func (ValidationError) Error

func (e ValidationError) Error() string

Error implements the error interface for ValidationError.

Jump to

Keyboard shortcuts

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