dbb420f79e
Co-authored-by: AlteredCoder Co-authored-by: erenJag
238 lines
7 KiB
Go
238 lines
7 KiB
Go
package acquisition
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/types"
|
|
log "github.com/sirupsen/logrus"
|
|
"github.com/stretchr/testify/assert"
|
|
tomb "gopkg.in/tomb.v2"
|
|
)
|
|
|
|
/*
|
|
As we can't decently run journalctl in the CI but we still need to test the command execution aspect :
|
|
- we create tests 'output only' (cf. TestSimJournalctlCat) that just produce outputs
|
|
- we run ourselves (os.Args[0]) with specific args to call specific 'output only' tests
|
|
- and this is how we test the behavior
|
|
*/
|
|
|
|
//14 lines of sshd logs
|
|
var testjournalctl_output_1 string = `-- Logs begin at Fri 2019-07-26 17:13:13 CEST, end at Mon 2020-11-23 09:17:34 CET. --
|
|
Nov 22 11:22:19 zeroed sshd[1480]: Invalid user wqeqwe from 127.0.0.1 port 55818
|
|
Nov 22 11:22:23 zeroed sshd[1480]: Failed password for invalid user wqeqwe from 127.0.0.1 port 55818 ssh2
|
|
Nov 22 11:23:22 zeroed sshd[1769]: Invalid user wqeqwe1 from 127.0.0.1 port 55824
|
|
Nov 22 11:23:24 zeroed sshd[1769]: Disconnecting invalid user wqeqwe1 127.0.0.1 port 55824: Too many authentication failures [preauth]
|
|
Nov 22 11:23:24 zeroed sshd[1777]: Invalid user wqeqwe2 from 127.0.0.1 port 55826
|
|
Nov 22 11:23:25 zeroed sshd[1777]: Disconnecting invalid user wqeqwe2 127.0.0.1 port 55826: Too many authentication failures [preauth]
|
|
Nov 22 11:23:25 zeroed sshd[1780]: Invalid user wqeqwe3 from 127.0.0.1 port 55828
|
|
Nov 22 11:23:26 zeroed sshd[1780]: Disconnecting invalid user wqeqwe3 127.0.0.1 port 55828: Too many authentication failures [preauth]
|
|
Nov 22 11:23:26 zeroed sshd[1786]: Invalid user wqeqwe4 from 127.0.0.1 port 55830
|
|
Nov 22 11:23:27 zeroed sshd[1786]: Failed password for invalid user wqeqwe4 from 127.0.0.1 port 55830 ssh2
|
|
Nov 22 11:23:27 zeroed sshd[1786]: Disconnecting invalid user wqeqwe4 127.0.0.1 port 55830: Too many authentication failures [preauth]
|
|
Nov 22 11:23:27 zeroed sshd[1791]: Invalid user wqeqwe5 from 127.0.0.1 port 55834
|
|
Nov 22 11:23:27 zeroed sshd[1791]: Failed password for invalid user wqeqwe5 from 127.0.0.1 port 55834 ssh2
|
|
`
|
|
|
|
func TestSimJournalctlCat(t *testing.T) {
|
|
if os.Getenv("GO_WANT_TEST_OUTPUT") != "1" {
|
|
return
|
|
}
|
|
defer os.Exit(0)
|
|
fmt.Print(testjournalctl_output_1)
|
|
}
|
|
|
|
func TestSimJournalctlCatError(t *testing.T) {
|
|
if os.Getenv("GO_WANT_TEST_OUTPUT") != "1" {
|
|
return
|
|
}
|
|
defer os.Exit(0)
|
|
fmt.Print("this is a single line being produced")
|
|
log.Warningf("this is an error message")
|
|
}
|
|
|
|
func TestSimJournalctlCatOneLine(t *testing.T) {
|
|
if os.Getenv("GO_WANT_TEST_OUTPUT") != "1" {
|
|
return
|
|
}
|
|
defer os.Exit(0)
|
|
fmt.Print("this is a single line being produced")
|
|
}
|
|
|
|
func TestJournaldTail(t *testing.T) {
|
|
tests := []struct {
|
|
cfg DataSourceCfg
|
|
config_error string
|
|
read_error string
|
|
tomb_error string
|
|
lines int
|
|
}{
|
|
{ //missing filename(s)
|
|
cfg: DataSourceCfg{
|
|
Mode: TAIL_MODE,
|
|
},
|
|
config_error: "journalctl_filter shouldn't be empty",
|
|
},
|
|
{ //bad mode
|
|
cfg: DataSourceCfg{
|
|
Mode: "ratatata",
|
|
JournalctlFilters: []string{"-test.run=DoesNotExist", "--"},
|
|
},
|
|
/*here would actually be the journalctl error message on bad args, but you get the point*/
|
|
config_error: "unknown mode 'ratatata' for journald source",
|
|
},
|
|
{ //wrong arguments
|
|
cfg: DataSourceCfg{
|
|
Mode: TAIL_MODE,
|
|
JournalctlFilters: []string{"--this-is-bad-option", "--"},
|
|
},
|
|
/*here would actually be the journalctl error message on bad args, but you get the point*/
|
|
tomb_error: "flag provided but not defined: -this-is-bad-option",
|
|
},
|
|
}
|
|
|
|
//we're actually using tests to do this, hold my beer and watch this
|
|
JOURNALD_CMD = os.Args[0]
|
|
JOURNALD_DEFAULT_TAIL_ARGS = []string{}
|
|
|
|
for tidx, test := range tests {
|
|
journalSrc := new(JournaldSource)
|
|
err := journalSrc.Configure(test.cfg)
|
|
if test.config_error != "" {
|
|
assert.Contains(t, fmt.Sprintf("%s", err), test.config_error)
|
|
log.Infof("expected config error ok : %s", test.config_error)
|
|
continue
|
|
} else {
|
|
if err != nil {
|
|
t.Fatalf("%d/%d unexpected config error %s", tidx, len(tests), err)
|
|
}
|
|
}
|
|
|
|
assert.Equal(t, journalSrc.Mode(), test.cfg.Mode)
|
|
|
|
//this tells our fake tests to produce data
|
|
journalSrc.Cmd.Env = []string{"GO_WANT_TEST_OUTPUT=1"}
|
|
|
|
out := make(chan types.Event)
|
|
tomb := tomb.Tomb{}
|
|
count := 0
|
|
|
|
//start consuming the data before we start the prog, so that chan isn't full
|
|
go func() {
|
|
for {
|
|
select {
|
|
case <-out:
|
|
count++
|
|
case <-time.After(1 * time.Second):
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
|
|
err = journalSrc.StartReading(out, &tomb)
|
|
if test.read_error != "" {
|
|
assert.Contains(t, fmt.Sprintf("%s", err), test.read_error)
|
|
log.Infof("expected read error ok : %s", test.read_error)
|
|
continue
|
|
} else {
|
|
if err != nil {
|
|
t.Fatalf("%d/%d unexpected read error %s", tidx, len(tests), err)
|
|
}
|
|
}
|
|
|
|
time.Sleep(2 * time.Second)
|
|
log.Printf("now let's check number of lines & errors")
|
|
if count != test.lines {
|
|
t.Fatalf("%d/%d expected %d line read, got %d", tidx, len(tests), test.lines, count)
|
|
}
|
|
|
|
if test.tomb_error != "" {
|
|
assert.Contains(t, fmt.Sprintf("%s", tomb.Err()), test.tomb_error)
|
|
log.Infof("expected tomb error ok : %s", test.read_error)
|
|
continue
|
|
} else {
|
|
if tomb.Err() != nil {
|
|
t.Fatalf("%d/%d unexpected tomb error %s", tidx, len(tests), tomb.Err())
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
func TestJournaldSimple(t *testing.T) {
|
|
JOURNALD_CMD = os.Args[0]
|
|
JOURNALD_DEFAULT_TAIL_ARGS = []string{}
|
|
jBaseCfg := DataSourceCfg{
|
|
JournalctlFilters: []string{"-test.run=TestSimJournalctlCat", "--"},
|
|
Mode: CAT_MODE,
|
|
}
|
|
|
|
journalSrc := new(JournaldSource)
|
|
err := journalSrc.Configure(jBaseCfg)
|
|
if err != nil {
|
|
t.Fatalf("configuring journalctl : %s", err)
|
|
}
|
|
journalSrc.Cmd.Env = []string{"GO_WANT_TEST_OUTPUT=1"}
|
|
|
|
out := make(chan types.Event)
|
|
tomb := tomb.Tomb{}
|
|
count := 0
|
|
|
|
//start the reading : it doesn't give hand back before it's done
|
|
err = journalSrc.StartReading(out, &tomb)
|
|
if err != nil {
|
|
t.Fatalf("unexpected read error %s", err)
|
|
}
|
|
|
|
RLOOP:
|
|
for {
|
|
select {
|
|
case <-out:
|
|
count++
|
|
case <-time.After(1 * time.Second):
|
|
break RLOOP
|
|
}
|
|
}
|
|
//we expect 14 lines to be read
|
|
assert.Equal(t, 14, count)
|
|
|
|
}
|
|
|
|
func TestJournalctlKill(t *testing.T) {
|
|
cfg := DataSourceCfg{
|
|
Mode: CAT_MODE,
|
|
JournalctlFilters: []string{"-test.run=TestSimJournalctlCatOneLine", "--"},
|
|
}
|
|
//we're actually using tests to do this, hold my beer and watch this
|
|
JOURNALD_CMD = os.Args[0]
|
|
JOURNALD_DEFAULT_TAIL_ARGS = []string{}
|
|
|
|
log.SetLevel(log.TraceLevel)
|
|
journalSrc := new(JournaldSource)
|
|
err := journalSrc.Configure(cfg)
|
|
if err != nil {
|
|
t.Fatalf("unexpected config error %s", err)
|
|
}
|
|
journalSrc.Cmd.Env = []string{"GO_WANT_TEST_OUTPUT=1"}
|
|
|
|
out := make(chan types.Event)
|
|
tb := tomb.Tomb{}
|
|
|
|
err = journalSrc.StartReading(out, &tb)
|
|
if err != nil {
|
|
t.Fatalf("unexpected read error %s", err)
|
|
}
|
|
time.Sleep(1 * time.Second)
|
|
if tb.Err() != tomb.ErrStillAlive {
|
|
t.Fatalf("unexpected tomb error %s (should be alive)", tb.Err())
|
|
}
|
|
//kill it :>
|
|
tb.Kill(nil)
|
|
time.Sleep(1 * time.Second)
|
|
if tb.Err() != nil {
|
|
t.Fatalf("unexpected tomb error %s (should be dead)", tb.Err())
|
|
}
|
|
|
|
}
|