diff --git a/alert/alert.go b/alert/alert.go index 754added8d31398be0b5250c238bda9aa5854ce8..41361be0ef00caeae3a7754275c170199ba5f8f4 100644 --- a/alert/alert.go +++ b/alert/alert.go @@ -4,13 +4,14 @@ import ( "bytes" "encoding/json" "fmt" - "log" "log/slog" "log/syslog" "net/http" "reflect" "strings" "time" + + "gitlab.cern.ch/ComputerSecurity/gocanary/clog" ) type Field struct { @@ -73,7 +74,7 @@ func Initialize(slackhook string, silenceSeconds uint16, doSyslog bool) { if doSyslog { sysl, err := syslog.New(syslog.LOG_INFO, "canary") if err != nil { - log.Fatalln(err) + clog.Fatal("%s", err) // Convert err to string } syslogger = sysl } @@ -93,14 +94,14 @@ func PostSlackHook(message string, attachments []Attachment) { // Marshal the payload into JSON jsonData, err := json.Marshal(payload) if err != nil { - log.Printf("ERROR: Slack Error encoding JSON: %s", err) + clog.Error("Slack Error encoding JSON: %s", err) return } // Create a new HTTP request with the JSON payload req, err := http.NewRequest("POST", config.SlackHook, bytes.NewBuffer(jsonData)) if err != nil { - log.Printf("ERROR: Slack Error creating request: %s", err) + clog.Error("Slack Error creating request: %s", err) return } @@ -111,13 +112,13 @@ func PostSlackHook(message string, attachments []Attachment) { client := &http.Client{} resp, err := client.Do(req) if err != nil { - log.Printf("ERROR: Slack Error sending request to Slack: %s", err) + clog.Error("Slack Error sending request to Slack: %s", err) return } defer resp.Body.Close() // Log the status code for the response - log.Printf("Slack Response StatusCode: %d", resp.StatusCode) + clog.Info("Slack Response StatusCode: %d", resp.StatusCode) } // Format the canary alert message diff --git a/clog/clog.go b/clog/clog.go new file mode 100644 index 0000000000000000000000000000000000000000..04230180f8174ce3bdc27779ebafe64f1dd1ae55 --- /dev/null +++ b/clog/clog.go @@ -0,0 +1,85 @@ +package clog + +import ( + "context" + "fmt" + "log/slog" + "os" +) + +const ( + LevelFatal = slog.Level(16) + LevelPanic = slog.Level(12) +) + +var LevelNames = map[slog.Level]string{ + LevelFatal: "FATAL", + LevelPanic: "PANIC", +} + +type CustomLogger struct { + *slog.Logger +} + +var defaultLogger *CustomLogger + +func init() { + defaultLogger = NewCustomLogger() + slog.SetDefault(defaultLogger.Logger) +} + +func NewCustomLogger() *CustomLogger { + // change level name + opts := slog.HandlerOptions{ + ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { + if a.Key == slog.LevelKey { + level := a.Value.Any().(slog.Level) + levelLabel, exists := LevelNames[level] + if !exists { + levelLabel = level.String() + } + + a.Value = slog.StringValue(levelLabel) + } + return a + }, + } + + handler := slog.NewJSONHandler(os.Stdout, &opts) + logger := slog.New(handler) + return &CustomLogger{Logger: logger} +} + +func (cl *CustomLogger) Log(level slog.Level, format string, v ...interface{}) { + msg := fmt.Sprintf(format, v...) + cl.Logger.Log(context.Background(), level, msg) + if level == LevelFatal { + os.Exit(1) + } else if level == LevelPanic { + panic(msg) + } +} + +func Info(format string, v ...interface{}) { + defaultLogger.Log(slog.LevelInfo, format, v...) +} + +func Debug(format string, v ...interface{}) { + defaultLogger.Log(slog.LevelDebug, format, v...) +} + +func Warn(format string, v ...interface{}) { + defaultLogger.Log(slog.LevelWarn, format, v...) +} + +func Error(format string, v ...interface{}) { + defaultLogger.Log(slog.LevelError, format, v...) +} + +func Panic(format string, v ...interface{}) { + defaultLogger.Log(LevelPanic, format, v...) +} + +func Fatal(format string, v ...interface{}) { + defaultLogger.Log(LevelFatal, format, v...) +} diff --git a/main.go b/main.go index e44f5603fd61d67f9b8816f259ac38d0b7bfbe4a..dc33138f5e73684021c50fd2235576c76bada4f4 100644 --- a/main.go +++ b/main.go @@ -9,13 +9,11 @@ package main import ( - "log" - "log/slog" - "os" "time" "github.com/spf13/cobra" "gitlab.cern.ch/ComputerSecurity/gocanary/alert" + "gitlab.cern.ch/ComputerSecurity/gocanary/clog" cdns "gitlab.cern.ch/ComputerSecurity/gocanary/server/dns" chttp "gitlab.cern.ch/ComputerSecurity/gocanary/server/http" "gitlab.cern.ch/ComputerSecurity/gocanary/tokens" @@ -34,7 +32,8 @@ func runCanary(cmd *cobra.Command, args []string) { //Start servers normalizeDomains() - log.Print("Servers starting") + + clog.Info("Servers starting") //Start DNS server if dnsEnabled { cdns.Start(getDNSConfig()) @@ -55,7 +54,7 @@ func runCanary(cmd *cobra.Command, args []string) { time.Sleep(1 * time.Second) harden.DropPrivs() - slog.Debug("LandLocking") + clog.Debug("LandLocking") //If autocert we need a writable cache directory by the dropped uid if len(autocertDomains) > 0 { harden.LandLock(binPath, cacheDir) @@ -72,9 +71,7 @@ func runCanary(cmd *cobra.Command, args []string) { // MAIN func main() { - logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) - slog.SetDefault(logger) if err := gocanaryCmd.Execute(); err != nil { - log.Fatalf("Error executing program: %s", err) + clog.Fatal("Error executing program: %v", err) } } diff --git a/server/dns/dns.go b/server/dns/dns.go index 56186e25aee0300117ac8d4b0242d6a13e701062..c57092ac3d3abe527a66244ca5b3e4fa8b63099a 100644 --- a/server/dns/dns.go +++ b/server/dns/dns.go @@ -2,7 +2,6 @@ package dns import ( "fmt" - "log" "log/slog" "net" "slices" @@ -12,6 +11,7 @@ import ( "github.com/miekg/dns" "gitlab.cern.ch/ComputerSecurity/gocanary/alert" + "gitlab.cern.ch/ComputerSecurity/gocanary/clog" "gitlab.cern.ch/ComputerSecurity/gocanary/tokens" ) @@ -98,7 +98,6 @@ func buildAnswer(q dns.Question, m *dns.Msg, canaryTriggered bool) { m.Answer = append(m.Answer, rr) m.Rcode = dns.RcodeNameError } - } } @@ -120,7 +119,7 @@ func handleDNS(w dns.ResponseWriter, r *dns.Msg) { } } - if serverConfig.IgnoreRequests != true { + if !serverConfig.IgnoreRequests { w.WriteMsg(m) } } @@ -128,10 +127,10 @@ func handleDNS(w dns.ResponseWriter, r *dns.Msg) { // DNS server proto udp func StartUDPListener(bindAddr string) { server := &dns.Server{Addr: bindAddr, Net: "udp"} - log.Printf("Starting UDP DNS server on %s", bindAddr) + clog.Info("Starting UDP DNS server on %s", bindAddr) go func() { if err := server.ListenAndServe(); err != nil { - log.Fatalf("COLLECTOR_DNS: Failed to set udp listener %s\n", err.Error()) + clog.Fatal("COLLECTOR_DNS: Failed to set udp listener %v\n", err.Error()) } }() } @@ -139,10 +138,10 @@ func StartUDPListener(bindAddr string) { // DNS server proto tcp func StartTCPListener(bindAddr string) { serverTCP := &dns.Server{Addr: bindAddr, Net: "tcp"} - log.Printf("Starting TCP DNS server on %s", bindAddr) + clog.Info("Starting TCP DNS server on %s", bindAddr) go func() { if err := serverTCP.ListenAndServe(); err != nil { - log.Fatalf("COLLECTOR_DNS: Failed to set tcp listener %s\n", err.Error()) + clog.Fatal("COLLECTOR_DNS: Failed to set tcp listener %v\n", err.Error()) } }() } diff --git a/server/http/http.go b/server/http/http.go index 0c00381e787e1ba7b5cd65b1d4b3e8392e661fc4..28eb7ce779eb6ae68fa108c5657650f34c18e804 100644 --- a/server/http/http.go +++ b/server/http/http.go @@ -3,7 +3,6 @@ package http import ( "crypto/tls" "fmt" - "log" "log/slog" "net" "net/http" @@ -14,6 +13,7 @@ import ( "time" "gitlab.cern.ch/ComputerSecurity/gocanary/alert" + "gitlab.cern.ch/ComputerSecurity/gocanary/clog" "gitlab.cern.ch/ComputerSecurity/gocanary/tokens" "golang.org/x/crypto/acme/autocert" ) @@ -151,11 +151,10 @@ func getTlsConfig(config HTTPServerConfig) *tls.Config { } else { cer, err := tls.LoadX509KeyPair(config.CertFile, config.CertKeyFile) if err != nil { - log.Fatalf("Error loading certificates") + clog.Fatal("Error loading certificates") } return &tls.Config{Certificates: []tls.Certificate{cer}} } - } // Initialize autocert manager for let's encrypt certificates @@ -173,7 +172,8 @@ func initializeAutocertManager(autocertDomains []string, cacheDir string) { func listenHTTP(config HTTPServerConfig, mux *http.ServeMux) { // Start HTTP server go func() { - log.Printf("Starting HTTP server on %s", config.HttpBindAddr) + clog.Info("Starting HTTP server on %s", config.HttpBindAddr) + srv := &http.Server{ Addr: config.HttpBindAddr, Handler: mux, @@ -189,7 +189,7 @@ func listenHTTP(config HTTPServerConfig, mux *http.ServeMux) { } if err := srv.ListenAndServe(); err != nil { - log.Fatalf("HTTP server failed: %s", err) + clog.Fatal("HTTP server failed: %s", err) } }() } @@ -197,7 +197,7 @@ func listenHTTP(config HTTPServerConfig, mux *http.ServeMux) { func listenHTTPS(config HTTPServerConfig, mux *http.ServeMux) { // Start HTTPS server go func() { - log.Printf("Starting HTTPS server on %s", config.HttpsBindAddr) + clog.Info("Starting HTTPS server on %s", config.HttpsBindAddr) tlsConfig := getTlsConfig(config) srv := &http.Server{ Addr: config.HttpsBindAddr, @@ -209,7 +209,7 @@ func listenHTTPS(config HTTPServerConfig, mux *http.ServeMux) { } if err := srv.ListenAndServeTLS("", ""); err != nil { - log.Fatalf("HTTPS server failed: %s", err) + clog.Fatal("HTTPS server failed: %s", err) } }() diff --git a/tokens/tokens.go b/tokens/tokens.go index 3980812b08d7412b2f9f58ea5213fecc6bd2c4c2..e68fb6ffd940460d4a7a8035445f552c06ec5757 100644 --- a/tokens/tokens.go +++ b/tokens/tokens.go @@ -2,9 +2,9 @@ package tokens import ( "fmt" - "log" "os" + "gitlab.cern.ch/ComputerSecurity/gocanary/clog" "gopkg.in/yaml.v2" ) @@ -22,13 +22,13 @@ var canaryRecord map[string]CanaryRecord func Initialize(tokenStorage string) { data, err := os.ReadFile(string(tokenStorage)) if err != nil { - log.Fatalf("error reading file: %v", err) + clog.Fatal("error reading file: %v", err) } var records []CanaryRecord err = yaml.Unmarshal(data, &records) if err != nil { - log.Fatalf("error parsing YAML: %v", err) + clog.Fatal("error parsing YAML: %v", err) } canaryRecord = make(map[string]CanaryRecord) diff --git a/utils/harden/harden.go b/utils/harden/harden.go index a1b73e5ad4edba771a1c6354c7d779aa38c1ab27..cd5967d3c70e40998b61f8eeb39cd76d10866c3e 100644 --- a/utils/harden/harden.go +++ b/utils/harden/harden.go @@ -1,13 +1,13 @@ package harden import ( - "log" "os/user" "strconv" "syscall" "github.com/landlock-lsm/go-landlock/landlock" "github.com/syndtr/gocapability/capability" + "gitlab.cern.ch/ComputerSecurity/gocanary/clog" ) var switchToUser = "nobody" @@ -16,13 +16,13 @@ var switchToUser = "nobody" func ShowCaps() { caps, err := capability.NewPid(0) if err != nil { - log.Fatal(err) + clog.Fatal("%v", err) } err = caps.Load() if err != nil { - log.Fatal(err) + clog.Fatal("%v", err) } - log.Printf("now: %+v", caps) + clog.Info("now: %+v", caps) } @@ -32,7 +32,7 @@ func MinCapabilities(hardeningEnabled bool) { // NewPid(0) means current process caps, err := capability.NewPid(0) if err != nil { - log.Fatal(err) + clog.Fatal(err.Error()) } caps.Clear(capability.CAPS) caps.Set(capability.CAPS, capability.CAP_NET_BIND_SERVICE) @@ -43,7 +43,7 @@ func MinCapabilities(hardeningEnabled bool) { } if err := caps.Apply(capability.CAPS); err != nil { - log.Fatalf("could not apply caps: %v", err) + clog.Fatal("could not apply caps: %v", err) } } @@ -51,34 +51,34 @@ func MinCapabilities(hardeningEnabled bool) { func DropRoot() { userInfo, err := user.Lookup(switchToUser) if err != nil { - log.Printf("User %s not found, changing to 65534", switchToUser) + clog.Error("User %s not found, changing to 65534", switchToUser) userInfo = &user.User{Gid: "65534", Uid: "65534"} } // Convert group ID and user ID from string to int. gid, err := strconv.Atoi(userInfo.Gid) if err != nil { - log.Fatal(err) + clog.Fatal(err.Error()) } uid, err := strconv.Atoi(userInfo.Uid) if err != nil { - log.Fatal(err) + clog.Fatal(err.Error()) } // Unset supplementary group IDs. err = syscall.Setgroups([]int{}) if err != nil { - log.Fatal("Failed to unset supplementary group IDs: " + err.Error()) + clog.Fatal("Failed to unset supplementary group IDs: %v", err) } // Set group ID (real and effective). err = syscall.Setgid(gid) if err != nil { - log.Fatal("Failed to set group ID: " + err.Error()) + clog.Fatal("Failed to set group ID: %v", err) } // Set user ID (real and effective). err = syscall.Setuid(uid) if err != nil { - log.Fatal("Failed to set user ID: " + err.Error()) + clog.Fatal("Failed to set user ID: %v", err) } } @@ -101,7 +101,7 @@ func LandLock(extraPath string, cacheDir string) { defaults..., ) if err != nil { - log.Fatal("Failed LandLock : " + err.Error()) + clog.Fatal("Failed LandLock : %v", err) } }