Close #3
This commit is contained in:
parent
88f3f86c7c
commit
75e2f95ff9
72
conf.go
72
conf.go
@ -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
|
||||||
Cmd []string
|
name string
|
||||||
After string `yaml:",omitempty"`
|
|
||||||
afterDuration time.Duration // Computed after YAML parsing
|
Cmd []string
|
||||||
|
|
||||||
|
After string `yaml:",omitempty"`
|
||||||
|
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
47
main.go
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
16
reaction.yml
16
reaction.yml
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user