diff --git a/.gitignore b/.gitignore index 873cb0404716cea333464ee83a1dd03dee24c0af..602d42cce055f0d21b1c59da309c6d43f1e6b755 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ gocanary cert.key cert.pem - +.DS_Store diff --git a/alert/alert.go b/alert/alert.go index 4841efb815496b7a9de5d9550f91a56df2523aed..754added8d31398be0b5250c238bda9aa5854ce8 100644 --- a/alert/alert.go +++ b/alert/alert.go @@ -8,12 +8,26 @@ import ( "log/slog" "log/syslog" "net/http" + "reflect" + "strings" "time" ) +type Field struct { + Title string `json:"title"` + Value string `json:"value"` + Short bool `json:"short"` +} + +type Attachment struct { + Color string `json:"color"` + Fields []Field `json:"fields"` +} + // Payload struct to hold the JSON structure for our message type Payload struct { - Text string `json:"text"` + Text string `json:"text"` + Attachments []Attachment `json:"attachments,omitempty"` } type Config struct { @@ -31,11 +45,11 @@ type Canary struct { type HTTPCanary struct { Canary + UserAgent string RemoteIP string RemotePort uint16 LocalIP string LocalPort uint16 - UserAgent string FullUrl string Referer string } @@ -67,15 +81,14 @@ func Initialize(slackhook string, silenceSeconds uint16, doSyslog bool) { } // Send a slack message -func PostSlackHook(message string) { +func PostSlackHook(message string, attachments []Attachment) { if time.Since(lastNotifTime).Seconds() < float64(config.silenceSeconds) { lastNotifTime = time.Now() return } lastNotifTime = time.Now() - toSend := fmt.Sprintf("TokenAlert `%s`", message) - payload := Payload{Text: toSend} + payload := Payload{Text: message, Attachments: attachments} // Marshal the payload into JSON jsonData, err := json.Marshal(payload) @@ -107,6 +120,64 @@ func PostSlackHook(message string) { log.Printf("Slack Response StatusCode: %d", resp.StatusCode) } +// Format the canary alert message +func formatSlackMessage(canary interface{}, remoteIP string) (string, []Attachment) { + v := reflect.ValueOf(canary) + t := v.Type() + + // Choose color depending on the alert level + var color string + switch v.FieldByName("Level").Interface() { + case "low": + color = "#0000ff" + case "medium": + color = "#ffcc00" + case "high": + color = "#ff0000" + default: + color = "#000000" + } + + var message strings.Builder + + message.WriteString("**Canary Alert**") + + var fields []Field + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + typeField := t.Field(i) + if typeField.Name == "Canary" { + for j := 0; j < field.NumField(); j++ { + subField := field.Field(j) + subTypeField := field.Type().Field(j) + fields = append(fields, Field{Title: subTypeField.Name, Value: fmt.Sprintf("%v", subField.Interface()), Short: true}) + } + } else { + fields = append(fields, Field{Title: typeField.Name, Value: fmt.Sprintf("%v", field.Interface()), Short: true}) + } + } + + // Append the ipinfo URL + fields = append(fields, Field{Title: "CheckRemoteIPInfo", Value: fmt.Sprintf("http://ipinfo.io/%s", remoteIP)}) + + attachments := []Attachment{ + { + Color: color, + Fields: fields, + }, + } + + return message.String(), attachments +} + +func (h HTTPCanary) FormatSlackMessage() (string, []Attachment) { + return formatSlackMessage(h, h.RemoteIP) +} + +func (d DNSCanary) FormatSlackMessage() (string, []Attachment) { + return formatSlackMessage(d, d.RemoteIP) +} + // Handler for HTTP canary alerts func HTTPAlert(canaryinfo HTTPCanary, alertType string) { if alertType == "log" || alertType == "all" { @@ -130,8 +201,8 @@ func HTTPAlert(canaryinfo HTTPCanary, alertType string) { } if alertType == "slack" || alertType == "all" { - //Slack alert if configured - PostSlackHook(fmt.Sprintf("``` %+v ```", canaryinfo)) + // Slack alert if configured + PostSlackHook(canaryinfo.FormatSlackMessage()) } } @@ -155,8 +226,8 @@ func DNSAlert(canaryinfo DNSCanary, alertType string) { } if alertType == "slack" || alertType == "all" { - //Slack alert if configured - PostSlackHook(fmt.Sprintf("``` %+v ```", canaryinfo)) + // Slack alert if configured + PostSlackHook(canaryinfo.FormatSlackMessage()) } } diff --git a/canary.yaml b/canary.yaml index 544f303866688d6adb9edd730c5bbf524bc471b3..05557cec7db7da1889846a7cb4556d824ca53b54 100644 --- a/canary.yaml +++ b/canary.yaml @@ -10,7 +10,8 @@ - key: 'mycanary2' tag: 'gitlab-canary2' description: 'test2' - level: 'low' + alert: 'all' + level: 'high' - key: 'mycanary3' tag: 'gitlab-canary3' description: 'test3 asdf lkasj kla sjlk j' diff --git a/docker-compose-with-build.yml b/docker-compose-with-build.yml index cff5bb31a5f4ccf074b886401d4a7c5297889731..8187828daf5297f740a8fcc40916e84c2fbbdbcf 100644 --- a/docker-compose-with-build.yml +++ b/docker-compose-with-build.yml @@ -11,6 +11,10 @@ services: - ./config.yaml:/canary/config.yaml - ./cert.pem:/canary/cert.pem - ./cert.key:/canary/cert.key + #Map known certificates + - /etc/ssl:/etc/ssl + #Or for redhat derivatives: + - /etc/pki:/etc/pki container_name: gocanary restart: always