571 lines
13 KiB
Go
571 lines
13 KiB
Go
|
package suricata
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"log"
|
||
|
"math/rand"
|
||
|
"net"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"strings"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/stretchr/testify/require"
|
||
|
|
||
|
"github.com/influxdata/telegraf"
|
||
|
"github.com/influxdata/telegraf/testutil"
|
||
|
)
|
||
|
|
||
|
var ex2 = `{"timestamp":"2017-03-06T07:43:39.000397+0000","event_type":"stats","stats":` +
|
||
|
`{"capture":{"kernel_packets":905344474,"kernel_drops":78355440,"kernel_packets_delta":2376742,"kernel_drops_delta":82049}}}`
|
||
|
var ex3 = `{"timestamp":"2017-03-06T07:43:39.000397+0000","event_type":"stats","stats":` +
|
||
|
`{"threads": { "W#05-wlp4s0": { "capture":{"kernel_packets":905344474,"kernel_drops":78355440}}}}}`
|
||
|
|
||
|
func TestSuricataLarge(t *testing.T) {
|
||
|
dir := t.TempDir()
|
||
|
tmpfn := filepath.Join(dir, fmt.Sprintf("t%d", rand.Int63()))
|
||
|
|
||
|
s := Suricata{
|
||
|
Source: tmpfn,
|
||
|
Delimiter: ".",
|
||
|
Alerts: true,
|
||
|
Log: testutil.Logger{
|
||
|
Name: "inputs.suricata",
|
||
|
},
|
||
|
}
|
||
|
acc := testutil.Accumulator{}
|
||
|
require.NoError(t, s.Start(&acc))
|
||
|
defer s.Stop()
|
||
|
|
||
|
data, err := os.ReadFile("testdata/test1.json")
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
c, err := net.Dial("unix", tmpfn)
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write(data)
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte("\n"))
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
// test suricata alerts
|
||
|
data2, err := os.ReadFile("testdata/test2.json")
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write(data2)
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte("\n"))
|
||
|
require.NoError(t, err)
|
||
|
require.NoError(t, c.Close())
|
||
|
|
||
|
acc.Wait(1)
|
||
|
}
|
||
|
|
||
|
func TestSuricataAlerts(t *testing.T) {
|
||
|
dir := t.TempDir()
|
||
|
tmpfn := filepath.Join(dir, fmt.Sprintf("t%d", rand.Int63()))
|
||
|
|
||
|
s := Suricata{
|
||
|
Source: tmpfn,
|
||
|
Delimiter: ".",
|
||
|
Alerts: true,
|
||
|
Log: testutil.Logger{
|
||
|
Name: "inputs.suricata",
|
||
|
},
|
||
|
}
|
||
|
acc := testutil.Accumulator{}
|
||
|
require.NoError(t, s.Start(&acc))
|
||
|
defer s.Stop()
|
||
|
|
||
|
data, err := os.ReadFile("testdata/test3.json")
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
c, err := net.Dial("unix", tmpfn)
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write(data)
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte("\n"))
|
||
|
require.NoError(t, err)
|
||
|
require.NoError(t, c.Close())
|
||
|
|
||
|
acc.Wait(1)
|
||
|
|
||
|
expected := []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"suricata_alert",
|
||
|
map[string]string{},
|
||
|
map[string]interface{}{
|
||
|
"action": "allowed",
|
||
|
"category": "Misc activity",
|
||
|
"gid": float64(1),
|
||
|
"rev": float64(0),
|
||
|
"signature": "Corrupted HTTP body",
|
||
|
"signature_id": float64(6),
|
||
|
"severity": float64(3),
|
||
|
"source.ip": "10.0.0.5",
|
||
|
"target.ip": "179.60.192.3",
|
||
|
"source.port": float64(18715),
|
||
|
"target.port": float64(80),
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
}
|
||
|
|
||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
||
|
}
|
||
|
|
||
|
func TestSuricata(t *testing.T) {
|
||
|
dir := t.TempDir()
|
||
|
tmpfn := filepath.Join(dir, fmt.Sprintf("t%d", rand.Int63()))
|
||
|
|
||
|
s := Suricata{
|
||
|
Source: tmpfn,
|
||
|
Delimiter: ".",
|
||
|
Log: testutil.Logger{
|
||
|
Name: "inputs.suricata",
|
||
|
},
|
||
|
}
|
||
|
acc := testutil.Accumulator{}
|
||
|
require.NoError(t, s.Start(&acc))
|
||
|
defer s.Stop()
|
||
|
|
||
|
c, err := net.Dial("unix", tmpfn)
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte(ex2))
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte("\n"))
|
||
|
require.NoError(t, err)
|
||
|
require.NoError(t, c.Close())
|
||
|
|
||
|
acc.Wait(1)
|
||
|
|
||
|
expected := []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"suricata",
|
||
|
map[string]string{
|
||
|
"thread": "total",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"capture.kernel_packets": float64(905344474),
|
||
|
"capture.kernel_drops": float64(78355440),
|
||
|
"capture.kernel_packets_delta": float64(2376742),
|
||
|
"capture.kernel_drops_delta": float64(82049),
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
}
|
||
|
|
||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
||
|
}
|
||
|
|
||
|
func TestThreadStats(t *testing.T) {
|
||
|
dir := t.TempDir()
|
||
|
tmpfn := filepath.Join(dir, fmt.Sprintf("t%d", rand.Int63()))
|
||
|
|
||
|
s := Suricata{
|
||
|
Source: tmpfn,
|
||
|
Delimiter: ".",
|
||
|
Log: testutil.Logger{
|
||
|
Name: "inputs.suricata",
|
||
|
},
|
||
|
}
|
||
|
|
||
|
acc := testutil.Accumulator{}
|
||
|
require.NoError(t, s.Start(&acc))
|
||
|
defer s.Stop()
|
||
|
|
||
|
c, err := net.Dial("unix", tmpfn)
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte(""))
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte("\n"))
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte("foobard}\n"))
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte(ex3))
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte("\n"))
|
||
|
require.NoError(t, err)
|
||
|
require.NoError(t, c.Close())
|
||
|
acc.Wait(2)
|
||
|
|
||
|
expected := []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"suricata",
|
||
|
map[string]string{
|
||
|
"thread": "W#05-wlp4s0",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"capture.kernel_packets": float64(905344474),
|
||
|
"capture.kernel_drops": float64(78355440),
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
}
|
||
|
|
||
|
testutil.RequireMetricsEqual(t, expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
||
|
}
|
||
|
|
||
|
func TestSuricataInvalid(t *testing.T) {
|
||
|
dir := t.TempDir()
|
||
|
tmpfn := filepath.Join(dir, fmt.Sprintf("t%d", rand.Int63()))
|
||
|
|
||
|
s := Suricata{
|
||
|
Source: tmpfn,
|
||
|
Log: testutil.Logger{
|
||
|
Name: "inputs.suricata",
|
||
|
},
|
||
|
}
|
||
|
acc := testutil.Accumulator{}
|
||
|
acc.SetDebug(true)
|
||
|
|
||
|
require.NoError(t, s.Start(&acc))
|
||
|
defer s.Stop()
|
||
|
|
||
|
c, err := net.Dial("unix", tmpfn)
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte("sfjiowef"))
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte("\n"))
|
||
|
require.NoError(t, err)
|
||
|
require.NoError(t, c.Close())
|
||
|
|
||
|
acc.WaitError(1)
|
||
|
}
|
||
|
|
||
|
func TestSuricataInvalidPath(t *testing.T) {
|
||
|
tmpfn := fmt.Sprintf("/t%d/X", rand.Int63())
|
||
|
s := Suricata{
|
||
|
Source: tmpfn,
|
||
|
Log: testutil.Logger{
|
||
|
Name: "inputs.suricata",
|
||
|
},
|
||
|
}
|
||
|
|
||
|
acc := testutil.Accumulator{}
|
||
|
require.Error(t, s.Start(&acc))
|
||
|
}
|
||
|
|
||
|
func TestSuricataTooLongLine(t *testing.T) {
|
||
|
tmpfn := testutil.TempSocket(t)
|
||
|
|
||
|
s := Suricata{
|
||
|
Source: tmpfn,
|
||
|
Log: testutil.Logger{
|
||
|
Name: "inputs.suricata",
|
||
|
},
|
||
|
}
|
||
|
acc := testutil.Accumulator{}
|
||
|
|
||
|
require.NoError(t, s.Start(&acc))
|
||
|
defer s.Stop()
|
||
|
|
||
|
c, err := net.Dial("unix", tmpfn)
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte(strings.Repeat("X", 20000000)))
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte("\n"))
|
||
|
require.NoError(t, err)
|
||
|
require.NoError(t, c.Close())
|
||
|
|
||
|
acc.WaitError(1)
|
||
|
}
|
||
|
|
||
|
func TestSuricataEmptyJSON(t *testing.T) {
|
||
|
tmpfn := testutil.TempSocket(t)
|
||
|
|
||
|
s := Suricata{
|
||
|
Source: tmpfn,
|
||
|
Log: testutil.Logger{
|
||
|
Name: "inputs.suricata",
|
||
|
},
|
||
|
}
|
||
|
acc := testutil.Accumulator{}
|
||
|
require.NoError(t, s.Start(&acc))
|
||
|
defer s.Stop()
|
||
|
|
||
|
c, err := net.Dial("unix", tmpfn)
|
||
|
if err != nil {
|
||
|
log.Println(err)
|
||
|
}
|
||
|
_, err = c.Write([]byte("\n"))
|
||
|
require.NoError(t, err)
|
||
|
require.NoError(t, c.Close())
|
||
|
|
||
|
acc.WaitError(1)
|
||
|
}
|
||
|
|
||
|
func TestSuricataDisconnectSocket(t *testing.T) {
|
||
|
tmpfn := testutil.TempSocket(t)
|
||
|
|
||
|
s := Suricata{
|
||
|
Source: tmpfn,
|
||
|
Log: testutil.Logger{
|
||
|
Name: "inputs.suricata",
|
||
|
},
|
||
|
}
|
||
|
acc := testutil.Accumulator{}
|
||
|
|
||
|
require.NoError(t, s.Start(&acc))
|
||
|
defer s.Stop()
|
||
|
|
||
|
c, err := net.Dial("unix", tmpfn)
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte(ex2))
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte("\n"))
|
||
|
require.NoError(t, err)
|
||
|
require.NoError(t, c.Close())
|
||
|
|
||
|
c, err = net.Dial("unix", tmpfn)
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte(ex3))
|
||
|
require.NoError(t, err)
|
||
|
_, err = c.Write([]byte("\n"))
|
||
|
require.NoError(t, err)
|
||
|
require.NoError(t, c.Close())
|
||
|
|
||
|
acc.Wait(2)
|
||
|
}
|
||
|
|
||
|
func TestSuricataStartStop(t *testing.T) {
|
||
|
tmpfn := testutil.TempSocket(t)
|
||
|
|
||
|
s := Suricata{
|
||
|
Source: tmpfn,
|
||
|
Log: testutil.Logger{
|
||
|
Name: "inputs.suricata",
|
||
|
},
|
||
|
}
|
||
|
acc := testutil.Accumulator{}
|
||
|
require.NoError(t, s.Start(&acc))
|
||
|
s.Stop()
|
||
|
}
|
||
|
|
||
|
func TestSuricataParse(t *testing.T) {
|
||
|
tests := []struct {
|
||
|
filename string
|
||
|
expected []telegraf.Metric
|
||
|
}{{
|
||
|
filename: "test2.json",
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"suricata",
|
||
|
map[string]string{
|
||
|
"thread": "W#01-ens2f1",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"detect_alert": float64(0),
|
||
|
"detect_engines_id": float64(0),
|
||
|
"detect_engines_last_reload": "2021-06-08T06:33:05.084872+0000",
|
||
|
"detect_engines_rules_failed": float64(0),
|
||
|
"detect_engines_rules_loaded": float64(22712),
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tc := range tests {
|
||
|
data, err := os.ReadFile("testdata/" + tc.filename)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
s := Suricata{
|
||
|
Delimiter: "_",
|
||
|
}
|
||
|
acc := testutil.Accumulator{}
|
||
|
err = s.parse(&acc, data)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
testutil.RequireMetricsEqual(t, tc.expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestSuricataParseVersion2(t *testing.T) {
|
||
|
tests := []struct {
|
||
|
filename string
|
||
|
expected []telegraf.Metric
|
||
|
}{
|
||
|
{
|
||
|
filename: "v2/alert.json",
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"suricata",
|
||
|
map[string]string{
|
||
|
"event_type": "alert",
|
||
|
"in_iface": "s1-suricata",
|
||
|
"proto": "TCP",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"action": "allowed",
|
||
|
"category": "Misc activity",
|
||
|
"dest_ip": "179.60.192.3",
|
||
|
"dest_port": int64(80),
|
||
|
"gid": float64(1),
|
||
|
"rev": float64(0),
|
||
|
"severity": float64(3),
|
||
|
"signature": "Corrupted HTTP body",
|
||
|
"signature_id": float64(6),
|
||
|
"sourceip": "10.0.0.5",
|
||
|
"sourceport": float64(18715),
|
||
|
"src_ip": "10.0.0.5",
|
||
|
"src_port": int64(18715),
|
||
|
"targetip": "179.60.192.3",
|
||
|
"targetport": float64(80),
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
filename: "v2/dns.json",
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"suricata",
|
||
|
map[string]string{
|
||
|
"event_type": "dns",
|
||
|
"in_iface": "eth1",
|
||
|
"proto": "UDP",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"dest_ip": "192.168.0.1",
|
||
|
"dest_port": int64(53),
|
||
|
"id": float64(7145),
|
||
|
"rrname": "reddit.com",
|
||
|
"rrtype": "A",
|
||
|
"src_ip": "192.168.0.100",
|
||
|
"type": "query",
|
||
|
"src_port": int64(39262),
|
||
|
"tx_id": float64(10),
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
filename: "v2/drop.json",
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"suricata",
|
||
|
map[string]string{
|
||
|
"event_type": "drop",
|
||
|
"in_iface": "eth1",
|
||
|
"proto": "TCP",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"dest_ip": "54.192.18.125",
|
||
|
"dest_port": int64(443),
|
||
|
"ipid": float64(62316),
|
||
|
"len": float64(76),
|
||
|
"reason": "stream error",
|
||
|
"src_ip": "192.168.0.110",
|
||
|
"src_port": int64(46016),
|
||
|
"tcpack": float64(2339873683),
|
||
|
"tcpres": float64(0),
|
||
|
"tcpseq": float64(3900248957),
|
||
|
"tcpurgp": float64(0),
|
||
|
"tcpwin": float64(501),
|
||
|
"tos": float64(0),
|
||
|
"ttl": float64(64),
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
filename: "v2/flow.json",
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"suricata",
|
||
|
map[string]string{
|
||
|
"event_type": "flow",
|
||
|
"in_iface": "eth1",
|
||
|
"proto": "TCP",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"age": float64(0),
|
||
|
"dest_ip": "142.251.130.3",
|
||
|
"dest_port": int64(443),
|
||
|
"src_ip": "192.168.0.121",
|
||
|
"src_port": int64(50212),
|
||
|
"state": "new",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
filename: "v2/http.json",
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"suricata",
|
||
|
map[string]string{
|
||
|
"event_type": "http",
|
||
|
"in_iface": "eth2",
|
||
|
"proto": "TCP",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"dest_ip": "203.205.239.179",
|
||
|
"dest_port": int64(80),
|
||
|
"hostname": "hkminorshort.weixin.qq.com",
|
||
|
"http_content_type": "application/octet-stream",
|
||
|
"http_method": "POST",
|
||
|
"http_user_agent": "MicroMessenger Client",
|
||
|
"length": float64(245),
|
||
|
"protocol": "HTTP/1.1",
|
||
|
"src_ip": "192.168.0.120",
|
||
|
"src_port": int64(33950),
|
||
|
"status": float64(200),
|
||
|
"url": "/mmtls/2d6d45f1",
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
filename: "v2/status.json",
|
||
|
expected: []telegraf.Metric{
|
||
|
testutil.MustMetric(
|
||
|
"suricata",
|
||
|
map[string]string{
|
||
|
"event_type": "stats",
|
||
|
},
|
||
|
map[string]interface{}{
|
||
|
"captureerrors": float64(0),
|
||
|
"capturekernel_drops": float64(0),
|
||
|
"capturekernel_packets": float64(522),
|
||
|
"flowemerg_mode_entered": float64(0),
|
||
|
"flowemerg_mode_over": float64(0),
|
||
|
"flowmemcap": float64(0),
|
||
|
"flowmemuse": float64(9965056),
|
||
|
"flowmgrclosed_pruned": float64(0),
|
||
|
"flowmgrfull_hash_pass": float64(1),
|
||
|
"flowmgrnew_pruned": float64(0),
|
||
|
"flowspare": float64(10100),
|
||
|
"flowtcp": float64(15),
|
||
|
"flowudp": float64(13),
|
||
|
"flowwrkspare_sync": float64(11),
|
||
|
"flowwrkspare_sync_avg": float64(100),
|
||
|
"uptime": float64(160),
|
||
|
},
|
||
|
time.Unix(0, 0),
|
||
|
),
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tc := range tests {
|
||
|
data, err := os.ReadFile("testdata/" + tc.filename)
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
s := Suricata{
|
||
|
Version: "2",
|
||
|
Log: testutil.Logger{},
|
||
|
}
|
||
|
acc := testutil.Accumulator{}
|
||
|
require.NoError(t, s.parse(&acc, data))
|
||
|
|
||
|
testutil.RequireMetricsEqual(t, tc.expected, acc.GetTelegrafMetrics(), testutil.IgnoreTime())
|
||
|
}
|
||
|
}
|