packagemainimport("context""crypto/tls""encoding/json""flag""fmt""log""log/slog""os""os/signal""sync""syscall""time""honeypot/internal/dashboard""honeypot/internal/database""honeypot/internal/geodb""honeypot/internal/honeypot"dnshoneypot"honeypot/internal/honeypot/dns""honeypot/internal/honeypot/ftp"httphoneypot"honeypot/internal/honeypot/http""honeypot/internal/honeypot/packetlogger""honeypot/internal/honeypot/rdp""honeypot/internal/honeypot/sip""honeypot/internal/honeypot/smtp""honeypot/internal/honeypot/ssh""honeypot/internal/honeypot/telnet""honeypot/internal/logger""honeypot/internal/metrics"tlscert"honeypot/internal/tls")// version is set at build time or by git hookvarversion="0.59.6"// Config holds the application configuration.typeConfigstruct{ListenAddrstring`json:"listen_addr"`LogFilestring`json:"log_file"`DatabaseFilestring`json:"database_file"`UIPortuint16`json:"ui_port"`UIPasswordstring`json:"ui_password"`APITokenstring`json:"api_token"`DisableMetricsbool`json:"disable_metrics"`DisableHWMetricsbool`json:"disable_hw_metrics"`DisableDashboardbool`json:"disable_dashboard"`Interfacestring`json:"interface"`BpfExpressionstring`json:"bpf_expression"`SSHPorts[]uint16`json:"ssh_ports"`TelnetPorts[]uint16`json:"telnet_ports"`RDPPorts[]uint16`json:"rdp_ports"`SMTPPorts[]uint16`json:"smtp_ports"`SMTPSPorts[]uint16`json:"smtps_ports"`FTPPorts[]uint16`json:"ftp_ports"`FTPSPorts[]uint16`json:"ftps_ports"`SIPPorts[]uint16`json:"sip_ports"`HTTPPorts[]uint16`json:"http_ports"`HTTPSPorts[]uint16`json:"https_ports"`DNSPorts[]uint16`json:"dns_ports"`CertConfigtlscert.CertConfig`json:"cert_config"`ASNDBFilestring`json:"asn_db_file"`CityDBFilestring`json:"city_db_file"`ASNDBURLstring`json:"asn_db_url"`CityDBURLstring`json:"city_db_url"`TrustedProxies[]string`json:"trusted_proxies"`}funcmain(){varversionFlagboolvarconfigFilestringflag.BoolVar(&versionFlag,"version",false,"print version and exit")flag.StringVar(&configFile,"config","config.json","path to configuration file")flag.Parse()ifversionFlag{fmt.Printf("honeypot version %s\n",version)os.Exit(0)}cfg:=parseConfig(configFile)l,err:=logger.Setup(cfg.LogFile)iferr!=nil{log.Fatalf("failed to setup logger: %v",err)}log.Println("Config OK")ctx,cancel:=context.WithCancel(context.Background())sigCh:=make(chanos.Signal,1)signal.Notify(sigCh,syscall.SIGINT,syscall.SIGTERM)gofunc(){sig:=<-sigChfmt.Println("Received signal",sig)cancel()}()// Generate shared certificate if TLS is needed (HTTPS, SMTPS, FTPS, or RDP)varsharedCert*tls.Certificateiflen(cfg.HTTPSPorts)>0||len(cfg.SMTPSPorts)>0||len(cfg.FTPSPorts)>0||len(cfg.RDPPorts)>0{cert,err:=tlscert.GenerateSelfSignedCert(cfg.ListenAddr,cfg.CertConfig,l)iferr!=nil{l.Error("failed to generate shared TLS certificate","error",err)// Continue without certificate - honeypots will handle this}else{sharedCert=certl.Info("generated shared TLS certificate for HTTPS, SMTPS, FTPS and RDP")}}honeypots:=[]honeypot.Honeypot{}// Create honeypots early so they can register their fields before restoration// Add SSH honeypot if ports are configurediflen(cfg.SSHPorts)>0{sshHp:=ssh.New(ssh.Config{ListenAddr:cfg.ListenAddr,Ports:cfg.SSHPorts,})honeypots=append(honeypots,sshHp)log.Println("SSH honeypot created",cfg.SSHPorts)}// Add Telnet honeypot if ports are configurediflen(cfg.TelnetPorts)>0{telnetHp:=telnet.New(telnet.Config{ListenAddr:cfg.ListenAddr,Ports:cfg.TelnetPorts,})honeypots=append(honeypots,telnetHp)log.Println("Telnet honeypot created",cfg.TelnetPorts)}// Add RDP honeypot if ports are configurediflen(cfg.RDPPorts)>0{rdpHp:=rdp.New(rdp.Config{ListenAddr:cfg.ListenAddr,Ports:cfg.RDPPorts,Certificate:sharedCert,})honeypots=append(honeypots,rdpHp)log.Println("RDP honeypot created",cfg.RDPPorts)}// Add SMTP honeypot if ports are configurediflen(cfg.SMTPPorts)>0||len(cfg.SMTPSPorts)>0{smtpHp:=smtp.New(smtp.Config{ListenAddr:cfg.ListenAddr,Ports:cfg.SMTPPorts,SMTPSPorts:cfg.SMTPSPorts,Certificate:sharedCert,CertConfig:cfg.CertConfig,})honeypots=append(honeypots,smtpHp)log.Println("SMTP honeypot created",cfg.SMTPPorts,cfg.SMTPSPorts)}// Add FTP honeypot if ports are configurediflen(cfg.FTPPorts)>0||len(cfg.FTPSPorts)>0{ftpHp:=ftp.New(ftp.Config{ListenAddr:cfg.ListenAddr,Ports:cfg.FTPPorts,FTPSPorts:cfg.FTPSPorts,Certificate:sharedCert,CertConfig:cfg.CertConfig,})honeypots=append(honeypots,ftpHp)log.Println("FTP honeypot created",cfg.FTPPorts,cfg.FTPSPorts)}// Add HTTP honeypot if ports are configurediflen(cfg.HTTPPorts)>0||len(cfg.HTTPSPorts)>0{httpHp:=httphoneypot.New(httphoneypot.Config{ListenAddr:cfg.ListenAddr,HTTPPorts:cfg.HTTPPorts,HTTPSPorts:cfg.HTTPSPorts,CertConfig:cfg.CertConfig,TrustedProxies:cfg.TrustedProxies,})honeypots=append(honeypots,httpHp)log.Println("HTTP honeypot created",cfg.HTTPPorts,cfg.HTTPSPorts)}// Add DNS honeypot if ports are configurediflen(cfg.DNSPorts)>0{dnsHp:=dnshoneypot.New(dnshoneypot.Config{ListenAddr:cfg.ListenAddr,Ports:cfg.DNSPorts,})honeypots=append(honeypots,dnsHp)log.Println("DNS honeypot created",cfg.DNSPorts)}// Add SIP honeypot if ports are configurediflen(cfg.SIPPorts)>0{sipHp:=sip.New(sip.Config{ListenAddr:cfg.ListenAddr,Ports:cfg.SIPPorts,})honeypots=append(honeypots,sipHp)log.Println("SIP honeypot created",cfg.SIPPorts)}// Add packet logger if interface is specifiedifcfg.Interface!=""{plHp:=packetlogger.New(packetlogger.Config{Interface:cfg.Interface,BpfExpression:cfg.BpfExpression,})honeypots=append(honeypots,plHp)log.Println("Packet logger created",cfg.Interface)}// Start dashboard server if configuredvardashboardShutdownDonechanstruct{}vargeoDb*geodb.GeoDBvardb*database.Databaseifcfg.UIPort>0{dashboardShutdownDone=make(chanstruct{})varerrerror// Download GeoDB if missingif_,err:=os.Stat(cfg.ASNDBFile);os.IsNotExist(err){log.Printf("ASN database missing, downloading from %s...",cfg.ASNDBURL)iferr:=geodb.DownloadFile(cfg.ASNDBURL,cfg.ASNDBFile);err!=nil{log.Printf("Warning: failed to download ASN database: %v",err)}}if_,err:=os.Stat(cfg.CityDBFile);os.IsNotExist(err){log.Printf("City database missing, downloading from %s...",cfg.CityDBURL)iferr:=geodb.DownloadFile(cfg.CityDBURL,cfg.CityDBFile);err!=nil{log.Printf("Warning: failed to download City database: %v",err)}}geoDb,err=geodb.NewGeoDB(cfg.ASNDBFile,cfg.CityDBFile)iferr!=nil{log.Printf("Warning: GeoDB could not be initialized: %v",err)fmt.Println("GeoDB could not be initialized. GeoLite2 databases not found.")}varmetricsCollectorlogger.MetricsCollectorif!cfg.DisableMetrics{metricsCollector=metrics.NewMetricsCollector(cfg.DisableHWMetrics)logger.RegisterMetricsCollector(metricsCollector)}db=database.NewDatabase(cfg.DatabaseFile)ifdb!=nil{iferr:=db.CreateTables();err!=nil{log.Fatalf("failed to create database tables: %v",err)}logger.RegisterDatabase(db)ifmetricsCollector!=nil{metricsCollector.SetDatabase(db)}log.Println("Database created",cfg.DatabaseFile)}dashboard.StartServer(ctx,dashboard.ServerConfig{ListenAddr:cfg.ListenAddr,UIPort:cfg.UIPort,UIPassword:cfg.UIPassword,APIToken:cfg.APIToken,LogFile:cfg.LogFile,DisableMetrics:cfg.DisableMetrics,DisableDashboard:cfg.DisableDashboard,ASNDBFile:cfg.ASNDBFile,CityDBFile:cfg.CityDBFile,ASNDBURL:cfg.ASNDBURL,CityDBURL:cfg.CityDBURL,},l,metricsCollector,honeypots,db,geoDb,dashboardShutdownDone,)// Start background IP info updatergostartIPInfoUpdater(ctx,db,geoDb,l)}iflen(honeypots)==0&&cfg.UIPort==0{l.Info("no honeypots or dashboard configured, exiting")return}varwgsync.WaitGroup// Start all honeypotsfor_,hp:=rangehoneypots{wg.Add(1)gofunc(hhoneypot.Honeypot){deferwg.Done()iferr:=h.Start(ctx,l);err!=nil{l.Error("honeypot failed","name",h.Name(),"error",err)}}(hp)}log.Println("Honeypots started",len(honeypots))<-ctx.Done()log.Println("Shutdown signal received")// Wait for dashboard to finish shutting downifdashboardShutdownDone!=nil{<-dashboardShutdownDonel.Info("dashboard server stopped")}// Close database connectionifdb!=nil{iferr:=db.Close();err!=nil{l.Error("failed to close database","error",err)}}ifgeoDb!=nil{iferr:=geoDb.Close();err!=nil{l.Error("failed to close geodb","error",err)}}l.Info("all services stopped, shutdown complete")}funcparseConfig(configFilestring)Config{varcfgConfig// Read config fileifconfigFile==""{log.Fatalf("config file path is required (use -config flag)")}data,err:=os.ReadFile(configFile)iferr!=nil{log.Fatalf("failed to read config file %s: %v",configFile,err)}iferr:=json.Unmarshal(data,&cfg);err!=nil{log.Fatalf("failed to parse config file %s: %v",configFile,err)}// Set defaults if not specifiedifcfg.ListenAddr==""{cfg.ListenAddr="0.0.0.0"}ifcfg.UIPort>0{ifcfg.ASNDBURL==""{if_,err:=os.Stat(cfg.ASNDBFile);os.IsNotExist(err){log.Fatalf("asn_db_url is required in config when asn_db_file is missing")}}ifcfg.CityDBURL==""{if_,err:=os.Stat(cfg.CityDBFile);os.IsNotExist(err){log.Fatalf("city_db_url is required in config when city_db_file is missing")}}}returncfg}funcstartIPInfoUpdater(ctxcontext.Context,db*database.Database,geoDb*geodb.GeoDB,l*slog.Logger){ifdb==nil||geoDb==nil{return}ticker:=time.NewTicker(time.Minute)deferticker.Stop()// Initial updatedb.UpdateIPInfo(ctx,geoDb,l)for{select{case<-ctx.Done():returncase<-ticker.C:db.UpdateIPInfo(ctx,geoDb,l)}}}