Initial commit: IT Nexus Web-App

This commit is contained in:
2026-06-01 20:49:07 +02:00
commit 8023765e6c
387 changed files with 106900 additions and 0 deletions

View File

@@ -0,0 +1,195 @@
package config
import (
"fmt"
"os"
"time"
"gopkg.in/yaml.v3"
)
// Config holds the full agent configuration loaded from config.yaml.
type Config struct {
Site string `yaml:"site"` // LUD or BAR
LogLevel string `yaml:"log_level"` // debug | info | warn | error
SetupComplete bool `yaml:"setup_complete"` // false → show setup wizard on first run
DB struct {
Path string `yaml:"path"`
} `yaml:"db"`
Web struct {
Addr string `yaml:"addr"`
Token string `yaml:"token"`
} `yaml:"web"`
Nexus struct {
URL string `yaml:"url"`
APIKey string `yaml:"api_key"`
} `yaml:"nexus"`
OfflineAfter time.Duration `yaml:"offline_after"` // mark hosts offline after this duration
Alert AlertConfig `yaml:"alert"`
Modules struct {
ARPDiscovery ARPConfig `yaml:"arp_discovery"`
ADSync ADConfig `yaml:"ad_sync"`
SiteMonitoring SiteMonConfig `yaml:"site_monitoring"`
PortScan PortScanConfig `yaml:"port_scan"`
SNMP SNMPConfig `yaml:"snmp"`
NexusReporter ReporterConfig `yaml:"nexus_reporter"`
} `yaml:"modules"`
}
// ADConfig holds settings for the ad_sync module.
type ADConfig struct {
Enabled bool `yaml:"enabled"`
Interval time.Duration `yaml:"interval"`
Server string `yaml:"server"` // DC hostname or IP
Port int `yaml:"port"` // default 389
BindDN string `yaml:"bind_dn"` // svc-scanner@winkel.local
BindPassword string `yaml:"bind_password"`
SearchBase string `yaml:"search_base"` // DC=winkel,DC=local
TLS bool `yaml:"tls"` // use LDAPS
}
// SiteMonConfig holds settings for the site_monitoring module.
type SiteMonConfig struct {
Enabled bool `yaml:"enabled"`
Interval time.Duration `yaml:"interval"`
}
// PortScanConfig holds settings for the port_scan module.
type PortScanConfig struct {
Enabled bool `yaml:"enabled"`
Interval time.Duration `yaml:"interval"`
Ports []int `yaml:"ports"` // empty = use defaults
Timeout time.Duration `yaml:"timeout"` // per-port connect timeout
}
// SNMPConfig holds settings for the snmp module.
type SNMPConfig struct {
Enabled bool `yaml:"enabled"`
Interval time.Duration `yaml:"interval"`
Community string `yaml:"community"` // default community string
}
// ReporterConfig holds settings for the nexus_reporter module.
type ReporterConfig struct {
Enabled bool `yaml:"enabled"`
Interval time.Duration `yaml:"interval"`
}
// AlertConfig holds notification settings.
type AlertConfig struct {
SMTP struct {
Enabled bool `yaml:"enabled"`
Host string `yaml:"host"`
Port int `yaml:"port"`
Username string `yaml:"username"`
Password string `yaml:"password"`
From string `yaml:"from"`
To string `yaml:"to"`
} `yaml:"smtp"`
NexusEnabled bool `yaml:"nexus_enabled"`
}
// ARPConfig holds settings for the arp_discovery module.
type ARPConfig struct {
Enabled bool `yaml:"enabled"`
Interval time.Duration `yaml:"interval"`
Subnets []string `yaml:"subnets"`
Interface string `yaml:"interface"`
}
// Load reads and validates the YAML config at path.
func Load(path string) (*Config, error) {
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("open config %q: %w", path, err)
}
defer f.Close()
var cfg Config
if err := yaml.NewDecoder(f).Decode(&cfg); err != nil {
return nil, fmt.Errorf("decode config: %w", err)
}
if err := cfg.applyDefaults(); err != nil {
return nil, fmt.Errorf("config validation: %w", err)
}
return &cfg, nil
}
// Save writes the config back to path in YAML format.
func Save(path string, cfg *Config) error {
f, err := os.Create(path)
if err != nil {
return fmt.Errorf("create config %q: %w", path, err)
}
defer f.Close()
if err := yaml.NewEncoder(f).Encode(cfg); err != nil {
return fmt.Errorf("encode config: %w", err)
}
return nil
}
func (c *Config) applyDefaults() error {
if c.Site == "" {
return fmt.Errorf("site is required (LUD or BAR)")
}
if c.Site != "LUD" && c.Site != "BAR" {
return fmt.Errorf("site must be LUD or BAR, got %q", c.Site)
}
if c.LogLevel == "" {
c.LogLevel = "info"
}
if c.DB.Path == "" {
c.DB.Path = "/var/lib/nexus-scanner/scanner.db"
}
if c.Web.Addr == "" {
c.Web.Addr = ":8080"
}
if c.Modules.ARPDiscovery.Interval == 0 {
c.Modules.ARPDiscovery.Interval = 5 * time.Minute
}
if c.Modules.ARPDiscovery.Interface == "" {
c.Modules.ARPDiscovery.Interface = "eth0"
}
if c.Modules.ADSync.Port == 0 {
c.Modules.ADSync.Port = 389
}
if c.Modules.ADSync.Interval == 0 {
c.Modules.ADSync.Interval = 30 * time.Minute
}
if c.Modules.ADSync.SearchBase == "" && c.Modules.ADSync.Server != "" {
c.Modules.ADSync.SearchBase = "DC=winkel,DC=local"
}
if c.Modules.SiteMonitoring.Interval == 0 {
c.Modules.SiteMonitoring.Interval = time.Minute
}
if c.Modules.PortScan.Interval == 0 {
c.Modules.PortScan.Interval = 15 * time.Minute
}
if c.Modules.PortScan.Timeout == 0 {
c.Modules.PortScan.Timeout = 500 * time.Millisecond
}
if c.Modules.SNMP.Interval == 0 {
c.Modules.SNMP.Interval = 5 * time.Minute
}
if c.Modules.SNMP.Community == "" {
c.Modules.SNMP.Community = "public"
}
if c.Modules.NexusReporter.Interval == 0 {
c.Modules.NexusReporter.Interval = 10 * time.Minute
}
if c.OfflineAfter == 0 {
c.OfflineAfter = 15 * time.Minute
}
if c.Alert.SMTP.Port == 0 {
c.Alert.SMTP.Port = 587
}
return nil
}