Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
194
plugins/inputs/suricata/README.md
Normal file
194
plugins/inputs/suricata/README.md
Normal file
|
@ -0,0 +1,194 @@
|
|||
# Suricata Input Plugin
|
||||
|
||||
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.
|
||||
It can also report for triggered Suricata IDS/IPS alerts.
|
||||
|
||||
## Service Input <!-- @/docs/includes/service_input.md -->
|
||||
|
||||
This plugin is a service input. Normal plugins gather metrics determined by the
|
||||
interval setting. Service plugins start a service to listen and wait for
|
||||
metrics or events to occur. Service plugins have two key differences from
|
||||
normal plugins:
|
||||
|
||||
1. The global or plugin specific `interval` setting may not apply
|
||||
2. The CLI options of `--test`, `--test-wait`, and `--once` may not produce
|
||||
output for this plugin
|
||||
|
||||
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
||||
|
||||
In addition to the plugin-specific configuration settings, plugins support
|
||||
additional global and plugin configuration settings. These settings are used to
|
||||
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
||||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||
|
||||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml @sample.conf
|
||||
# Suricata stats and alerts plugin
|
||||
[[inputs.suricata]]
|
||||
## Source
|
||||
## 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
|
||||
## Used for flattening field keys, e.g. subitem "alert" of "detect" becomes
|
||||
## "detect_alert" when delimiter is "_".
|
||||
# delimiter = "_"
|
||||
|
||||
## Metric version
|
||||
## Version 1 only collects stats and optionally will look for alerts if
|
||||
## the configuration setting alerts is set to true.
|
||||
## Version 2 parses any event type message by default and produced metrics
|
||||
## under a single metric name using a tag to differentiate between event
|
||||
## types. The timestamp for the message is applied to the generated metric.
|
||||
## Additional tags and fields are included as well.
|
||||
# version = "1"
|
||||
|
||||
## Alerts
|
||||
## In metric version 1, only status is captured by default, alerts must be
|
||||
## turned on with this configuration option. This option does not apply for
|
||||
## metric version 2.
|
||||
# alerts = false
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
||||
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 for Suricata stats are numeric.
|
||||
|
||||
- suricata
|
||||
- tags:
|
||||
- thread: `Global` for global statistics (if enabled), thread IDs (e.g. `W#03-enp0s31f6`) for thread-specific statistics
|
||||
- fields:
|
||||
- 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
|
||||
- ...
|
||||
|
||||
Some fields of the Suricata alerts are strings, for example the signatures. See
|
||||
the Suricata [event docs][1] for more information.
|
||||
|
||||
- suricata_alert
|
||||
- fields:
|
||||
- action
|
||||
- gid
|
||||
- severity
|
||||
- signature
|
||||
- source_ip
|
||||
- source_port
|
||||
- target_port
|
||||
- target_port
|
||||
- ...
|
||||
|
||||
[1]: https://suricata.readthedocs.io/en/suricata-6.0.0/output/eve/eve-json-format.html?highlight=priority#event-type-alert
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### FreeBSD tuning
|
||||
|
||||
Under FreeBSD it is necessary to increase the localhost buffer space to at least
|
||||
16384, default is 8192 otherwise messages from Suricata are truncated as they
|
||||
exceed the default available buffer space, consequently no statistics are
|
||||
processed by the plugin.
|
||||
|
||||
```text
|
||||
sysctl -w net.local.stream.recvspace=16384
|
||||
sysctl -w net.local.stream.sendspace=16384
|
||||
```
|
||||
|
||||
## 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
|
||||
```
|
26
plugins/inputs/suricata/sample.conf
Normal file
26
plugins/inputs/suricata/sample.conf
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Suricata stats and alerts plugin
|
||||
[[inputs.suricata]]
|
||||
## Source
|
||||
## 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
|
||||
## Used for flattening field keys, e.g. subitem "alert" of "detect" becomes
|
||||
## "detect_alert" when delimiter is "_".
|
||||
# delimiter = "_"
|
||||
|
||||
## Metric version
|
||||
## Version 1 only collects stats and optionally will look for alerts if
|
||||
## the configuration setting alerts is set to true.
|
||||
## Version 2 parses any event type message by default and produced metrics
|
||||
## under a single metric name using a tag to differentiate between event
|
||||
## types. The timestamp for the message is applied to the generated metric.
|
||||
## Additional tags and fields are included as well.
|
||||
# version = "1"
|
||||
|
||||
## Alerts
|
||||
## In metric version 1, only status is captured by default, alerts must be
|
||||
## turned on with this configuration option. This option does not apply for
|
||||
## metric version 2.
|
||||
# alerts = false
|
352
plugins/inputs/suricata/suricata.go
Normal file
352
plugins/inputs/suricata/suricata.go
Normal file
|
@ -0,0 +1,352 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package suricata
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/internal"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
type Suricata struct {
|
||||
Source string `toml:"source"`
|
||||
Delimiter string `toml:"delimiter"`
|
||||
Alerts bool `toml:"alerts"`
|
||||
Version string `toml:"version"`
|
||||
|
||||
inputListener *net.UnixListener
|
||||
cancel context.CancelFunc
|
||||
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func (*Suricata) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (s *Suricata) Init() error {
|
||||
if s.Source == "" {
|
||||
s.Source = "/var/run/suricata-stats.sock"
|
||||
}
|
||||
|
||||
if s.Delimiter == "" {
|
||||
s.Delimiter = "_"
|
||||
}
|
||||
|
||||
switch s.Version {
|
||||
case "":
|
||||
s.Version = "1"
|
||||
case "1", "2":
|
||||
default:
|
||||
return fmt.Errorf("invalid version %q, use either 1 or 2", s.Version)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Gather measures and submits one full set of telemetry to Telegraf.
|
||||
// Not used here, submission is completely input-driven.
|
||||
func (*Suricata) Gather(telegraf.Accumulator) error {
|
||||
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
|
||||
}
|
||||
if len(line) > 0 {
|
||||
err := s.parse(acc, line)
|
||||
if err != nil {
|
||||
acc.AddError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 !errors.Is(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 []interface{}:
|
||||
for _, v := range t {
|
||||
err := flexFlatten(outmap, field, v, delimiter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
case string:
|
||||
outmap[field] = v
|
||||
case float64:
|
||||
outmap[field] = t
|
||||
default:
|
||||
return fmt.Errorf("unsupported type %T encountered", t)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Suricata) parseAlert(acc telegraf.Accumulator, result map[string]interface{}) {
|
||||
if _, ok := result["alert"].(map[string]interface{}); !ok {
|
||||
s.Log.Debug("'alert' sub-object does not have required structure")
|
||||
return
|
||||
}
|
||||
|
||||
totalmap := make(map[string]interface{})
|
||||
for k, v := range result["alert"].(map[string]interface{}) {
|
||||
// source and target fields are maps
|
||||
err := flexFlatten(totalmap, k, v, s.Delimiter)
|
||||
if err != nil {
|
||||
s.Log.Debugf("Flattening alert failed: %v", err)
|
||||
// we skip this subitem as something did not parse correctly
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// threads field do not exist in alert output, always global
|
||||
acc.AddFields("suricata_alert", totalmap, nil)
|
||||
}
|
||||
|
||||
func (s *Suricata) parseStats(acc telegraf.Accumulator, result map[string]interface{}) {
|
||||
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.Debugf("Flattening alert failed: %v", 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.Debugf("Flattening alert failed: %v", err)
|
||||
// we skip this subitem as something did not parse correctly
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
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})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Suricata) parseGeneric(acc telegraf.Accumulator, result map[string]interface{}) error {
|
||||
eventType := ""
|
||||
if _, ok := result["event_type"]; !ok {
|
||||
return fmt.Errorf("unable to determine event type of message: %s", result)
|
||||
}
|
||||
value, err := internal.ToString(result["event_type"])
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to convert event type %q to string: %w", result["event_type"], err)
|
||||
}
|
||||
eventType = value
|
||||
|
||||
timestamp := time.Now()
|
||||
if val, ok := result["timestamp"]; ok {
|
||||
value, err := internal.ToString(val)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to convert timestamp %q to string: %w", val, err)
|
||||
}
|
||||
timestamp, err = time.Parse("2006-01-02T15:04:05.999999-0700", value)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse timestamp %q: %w", val, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure the event key exists first
|
||||
if _, ok := result[eventType].(map[string]interface{}); !ok {
|
||||
return fmt.Errorf("unable to find key %q in %s", eventType, result)
|
||||
}
|
||||
|
||||
fields := make(map[string]interface{})
|
||||
for k, v := range result[eventType].(map[string]interface{}) {
|
||||
err := flexFlatten(fields, k, v, s.Delimiter)
|
||||
if err != nil {
|
||||
s.Log.Debugf("Flattening %q failed: %v", eventType, err)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"event_type": eventType,
|
||||
}
|
||||
|
||||
// best effort to gather these tags and fields, if errors are encountered
|
||||
// we ignore and move on
|
||||
for _, key := range []string{"proto", "out_iface", "in_iface"} {
|
||||
if val, ok := result[key]; ok {
|
||||
if convertedVal, err := internal.ToString(val); err == nil {
|
||||
tags[key] = convertedVal
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, key := range []string{"src_ip", "dest_ip"} {
|
||||
if val, ok := result[key]; ok {
|
||||
if convertedVal, err := internal.ToString(val); err == nil {
|
||||
fields[key] = convertedVal
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, key := range []string{"src_port", "dest_port"} {
|
||||
if val, ok := result[key]; ok {
|
||||
if convertedVal, err := internal.ToInt64(val); err == nil || errors.Is(err, internal.ErrOutOfRange) {
|
||||
fields[key] = convertedVal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
acc.AddFields("suricata", fields, tags, timestamp)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Suricata) parse(acc telegraf.Accumulator, sjson []byte) error {
|
||||
// initial parsing
|
||||
var result map[string]interface{}
|
||||
err := json.Unmarshal(sjson, &result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if s.Version == "2" {
|
||||
return s.parseGeneric(acc, result)
|
||||
}
|
||||
|
||||
// Version 1 parsing of stats and optionally alerts
|
||||
if _, ok := result["stats"]; ok {
|
||||
s.parseStats(acc, result)
|
||||
} else if _, ok := result["alert"]; ok {
|
||||
if s.Alerts {
|
||||
s.parseAlert(acc, result)
|
||||
}
|
||||
} else {
|
||||
s.Log.Debugf("Invalid input without 'stats' or 'alert' object: %v", result)
|
||||
return errors.New("input does not contain 'stats' or 'alert' object")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("suricata", func() telegraf.Input {
|
||||
return &Suricata{}
|
||||
})
|
||||
}
|
570
plugins/inputs/suricata/suricata_test.go
Normal file
570
plugins/inputs/suricata/suricata_test.go
Normal file
|
@ -0,0 +1,570 @@
|
|||
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())
|
||||
}
|
||||
}
|
1
plugins/inputs/suricata/testdata/test1.json
vendored
Normal file
1
plugins/inputs/suricata/testdata/test1.json
vendored
Normal file
File diff suppressed because one or more lines are too long
21
plugins/inputs/suricata/testdata/test2.json
vendored
Normal file
21
plugins/inputs/suricata/testdata/test2.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"timestamp": "2021-06-08T06:34:49.237367+0000",
|
||||
"event_type": "stats",
|
||||
"stats": {
|
||||
"threads": {
|
||||
"W#01-ens2f1": {
|
||||
"detect": {
|
||||
"engines": [
|
||||
{
|
||||
"id": 0,
|
||||
"last_reload": "2021-06-08T06:33:05.084872+0000",
|
||||
"rules_loaded": 22712,
|
||||
"rules_failed": 0
|
||||
}
|
||||
],
|
||||
"alert": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
plugins/inputs/suricata/testdata/test3.json
vendored
Normal file
1
plugins/inputs/suricata/testdata/test3.json
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
{"timestamp":"2021-05-30T20:07:13.208777+0200","flow_id":1696236471136137,"in_iface":"s1-suricata","event_type":"alert","src_ip":"10.0.0.5","src_port":18715,"dest_ip":"179.60.192.3","dest_port":80,"proto":"TCP","alert":{"action":"allowed","gid":1,"source":{"ip":"10.0.0.5","port":18715},"target":{"ip":"179.60.192.3","port":80},"signature_id":6,"rev":0,"signature":"Corrupted HTTP body","severity": 3,"category":"Misc activity","severity":3},"flow":{"pkts_toserver":1,"pkts_toclient":0,"bytes_toserver":174,"bytes_toclient":0,"start":"2021-05-30T20:07:13.208777+0200"}}
|
35
plugins/inputs/suricata/testdata/v2/alert.json
vendored
Normal file
35
plugins/inputs/suricata/testdata/v2/alert.json
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"timestamp": "2021-05-30T20:07:13.208777+0200",
|
||||
"flow_id": 1696236471136137,
|
||||
"in_iface": "s1-suricata",
|
||||
"event_type": "alert",
|
||||
"src_ip": "10.0.0.5",
|
||||
"src_port": 18715,
|
||||
"dest_ip": "179.60.192.3",
|
||||
"dest_port": 80,
|
||||
"proto": "TCP",
|
||||
"alert": {
|
||||
"action": "allowed",
|
||||
"gid": 1,
|
||||
"source": {
|
||||
"ip": "10.0.0.5",
|
||||
"port": 18715
|
||||
},
|
||||
"target": {
|
||||
"ip": "179.60.192.3",
|
||||
"port": 80
|
||||
},
|
||||
"signature_id": 6,
|
||||
"rev": 0,
|
||||
"signature": "Corrupted HTTP body",
|
||||
"severity": 3,
|
||||
"category": "Misc activity"
|
||||
},
|
||||
"flow": {
|
||||
"pkts_toserver": 1,
|
||||
"pkts_toclient": 0,
|
||||
"bytes_toserver": 174,
|
||||
"bytes_toclient": 0,
|
||||
"start": "2021-05-30T20:07:13.208777+0200"
|
||||
}
|
||||
}
|
18
plugins/inputs/suricata/testdata/v2/dns.json
vendored
Normal file
18
plugins/inputs/suricata/testdata/v2/dns.json
vendored
Normal file
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"timestamp": "2023-04-07T00:20:57.995497+0800",
|
||||
"flow_id": 2150129093506313,
|
||||
"in_iface": "eth1",
|
||||
"event_type": "dns",
|
||||
"src_ip": "192.168.0.100",
|
||||
"src_port": 39262,
|
||||
"dest_ip": "192.168.0.1",
|
||||
"dest_port": 53,
|
||||
"proto": "UDP",
|
||||
"dns": {
|
||||
"type": "query",
|
||||
"id": 7145,
|
||||
"rrname": "reddit.com",
|
||||
"rrtype": "A",
|
||||
"tx_id": 10
|
||||
}
|
||||
}
|
29
plugins/inputs/suricata/testdata/v2/drop.json
vendored
Normal file
29
plugins/inputs/suricata/testdata/v2/drop.json
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"timestamp": "2023-04-07T00:21:01.318245+0800",
|
||||
"flow_id": 180225164834117,
|
||||
"in_iface": "eth1",
|
||||
"event_type": "drop",
|
||||
"src_ip": "192.168.0.110",
|
||||
"src_port": 46016,
|
||||
"dest_ip": "54.192.18.125",
|
||||
"dest_port": 443,
|
||||
"proto": "TCP",
|
||||
"drop": {
|
||||
"len": 76,
|
||||
"tos": 0,
|
||||
"ttl": 64,
|
||||
"ipid": 62316,
|
||||
"tcpseq": 3900248957,
|
||||
"tcpack": 2339873683,
|
||||
"tcpwin": 501,
|
||||
"syn": false,
|
||||
"ack": true,
|
||||
"psh": true,
|
||||
"rst": false,
|
||||
"urg": false,
|
||||
"fin": true,
|
||||
"tcpres": 0,
|
||||
"tcpurgp": 0,
|
||||
"reason": "stream error"
|
||||
}
|
||||
}
|
21
plugins/inputs/suricata/testdata/v2/flow.json
vendored
Normal file
21
plugins/inputs/suricata/testdata/v2/flow.json
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"timestamp": "2023-04-07T00:28:22.136079+0800",
|
||||
"flow_id": 911610881873910,
|
||||
"in_iface": "eth1",
|
||||
"event_type": "flow",
|
||||
"src_ip": "192.168.0.121",
|
||||
"src_port": 50212,
|
||||
"dest_ip": "142.251.130.3",
|
||||
"dest_port": 443,
|
||||
"proto": "TCP",
|
||||
"flow": {
|
||||
"age": 0,
|
||||
"state": "new",
|
||||
"alerted": false
|
||||
},
|
||||
"tcp": {
|
||||
"tcp_flags": "00",
|
||||
"tcp_flags_ts": "00",
|
||||
"tcp_flags_tc": "00"
|
||||
}
|
||||
}
|
22
plugins/inputs/suricata/testdata/v2/http.json
vendored
Normal file
22
plugins/inputs/suricata/testdata/v2/http.json
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"timestamp": "2023-04-07T00:27:50.220224+0800",
|
||||
"flow_id": 1124332026121723,
|
||||
"in_iface": "eth2",
|
||||
"event_type": "http",
|
||||
"src_ip": "192.168.0.120",
|
||||
"src_port": 33950,
|
||||
"dest_ip": "203.205.239.179",
|
||||
"dest_port": 80,
|
||||
"proto": "TCP",
|
||||
"tx_id": 0,
|
||||
"http": {
|
||||
"hostname": "hkminorshort.weixin.qq.com",
|
||||
"url": "/mmtls/2d6d45f1",
|
||||
"http_user_agent": "MicroMessenger Client",
|
||||
"http_content_type": "application/octet-stream",
|
||||
"http_method": "POST",
|
||||
"protocol": "HTTP/1.1",
|
||||
"status": 200,
|
||||
"length": 245
|
||||
}
|
||||
}
|
30
plugins/inputs/suricata/testdata/v2/status.json
vendored
Normal file
30
plugins/inputs/suricata/testdata/v2/status.json
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
{
|
||||
"timestamp": "2023-04-07T00:21:07.805683+0800",
|
||||
"event_type": "stats",
|
||||
"stats": {
|
||||
"uptime": 160,
|
||||
"capture": {
|
||||
"kernel_packets": 522,
|
||||
"kernel_drops": 0,
|
||||
"errors": 0
|
||||
},
|
||||
"flow": {
|
||||
"memcap": 0,
|
||||
"tcp": 15,
|
||||
"udp": 13,
|
||||
"wrk": {
|
||||
"spare_sync_avg": 100,
|
||||
"spare_sync": 11
|
||||
},
|
||||
"mgr": {
|
||||
"full_hash_pass": 1,
|
||||
"closed_pruned": 0,
|
||||
"new_pruned": 0
|
||||
},
|
||||
"spare": 10100,
|
||||
"emerg_mode_entered": 0,
|
||||
"emerg_mode_over": 0,
|
||||
"memuse": 9965056
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue