parent
9e702c4cdd
commit
94d023e78c
47
conf.go
47
conf.go
@ -2,23 +2,49 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
// "flag"
|
// "flag"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
"regexp"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Conf struct {
|
type Conf struct {
|
||||||
// Definitions []string
|
Streams map[string]Stream
|
||||||
Streams []struct {
|
}
|
||||||
Cmd string
|
|
||||||
Filters []struct {
|
type Stream struct {
|
||||||
|
Cmd []string
|
||||||
|
Filters map[string]*Filter
|
||||||
|
}
|
||||||
|
|
||||||
|
type Filter struct {
|
||||||
Regex []string
|
Regex []string
|
||||||
|
compiledRegex []regexp.Regexp
|
||||||
Retry uint
|
Retry uint
|
||||||
RetryPeriod string `yaml:"retry-period"`
|
RetryPeriod string `yaml:"retry-period"`
|
||||||
Actions []struct {
|
Actions map[string]*Action
|
||||||
Cmd string
|
}
|
||||||
|
|
||||||
|
type Action struct {
|
||||||
|
name, filterName, streamName string
|
||||||
|
Cmd []string
|
||||||
After string `yaml:",omitempty"`
|
After string `yaml:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Conf) setup() {
|
||||||
|
for streamName, stream := range c.Streams {
|
||||||
|
for filterName, filter := range stream.Filters {
|
||||||
|
// Compute Regexes
|
||||||
|
for _, regex := range filter.Regex {
|
||||||
|
filter.compiledRegex = append(filter.compiledRegex, *regexp.MustCompile(regex))
|
||||||
|
}
|
||||||
|
// Give all relevant infos to Actions
|
||||||
|
for actionName, action := range filter.Actions {
|
||||||
|
action.name = actionName
|
||||||
|
action.filterName = filterName
|
||||||
|
action.streamName = streamName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,13 +63,10 @@ func parseConf(filename string) *Conf {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalln("Failed to parse configuration file:", err)
|
log.Fatalln("Failed to parse configuration file:", err)
|
||||||
}
|
}
|
||||||
log.Println(conf)
|
|
||||||
|
|
||||||
yaml, err := yaml.Marshal(conf)
|
conf.setup()
|
||||||
if err != nil {
|
fmt.Printf("conf.Streams[0].Filters[0].Actions: %s\n", conf.Streams["tailDown"].Filters["lookForProuts"].Actions)
|
||||||
log.Fatalln("Failed to rewrite configuration file:", err)
|
|
||||||
}
|
|
||||||
log.Println(string(yaml))
|
|
||||||
return &conf
|
return &conf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
110
main.go
110
main.go
@ -4,37 +4,13 @@ import (
|
|||||||
"bufio"
|
"bufio"
|
||||||
"log"
|
"log"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"regexp"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Action struct {
|
func cmdStdout(commandline []string) chan string {
|
||||||
regex, cmd []string
|
lines := make(chan string)
|
||||||
}
|
|
||||||
|
|
||||||
type compiledAction struct {
|
go func() {
|
||||||
regex []regexp.Regexp
|
cmd := exec.Command(commandline[0], commandline[1:]...)
|
||||||
cmd []string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Stream struct {
|
|
||||||
cmd []string
|
|
||||||
actions []Action
|
|
||||||
}
|
|
||||||
|
|
||||||
func compileAction(action Action) compiledAction {
|
|
||||||
var ca compiledAction
|
|
||||||
ca.cmd = action.cmd
|
|
||||||
for _, regex := range action.regex {
|
|
||||||
ca.regex = append(ca.regex, *regexp.MustCompile(regex))
|
|
||||||
}
|
|
||||||
return ca
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle a log command
|
|
||||||
// Must be started in a goroutine
|
|
||||||
func streamHandle(stream Stream, execQueue chan []string) {
|
|
||||||
log.Printf("streamHandle{%v}: start\n", stream.cmd)
|
|
||||||
cmd := exec.Command(stream.cmd[0], stream.cmd[1:]...)
|
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("couldn't open stdout on command:", err)
|
log.Fatal("couldn't open stdout on command:", err)
|
||||||
@ -44,55 +20,63 @@ func streamHandle(stream Stream, execQueue chan []string) {
|
|||||||
}
|
}
|
||||||
defer stdout.Close()
|
defer stdout.Close()
|
||||||
|
|
||||||
compiledActions := make([]compiledAction, 0, len(stream.actions))
|
|
||||||
for _, action := range stream.actions {
|
|
||||||
compiledActions = append(compiledActions, compileAction(action))
|
|
||||||
}
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(stdout)
|
scanner := bufio.NewScanner(stdout)
|
||||||
for scanner.Scan() {
|
for scanner.Scan() {
|
||||||
line := scanner.Text()
|
lines <- scanner.Text()
|
||||||
for _, action := range compiledActions {
|
}
|
||||||
for _, regex := range action.regex {
|
close(lines)
|
||||||
|
}()
|
||||||
|
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Filter) match(line string) bool {
|
||||||
|
log.Printf("trying to match line {%s}...\n", line)
|
||||||
|
for _, regex := range f.compiledRegex {
|
||||||
|
log.Printf("...on %s\n", regex.String())
|
||||||
if match := regex.FindString(line); match != "" {
|
if match := regex.FindString(line); match != "" {
|
||||||
log.Printf("match `%v` in line: `%v`\n", regex.String(), line)
|
log.Printf("match `%v` in line: `%v`\n", regex.String(), line)
|
||||||
execQueue <- action.cmd
|
return true
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Filter) launch(line *string) {
|
||||||
|
for _, a := range f.Actions {
|
||||||
|
go a.launch(line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func execQueue() chan []string {
|
func (a *Action) launch(line *string) {
|
||||||
queue := make(chan []string)
|
log.Printf("INFO %s.%s.%s: line {%s} → run {%s}\n", a.streamName, a.filterName, a.name, *line, a.Cmd)
|
||||||
go func() {
|
|
||||||
for {
|
cmd := exec.Command(a.Cmd[0], a.Cmd[1:]...)
|
||||||
command := <-queue
|
|
||||||
cmd := exec.Command(command[0], command[1:]...)
|
|
||||||
if ret := cmd.Run(); ret != nil {
|
if ret := cmd.Run(); ret != nil {
|
||||||
log.Printf("Error launching `%v`: code %v\n", cmd, ret)
|
log.Printf("ERR %s.%s.%s: line {%s} → run %s, code {%s}\n", a.streamName, a.filterName, a.name, *line, a.Cmd, ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stream) handle() {
|
||||||
|
log.Printf("streamHandle{%v}: start\n", s.Cmd)
|
||||||
|
|
||||||
|
lines := cmdStdout(s.Cmd)
|
||||||
|
|
||||||
|
for line := range lines {
|
||||||
|
for _, filter := range s.Filters {
|
||||||
|
if filter.match(line) {
|
||||||
|
filter.launch(&line)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
return queue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
conf := parseConf("./reaction.yml")
|
conf := parseConf("./reaction.yml")
|
||||||
conf = conf
|
|
||||||
// mockstreams := []Stream{Stream{
|
for _, stream := range conf.Streams {
|
||||||
// []string{"tail", "-f", "/home/ao/DOWN"},
|
go stream.handle()
|
||||||
// []Action{Action{
|
}
|
||||||
// []string{"prout.dev"},
|
// Infinite wait
|
||||||
// []string{"touch", "/home/ao/DAMN"},
|
<-make(chan bool)
|
||||||
// }},
|
|
||||||
// }}
|
|
||||||
// streams := mockstreams
|
|
||||||
// log.Println(streams)
|
|
||||||
// queue := execQueue()
|
|
||||||
// for _, stream := range streams {
|
|
||||||
// go streamHandle(stream, queue)
|
|
||||||
// }
|
|
||||||
// // Infinite wait
|
|
||||||
// <-make(chan bool)
|
|
||||||
}
|
}
|
||||||
|
31
reaction.yml
31
reaction.yml
@ -7,13 +7,28 @@ definitions:
|
|||||||
# 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:
|
||||||
- cmd: journalctl -fu phpfpm-nextcloud.service
|
tailDown:
|
||||||
|
cmd: [ "tail", "-f", "/home/ao/DOWN" ]
|
||||||
filters:
|
filters:
|
||||||
- regex:
|
lookForProuts:
|
||||||
- '"message":"Login failed: .\+ (Remote IP: <ip>)"'
|
regex:
|
||||||
retry: 3
|
- prout
|
||||||
retry-period: 1h
|
retry: 1
|
||||||
|
retry-period: 1s
|
||||||
actions:
|
actions:
|
||||||
- cmd: *iptablesban
|
damn:
|
||||||
- cmd: *iptablesunban
|
cmd: [ "echo", "DAMN" ]
|
||||||
after: 1h
|
sleepdamn:
|
||||||
|
cmd: [ "echo", "sleepDAMN" ]
|
||||||
|
after: 2s
|
||||||
|
|
||||||
|
# - cmd: journalctl -fu phpfpm-nextcloud.service
|
||||||
|
# filters:
|
||||||
|
# - regex:
|
||||||
|
# - '"message":"Login failed: .\+ (Remote IP: <ip>)"'
|
||||||
|
# retry: 3
|
||||||
|
# retry-period: 1h
|
||||||
|
# actions:
|
||||||
|
# - cmd: *iptablesban
|
||||||
|
# - cmd: *iptablesunban
|
||||||
|
# after: 1h
|
||||||
|
Loading…
Reference in New Issue
Block a user