Add Suricata input plugin (#3145)
This commit is contained in:
parent
f669ef4452
commit
d2d6f1ab21
|
@ -140,6 +140,7 @@ import (
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/sqlserver"
|
_ "github.com/influxdata/telegraf/plugins/inputs/sqlserver"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/stackdriver"
|
_ "github.com/influxdata/telegraf/plugins/inputs/stackdriver"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/statsd"
|
_ "github.com/influxdata/telegraf/plugins/inputs/statsd"
|
||||||
|
_ "github.com/influxdata/telegraf/plugins/inputs/suricata"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/swap"
|
_ "github.com/influxdata/telegraf/plugins/inputs/swap"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/syslog"
|
_ "github.com/influxdata/telegraf/plugins/inputs/syslog"
|
||||||
_ "github.com/influxdata/telegraf/plugins/inputs/sysstat"
|
_ "github.com/influxdata/telegraf/plugins/inputs/sysstat"
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
# Suricata plugin for Telegraf
|
||||||
|
|
||||||
|
This plugin reports internal performance counters of the Suricata IDS/IPS
|
||||||
|
engine, such as captured traffic volume, memory usage, uptime, flow counters,
|
||||||
|
and much more. It provides a socket for the Suricata log output to write JSON
|
||||||
|
stats output to, and processes the incoming data to fit Telegraf's format.
|
||||||
|
|
||||||
|
### Configuration:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[[input.suricata]]
|
||||||
|
## Data sink for Suricata stats log.
|
||||||
|
# This is expected to be a filename of a
|
||||||
|
# unix socket to be created for listening.
|
||||||
|
source = "/var/run/suricata-stats.sock"
|
||||||
|
|
||||||
|
# Delimiter for flattening field keys, e.g. subitem "alert" of "detect"
|
||||||
|
# becomes "detect_alert" when delimiter is "_".
|
||||||
|
delimiter = "_"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Measurements & Fields:
|
||||||
|
|
||||||
|
Fields in the 'suricata' measurement follow the JSON format used by Suricata's
|
||||||
|
stats output.
|
||||||
|
See http://suricata.readthedocs.io/en/latest/performance/statistics.html for
|
||||||
|
more information.
|
||||||
|
|
||||||
|
All fields are numeric.
|
||||||
|
- suricata
|
||||||
|
- app_layer_flow_dcerpc_udp
|
||||||
|
- app_layer_flow_dns_tcp
|
||||||
|
- app_layer_flow_dns_udp
|
||||||
|
- app_layer_flow_enip_udp
|
||||||
|
- app_layer_flow_failed_tcp
|
||||||
|
- app_layer_flow_failed_udp
|
||||||
|
- app_layer_flow_http
|
||||||
|
- app_layer_flow_ssh
|
||||||
|
- app_layer_flow_tls
|
||||||
|
- app_layer_tx_dns_tcp
|
||||||
|
- app_layer_tx_dns_udp
|
||||||
|
- app_layer_tx_enip_udp
|
||||||
|
- app_layer_tx_http
|
||||||
|
- app_layer_tx_smtp
|
||||||
|
- capture_kernel_drops
|
||||||
|
- capture_kernel_packets
|
||||||
|
- decoder_avg_pkt_size
|
||||||
|
- decoder_bytes
|
||||||
|
- decoder_ethernet
|
||||||
|
- decoder_gre
|
||||||
|
- decoder_icmpv4
|
||||||
|
- decoder_icmpv4_ipv4_unknown_ver
|
||||||
|
- decoder_icmpv6
|
||||||
|
- decoder_invalid
|
||||||
|
- decoder_ipv4
|
||||||
|
- decoder_ipv6
|
||||||
|
- decoder_max_pkt_size
|
||||||
|
- decoder_pkts
|
||||||
|
- decoder_tcp
|
||||||
|
- decoder_tcp_hlen_too_small
|
||||||
|
- decoder_tcp_invalid_optlen
|
||||||
|
- decoder_teredo
|
||||||
|
- decoder_udp
|
||||||
|
- decoder_vlan
|
||||||
|
- detect_alert
|
||||||
|
- dns_memcap_global
|
||||||
|
- dns_memuse
|
||||||
|
- flow_memuse
|
||||||
|
- flow_mgr_closed_pruned
|
||||||
|
- flow_mgr_est_pruned
|
||||||
|
- flow_mgr_flows_checked
|
||||||
|
- flow_mgr_flows_notimeout
|
||||||
|
- flow_mgr_flows_removed
|
||||||
|
- flow_mgr_flows_timeout
|
||||||
|
- flow_mgr_flows_timeout_inuse
|
||||||
|
- flow_mgr_new_pruned
|
||||||
|
- flow_mgr_rows_checked
|
||||||
|
- flow_mgr_rows_empty
|
||||||
|
- flow_mgr_rows_maxlen
|
||||||
|
- flow_mgr_rows_skipped
|
||||||
|
- flow_spare
|
||||||
|
- flow_tcp_reuse
|
||||||
|
- http_memuse
|
||||||
|
- tcp_memuse
|
||||||
|
- tcp_pseudo
|
||||||
|
- tcp_reassembly_gap
|
||||||
|
- tcp_reassembly_memuse
|
||||||
|
- tcp_rst
|
||||||
|
- tcp_sessions
|
||||||
|
- tcp_syn
|
||||||
|
- tcp_synack
|
||||||
|
- ...
|
||||||
|
|
||||||
|
### Tags:
|
||||||
|
|
||||||
|
The `suricata` measurement has the following tags:
|
||||||
|
|
||||||
|
- thread: `Global` for global statistics (if enabled), thread IDs (e.g. `W#03-enp0s31f6`) for thread-specific statistics
|
||||||
|
|
||||||
|
## Suricata configuration
|
||||||
|
|
||||||
|
Suricata needs to deliver the 'stats' event type to a given unix socket for
|
||||||
|
this plugin to pick up. This can be done, for example, by creating an additional
|
||||||
|
output in the Suricata configuration file:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- eve-log:
|
||||||
|
enabled: yes
|
||||||
|
filetype: unix_stream
|
||||||
|
filename: /tmp/suricata-stats.sock
|
||||||
|
types:
|
||||||
|
- stats:
|
||||||
|
threads: yes
|
||||||
|
```
|
||||||
|
|
||||||
|
## Example Output:
|
||||||
|
|
||||||
|
```text
|
||||||
|
suricata,host=myhost,thread=FM#01 flow_mgr_rows_empty=0,flow_mgr_rows_checked=65536,flow_mgr_closed_pruned=0,flow_emerg_mode_over=0,flow_mgr_flows_timeout_inuse=0,flow_mgr_rows_skipped=65535,flow_mgr_bypassed_pruned=0,flow_mgr_flows_removed=0,flow_mgr_est_pruned=0,flow_mgr_flows_notimeout=1,flow_mgr_flows_checked=1,flow_mgr_rows_busy=0,flow_spare=10000,flow_mgr_rows_maxlen=1,flow_mgr_new_pruned=0,flow_emerg_mode_entered=0,flow_tcp_reuse=0,flow_mgr_flows_timeout=0 1568368562545197545
|
||||||
|
suricata,host=myhost,thread=W#04-wlp4s0 decoder_ltnull_pkt_too_small=0,decoder_ipraw_invalid_ip_version=0,defrag_ipv4_reassembled=0,tcp_no_flow=0,app_layer_flow_tls=1,decoder_udp=25,defrag_ipv6_fragments=0,defrag_ipv4_fragments=0,decoder_tcp=59,decoder_vlan=0,decoder_pkts=84,decoder_vlan_qinq=0,decoder_avg_pkt_size=574,flow_memcap=0,defrag_max_frag_hits=0,tcp_ssn_memcap_drop=0,capture_kernel_packets=84,app_layer_flow_dcerpc_udp=0,app_layer_tx_dns_tcp=0,tcp_rst=0,decoder_icmpv4=0,app_layer_tx_tls=0,decoder_ipv4=84,decoder_erspan=0,decoder_ltnull_unsupported_type=0,decoder_invalid=0,app_layer_flow_ssh=0,capture_kernel_drops=0,app_layer_flow_ftp=0,app_layer_tx_http=0,tcp_pseudo_failed=0,defrag_ipv6_reassembled=0,defrag_ipv6_timeouts=0,tcp_pseudo=0,tcp_sessions=1,decoder_ethernet=84,decoder_raw=0,decoder_sctp=0,app_layer_flow_dns_udp=1,decoder_gre=0,app_layer_flow_http=0,app_layer_flow_imap=0,tcp_segment_memcap_drop=0,detect_alert=0,app_layer_flow_failed_tcp=0,decoder_teredo=0,decoder_mpls=0,decoder_ppp=0,decoder_max_pkt_size=1422,decoder_ipv6=0,tcp_reassembly_gap=0,app_layer_flow_dcerpc_tcp=0,decoder_ipv4_in_ipv6=0,tcp_stream_depth_reached=0,app_layer_flow_dns_tcp=0,app_layer_flow_smtp=0,tcp_syn=1,decoder_sll=0,tcp_invalid_checksum=0,app_layer_tx_dns_udp=1,decoder_bytes=48258,defrag_ipv4_timeouts=0,app_layer_flow_msn=0,decoder_pppoe=0,decoder_null=0,app_layer_flow_failed_udp=3,app_layer_tx_smtp=0,decoder_icmpv6=0,decoder_ipv6_in_ipv6=0,tcp_synack=1,app_layer_flow_smb=0,decoder_dce_pkt_too_small=0 1568368562545174807
|
||||||
|
suricata,host=myhost,thread=W#01-wlp4s0 tcp_synack=0,app_layer_flow_imap=0,decoder_ipv4_in_ipv6=0,decoder_max_pkt_size=684,decoder_gre=0,defrag_ipv4_timeouts=0,tcp_invalid_checksum=0,decoder_ipv4=53,flow_memcap=0,app_layer_tx_http=0,app_layer_tx_smtp=0,decoder_null=0,tcp_no_flow=0,app_layer_tx_tls=0,app_layer_flow_ssh=0,app_layer_flow_smtp=0,decoder_pppoe=0,decoder_teredo=0,decoder_ipraw_invalid_ip_version=0,decoder_ltnull_pkt_too_small=0,tcp_rst=0,decoder_ppp=0,decoder_ipv6=29,app_layer_flow_dns_udp=3,decoder_vlan=0,app_layer_flow_dcerpc_tcp=0,tcp_syn=0,defrag_ipv4_fragments=0,defrag_ipv6_timeouts=0,decoder_raw=0,defrag_ipv6_reassembled=0,tcp_reassembly_gap=0,tcp_sessions=0,decoder_udp=44,tcp_segment_memcap_drop=0,app_layer_tx_dns_udp=3,app_layer_flow_tls=0,decoder_tcp=37,defrag_ipv4_reassembled=0,app_layer_flow_failed_udp=6,app_layer_flow_ftp=0,decoder_icmpv6=1,tcp_stream_depth_reached=0,capture_kernel_drops=0,decoder_sll=0,decoder_bytes=15883,decoder_ethernet=91,tcp_pseudo=0,app_layer_flow_http=0,decoder_sctp=0,decoder_pkts=91,decoder_avg_pkt_size=174,decoder_erspan=0,app_layer_flow_msn=0,app_layer_flow_smb=0,capture_kernel_packets=91,decoder_icmpv4=0,decoder_ipv6_in_ipv6=0,tcp_ssn_memcap_drop=0,decoder_vlan_qinq=0,decoder_ltnull_unsupported_type=0,decoder_invalid=0,defrag_max_frag_hits=0,tcp_pseudo_failed=0,detect_alert=0,app_layer_tx_dns_tcp=0,app_layer_flow_failed_tcp=0,app_layer_flow_dcerpc_udp=0,app_layer_flow_dns_tcp=0,defrag_ipv6_fragments=0,decoder_mpls=0,decoder_dce_pkt_too_small=0 1568368562545148438
|
||||||
|
suricata,host=myhost flow_memuse=7094464,tcp_memuse=3276800,tcp_reassembly_memuse=12332832,dns_memuse=0,dns_memcap_state=0,dns_memcap_global=0,http_memuse=0,http_memcap=0 1568368562545144569
|
||||||
|
suricata,host=myhost,thread=W#07-wlp4s0 app_layer_tx_http=0,app_layer_tx_dns_tcp=0,decoder_vlan=0,decoder_pppoe=0,decoder_sll=0,decoder_tcp=0,flow_memcap=0,app_layer_flow_msn=0,tcp_no_flow=0,tcp_rst=0,tcp_segment_memcap_drop=0,tcp_sessions=0,detect_alert=0,defrag_ipv6_reassembled=0,decoder_ipraw_invalid_ip_version=0,decoder_erspan=0,decoder_icmpv4=0,app_layer_tx_dns_udp=2,decoder_ltnull_pkt_too_small=0,decoder_bytes=1998,decoder_ipv6=1,defrag_ipv4_fragments=0,defrag_ipv6_fragments=0,app_layer_tx_smtp=0,decoder_ltnull_unsupported_type=0,decoder_max_pkt_size=342,app_layer_flow_ftp=0,decoder_ipv6_in_ipv6=0,defrag_ipv4_reassembled=0,defrag_ipv6_timeouts=0,app_layer_flow_dns_tcp=0,decoder_avg_pkt_size=181,defrag_ipv4_timeouts=0,tcp_stream_depth_reached=0,decoder_mpls=0,app_layer_flow_dns_udp=2,tcp_ssn_memcap_drop=0,app_layer_flow_dcerpc_tcp=0,app_layer_flow_failed_udp=2,app_layer_flow_smb=0,app_layer_flow_failed_tcp=0,decoder_invalid=0,decoder_null=0,decoder_gre=0,decoder_ethernet=11,app_layer_flow_ssh=0,defrag_max_frag_hits=0,capture_kernel_drops=0,tcp_pseudo_failed=0,app_layer_flow_smtp=0,decoder_udp=10,decoder_sctp=0,decoder_teredo=0,decoder_icmpv6=1,tcp_pseudo=0,tcp_synack=0,app_layer_tx_tls=0,app_layer_flow_imap=0,capture_kernel_packets=11,decoder_pkts=11,decoder_raw=0,decoder_ppp=0,tcp_syn=0,tcp_invalid_checksum=0,app_layer_flow_tls=0,decoder_ipv4_in_ipv6=0,app_layer_flow_http=0,decoder_dce_pkt_too_small=0,decoder_ipv4=10,decoder_vlan_qinq=0,tcp_reassembly_gap=0,app_layer_flow_dcerpc_udp=0 1568368562545110847
|
||||||
|
suricata,host=myhost,thread=W#06-wlp4s0 app_layer_tx_smtp=0,decoder_ipv6_in_ipv6=0,decoder_dce_pkt_too_small=0,tcp_segment_memcap_drop=0,tcp_sessions=1,decoder_ppp=0,tcp_pseudo_failed=0,app_layer_tx_dns_tcp=0,decoder_invalid=0,defrag_ipv4_timeouts=0,app_layer_flow_smb=0,app_layer_flow_ssh=0,decoder_bytes=19407,decoder_null=0,app_layer_flow_tls=1,decoder_avg_pkt_size=473,decoder_pkts=41,decoder_pppoe=0,decoder_tcp=32,defrag_ipv4_reassembled=0,tcp_reassembly_gap=0,decoder_raw=0,flow_memcap=0,defrag_ipv6_timeouts=0,app_layer_flow_smtp=0,app_layer_tx_http=0,decoder_sll=0,decoder_udp=8,decoder_ltnull_pkt_too_small=0,decoder_ltnull_unsupported_type=0,decoder_ipv4_in_ipv6=0,decoder_vlan=0,decoder_max_pkt_size=1422,tcp_no_flow=0,app_layer_flow_failed_tcp=0,app_layer_flow_dns_tcp=0,app_layer_flow_ftp=0,decoder_icmpv4=0,defrag_max_frag_hits=0,tcp_rst=0,app_layer_flow_msn=0,app_layer_flow_failed_udp=2,app_layer_flow_dns_udp=0,app_layer_flow_dcerpc_udp=0,decoder_ipv4=39,decoder_ethernet=41,defrag_ipv6_reassembled=0,tcp_ssn_memcap_drop=0,app_layer_tx_tls=0,decoder_gre=0,decoder_vlan_qinq=0,tcp_pseudo=0,app_layer_flow_imap=0,app_layer_flow_dcerpc_tcp=0,defrag_ipv4_fragments=0,defrag_ipv6_fragments=0,tcp_synack=1,app_layer_flow_http=0,app_layer_tx_dns_udp=0,capture_kernel_packets=41,decoder_ipv6=2,tcp_invalid_checksum=0,tcp_stream_depth_reached=0,decoder_ipraw_invalid_ip_version=0,decoder_icmpv6=1,tcp_syn=1,detect_alert=0,capture_kernel_drops=0,decoder_teredo=0,decoder_erspan=0,decoder_sctp=0,decoder_mpls=0 1568368562545084670
|
||||||
|
suricata,host=myhost,thread=W#02-wlp4s0 decoder_tcp=53,tcp_rst=3,tcp_reassembly_gap=0,defrag_ipv6_timeouts=0,tcp_ssn_memcap_drop=0,app_layer_flow_dcerpc_tcp=0,decoder_max_pkt_size=1422,decoder_ipv6_in_ipv6=0,tcp_no_flow=0,app_layer_flow_ftp=0,app_layer_flow_ssh=0,decoder_pkts=82,decoder_sctp=0,tcp_invalid_checksum=0,app_layer_flow_dns_tcp=0,decoder_ipraw_invalid_ip_version=0,decoder_bytes=26441,decoder_erspan=0,tcp_pseudo_failed=0,tcp_syn=1,app_layer_tx_http=0,app_layer_tx_smtp=0,decoder_teredo=0,decoder_ipv4=80,defrag_ipv4_fragments=0,tcp_stream_depth_reached=0,app_layer_flow_smb=0,capture_kernel_packets=82,decoder_null=0,decoder_ltnull_pkt_too_small=0,decoder_ppp=0,decoder_icmpv6=1,app_layer_flow_dns_udp=2,app_layer_flow_http=0,app_layer_tx_dns_udp=3,decoder_mpls=0,decoder_sll=0,defrag_ipv4_reassembled=0,tcp_segment_memcap_drop=0,app_layer_flow_imap=0,decoder_ltnull_unsupported_type=0,decoder_icmpv4=0,decoder_raw=0,defrag_ipv4_timeouts=0,app_layer_flow_failed_udp=8,decoder_gre=0,capture_kernel_drops=0,defrag_ipv6_reassembled=0,tcp_pseudo=0,app_layer_flow_tls=1,decoder_avg_pkt_size=322,decoder_dce_pkt_too_small=0,decoder_ethernet=82,defrag_ipv6_fragments=0,tcp_sessions=1,tcp_synack=1,app_layer_tx_dns_tcp=0,decoder_vlan=0,flow_memcap=0,decoder_vlan_qinq=0,decoder_udp=28,decoder_invalid=0,detect_alert=0,app_layer_flow_failed_tcp=0,app_layer_tx_tls=0,decoder_pppoe=0,decoder_ipv6=2,decoder_ipv4_in_ipv6=0,defrag_max_frag_hits=0,app_layer_flow_dcerpc_udp=0,app_layer_flow_smtp=0,app_layer_flow_msn=0 1568368562545061864
|
||||||
|
suricata,host=myhost,thread=W#08-wlp4s0 decoder_dce_pkt_too_small=0,app_layer_tx_dns_tcp=0,decoder_pkts=58,decoder_ppp=0,decoder_raw=0,decoder_ipv4_in_ipv6=0,decoder_max_pkt_size=1392,tcp_invalid_checksum=0,tcp_syn=0,decoder_ipv4=51,decoder_ipv6_in_ipv6=0,decoder_tcp=0,decoder_ltnull_pkt_too_small=0,flow_memcap=0,decoder_udp=58,tcp_ssn_memcap_drop=0,tcp_pseudo=0,app_layer_flow_dcerpc_udp=0,app_layer_flow_dns_udp=5,app_layer_tx_http=0,capture_kernel_drops=0,decoder_vlan=0,tcp_segment_memcap_drop=0,app_layer_flow_ftp=0,app_layer_flow_imap=0,app_layer_flow_http=0,app_layer_flow_tls=0,decoder_icmpv4=0,decoder_sctp=0,defrag_ipv4_timeouts=0,tcp_reassembly_gap=0,detect_alert=0,decoder_ethernet=58,tcp_pseudo_failed=0,decoder_teredo=0,defrag_ipv4_reassembled=0,tcp_sessions=0,app_layer_flow_msn=0,decoder_ipraw_invalid_ip_version=0,tcp_no_flow=0,app_layer_flow_dns_tcp=0,decoder_null=0,defrag_ipv4_fragments=0,app_layer_flow_dcerpc_tcp=0,app_layer_flow_failed_udp=8,app_layer_tx_tls=0,decoder_bytes=15800,decoder_ipv6=7,tcp_stream_depth_reached=0,decoder_invalid=0,decoder_ltnull_unsupported_type=0,app_layer_tx_dns_udp=6,decoder_pppoe=0,decoder_avg_pkt_size=272,decoder_erspan=0,defrag_ipv6_timeouts=0,app_layer_flow_failed_tcp=0,decoder_gre=0,decoder_sll=0,defrag_max_frag_hits=0,app_layer_flow_ssh=0,capture_kernel_packets=58,decoder_mpls=0,decoder_vlan_qinq=0,tcp_rst=0,app_layer_flow_smb=0,app_layer_tx_smtp=0,decoder_icmpv6=0,defrag_ipv6_fragments=0,defrag_ipv6_reassembled=0,tcp_synack=0,app_layer_flow_smtp=0 1568368562545035575
|
||||||
|
suricata,host=myhost,thread=W#05-wlp4s0 tcp_reassembly_gap=0,capture_kernel_drops=0,decoder_ltnull_unsupported_type=0,tcp_sessions=0,tcp_stream_depth_reached=0,tcp_pseudo_failed=0,app_layer_flow_failed_tcp=0,app_layer_tx_dns_tcp=0,decoder_null=0,decoder_dce_pkt_too_small=0,decoder_udp=7,tcp_rst=3,app_layer_flow_dns_tcp=0,decoder_invalid=0,defrag_ipv4_reassembled=0,tcp_synack=0,app_layer_flow_ftp=0,decoder_bytes=3117,decoder_pppoe=0,app_layer_flow_dcerpc_tcp=0,app_layer_flow_smb=0,decoder_ipv6_in_ipv6=0,decoder_ipraw_invalid_ip_version=0,app_layer_flow_imap=0,app_layer_tx_dns_udp=2,decoder_ppp=0,decoder_ipv4=21,decoder_tcp=14,flow_memcap=0,tcp_syn=0,tcp_invalid_checksum=0,decoder_teredo=0,decoder_ltnull_pkt_too_small=0,defrag_max_frag_hits=0,app_layer_tx_tls=0,decoder_pkts=24,decoder_sll=0,defrag_ipv6_fragments=0,app_layer_flow_dcerpc_udp=0,app_layer_flow_smtp=0,decoder_icmpv6=3,defrag_ipv6_timeouts=0,decoder_ipv6=3,decoder_raw=0,defrag_ipv6_reassembled=0,tcp_no_flow=0,detect_alert=0,app_layer_flow_tls=0,decoder_ethernet=24,decoder_vlan=0,decoder_icmpv4=0,decoder_ipv4_in_ipv6=0,app_layer_flow_failed_udp=1,decoder_mpls=0,decoder_max_pkt_size=653,decoder_sctp=0,defrag_ipv4_timeouts=0,tcp_ssn_memcap_drop=0,app_layer_flow_dns_udp=1,app_layer_tx_smtp=0,capture_kernel_packets=24,decoder_vlan_qinq=0,decoder_gre=0,app_layer_flow_ssh=0,app_layer_flow_msn=0,defrag_ipv4_fragments=0,app_layer_flow_http=0,tcp_segment_memcap_drop=0,tcp_pseudo=0,app_layer_tx_http=0,decoder_erspan=0,decoder_avg_pkt_size=129 1568368562545009684
|
||||||
|
suricata,host=myhost,thread=W#03-wlp4s0 app_layer_flow_failed_tcp=0,decoder_teredo=0,decoder_ipv6_in_ipv6=0,tcp_pseudo_failed=0,tcp_stream_depth_reached=0,tcp_syn=0,decoder_gre=0,tcp_segment_memcap_drop=0,tcp_ssn_memcap_drop=0,app_layer_tx_smtp=0,decoder_raw=0,decoder_ltnull_pkt_too_small=0,tcp_sessions=0,tcp_reassembly_gap=0,app_layer_flow_ssh=0,app_layer_flow_imap=0,decoder_ipv4=463,decoder_ethernet=463,capture_kernel_packets=463,decoder_pppoe=0,defrag_ipv4_reassembled=0,app_layer_flow_tls=0,app_layer_flow_dcerpc_udp=0,app_layer_flow_dns_udp=0,decoder_vlan=0,decoder_ipraw_invalid_ip_version=0,decoder_mpls=0,tcp_no_flow=0,decoder_avg_pkt_size=445,decoder_udp=432,flow_memcap=0,app_layer_tx_dns_udp=0,app_layer_flow_msn=0,app_layer_flow_http=0,app_layer_flow_dcerpc_tcp=0,decoder_ipv6=0,decoder_ipv4_in_ipv6=0,defrag_ipv4_timeouts=0,defrag_ipv4_fragments=0,defrag_ipv6_timeouts=0,decoder_sctp=0,defrag_ipv6_fragments=0,app_layer_flow_dns_tcp=0,app_layer_tx_tls=0,defrag_max_frag_hits=0,decoder_bytes=206345,decoder_vlan_qinq=0,decoder_invalid=0,decoder_ppp=0,tcp_rst=0,detect_alert=0,capture_kernel_drops=0,app_layer_flow_failed_udp=4,decoder_null=0,decoder_icmpv4=0,decoder_icmpv6=0,decoder_ltnull_unsupported_type=0,defrag_ipv6_reassembled=0,tcp_invalid_checksum=0,tcp_synack=0,decoder_tcp=31,tcp_pseudo=0,app_layer_flow_smb=0,app_layer_flow_smtp=0,decoder_max_pkt_size=1463,decoder_dce_pkt_too_small=0,app_layer_tx_http=0,decoder_pkts=463,decoder_sll=0,app_layer_flow_ftp=0,app_layer_tx_dns_tcp=0,decoder_erspan=0 1568368562544966078
|
||||||
|
```
|
|
@ -0,0 +1,229 @@
|
||||||
|
package suricata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// InBufSize is the input buffer size for JSON received via socket.
|
||||||
|
// Set to 10MB, as depending on the number of threads the output might be
|
||||||
|
// large.
|
||||||
|
InBufSize = 10 * 1024 * 1024
|
||||||
|
)
|
||||||
|
|
||||||
|
// Suricata is a Telegraf input plugin for Suricata runtime statistics.
|
||||||
|
type Suricata struct {
|
||||||
|
Source string `toml:"source"`
|
||||||
|
Delimiter string `toml:"delimiter"`
|
||||||
|
|
||||||
|
inputListener *net.UnixListener
|
||||||
|
cancel context.CancelFunc
|
||||||
|
|
||||||
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
|
||||||
|
wg sync.WaitGroup
|
||||||
|
}
|
||||||
|
|
||||||
|
// Description returns the plugin description.
|
||||||
|
func (s *Suricata) Description() string {
|
||||||
|
return "Suricata stats plugin"
|
||||||
|
}
|
||||||
|
|
||||||
|
const sampleConfig = `
|
||||||
|
## Data sink for Suricata stats log
|
||||||
|
# This is expected to be a filename of a
|
||||||
|
# unix socket to be created for listening.
|
||||||
|
source = "/var/run/suricata-stats.sock"
|
||||||
|
|
||||||
|
# Delimiter for flattening field keys, e.g. subitem "alert" of "detect"
|
||||||
|
# becomes "detect_alert" when delimiter is "_".
|
||||||
|
delimiter = "_"
|
||||||
|
`
|
||||||
|
|
||||||
|
// SampleConfig returns a sample TOML section to illustrate configuration
|
||||||
|
// options.
|
||||||
|
func (s *Suricata) SampleConfig() string {
|
||||||
|
return sampleConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start initiates background collection of JSON data from the socket
|
||||||
|
// provided to Suricata.
|
||||||
|
func (s *Suricata) Start(acc telegraf.Accumulator) error {
|
||||||
|
var err error
|
||||||
|
s.inputListener, err = net.ListenUnix("unix", &net.UnixAddr{
|
||||||
|
Name: s.Source,
|
||||||
|
Net: "unix",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
s.cancel = cancel
|
||||||
|
s.inputListener.SetUnlinkOnClose(true)
|
||||||
|
s.wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer s.wg.Done()
|
||||||
|
go s.handleServerConnection(ctx, acc)
|
||||||
|
}()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop causes the plugin to cease collecting JSON data from the socket provided
|
||||||
|
// to Suricata.
|
||||||
|
func (s *Suricata) Stop() {
|
||||||
|
s.inputListener.Close()
|
||||||
|
if s.cancel != nil {
|
||||||
|
s.cancel()
|
||||||
|
}
|
||||||
|
s.wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Suricata) readInput(ctx context.Context, acc telegraf.Accumulator, conn net.Conn) error {
|
||||||
|
reader := bufio.NewReaderSize(conn, InBufSize)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
line, rerr := reader.ReadBytes('\n')
|
||||||
|
if rerr != nil {
|
||||||
|
return rerr
|
||||||
|
} else if len(line) > 0 {
|
||||||
|
s.parse(acc, line)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Suricata) handleServerConnection(ctx context.Context, acc telegraf.Accumulator) {
|
||||||
|
var err error
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-ctx.Done():
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
var conn net.Conn
|
||||||
|
conn, err = s.inputListener.Accept()
|
||||||
|
if err != nil {
|
||||||
|
if !strings.HasSuffix(err.Error(), ": use of closed network connection") {
|
||||||
|
acc.AddError(err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
err = s.readInput(ctx, acc, conn)
|
||||||
|
// we want to handle EOF as an opportunity to wait for a new
|
||||||
|
// connection -- this could, for example, happen when Suricata is
|
||||||
|
// restarted while Telegraf is running.
|
||||||
|
if err != io.EOF {
|
||||||
|
acc.AddError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func flexFlatten(outmap map[string]interface{}, field string, v interface{}, delimiter string) error {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case map[string]interface{}:
|
||||||
|
for k, v := range t {
|
||||||
|
var err error
|
||||||
|
if field == "" {
|
||||||
|
err = flexFlatten(outmap, k, v, delimiter)
|
||||||
|
} else {
|
||||||
|
err = flexFlatten(outmap, fmt.Sprintf("%s%s%s", field, delimiter, k), v, delimiter)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
outmap[field] = v.(float64)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("Unsupported type %T encountered", t)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Suricata) parse(acc telegraf.Accumulator, sjson []byte) {
|
||||||
|
// initial parsing
|
||||||
|
var result map[string]interface{}
|
||||||
|
err := json.Unmarshal([]byte(sjson), &result)
|
||||||
|
if err != nil {
|
||||||
|
acc.AddError(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for presence of relevant stats
|
||||||
|
if _, ok := result["stats"]; !ok {
|
||||||
|
s.Log.Debug("Input does not contain necessary 'stats' sub-object")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := result["stats"].(map[string]interface{}); !ok {
|
||||||
|
s.Log.Debug("The 'stats' sub-object does not have required structure")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := make(map[string](map[string]interface{}))
|
||||||
|
totalmap := make(map[string]interface{})
|
||||||
|
for k, v := range result["stats"].(map[string]interface{}) {
|
||||||
|
if k == "threads" {
|
||||||
|
if v, ok := v.(map[string]interface{}); ok {
|
||||||
|
for k, t := range v {
|
||||||
|
outmap := make(map[string]interface{})
|
||||||
|
if threadStruct, ok := t.(map[string]interface{}); ok {
|
||||||
|
err = flexFlatten(outmap, "", threadStruct, s.Delimiter)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Debug(err)
|
||||||
|
// we skip this thread as something did not parse correctly
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fields[k] = outmap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
s.Log.Debug("The 'threads' sub-object does not have required structure")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err = flexFlatten(totalmap, k, v, s.Delimiter)
|
||||||
|
if err != nil {
|
||||||
|
s.Log.Debug(err.Error())
|
||||||
|
// we skip this subitem as something did not parse correctly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fields["total"] = totalmap
|
||||||
|
|
||||||
|
for k := range fields {
|
||||||
|
if k == "Global" {
|
||||||
|
acc.AddFields("suricata", fields[k], nil)
|
||||||
|
} else {
|
||||||
|
acc.AddFields("suricata", fields[k], map[string]string{"thread": k})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gather measures and submits one full set of telemetry to Telegraf.
|
||||||
|
// Not used here, submission is completely input-driven.
|
||||||
|
func (s *Suricata) Gather(acc telegraf.Accumulator) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
inputs.Add("suricata", func() telegraf.Input {
|
||||||
|
return &Suricata{
|
||||||
|
Source: "/var/run/suricata-stats.sock",
|
||||||
|
Delimiter: "_",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,472 @@
|
||||||
|
package suricata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"math/rand"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
"github.com/influxdata/telegraf/testutil"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
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": { "foo": { "capture":{"kernel_packets":905344474,"kernel_drops":78355440}}}}}`
|
||||||
|
var ex4 = `{"timestamp":"2017-03-06T07:43:39.000397+0000","event_type":"stats","stats":{"threads": { "W1#en..bar1": { "capture":{"kernel_packets":905344474,"kernel_drops":78355440}}}}}`
|
||||||
|
var brokenType1 = `{"timestamp":"2017-03-06T07:43:39.000397+0000","event_type":"stats","stats":{"threads": { "W1#en..bar1": { "capture":{"kernel_packets":905344474,"kernel_drops": true}}}}}`
|
||||||
|
var brokenType2 = `{"timestamp":"2017-03-06T07:43:39.000397+0000","event_type":"stats","stats":{"threads": { "W1#en..bar1": { "capture":{"kernel_packets":905344474,"kernel_drops": ["foo"]}}}}}`
|
||||||
|
var brokenType3 = `{"timestamp":"2017-03-06T07:43:39.000397+0000","event_type":"stats","stats":{"threads": { "W1#en..bar1": { "capture":{"kernel_packets":905344474,"kernel_drops":"none this time"}}}}}`
|
||||||
|
var brokenType4 = `{"timestamp":"2017-03-06T07:43:39.000397+0000","event_type":"stats","stats":{"threads": { "W1#en..bar1": { "capture":{"kernel_packets":905344474,"kernel_drops":null}}}}}`
|
||||||
|
var brokenType5 = `{"timestamp":"2017-03-06T07:43:39.000397+0000","event_type":"stats","stats":{"foo": null}}`
|
||||||
|
var brokenStruct1 = `{"timestamp":"2017-03-06T07:43:39.000397+0000","event_type":"stats","stats":{"threads": ["foo"]}}`
|
||||||
|
var brokenStruct2 = `{"timestamp":"2017-03-06T07:43:39.000397+0000","event_type":"stats"}`
|
||||||
|
var brokenStruct3 = `{"timestamp":"2017-03-06T07:43:39.000397+0000","event_type":"stats","stats": "foobar"}`
|
||||||
|
var brokenStruct4 = `{"timestamp":"2017-03-06T07:43:39.000397+0000","event_type":"stats","stats": null}`
|
||||||
|
var singleDotRegexp = regexp.MustCompilePOSIX(`[^.]\.[^.]`)
|
||||||
|
|
||||||
|
func TestSuricataLarge(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
tmpfn := filepath.Join(dir, fmt.Sprintf("t%d", rand.Int63()))
|
||||||
|
|
||||||
|
s := Suricata{
|
||||||
|
Source: tmpfn,
|
||||||
|
Delimiter: ".",
|
||||||
|
Log: testutil.Logger{
|
||||||
|
Name: "inputs.suricata",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
acc.SetDebug(true)
|
||||||
|
assert.NoError(t, s.Start(&acc))
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile("testdata/test1.json")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
c, err := net.Dial("unix", tmpfn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
c.Write([]byte(data))
|
||||||
|
c.Write([]byte("\n"))
|
||||||
|
c.Close()
|
||||||
|
|
||||||
|
acc.Wait(1)
|
||||||
|
|
||||||
|
s.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuricata(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
tmpfn := filepath.Join(dir, fmt.Sprintf("t%d", rand.Int63()))
|
||||||
|
|
||||||
|
s := Suricata{
|
||||||
|
Source: tmpfn,
|
||||||
|
Delimiter: ".",
|
||||||
|
Log: testutil.Logger{
|
||||||
|
Name: "inputs.suricata",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
acc.SetDebug(true)
|
||||||
|
assert.NoError(t, s.Start(&acc))
|
||||||
|
|
||||||
|
c, err := net.Dial("unix", tmpfn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed: %s", err.Error())
|
||||||
|
}
|
||||||
|
c.Write([]byte(ex2))
|
||||||
|
c.Write([]byte("\n"))
|
||||||
|
c.Close()
|
||||||
|
|
||||||
|
acc.Wait(1)
|
||||||
|
|
||||||
|
s.Stop()
|
||||||
|
s = Suricata{
|
||||||
|
Source: tmpfn,
|
||||||
|
Delimiter: ".",
|
||||||
|
Log: testutil.Logger{
|
||||||
|
Name: "inputs.suricata",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
acc.AssertContainsTaggedFields(t, "suricata",
|
||||||
|
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),
|
||||||
|
},
|
||||||
|
map[string]string{"thread": "total"})
|
||||||
|
|
||||||
|
acc = testutil.Accumulator{}
|
||||||
|
acc.SetDebug(true)
|
||||||
|
assert.NoError(t, s.Start(&acc))
|
||||||
|
|
||||||
|
c, err = net.Dial("unix", tmpfn)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
c.Write([]byte(""))
|
||||||
|
c.Write([]byte("\n"))
|
||||||
|
c.Write([]byte("foobard}\n"))
|
||||||
|
c.Write([]byte(ex3))
|
||||||
|
c.Write([]byte("\n"))
|
||||||
|
c.Close()
|
||||||
|
acc.Wait(1)
|
||||||
|
|
||||||
|
s.Stop()
|
||||||
|
|
||||||
|
acc.AssertContainsTaggedFields(t, "suricata",
|
||||||
|
map[string]interface{}{
|
||||||
|
"capture.kernel_packets": float64(905344474),
|
||||||
|
"capture.kernel_drops": float64(78355440),
|
||||||
|
},
|
||||||
|
map[string]string{"thread": "foo"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuricataInvalid(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
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)
|
||||||
|
|
||||||
|
assert.NoError(t, s.Start(&acc))
|
||||||
|
|
||||||
|
c, err := net.Dial("unix", tmpfn)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
c.Write([]byte("sfjiowef"))
|
||||||
|
c.Write([]byte("\n"))
|
||||||
|
c.Close()
|
||||||
|
|
||||||
|
acc.WaitError(1)
|
||||||
|
s.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func splitAtSingleDot(in string) []string {
|
||||||
|
res := singleDotRegexp.FindAllStringIndex(in, -1)
|
||||||
|
if res == nil {
|
||||||
|
return []string{in}
|
||||||
|
}
|
||||||
|
ret := make([]string, 0)
|
||||||
|
startpos := 0
|
||||||
|
for _, v := range res {
|
||||||
|
ret = append(ret, in[startpos:v[0]+1])
|
||||||
|
startpos = v[1] - 1
|
||||||
|
}
|
||||||
|
return append(ret, in[startpos:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuricataSplitDots(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
tmpfn := filepath.Join(dir, fmt.Sprintf("t%d", rand.Int63()))
|
||||||
|
|
||||||
|
out := splitAtSingleDot("foo")
|
||||||
|
if len(out) != 1 {
|
||||||
|
t.Fatalf("splitting 'foo' should yield one result")
|
||||||
|
}
|
||||||
|
if out[0] != "foo" {
|
||||||
|
t.Fatalf("splitting 'foo' should yield one result, 'foo'")
|
||||||
|
}
|
||||||
|
|
||||||
|
s := Suricata{
|
||||||
|
Source: tmpfn,
|
||||||
|
Delimiter: ".",
|
||||||
|
Log: testutil.Logger{
|
||||||
|
Name: "inputs.suricata",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
acc.SetDebug(true)
|
||||||
|
|
||||||
|
assert.NoError(t, s.Start(&acc))
|
||||||
|
|
||||||
|
c, err := net.Dial("unix", tmpfn)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
c.Write([]byte(ex4))
|
||||||
|
c.Write([]byte("\n"))
|
||||||
|
c.Close()
|
||||||
|
|
||||||
|
acc.Wait(1)
|
||||||
|
acc.AssertContainsTaggedFields(t, "suricata",
|
||||||
|
map[string]interface{}{
|
||||||
|
"capture.kernel_packets": float64(905344474),
|
||||||
|
"capture.kernel_drops": float64(78355440),
|
||||||
|
},
|
||||||
|
map[string]string{"thread": "W1#en..bar1"})
|
||||||
|
|
||||||
|
s.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
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{}
|
||||||
|
acc.SetDebug(true)
|
||||||
|
|
||||||
|
assert.Error(t, s.Start(&acc))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuricataTooLongLine(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
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)
|
||||||
|
|
||||||
|
assert.NoError(t, s.Start(&acc))
|
||||||
|
|
||||||
|
c, err := net.Dial("unix", tmpfn)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
c.Write([]byte(strings.Repeat("X", 20000000)))
|
||||||
|
c.Write([]byte("\n"))
|
||||||
|
c.Close()
|
||||||
|
|
||||||
|
acc.WaitError(1)
|
||||||
|
|
||||||
|
s.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuricataEmptyJSON(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
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)
|
||||||
|
|
||||||
|
assert.NoError(t, s.Start(&acc))
|
||||||
|
|
||||||
|
c, err := net.Dial("unix", tmpfn)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
c.Write([]byte("\n"))
|
||||||
|
c.Close()
|
||||||
|
|
||||||
|
acc.WaitError(1)
|
||||||
|
|
||||||
|
s.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuricataInvalidInputs(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
defer func() {
|
||||||
|
log.SetOutput(os.Stderr)
|
||||||
|
}()
|
||||||
|
tmpfn := filepath.Join(dir, fmt.Sprintf("t%d", rand.Int63()))
|
||||||
|
|
||||||
|
for input, errmsg := range map[string]string{
|
||||||
|
brokenType1: `Unsupported type bool encountered`,
|
||||||
|
brokenType2: `Unsupported type []interface {} encountered`,
|
||||||
|
brokenType3: `Unsupported type string encountered`,
|
||||||
|
brokenType4: `Unsupported type <nil> encountered`,
|
||||||
|
brokenType5: `Unsupported type <nil> encountered`,
|
||||||
|
brokenStruct1: `The 'threads' sub-object does not have required structure`,
|
||||||
|
brokenStruct2: `Input does not contain necessary 'stats' sub-object`,
|
||||||
|
brokenStruct3: `The 'stats' sub-object does not have required structure`,
|
||||||
|
brokenStruct4: `The 'stats' sub-object does not have required structure`,
|
||||||
|
} {
|
||||||
|
var logBuf buffer
|
||||||
|
logBuf.Reset()
|
||||||
|
log.SetOutput(&logBuf)
|
||||||
|
|
||||||
|
acc := testutil.Accumulator{}
|
||||||
|
acc.SetDebug(true)
|
||||||
|
|
||||||
|
s := Suricata{
|
||||||
|
Source: tmpfn,
|
||||||
|
Delimiter: ".",
|
||||||
|
Log: testutil.Logger{
|
||||||
|
Name: "inputs.suricata",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.NoError(t, s.Start(&acc))
|
||||||
|
|
||||||
|
c, err := net.Dial("unix", tmpfn)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
c.Write([]byte(input))
|
||||||
|
c.Write([]byte("\n"))
|
||||||
|
c.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
if bytes.Count(logBuf.Bytes(), []byte{'\n'}) > 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
time.Sleep(50 * time.Millisecond)
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Contains(t, logBuf.String(), errmsg)
|
||||||
|
s.Stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuricataDisconnectSocket(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
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)
|
||||||
|
|
||||||
|
assert.NoError(t, s.Start(&acc))
|
||||||
|
|
||||||
|
c, err := net.Dial("unix", tmpfn)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
c.Write([]byte(ex2))
|
||||||
|
c.Write([]byte("\n"))
|
||||||
|
c.Close()
|
||||||
|
|
||||||
|
c, err = net.Dial("unix", tmpfn)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
c.Write([]byte(ex3))
|
||||||
|
c.Write([]byte("\n"))
|
||||||
|
c.Close()
|
||||||
|
|
||||||
|
acc.Wait(2)
|
||||||
|
|
||||||
|
s.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuricataPluginDesc(t *testing.T) {
|
||||||
|
v, ok := inputs.Inputs["suricata"]
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("suricata plugin not registered")
|
||||||
|
}
|
||||||
|
desc := v().Description()
|
||||||
|
if desc != "Suricata stats plugin" {
|
||||||
|
t.Fatal("invalid description ", desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuricataStartStop(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
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)
|
||||||
|
assert.NoError(t, s.Start(&acc))
|
||||||
|
s.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuricataGather(t *testing.T) {
|
||||||
|
dir, err := ioutil.TempDir("", "test")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
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)
|
||||||
|
assert.NoError(t, s.Gather(&acc))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSuricataSampleConfig(t *testing.T) {
|
||||||
|
v, ok := inputs.Inputs["suricata"]
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("suricata plugin not registered")
|
||||||
|
}
|
||||||
|
if v().SampleConfig() != sampleConfig {
|
||||||
|
t.Fatal("wrong sampleconfig")
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
package suricata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A thread-safe Buffer wrapper to enable concurrent access to log output.
|
||||||
|
type buffer struct {
|
||||||
|
b bytes.Buffer
|
||||||
|
m sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *buffer) Read(p []byte) (n int, err error) {
|
||||||
|
b.m.Lock()
|
||||||
|
defer b.m.Unlock()
|
||||||
|
return b.b.Read(p)
|
||||||
|
}
|
||||||
|
func (b *buffer) Write(p []byte) (n int, err error) {
|
||||||
|
b.m.Lock()
|
||||||
|
defer b.m.Unlock()
|
||||||
|
return b.b.Write(p)
|
||||||
|
}
|
||||||
|
func (b *buffer) String() string {
|
||||||
|
b.m.Lock()
|
||||||
|
defer b.m.Unlock()
|
||||||
|
return b.b.String()
|
||||||
|
}
|
||||||
|
func (b *buffer) Reset() {
|
||||||
|
b.m.Lock()
|
||||||
|
defer b.m.Unlock()
|
||||||
|
b.b.Reset()
|
||||||
|
}
|
||||||
|
func (b *buffer) Bytes() []byte {
|
||||||
|
b.m.Lock()
|
||||||
|
defer b.m.Unlock()
|
||||||
|
return b.b.Bytes()
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue