diff --git a/README.md b/README.md index d68f16449e0de361d35f5b69d74cea702861a272..9fb9694188da3384d742f7c41f5d7e93fcd85838 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,11 @@ Gocanary is a single binary that accepts command line options. Gocanary also rea It is intended to be run as `root` (to be able to bind to default DNS and WEB ports) and will drop privileges to `nobody` and sandbox file access using landlock (https://docs.kernel.org/userspace-api/landlock.html) Alternatively can be run as nonroot and changing the different ports, and then using iptables/nftables to redirect traffic to it. +For generating a self-signed certificate: +```bash +openssl req -new -newkey rsa:4096 -days 3650 -nodes -keyout cert.key -out cert.pem -x509 -subj "/C=CH/ST=GE/L=Geneva/O=CERN/CN=localhost.cern.ch" +``` + ``` diff --git a/alert/alert.go b/alert/alert.go index f7ce609579fbd540e8a73eed8064c2f7d21797f5..caae3d90534d718fb0771f578bf512b8c624faba 100644 --- a/alert/alert.go +++ b/alert/alert.go @@ -56,8 +56,8 @@ func Initialize(slackhook string, silenceSeconds uint16) { syslogger = sysl } +// Send a slack message func PostSlackHook(message string) { - if time.Since(lastNotifTime).Seconds() < float64(config.silenceSeconds) { lastNotifTime = time.Now() return @@ -97,6 +97,7 @@ func PostSlackHook(message string) { log.Printf("Slack Response StatusCode: %d", resp.StatusCode) } +// Handler for HTTP canary alerts func HTTPAlert(canaryinfo HTTPCanary, alertType string) { if alertType == "log" || alertType == "all" { slog.Info("token-alert", @@ -123,6 +124,7 @@ func HTTPAlert(canaryinfo HTTPCanary, alertType string) { } } +// Handler for DNS canary alerts func DNSAlert(canaryinfo DNSCanary, alertType string) { if alertType == "log" || alertType == "all" { slog.Info("token-alert", diff --git a/main.go b/main.go index 5a90d0ad3ece9bcc73a792cf1263003dfe16fffb..09e075d2bedf0c6d544f56688ccd25f44f988d04 100644 --- a/main.go +++ b/main.go @@ -55,6 +55,7 @@ func runCanary(cmd *cobra.Command, args []string) { //Drop to nobody and LandLock harden.DropPrivs() + slog.Debug("LandLocking") harden.LandLock(binPath) // Block forever if any server is running diff --git a/server/dns/dns.go b/server/dns/dns.go index 3c8a0f3db854d38cc6ecd3a927007b16fc870aa7..2de3be7f75de99a48a97ac5b4a8bd8442518e20b 100644 --- a/server/dns/dns.go +++ b/server/dns/dns.go @@ -49,6 +49,7 @@ func checkAndAlert(q dns.Question, w dns.ResponseWriter, r *dns.Msg) { // Builds the A record, can be NXDOMAIN or a fixed answer from config func buildAnswer(q dns.Question, m *dns.Msg) { + log.Printf("hello3 ") //Fixed answer if serverConfig.AnswerWith != "" { rr, err := dns.NewRR(fmt.Sprintf("%s A %s", q.Name, serverConfig.AnswerWith)) @@ -66,6 +67,7 @@ func buildAnswer(q dns.Question, m *dns.Msg) { } } +// Handle all DNS queries func handleDNS(w dns.ResponseWriter, r *dns.Msg) { m := new(dns.Msg) m.SetReply(r) @@ -87,7 +89,7 @@ func handleDNS(w dns.ResponseWriter, r *dns.Msg) { } } -// bind and port num +// DNS server proto udp func StartUDPListener(bindAddr string) { server := &dns.Server{Addr: bindAddr, Net: "udp"} log.Printf("Starting UDP DNS server on %s", bindAddr) @@ -98,6 +100,7 @@ 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) @@ -108,7 +111,7 @@ func StartTCPListener(bindAddr string) { }() } -// bind ip and port num +// Start the two types of DNS server func Start(runConfig DNSServerConfig) { serverConfig = runConfig dns.HandleFunc(".", handleDNS) diff --git a/server/http/http.go b/server/http/http.go index 202e27dd6ebcc4918e15a26c51b83f69f869250d..456202f4716cce104c9e74f4e27aa101c27a85bf 100644 --- a/server/http/http.go +++ b/server/http/http.go @@ -132,7 +132,7 @@ func StartHTTPS(config HTTPSServerConfig) { // Start HTTPS server go func() { - + log.Printf("Starting HTTPS server on %s", config.BindAddr) if err := http.ListenAndServeTLS(config.BindAddr, config.CertFile, config.CertKeyFile, mux); err != nil { log.Fatalf("HTTPS server failed: %s", err) } diff --git a/server/utils.go b/server/utils.go index f9ad78e72b6f80d27e067b53c2f8d16fe99ad4a0..db2550c8f52140a7b93da416c717a82021b6060b 100644 --- a/server/utils.go +++ b/server/utils.go @@ -8,6 +8,7 @@ import ( "net" ) +// Returns the public IP by creating a socket to a well known ip func getOutboundIPv4() net.IP { conn, err := net.Dial("udp", "8.8.8.8:80") if err != nil { @@ -20,6 +21,7 @@ func getOutboundIPv4() net.IP { return localAddr.IP } +// Returns a bind string based on the public IP and the port given as a parameter func GetBindAddr(bindAddr string, port uint16) string { if bindAddr != "" { return fmt.Sprintf("%s:%d", bindAddr, port) diff --git a/utils/harden/harden.go b/utils/harden/harden.go index c211024ddd2fa6fd414a1c48708713f2c47a4786..89f55032288844ee32d6fcb74b8e96971a551f93 100644 --- a/utils/harden/harden.go +++ b/utils/harden/harden.go @@ -12,6 +12,7 @@ import ( var switchToUser = "nobody" +// Show current capabilities of the running process func ShowCaps() { caps, err := capability.NewPid(0) if err != nil { @@ -25,6 +26,7 @@ func ShowCaps() { } +// As soon as possible drop root capabilities only to be able to bind and change user/group func MinCapabilities() { // Keep the ability to bind to lower ports and drop privs // NewPid(0) means current process @@ -44,6 +46,7 @@ func MinCapabilities() { log.Printf("now: %+v", caps) } +// Drop to another user/group and clean supplementary groups func DropRoot() { userInfo, err := user.Lookup(switchToUser) if err != nil { @@ -77,6 +80,7 @@ func DropRoot() { } } +// Locks this process to be able to read/write on certain file/paths func LandLock(extraPath string) { err := landlock.V4.BestEffort().RestrictPaths( landlock.RODirs("/etc"), @@ -90,6 +94,7 @@ func LandLock(extraPath string) { } } +// If root drop privs to another user func DropPrivs() { if syscall.Geteuid() == 0 { DropRoot()