Initial commit: IT Nexus Web-App
This commit is contained in:
156
nexus-scanner/cmd/nexus-scanner/main.go
Normal file
156
nexus-scanner/cmd/nexus-scanner/main.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/cereda-systems/nexus-scanner/internal/config"
|
||||
"github.com/cereda-systems/nexus-scanner/internal/db"
|
||||
"github.com/cereda-systems/nexus-scanner/internal/module"
|
||||
arpmod "github.com/cereda-systems/nexus-scanner/internal/modules/arp"
|
||||
admod "github.com/cereda-systems/nexus-scanner/internal/modules/adsync"
|
||||
dnsmod "github.com/cereda-systems/nexus-scanner/internal/modules/dnsreverse"
|
||||
vendormod "github.com/cereda-systems/nexus-scanner/internal/modules/macvendor"
|
||||
reportermod "github.com/cereda-systems/nexus-scanner/internal/modules/nexusreporter"
|
||||
portscanmod "github.com/cereda-systems/nexus-scanner/internal/modules/portscan"
|
||||
sitemon "github.com/cereda-systems/nexus-scanner/internal/modules/sitemon"
|
||||
snmpmod "github.com/cereda-systems/nexus-scanner/internal/modules/snmpmod"
|
||||
sysarpmod "github.com/cereda-systems/nexus-scanner/internal/modules/sysarp"
|
||||
"github.com/cereda-systems/nexus-scanner/internal/scheduler"
|
||||
"github.com/cereda-systems/nexus-scanner/internal/web"
|
||||
)
|
||||
|
||||
var version = "dev"
|
||||
|
||||
func main() {
|
||||
cfgPath := "/etc/nexus-scanner/config.yaml"
|
||||
if len(os.Args) > 1 {
|
||||
cfgPath = os.Args[1]
|
||||
}
|
||||
|
||||
cfg, err := config.Load(cfgPath)
|
||||
if err != nil {
|
||||
slog.Error("failed to load config", "path", cfgPath, "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Log buffer captures entries for the /logs web page.
|
||||
logBuf := web.NewLogBuffer(500)
|
||||
|
||||
level := slog.LevelInfo
|
||||
if cfg.LogLevel == "debug" {
|
||||
level = slog.LevelDebug
|
||||
}
|
||||
inner := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: level})
|
||||
handler := web.NewSlogHandler(inner, logBuf)
|
||||
slog.SetDefault(slog.New(handler))
|
||||
|
||||
slog.Info("nexus-scanner starting", "version", version, "site", cfg.Site)
|
||||
|
||||
store, err := db.Open(cfg.DB.Path)
|
||||
if err != nil {
|
||||
slog.Error("failed to open database", "path", cfg.DB.Path, "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
registry := module.NewRegistry()
|
||||
|
||||
// sys_arp: reads the OS ARP cache — works on Windows, Linux and macOS
|
||||
// without raw-socket privileges. Always enabled.
|
||||
registry.Register(sysarpmod.New(cfg.Site, store))
|
||||
slog.Info("module registered", "module", "sys_arp")
|
||||
|
||||
// dns_reverse: enriches known hosts with reverse-DNS hostnames.
|
||||
registry.Register(dnsmod.New(cfg.Site, store))
|
||||
slog.Info("module registered", "module", "dns_reverse")
|
||||
|
||||
// mac_vendor: assigns OUI-based vendor names to known hosts (runs once at startup).
|
||||
registry.Register(vendormod.New(cfg.Site, store))
|
||||
slog.Info("module registered", "module", "mac_vendor")
|
||||
|
||||
// ad_sync: queries Active Directory for all computer objects.
|
||||
if cfg.Modules.ADSync.Enabled {
|
||||
registry.Register(admod.New(cfg.Site, cfg.Modules.ADSync, store))
|
||||
slog.Info("module registered", "module", "ad_sync",
|
||||
"server", cfg.Modules.ADSync.Server,
|
||||
"search_base", cfg.Modules.ADSync.SearchBase,
|
||||
)
|
||||
}
|
||||
|
||||
// site_monitoring: availability checks (ping, http, tcp) — always enabled,
|
||||
// checks are managed via the web UI.
|
||||
cfg.Modules.SiteMonitoring.Enabled = true
|
||||
registry.Register(sitemon.New(cfg.Site, cfg.Modules.SiteMonitoring, cfg.Alert, *cfg, store))
|
||||
slog.Info("module registered", "module", "site_monitoring",
|
||||
"interval", cfg.Modules.SiteMonitoring.Interval)
|
||||
|
||||
// port_scan: TCP port scanner for all online hosts.
|
||||
cfg.Modules.PortScan.Enabled = true
|
||||
registry.Register(portscanmod.New(cfg.Site, cfg.Modules.PortScan, store))
|
||||
slog.Info("module registered", "module", "port_scan",
|
||||
"interval", cfg.Modules.PortScan.Interval)
|
||||
|
||||
// snmp: polls SNMP targets for device info (managed via web UI).
|
||||
cfg.Modules.SNMP.Enabled = true
|
||||
registry.Register(snmpmod.New(cfg.Site, cfg.Modules.SNMP, store))
|
||||
slog.Info("module registered", "module", "snmp",
|
||||
"interval", cfg.Modules.SNMP.Interval)
|
||||
|
||||
// nexus_reporter: pushes changed assets to IT Nexus API.
|
||||
if cfg.Nexus.URL != "" && cfg.Nexus.APIKey != "" {
|
||||
cfg.Modules.NexusReporter.Enabled = true
|
||||
registry.Register(reportermod.New(cfg.Site, cfg.Modules.NexusReporter, *cfg, store))
|
||||
slog.Info("module registered", "module", "nexus_reporter")
|
||||
}
|
||||
|
||||
// arp_discovery: full ARP sweep via raw sockets (Linux only).
|
||||
if cfg.Modules.ARPDiscovery.Enabled {
|
||||
registry.Register(arpmod.New(cfg.Site, cfg.Modules.ARPDiscovery, store))
|
||||
slog.Info("module registered", "module", "arp_discovery",
|
||||
"interval", cfg.Modules.ARPDiscovery.Interval,
|
||||
"subnets", cfg.Modules.ARPDiscovery.Subnets,
|
||||
)
|
||||
}
|
||||
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
// Offline-Sweeper: marks hosts as offline when not seen for OfflineAfter duration.
|
||||
go func() {
|
||||
ticker := time.NewTicker(5 * time.Minute)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
n, err := store.MarkStaleHostsOffline(cfg.OfflineAfter)
|
||||
if err != nil {
|
||||
slog.Error("offline sweep", "err", err)
|
||||
} else if n > 0 {
|
||||
slog.Info("hosts marked offline", "count", n, "threshold", cfg.OfflineAfter)
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
sched := scheduler.New(registry)
|
||||
go sched.Run(ctx)
|
||||
|
||||
srv := web.NewServer(cfgPath, cfg, store, registry, logBuf)
|
||||
go func() {
|
||||
if err := srv.ListenAndServe(ctx); err != nil {
|
||||
slog.Error("web server stopped", "err", err)
|
||||
stop()
|
||||
}
|
||||
}()
|
||||
|
||||
slog.Info("nexus-scanner ready", "addr", cfg.Web.Addr)
|
||||
<-ctx.Done()
|
||||
slog.Info("nexus-scanner shutting down")
|
||||
}
|
||||
Reference in New Issue
Block a user