Files
deceptifeed/cmd/cti-honeypot/main.go
2024-10-16 11:48:13 -07:00

121 lines
4.6 KiB
Go

package main
import (
"flag"
"log"
"log/slog"
"os"
"sync"
"github.com/r-smith/cti-honeypot/internal/config"
"github.com/r-smith/cti-honeypot/internal/httpserver"
"github.com/r-smith/cti-honeypot/internal/sshserver"
"github.com/r-smith/cti-honeypot/internal/tcpserver"
"github.com/r-smith/cti-honeypot/internal/threatfeed"
"github.com/r-smith/cti-honeypot/internal/udpserver"
)
func main() {
// Initialize config structs for parsing command-line flags.
cfg := config.Config{}
http := config.Server{Type: config.HTTP}
https := config.Server{Type: config.HTTPS}
ssh := config.Server{Type: config.SSH}
// Parse command line flags.
configFile := flag.String("config", "", "Path to optional XML configuration file")
flag.BoolVar(&http.Enabled, "enable-http", config.DefaultEnableHTTP, "Enable HTTP server")
flag.BoolVar(&https.Enabled, "enable-https", config.DefaultEnableHTTPS, "Enable HTTPS server")
flag.BoolVar(&ssh.Enabled, "enable-ssh", config.DefaultEnableSSH, "Enable SSH server")
flag.BoolVar(&cfg.ThreatFeed.Enabled, "enable-threatfeed", config.DefaultEnableThreatFeed, "Enable threat feed server")
flag.StringVar(&cfg.LogPath, "log", config.DefaultLogPath, "Path to log file")
flag.StringVar(&cfg.ThreatFeed.DatabasePath, "threat-database", config.DefaultThreatDatabasePath, "Path to threat feed database file")
flag.UintVar(&cfg.ThreatFeed.ExpiryHours, "threat-expiry-hours", config.DefaultThreatExpiryHours, "Remove inactive IPs from threat feed after specified hours")
flag.BoolVar(&cfg.ThreatFeed.IsPrivateIncluded, "threat-include-private", config.DefaultThreatIncludePrivate, "Include private IPs in threat feed (default false)")
flag.StringVar(&http.HtmlPath, "html", config.DefaultHtmlPath, "Path to optional HTML file to serve")
flag.StringVar(&http.Port, "port-http", config.DefaultPortHTTP, "Port number to listen on for HTTP server")
flag.StringVar(&https.Port, "port-https", config.DefaultPortHTTPS, "Port number to listen on for HTTPS server")
flag.StringVar(&ssh.Port, "port-ssh", config.DefaultPortSSH, "Port number to listen on for SSH server")
flag.StringVar(&cfg.ThreatFeed.Port, "port-threatfeed", config.DefaultPortThreatFeed, "Port number to listen on for threat feed server")
flag.StringVar(&https.CertPath, "https-cert", config.DefaultCertPathHTTPS, "Path to optional TLS public certificate")
flag.StringVar(&https.KeyPath, "https-key", config.DefaultKeyPathHTTPS, "Path to optional TLS private key")
flag.StringVar(&ssh.KeyPath, "ssh-key", config.DefaultKeyPathSSH, "Path to optional SSH private key")
flag.Parse()
// If the '-config' flag is provided, the specified configuration file is
// loaded. When a config file is used, all other command-line flags are
// ignored. The 'cfg' variable will contain all settings parsed from the
// configuration file.
if *configFile != "" {
cfgFromFile, err := config.Load(*configFile)
if err != nil {
log.Fatalln("Failed to load config:", err)
}
cfg = *cfgFromFile
} else {
https.HtmlPath = http.HtmlPath
cfg.Servers = append(cfg.Servers, http, https, ssh)
}
// Initialize a structured logger to record events in a text file. This
// logger captures all interactions with the honeypot servers.
f, err := os.OpenFile(cfg.LogPath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0644)
if err != nil {
log.Fatalln("Failed to open log file:", err)
}
defer f.Close()
cfg.Logger = slog.New(slog.NewJSONHandler(f, &slog.HandlerOptions{
ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
// Remove 'message' and 'log level' fields from output.
if a.Key == slog.MessageKey || a.Key == slog.LevelKey {
return slog.Attr{}
}
return a
},
}))
// Initialize a WaitGroup, as each server operates in its own goroutine.
// The WaitGroup counter is set to the number of configured honeypot
// servers, plus one additional count for the threat feed server.
var wg sync.WaitGroup
wg.Add(len(cfg.Servers) + 1)
// Start the threat feed.
go func() {
defer wg.Done()
if !cfg.ThreatFeed.Enabled {
return
}
threatfeed.StartThreatFeed(&cfg.ThreatFeed)
}()
// Start the honeypot servers.
for _, server := range cfg.Servers {
go func() {
defer wg.Done()
if !server.Enabled || len(server.Port) == 0 {
return
}
switch server.Type {
case config.HTTP:
httpserver.StartHTTP(&cfg, &server)
case config.HTTPS:
httpserver.StartHTTPS(&cfg, &server)
case config.SSH:
sshserver.StartSSH(&cfg, &server)
case config.TCP:
tcpserver.StartTCP(&cfg, &server)
case config.UDP:
udpserver.StartUDP(&cfg, &server)
}
}()
}
// Wait for all servers to end.
wg.Wait()
}