Close #3
This commit is contained in:
		
							
								
								
									
										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
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user