From 0e160dc20c5b69113791cdf86a4b7a2396569221 Mon Sep 17 00:00:00 2001
From: Jose Carlos Luna <Jose.Carlos.Luna@cern.ch>
Date: Thu, 16 May 2024 10:21:45 +0200
Subject: [PATCH] Improved logging & alerts

- Separate IPs and Ports in logs and alerts
- Added timestamps in alerts
- Escape URL paths in alerts
---
 .gitignore          |  3 +++
 alert/alert.go      | 25 +++++++++++++++++--------
 server/dns/dns.go   | 30 +++++++++++++++++++++++++++---
 server/http/http.go | 31 +++++++++++++++++++++++++++----
 4 files changed, 74 insertions(+), 15 deletions(-)

diff --git a/.gitignore b/.gitignore
index a3fff09..873cb04 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
 gocanary
+cert.key
+cert.pem
+
diff --git a/alert/alert.go b/alert/alert.go
index 4ba3f81..4841efb 100644
--- a/alert/alert.go
+++ b/alert/alert.go
@@ -26,12 +26,15 @@ type Canary struct {
 	Tag   string
 	Level string
 	Type  string
+	Time  string
 }
 
 type HTTPCanary struct {
 	Canary
-	RemoteAddr string
-	LocalAddr  string
+	RemoteIP   string
+	RemotePort uint16
+	LocalIP    string
+	LocalPort  uint16
 	UserAgent  string
 	FullUrl    string
 	Referer    string
@@ -39,8 +42,10 @@ type HTTPCanary struct {
 
 type DNSCanary struct {
 	Canary
-	RemoteAddr string
-	LocalAddr  string
+	RemoteIP   string
+	RemotePort uint16
+	LocalIP    string
+	LocalPort  uint16
 	Proto      string
 }
 
@@ -111,8 +116,10 @@ func HTTPAlert(canaryinfo HTTPCanary, alertType string) {
 			"Level", canaryinfo.Canary.Level,
 			"FullUrl", canaryinfo.FullUrl,
 			"UserAgent", canaryinfo.UserAgent,
-			"RemoteAddr", canaryinfo.RemoteAddr,
-			"LocalAddr", canaryinfo.LocalAddr,
+			"RemoteIP", canaryinfo.RemoteIP,
+			"RemotePort", canaryinfo.RemotePort,
+			"LocalIP", canaryinfo.LocalIP,
+			"LocalPort", canaryinfo.LocalPort,
 			"Referer", canaryinfo.Referer,
 			"Type", "token-http",
 		)
@@ -135,8 +142,10 @@ func DNSAlert(canaryinfo DNSCanary, alertType string) {
 			"Key", canaryinfo.Canary.Key,
 			"Tag", canaryinfo.Canary.Tag,
 			"Level", canaryinfo.Canary.Level,
-			"RemoteAddr", canaryinfo.RemoteAddr,
-			"LocalAddr", canaryinfo.LocalAddr,
+			"RemoteIP", canaryinfo.RemoteIP,
+			"RemotePort", canaryinfo.RemotePort,
+			"LocalIP", canaryinfo.LocalIP,
+			"LocalPort", canaryinfo.LocalPort,
 			"Type", "token-dns",
 		)
 	}
diff --git a/server/dns/dns.go b/server/dns/dns.go
index 90b511f..56186e2 100644
--- a/server/dns/dns.go
+++ b/server/dns/dns.go
@@ -4,8 +4,11 @@ import (
 	"fmt"
 	"log"
 	"log/slog"
+	"net"
 	"slices"
+	"strconv"
 	"strings"
+	"time"
 
 	"github.com/miekg/dns"
 	"gitlab.cern.ch/ComputerSecurity/gocanary/alert"
@@ -22,15 +25,31 @@ type DNSServerConfig struct {
 var serverConfig DNSServerConfig
 
 func getCanaryInfo(t tokens.CanaryRecord, w dns.ResponseWriter) alert.DNSCanary {
+
+	rIP, rPortStr, err := net.SplitHostPort(w.RemoteAddr().String())
+	if err != nil {
+		rIP = w.RemoteAddr().String()
+	}
+	rPort, _ := strconv.Atoi(rPortStr)
+
+	lIP, lPortStr, err := net.SplitHostPort(w.LocalAddr().String())
+	if err != nil {
+		lIP = w.LocalAddr().String()
+	}
+	lPort, _ := strconv.Atoi(lPortStr)
+
 	return alert.DNSCanary{
 		Canary: alert.Canary{
 			Key:   t.Key,
 			Tag:   t.Tag,
 			Level: t.Level,
 			Type:  "token-dns",
+			Time:  time.Now().Format(time.RFC3339),
 		},
-		RemoteAddr: w.RemoteAddr().String(),
-		LocalAddr:  w.LocalAddr().String(),
+		RemoteIP:   rIP,
+		RemotePort: uint16(rPort),
+		LocalIP:    lIP,
+		LocalPort:  uint16(lPort),
 		Proto:      w.RemoteAddr().Network(),
 	}
 }
@@ -52,10 +71,15 @@ func checkAndAlert(q dns.Question, w dns.ResponseWriter, r *dns.Msg) bool {
 }
 
 func logRequest(q dns.Question, w dns.ResponseWriter) {
+	rIP, rPortStr, err := net.SplitHostPort(w.RemoteAddr().String())
+	if err != nil {
+		rIP = w.RemoteAddr().String()
+	}
 	slog.Info("dns-request",
 		"Type", "dns-request",
 		"Query", q.Name,
-		"RemoteAddr", w.RemoteAddr().String(),
+		"RemoteIP", rIP,
+		"RemotePort", rPortStr,
 	)
 }
 
diff --git a/server/http/http.go b/server/http/http.go
index 3bf1a09..0c00381 100644
--- a/server/http/http.go
+++ b/server/http/http.go
@@ -9,6 +9,7 @@ import (
 	"net/http"
 	"path"
 	"slices"
+	"strconv"
 	"strings"
 	"time"
 
@@ -43,23 +44,40 @@ func getSchema(r *http.Request) string {
 
 // Returns full URL of request, including schema
 func getFullUrl(r *http.Request) string {
-	return fmt.Sprintf("%s://%s%s", getSchema(r), r.Host, r.URL.Path)
+	return fmt.Sprintf("%s://%s%s", getSchema(r), r.Host, r.URL.EscapedPath())
 }
 
 // Builds the canary alert info
 func getCanaryInfo(t tokens.CanaryRecord, r *http.Request) alert.HTTPCanary {
+
+	rIP, rPortStr, err := net.SplitHostPort(r.RemoteAddr)
+	if err != nil {
+		rIP = r.RemoteAddr
+	}
+	rPort, _ := strconv.Atoi(rPortStr)
+
+	lAddr := r.Context().Value(http.LocalAddrContextKey).(net.Addr).String()
+	lIP, lPortStr, err := net.SplitHostPort(lAddr)
+	if err != nil {
+		lIP = lAddr
+	}
+	lPort, _ := strconv.Atoi(lPortStr)
+
 	return alert.HTTPCanary{
 		Canary: alert.Canary{
 			Key:   t.Key,
 			Tag:   t.Tag,
 			Level: t.Level,
 			Type:  "token-http",
+			Time:  time.Now().Format(time.RFC3339),
 		},
 		UserAgent:  r.UserAgent(),
 		FullUrl:    getFullUrl(r),
 		Referer:    r.Header.Get("Referer"),
-		RemoteAddr: r.RemoteAddr,
-		LocalAddr:  r.Context().Value(http.LocalAddrContextKey).(net.Addr).String(),
+		RemoteIP:   rIP,
+		RemotePort: uint16(rPort),
+		LocalIP:    lIP,
+		LocalPort:  uint16(lPort),
 	}
 }
 
@@ -81,6 +99,10 @@ func getNormalizedHostDomain(rHost string) (string, string) {
 
 // Log all requests
 func logRequest(r *http.Request) {
+	rIP, rPortStr, err := net.SplitHostPort(r.RemoteAddr)
+	if err != nil {
+		rIP = r.RemoteAddr
+	}
 	slog.Info("http-request",
 		"Type", "http-request",
 		"Uri", r.RequestURI,
@@ -88,7 +110,8 @@ func logRequest(r *http.Request) {
 		"Method", r.Method,
 		"Host", r.Host,
 		"UserAgent", r.UserAgent(),
-		"RemoteAddr", r.RemoteAddr,
+		"RemoteIP", rIP,
+		"RemotePort", rPortStr,
 		"Referer", r.Header.Get("Referer"),
 	)
 }
-- 
GitLab