fluent-plugin-elastic-log 0.4.1 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +48 -21
- data/fluent-plugin-elastic-log.gemspec +1 -1
- data/lib/fluent/plugin/elastic_log/audit_log_to_metric_processor.rb +19 -2
- data/lib/fluent/plugin/elastic_log/failed_login_metric.rb +105 -0
- data/lib/fluent/plugin/elastic_log/granted_privileges_metric.rb +14 -7
- data/lib/fluent/plugin/out_elastic_audit_log_metric.rb +16 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8fc9200f1f1d69c9899d9d0a3daf550ea117c40794bfb0c4aea36bf98514df60
|
4
|
+
data.tar.gz: 5d6d1da9522cbd4e4888f791323f1402107714d522bd30a44eb1d04f58bbacfb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a98aad4e1f27636f65a677680146fbc0e286f3875f175ec172b2e33f286e60219d24d8945bae7022498c8d92f07a2df2e2e2e35e94d5101eef4c49a1e0f19d1f
|
7
|
+
data.tar.gz: 51a82ded9b94c7841b9da3d67611c928b0ec315e276cdd314c09ca15fe4d5aef3e0073d8a2614e21871d31019148fc04bd45227b15f5d9f2717d5a98645ab206
|
data/README.md
CHANGED
@@ -1,40 +1,67 @@
|
|
1
1
|
# fluent-plugin-elastic-log
|
2
2
|
|
3
|
-
[Fluentd](https://fluentd.org/) filter plugin to
|
3
|
+
[Fluentd](https://fluentd.org/) filter plugin to process elastic logs.
|
4
4
|
|
5
|
-
|
5
|
+
## plugins
|
6
6
|
|
7
|
-
|
7
|
+
### out - elastic_audit_log_metric
|
8
8
|
|
9
|
-
|
9
|
+
process audit logs and transform to metrics.
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
Example:
|
12
|
+
|
13
|
+
``` conf
|
14
|
+
<match my_tag_pattern>
|
15
|
+
@type elastic_audit_log_metric
|
16
|
+
|
17
|
+
tag elastic_audit_log_metric
|
18
|
+
timestamp_key timestamp
|
19
|
+
timestamp_format epochmillis
|
20
|
+
prefix tags_
|
21
|
+
</match>
|
13
22
|
```
|
14
23
|
|
15
|
-
|
24
|
+
parameters are:
|
25
|
+
* tag : Tag to emit metric events
|
26
|
+
|
27
|
+
parameters for input record:
|
28
|
+
* categories: Categories selected to be converted to metrics
|
29
|
+
* category_key: Category key in input record
|
30
|
+
* layer_key: Layer key in input record
|
31
|
+
* request_type_key: Request type key in input record
|
32
|
+
* cluster_key: Cluster key in input record
|
33
|
+
* user_key: Request user key in input record
|
34
|
+
* indices_key: Indices key in input record
|
35
|
+
* r_indices_key: Resolved indices key in input record
|
36
|
+
* timestamp_key: Timestamp key in input record
|
37
|
+
* privilege_key: Request privilege key in input record
|
38
|
+
* rest_request_path_key: Rest request path key in input record
|
39
|
+
* request_body_key: Request body key in input record
|
40
|
+
|
41
|
+
parameters for output metric:
|
42
|
+
* timestamp_format: Timestamp format (iso, epochmillis, epochmillis_str)
|
43
|
+
* prefix: Attribute prefix for output metric
|
44
|
+
* aggregate_index: Aggregate index (remove ilm suffix, wildcard suffix)
|
45
|
+
|
46
|
+
More details from the
|
47
|
+
[elastic_audit_log_metric output plugin code](lib/fluent/plugin/out_elastic_audit_log_metric.rb#L49)
|
16
48
|
|
17
|
-
|
49
|
+
## Installation
|
18
50
|
|
19
|
-
```ruby
|
20
|
-
gem "fluent-plugin-elastic-log"
|
21
|
-
```
|
22
51
|
|
23
|
-
|
52
|
+
Manual install, by executing:
|
24
53
|
|
25
|
-
|
26
|
-
$ bundle
|
27
|
-
```
|
54
|
+
$ gem install fluent-plugin-elastic-log
|
28
55
|
|
29
|
-
|
56
|
+
Add to Gemfile with:
|
30
57
|
|
31
|
-
|
58
|
+
$ bundle add fluent-plugin-elastic-log
|
32
59
|
|
33
|
-
|
34
|
-
$ fluent-plugin-config-format filter elastic-log
|
35
|
-
```
|
60
|
+
## Compatibility
|
36
61
|
|
37
|
-
|
62
|
+
plugin in 1.x.x will work with:
|
63
|
+
- ruby >= 2.4.10
|
64
|
+
- td-agent >= 3.8.1-0
|
38
65
|
|
39
66
|
## Copyright
|
40
67
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'granted_privileges_metric'
|
4
|
+
require_relative 'failed_login_metric'
|
4
5
|
|
5
6
|
module Fluent
|
6
7
|
module Plugin
|
@@ -14,7 +15,7 @@ module Fluent
|
|
14
15
|
end
|
15
16
|
|
16
17
|
def process(_tag, log_es)
|
17
|
-
metric_es =
|
18
|
+
metric_es = []
|
18
19
|
|
19
20
|
log_es.each do |time, record|
|
20
21
|
next unless record
|
@@ -22,7 +23,7 @@ module Fluent
|
|
22
23
|
next unless conf.categories.include? category
|
23
24
|
|
24
25
|
new_records = send("generate_#{category.downcase}_metrics_for", record)
|
25
|
-
new_records&.each { |new_record| metric_es
|
26
|
+
new_records&.each { |new_record| metric_es << [time, new_record] }
|
26
27
|
end
|
27
28
|
metric_es
|
28
29
|
end
|
@@ -48,6 +49,22 @@ module Fluent
|
|
48
49
|
).generate_metrics
|
49
50
|
end
|
50
51
|
# rubocop:enable Metrics/AbcSize
|
52
|
+
|
53
|
+
# rubocop:disable Metrics/AbcSize
|
54
|
+
def generate_failed_login_metrics_for(record)
|
55
|
+
FailedLoginMetric.new(
|
56
|
+
record: {
|
57
|
+
timestamp: record[conf.timestamp_key],
|
58
|
+
user: record[conf.user_key],
|
59
|
+
cluster: record[conf.cluster_key],
|
60
|
+
layer: record[conf.layer_key],
|
61
|
+
request_path: record[conf.rest_request_path_key],
|
62
|
+
request_body: record[conf.request_body_key]
|
63
|
+
},
|
64
|
+
conf: conf
|
65
|
+
).generate_metrics
|
66
|
+
end
|
67
|
+
# rubocop:enable Metrics/AbcSize
|
51
68
|
end
|
52
69
|
end
|
53
70
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require 'time'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
module Fluent
|
8
|
+
module Plugin
|
9
|
+
module ElasticLog
|
10
|
+
# record to metric converter
|
11
|
+
# for FAILED_LOGIN
|
12
|
+
class FailedLoginMetric
|
13
|
+
attr_reader :record, :conf
|
14
|
+
|
15
|
+
ELASTIC_URL_PATTERN = %r{(?:/(?<target>[^/]*))?/(?<action>_\w+)}.freeze
|
16
|
+
QUERY_TYPE_MAP = {
|
17
|
+
'_msearch' => 'msearch',
|
18
|
+
'_bulk' => 'bulk',
|
19
|
+
'_doc' => 'write',
|
20
|
+
'_create' => 'write',
|
21
|
+
'_search' => 'search'
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
INDEX_PATTERN = /-?\*$/.freeze
|
25
|
+
ILM_PATTERN = /-\d{6}$/.freeze
|
26
|
+
|
27
|
+
def initialize(record:, conf:)
|
28
|
+
@record = record
|
29
|
+
@conf = conf
|
30
|
+
end
|
31
|
+
|
32
|
+
def timestamp
|
33
|
+
timestamp = Time.parse(record[:timestamp])
|
34
|
+
|
35
|
+
return (timestamp.utc.to_f * 1000).to_i if conf.timestamp_format == :epochmillis
|
36
|
+
return timestamp.utc.strftime('%s%3N') if conf.timestamp_format == :epochmillis_str
|
37
|
+
|
38
|
+
timestamp.utc.iso8601(3)
|
39
|
+
rescue StandardError
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def query_details
|
44
|
+
if (match = ELASTIC_URL_PATTERN.match(record[:request_path]))
|
45
|
+
return [QUERY_TYPE_MAP.fetch(match[:action], 'other'),
|
46
|
+
match[:target]]
|
47
|
+
end
|
48
|
+
['other', nil]
|
49
|
+
end
|
50
|
+
|
51
|
+
def base
|
52
|
+
{
|
53
|
+
'timestamp' => timestamp,
|
54
|
+
'metric_name' => 'failed_login_count',
|
55
|
+
'metric_value' => 1,
|
56
|
+
"#{conf.prefix}user" => record[:user],
|
57
|
+
"#{conf.prefix}cluster" => record[:cluster]
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def bulk_indices
|
62
|
+
req_body = record.fetch(:request_body, {})
|
63
|
+
return [] if req_body.empty?
|
64
|
+
|
65
|
+
req_body.each_line.each_slice(2).with_object(Set.new) do |(cmd_line, _data_line), acc|
|
66
|
+
cmd = JSON.parse(cmd_line)
|
67
|
+
acc << aggregate_index(cmd[cmd.keys.first]['_index'])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def msearch_indices
|
72
|
+
req_body = record.fetch(:request_body, {})
|
73
|
+
return [] if req_body.empty?
|
74
|
+
|
75
|
+
req_body.each_line.each_slice(2).with_object(Set.new) do |(cmd_line, _data_line), acc|
|
76
|
+
cmd = JSON.parse(cmd_line)
|
77
|
+
acc << aggregate_index(cmd['index'])
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def aggregate_index(index)
|
82
|
+
return unless index
|
83
|
+
return index unless conf.aggregate_index
|
84
|
+
|
85
|
+
index.sub(INDEX_PATTERN, '').sub(ILM_PATTERN, '')
|
86
|
+
end
|
87
|
+
|
88
|
+
def generate_metrics
|
89
|
+
query_action, query_index = query_details
|
90
|
+
indices = case query_action
|
91
|
+
when 'bulk' then bulk_indices
|
92
|
+
when 'msearch' then msearch_indices
|
93
|
+
else []
|
94
|
+
end
|
95
|
+
indices << query_index if query_index || indices.empty?
|
96
|
+
|
97
|
+
indices.inject([]) do |metrics, index|
|
98
|
+
metrics << base.merge("#{conf.prefix}index" => index,
|
99
|
+
"#{conf.prefix}query_type" => query_action)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -29,7 +29,8 @@ module Fluent
|
|
29
29
|
'indices:monitor/' => 'monitor'
|
30
30
|
}.freeze
|
31
31
|
|
32
|
-
|
32
|
+
INDEX_PATTERN = /-?\*$/.freeze
|
33
|
+
ILM_PATTERN = /-\d{6}$/.freeze
|
33
34
|
|
34
35
|
attr_reader :record, :conf
|
35
36
|
|
@@ -69,19 +70,25 @@ module Fluent
|
|
69
70
|
|
70
71
|
def indices
|
71
72
|
indices = record[:r_indices] || record[:indices] || [nil]
|
72
|
-
if conf.
|
73
|
+
if conf.aggregate_index
|
73
74
|
indices = indices.inject(Set.new) do |acc, index|
|
74
|
-
|
75
|
-
|
76
|
-
end.to_a
|
75
|
+
acc << aggregate_index(index)
|
76
|
+
end
|
77
77
|
end
|
78
78
|
indices
|
79
79
|
end
|
80
80
|
|
81
|
+
def aggregate_index(index)
|
82
|
+
return unless index
|
83
|
+
return index unless conf.aggregate_index
|
84
|
+
|
85
|
+
index.sub(INDEX_PATTERN, '').sub(ILM_PATTERN, '')
|
86
|
+
end
|
87
|
+
|
81
88
|
def generate_metrics
|
82
89
|
metrics = []
|
83
|
-
indices.each do |
|
84
|
-
metrics << base.merge("#{conf.prefix}
|
90
|
+
indices.each do |index|
|
91
|
+
metrics << base.merge("#{conf.prefix}index" => index)
|
85
92
|
end
|
86
93
|
metrics
|
87
94
|
end
|
@@ -31,7 +31,7 @@ module Fluent
|
|
31
31
|
|
32
32
|
helpers :event_emitter
|
33
33
|
|
34
|
-
ALLOWED_CATEGORIES = %w[GRANTED_PRIVILEGES].freeze
|
34
|
+
ALLOWED_CATEGORIES = %w[GRANTED_PRIVILEGES FAILED_LOGIN].freeze
|
35
35
|
# FAILED_LOGIN AUTHENTICATED MISSING_PRIVILEGES SSL_EXCEPTION
|
36
36
|
# OPENDISTRO_SECURITY_INDEX_ATTEMPT BAD_HEADERS
|
37
37
|
|
@@ -42,6 +42,8 @@ module Fluent
|
|
42
42
|
DEFAULT_USER_KEY = 'audit_request_effective_user'
|
43
43
|
DEFAULT_INDICES_KEY = 'audit_trace_indices'
|
44
44
|
DEFAULT_R_INDICES_KEY = 'audit_trace_resolved_indices'
|
45
|
+
DEFAULT_REST_REQUEST_PATH = 'audit_rest_request_path'
|
46
|
+
DEFAULT_REQUEST_BODY = 'audit_request_body'
|
45
47
|
DEFAULT_TIMESTAMP_KEY = '@timestamp'
|
46
48
|
DEFAULT_PRIVILEGE_KEY = 'audit_request_privilege'
|
47
49
|
DEFAULT_PREFIX = ''
|
@@ -69,13 +71,19 @@ module Fluent
|
|
69
71
|
config_param :timestamp_key, :string, default: DEFAULT_TIMESTAMP_KEY
|
70
72
|
desc 'Request privilege key'
|
71
73
|
config_param :privilege_key, :string, default: DEFAULT_PRIVILEGE_KEY
|
74
|
+
desc 'Rest request path key'
|
75
|
+
config_param :rest_request_path_key, :string, default: DEFAULT_REST_REQUEST_PATH
|
76
|
+
desc 'Request body key'
|
77
|
+
config_param :request_body_key, :string, default: DEFAULT_REQUEST_BODY
|
72
78
|
|
73
79
|
desc 'Timestamp format'
|
74
80
|
config_param :timestamp_format, :enum, list: %i[iso epochmillis epochmillis_str], default: :iso
|
75
81
|
desc 'Attribute prefix'
|
76
82
|
config_param :prefix, :string, default: DEFAULT_PREFIX
|
77
|
-
desc 'Aggregate
|
78
|
-
config_param :
|
83
|
+
desc 'Aggregate index'
|
84
|
+
config_param :aggregate_index, :bool, default: true
|
85
|
+
desc 'Events block size'
|
86
|
+
config_param :event_stream_size, :integer, default: 1000
|
79
87
|
|
80
88
|
attr_reader :metric_processor
|
81
89
|
|
@@ -109,7 +117,11 @@ module Fluent
|
|
109
117
|
|
110
118
|
def process(_tag, es)
|
111
119
|
metrics = metric_processor.process(tag, es) || []
|
112
|
-
|
120
|
+
metrics.each_slice(event_stream_size) do |metrics_slice|
|
121
|
+
metrics_es = MultiEventStream.new
|
122
|
+
metrics_slice.each { |time, record| metrics_es.add(time, record) }
|
123
|
+
router.emit_stream(tag, metrics_es)
|
124
|
+
end
|
113
125
|
end
|
114
126
|
end
|
115
127
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-elastic-log
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Thomas Tych
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-06-
|
11
|
+
date: 2023-06-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bump
|
@@ -163,6 +163,7 @@ files:
|
|
163
163
|
- Rakefile
|
164
164
|
- fluent-plugin-elastic-log.gemspec
|
165
165
|
- lib/fluent/plugin/elastic_log/audit_log_to_metric_processor.rb
|
166
|
+
- lib/fluent/plugin/elastic_log/failed_login_metric.rb
|
166
167
|
- lib/fluent/plugin/elastic_log/granted_privileges_metric.rb
|
167
168
|
- lib/fluent/plugin/out_elastic_audit_log_metric.rb
|
168
169
|
homepage: https://gitlab.com/ttych/fluent-plugin-elastic-log
|