modularize telegram bot and log watcher
This commit is contained in:
parent
d57c6ea6aa
commit
5dcb467842
98
server/internal/log_watcher/log_watcher.go
Normal file
98
server/internal/log_watcher/log_watcher.go
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
package log_watcher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/Vomitblood/cspj-application/server/internal/telegram"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
tg "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// TODO: add config for user to specify log location
|
||||||
|
modsecLogFile = "/home/vomitblood/build/cspj-application/docker/chungus/logs/host-fs-auditlog.log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var lastReadPosition int64 = 0
|
||||||
|
|
||||||
|
type LogEntry struct {
|
||||||
|
AuditData struct {
|
||||||
|
Messages []string `json:"messages"`
|
||||||
|
} `json:"audit_data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func watchLogFile(bot *tg.BotAPI) {
|
||||||
|
watcher, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to initialize watcher:", err)
|
||||||
|
}
|
||||||
|
defer watcher.Close()
|
||||||
|
|
||||||
|
// add log file to watcher
|
||||||
|
err = watcher.Add(modsecLogFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Failed to watch log file:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Monitoring log file for changes...")
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case event, ok := <-watcher.Events:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if event.Op&fsnotify.Write == fsnotify.Write {
|
||||||
|
log.Println("Log file updated, reading new entries...")
|
||||||
|
readNewLines(bot)
|
||||||
|
}
|
||||||
|
|
||||||
|
case err, ok := <-watcher.Errors:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Watcher error:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readNewLines(bot *tg.BotAPI) {
|
||||||
|
file, err := os.Open(modsecLogFile)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to reopen log file:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// move to the last read position
|
||||||
|
file.Seek(lastReadPosition, os.SEEK_SET)
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
var logEntry LogEntry
|
||||||
|
|
||||||
|
// try to parse json
|
||||||
|
if err := json.Unmarshal([]byte(line), &logEntry); err != nil {
|
||||||
|
log.Println("⚠️ Failed to parse JSON:", err)
|
||||||
|
// skip invalid json lines
|
||||||
|
// very crucial as modsecurity does not respect the json spec
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// send index 0 element will do
|
||||||
|
if len(logEntry.AuditData.Messages) > 0 {
|
||||||
|
telegram.SendTelegramAlert(bot, logEntry.AuditData.Messages[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// update last read position
|
||||||
|
lastReadPosition, _ = file.Seek(0, os.SEEK_CUR)
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
log.Println("Error reading log file:", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,32 +1,24 @@
|
||||||
package telegram
|
package telegram
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
|
||||||
tg "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
tg "github.com/go-telegram-bot-api/telegram-bot-api/v5"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// TODO: add config for user to specify log location
|
|
||||||
modsecLogFile = "/home/vomitblood/build/cspj-application/docker/chungus/logs/host-fs-auditlog.log"
|
|
||||||
telegramToken = "7215466800:AAGwjZnXEfbbjQiA0y7qtSzbSZNUWQJnyjo"
|
telegramToken = "7215466800:AAGwjZnXEfbbjQiA0y7qtSzbSZNUWQJnyjo"
|
||||||
telegramChatID = 622943829
|
telegramChatID = 622943829
|
||||||
)
|
)
|
||||||
|
|
||||||
var lastReadPosition int64 = 0
|
|
||||||
|
|
||||||
type LogEntry struct {
|
type LogEntry struct {
|
||||||
AuditData struct {
|
AuditData struct {
|
||||||
Messages []string `json:"messages"`
|
Messages []string `json:"messages"`
|
||||||
} `json:"audit_data"`
|
} `json:"audit_data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func TelegramBotInit() {
|
func TelegramBotInit() (*tg.BotAPI, error) {
|
||||||
bot, err := tg.NewBotAPI(telegramToken)
|
bot, err := tg.NewBotAPI(telegramToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Failed to create Telegram bot:", err)
|
log.Fatal("Failed to create Telegram bot:", err)
|
||||||
|
@ -41,84 +33,10 @@ func TelegramBotInit() {
|
||||||
log.Fatal("Failed to send test message:", err)
|
log.Fatal("Failed to send test message:", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// start watching the log file for changes
|
return bot, nil
|
||||||
watchLogFile(bot)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func watchLogFile(bot *tg.BotAPI) {
|
func SendTelegramAlert(bot *tg.BotAPI, message string) {
|
||||||
watcher, err := fsnotify.NewWatcher()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to initialize watcher:", err)
|
|
||||||
}
|
|
||||||
defer watcher.Close()
|
|
||||||
|
|
||||||
// add log file to watcher
|
|
||||||
err = watcher.Add(modsecLogFile)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal("Failed to watch log file:", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println("Monitoring log file for changes...")
|
|
||||||
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case event, ok := <-watcher.Events:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if event.Op&fsnotify.Write == fsnotify.Write {
|
|
||||||
log.Println("Log file updated, reading new entries...")
|
|
||||||
readNewLines(bot)
|
|
||||||
}
|
|
||||||
|
|
||||||
case err, ok := <-watcher.Errors:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Println("Watcher error:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func readNewLines(bot *tg.BotAPI) {
|
|
||||||
file, err := os.Open(modsecLogFile)
|
|
||||||
if err != nil {
|
|
||||||
log.Println("Failed to reopen log file:", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
// move to the last read position
|
|
||||||
file.Seek(lastReadPosition, os.SEEK_SET)
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
|
|
||||||
for scanner.Scan() {
|
|
||||||
line := scanner.Text()
|
|
||||||
var logEntry LogEntry
|
|
||||||
|
|
||||||
// try to parse json
|
|
||||||
if err := json.Unmarshal([]byte(line), &logEntry); err != nil {
|
|
||||||
log.Println("⚠️ Failed to parse JSON:", err)
|
|
||||||
// skip invalid json lines
|
|
||||||
// very crucial as modsecurity does not respect the json spec
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// send index 0 element will do
|
|
||||||
if len(logEntry.AuditData.Messages) > 0 {
|
|
||||||
sendTelegramAlert(bot, logEntry.AuditData.Messages[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update last read position
|
|
||||||
lastReadPosition, _ = file.Seek(0, os.SEEK_CUR)
|
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
log.Println("Error reading log file:", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendTelegramAlert(bot *tg.BotAPI, message string) {
|
|
||||||
msg := tg.NewMessage(telegramChatID, fmt.Sprintf("🚨 *WEEWOO ALERT* 🚨\n%s", message))
|
msg := tg.NewMessage(telegramChatID, fmt.Sprintf("🚨 *WEEWOO ALERT* 🚨\n%s", message))
|
||||||
_, err := bot.Send(msg)
|
_, err := bot.Send(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
Loading…
Reference in a new issue