This commit is contained in:
ppom 2023-03-24 17:36:41 +01:00
parent 88f3f86c7c
commit 75e2f95ff9
3 changed files with 95 additions and 40 deletions

66
conf.go
View File

@ -6,39 +6,63 @@ import (
"log" "log"
"os" "os"
"regexp" "regexp"
"strings"
"time" "time"
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
type Conf struct { type Conf struct {
Streams map[string]Stream Patterns map[string]string
Streams map[string]*Stream
} }
type Stream struct { type Stream struct {
name string
Cmd []string Cmd []string
Filters map[string]*Filter Filters map[string]*Filter
} }
type Filter struct { type Filter struct {
stream *Stream
name string
Regex []string Regex []string
compiledRegex []regexp.Regexp // Computed after YAML parsing compiledRegex []regexp.Regexp
patternName string
Retry uint Retry uint
RetryPeriod string `yaml:"retry-period"` RetryPeriod string `yaml:"retry-period"`
retryDuration time.Duration // Computed after YAML parsing retryDuration time.Duration
Actions map[string]*Action Actions map[string]*Action
} }
type Action struct { type Action struct {
name, filterName, streamName string // Computed after YAML parsing filter *Filter
name string
Cmd []string Cmd []string
After string `yaml:",omitempty"` After string `yaml:",omitempty"`
afterDuration time.Duration // Computed after YAML parsing afterDuration time.Duration
} }
func (c *Conf) setup() { func (c *Conf) setup() {
for streamName, stream := range c.Streams { for patternName, pattern := range c.Patterns {
for filterName, filter := range stream.Filters { c.Patterns[patternName] = fmt.Sprintf("(?P<%s>%s)", patternName, pattern)
}
for streamName := range c.Streams {
stream := c.Streams[streamName]
stream.name = streamName
for filterName := range stream.Filters {
filter := stream.Filters[filterName]
filter.stream = stream
filter.name = filterName
// Parse Duration // Parse Duration
retryDuration, err := time.ParseDuration(filter.RetryPeriod) retryDuration, err := time.ParseDuration(filter.RetryPeriod)
@ -48,16 +72,35 @@ func (c *Conf) setup() {
filter.retryDuration = retryDuration filter.retryDuration = retryDuration
// Compute Regexes // Compute Regexes
// Look for Patterns inside Regexes
for _, regex := range filter.Regex { for _, regex := range filter.Regex {
for patternName, pattern := range c.Patterns {
if strings.Contains(regex, patternName) {
switch filter.patternName {
case "":
filter.patternName = patternName
case patternName:
// no op
filter.patternName = patternName
default:
log.Fatalf(
"ERROR Can't mix different patterns (%s, %s) in same filter (%s.%s)\n",
filter.patternName, patternName, streamName, filterName,
)
}
regex = strings.Replace(regex, fmt.Sprintf("<%s>", patternName), pattern, 1)
}
}
filter.compiledRegex = append(filter.compiledRegex, *regexp.MustCompile(regex)) filter.compiledRegex = append(filter.compiledRegex, *regexp.MustCompile(regex))
} }
for actionName, action := range filter.Actions { for actionName := range filter.Actions {
// Give all relevant infos to Actions action := filter.Actions[actionName]
action.filter = filter
action.name = actionName action.name = actionName
action.filterName = filterName
action.streamName = streamName
// Parse Duration // Parse Duration
if action.After != "" { if action.After != "" {
@ -87,7 +130,6 @@ func parseConf(filename string) *Conf {
} }
conf.setup() conf.setup()
fmt.Printf("conf.Streams[0].Filters[0].Actions: %s\n", conf.Streams["tailDown"].Filters["lookForProuts"].Actions)
return &conf return &conf
} }

47
main.go
View File

@ -2,11 +2,14 @@ package main
import ( import (
"bufio" "bufio"
"fmt"
"log" "log"
"time"
"os/exec" "os/exec"
"strings"
"time"
) )
// Executes a command and channel-send its stdout
func cmdStdout(commandline []string) chan string { func cmdStdout(commandline []string) chan string {
lines := make(chan string) lines := make(chan string)
@ -31,45 +34,55 @@ func cmdStdout(commandline []string) chan string {
return lines return lines
} }
func (f *Filter) match(line string) bool { // Whether one of the filter's regexes is matched on a line
log.Printf("trying to match line {%s}...\n", line) func (f *Filter) match(line string) string {
for _, regex := range f.compiledRegex { for _, regex := range f.compiledRegex {
log.Printf("...on %s\n", regex.String())
if match := regex.FindString(line); match != "" { if matches := regex.FindStringSubmatch(line); matches != nil {
log.Printf("match `%v` in line: `%v`\n", regex.String(), line)
return true match := matches[regex.SubexpIndex(f.patternName)]
log.Printf("INFO %s.%s: match `%v`\n", f.stream.name, f.name, match)
return match
} }
} }
return false return ""
} }
func (f *Filter) launch(line *string) { func (f *Filter) execActions(match string) {
pattern := fmt.Sprintf("<%s>", f.patternName)
for _, a := range f.Actions { for _, a := range f.Actions {
go a.launch(line) go a.exec(match, pattern)
} }
} }
func (a *Action) launch(line *string) { func (a *Action) exec(match, pattern string) {
if a.afterDuration != 0 { if a.afterDuration != 0 {
time.Sleep(a.afterDuration) time.Sleep(a.afterDuration)
} }
log.Printf("INFO %s.%s.%s: line {%s} → run %s\n", a.streamName, a.filterName, a.name, *line, a.Cmd)
cmd := exec.Command(a.Cmd[0], a.Cmd[1:]...) computedCommand := make([]string, 0, len(a.Cmd))
for _, item := range a.Cmd {
computedCommand = append(computedCommand, strings.ReplaceAll(item, pattern, match))
}
log.Printf("INFO %s.%s.%s: run %s\n", a.filter.stream.name, a.filter.name, a.name, computedCommand)
cmd := exec.Command(computedCommand[0], computedCommand[1:]...)
if ret := cmd.Run(); ret != nil { if ret := cmd.Run(); ret != nil {
log.Printf("ERR %s.%s.%s: line {%s} → run %s, code {%s}\n", a.streamName, a.filterName, a.name, *line, a.Cmd, ret) log.Printf("ERR %s.%s.%s: run %s, code %s\n", a.filter.stream.name, a.filter.name, a.name, computedCommand, ret)
} }
} }
func (s *Stream) handle() { func (s *Stream) handle() {
log.Printf("streamHandle{%v}: start\n", s.Cmd) log.Printf("INFO %s: start %s\n", s.name, s.Cmd)
lines := cmdStdout(s.Cmd) lines := cmdStdout(s.Cmd)
for line := range lines { for line := range lines {
for _, filter := range s.Filters { for _, filter := range s.Filters {
if filter.match(line) { if match := filter.match(line); match != "" {
filter.launch(&line) filter.execActions(match)
} }
} }
} }

View File

@ -3,21 +3,21 @@ definitions:
- &iptablesban iptables -I reaction 1 -s <ip> -j block - &iptablesban iptables -I reaction 1 -s <ip> -j block
- &iptablesunban iptables -D reaction 1 -s <ip> -j block - &iptablesunban iptables -D reaction 1 -s <ip> -j block
# regexes: patterns:
# ip: '(([0-9]{1,3}\.){3}[0-9]{1,3})|([0-9a-fA-F:]{2,90})' ip: '(([0-9]{1,3}\.){3}[0-9]{1,3})|([0-9a-fA-F:]{2,90})'
streams: streams:
tailDown: tailDown:
cmd: [ "tail", "-f", "/home/ao/DOWN" ] cmd: [ "tail", "-f", "/home/ao/DOWN" ]
filters: filters:
lookForProuts: findIP:
regex: regex:
- prout - found <ip>
retry: 1 # retry: 1
retry-period: 1s retry-period: 1s
actions: actions:
damn: damn:
cmd: [ "echo", "DAMN" ] cmd: [ "echo", "<ip>" ]
sleepdamn: sleepdamn:
cmd: [ "echo", "sleepDAMN" ] cmd: [ "echo", "sleep", "<ip>" ]
after: 2s after: 1s