fluent-auditify 0.1.0 → 0.3.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/CHANGELOG.md +9 -0
- data/lib/fluent/auditify/helper/test.rb +1 -0
- data/lib/fluent/auditify/parser/v1config.rb +74 -36
- data/lib/fluent/auditify/parsletutil.rb +99 -31
- data/lib/fluent/auditify/plugin/conf_mask_secrets.rb +4 -0
- data/lib/fluent/auditify/plugin/conf_plugin_type.rb +1 -1
- data/lib/fluent/auditify/plugin_manager.rb +42 -12
- data/lib/fluent/auditify/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0e823faccd2bd005e3878da8342af95d5ee2943bc019d7b961a76464ce1f138b
|
|
4
|
+
data.tar.gz: 877ea6a2fd26b975c72421228ab97d1d5287f04f64fcef7bd8dd1e542cd7f851
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 76ae20f1d2c181edd3bb9aa708ad348e84d61e0a371bfbcbfabd01c5bced5c331e34b53ca155e45ce05d6960831d2d447b344d4dc66a29c782fe4dc644bbf393
|
|
7
|
+
data.tar.gz: 56df65aeb54848dfe60961bd8581ab0776d14b3a6fade5be889d35e37ae4473549f205f9cf1a719e701095c861d1df4255947f9ef6c896a965bec4bf17dddf0f
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.3.0] - 2025-11-23
|
|
4
|
+
|
|
5
|
+
* Fixed a bug that included .conf can't be exported correctly.
|
|
6
|
+
* Change the scope of parser methods.
|
|
7
|
+
|
|
8
|
+
## [0.2.0] - 2025-11-15
|
|
9
|
+
|
|
10
|
+
* Support to load 3rd party plugin.
|
|
11
|
+
|
|
3
12
|
## [0.1.0] - 2025-11-14
|
|
4
13
|
|
|
5
14
|
- Initial release. still in alpha version.
|
|
@@ -95,7 +95,7 @@ module Fluent
|
|
|
95
95
|
# match is reserved word
|
|
96
96
|
rule(:match_directive) do
|
|
97
97
|
space? >> str('<match').as(:match) >> space? >> pattern?.as(:pattern) >> str('>') >> space_or_newline >>
|
|
98
|
-
(comment | key_value | empty_line.as(:empty_line) | section).repeat.as(:body) >>
|
|
98
|
+
(comment | key_value | empty_line.as(:empty_line) | key_line | section).repeat.as(:body) >>
|
|
99
99
|
space? >> str('</match>') >> eof?
|
|
100
100
|
end
|
|
101
101
|
rule(:label) do
|
|
@@ -109,46 +109,25 @@ module Fluent
|
|
|
109
109
|
root :conf
|
|
110
110
|
|
|
111
111
|
# expand @include directive
|
|
112
|
-
def
|
|
112
|
+
def eval(object, options={})
|
|
113
|
+
base_dir = options[:base_dir] || ''
|
|
114
|
+
path = options[:path] || ''
|
|
113
115
|
modified = []
|
|
114
|
-
object.
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
unless
|
|
118
|
-
if
|
|
119
|
-
modified <<
|
|
120
|
-
elsif element[:body].collect { |v| v[:name].to_s }.any?('@include')
|
|
121
|
-
# include section
|
|
122
|
-
modified_body = []
|
|
123
|
-
element[:body].each do |body_element|
|
|
124
|
-
if body_element[:name].to_s == '@include'
|
|
125
|
-
parser = Fluent::Auditify::Parser::V1ConfigSectionParser.new
|
|
126
|
-
parsed = parser.parse(File.read(File.join(base_dir, body_element[:value])))
|
|
127
|
-
parsed.each do |elem|
|
|
128
|
-
elem[:__PATH__] = body_element[:value].to_s
|
|
129
|
-
modified_body << elem
|
|
130
|
-
end
|
|
131
|
-
else
|
|
132
|
-
modified_body << body_element
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
element[:body] = modified_body
|
|
136
|
-
modified << element
|
|
116
|
+
object.each do |directive|
|
|
117
|
+
directive[:__BASE__] = base_dir
|
|
118
|
+
directive[:__PATH__] = path
|
|
119
|
+
unless directive[:include]
|
|
120
|
+
if directive[:body]
|
|
121
|
+
modified << eval_body(directive, options)
|
|
137
122
|
else
|
|
138
|
-
modified <<
|
|
123
|
+
modified << directive
|
|
139
124
|
end
|
|
140
125
|
next
|
|
141
126
|
end
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
included.each do |child|
|
|
147
|
-
child[:__PATTERN__] = element[:include_path].to_s
|
|
148
|
-
child[:__PATH__] = Pathname.new(path).relative_path_from(base_dir).to_s
|
|
149
|
-
child[:__BASE__] = base_dir
|
|
150
|
-
modified << child
|
|
151
|
-
end
|
|
127
|
+
# top-level @include
|
|
128
|
+
eval_include(directive, options).each do |child|
|
|
129
|
+
child[:__PARENT__] = path
|
|
130
|
+
modified << child
|
|
152
131
|
end
|
|
153
132
|
end
|
|
154
133
|
modified
|
|
@@ -166,6 +145,65 @@ module Fluent
|
|
|
166
145
|
end
|
|
167
146
|
nil
|
|
168
147
|
end
|
|
148
|
+
|
|
149
|
+
private
|
|
150
|
+
|
|
151
|
+
def eval_body(directive, options={})
|
|
152
|
+
base_dir = options[:base_dir] || ''
|
|
153
|
+
# include section
|
|
154
|
+
modified_body = []
|
|
155
|
+
directive[:body].each do |body_element|
|
|
156
|
+
if body_element[:name].to_s == '@include'
|
|
157
|
+
parser = Fluent::Auditify::Parser::V1ConfigSectionParser.new
|
|
158
|
+
pattern = File.expand_path(body_element[:value].to_s, base_dir)
|
|
159
|
+
Dir.glob(pattern).sort.each do |path|
|
|
160
|
+
object = parser.parse(File.read(path))
|
|
161
|
+
object.each do |element|
|
|
162
|
+
element[:__PATTERN__] = body_element[:value].to_s
|
|
163
|
+
element[:__PATH__] = File.basename(path)
|
|
164
|
+
if element[:section]
|
|
165
|
+
element[:body] = eval_body(element, options)[:body]
|
|
166
|
+
modified_body << element
|
|
167
|
+
else
|
|
168
|
+
modified_body << element
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
elsif body_element[:section]
|
|
173
|
+
section_body = []
|
|
174
|
+
body_element[:body].each do |child|
|
|
175
|
+
child[:__PATH__] = body_element[:__PATH__]
|
|
176
|
+
section_body << child
|
|
177
|
+
end
|
|
178
|
+
body_element[:body] = section_body
|
|
179
|
+
modified_body << body_element
|
|
180
|
+
else
|
|
181
|
+
body_element[:__PATH__] = directive[:__PATH__]
|
|
182
|
+
modified_body << body_element
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
directive[:body] = modified_body
|
|
186
|
+
directive
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def eval_include(directive, options={})
|
|
190
|
+
base_dir = options[:base_dir] || ''
|
|
191
|
+
parser = Fluent::Auditify::Parser::V1ConfigParser.new
|
|
192
|
+
pattern = File.expand_path(directive[:include_path].to_s, base_dir)
|
|
193
|
+
modified = []
|
|
194
|
+
Dir.glob(pattern).sort.each do |path|
|
|
195
|
+
included = parser.parse(File.read(path))
|
|
196
|
+
included.each do |included_directive|
|
|
197
|
+
included_directive[:__PATTERN__] = directive[:include_path].to_s
|
|
198
|
+
included_directive[:__PATH__] = Pathname.new(path).relative_path_from(base_dir).to_s
|
|
199
|
+
included_directive[:__BASE__] = base_dir
|
|
200
|
+
included_directive[:body] = eval_body(included_directive, options)[:body]
|
|
201
|
+
modified << included_directive
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
modified
|
|
205
|
+
end
|
|
206
|
+
|
|
169
207
|
end
|
|
170
208
|
end
|
|
171
209
|
end
|
|
@@ -13,11 +13,25 @@ module Fluent
|
|
|
13
13
|
@content = StringIO.new
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
def handler_key(object, parent = nil)
|
|
17
|
+
if object[:__BASE__] and object[:__PATH__]
|
|
18
|
+
# directive
|
|
19
|
+
File.join(object[:__BASE__], object[:__PATH__])
|
|
20
|
+
elsif parent and parent[:__BASE__] and object[:__PATH__]
|
|
21
|
+
# section, body
|
|
22
|
+
File.join(parent[:__BASE__], object[:__PATH__])
|
|
23
|
+
elsif parent and parent[:__BASE__] and parent[:__PATH__]
|
|
24
|
+
File.join(parent[:__BASE__], parent[:__PATH__])
|
|
25
|
+
else
|
|
26
|
+
nil
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
16
30
|
def collect_file_handlers(object)
|
|
17
31
|
handlers = {}
|
|
18
32
|
object.each do |directive|
|
|
19
33
|
if directive[:empty_line]
|
|
20
|
-
key = directive
|
|
34
|
+
key = handler_key(directive, object)
|
|
21
35
|
unless key and handlers.key?(key)
|
|
22
36
|
if key
|
|
23
37
|
handlers[key] = File.open(key, 'w+')
|
|
@@ -25,14 +39,14 @@ module Fluent
|
|
|
25
39
|
end
|
|
26
40
|
elsif directive[:source] or directive[:match] or
|
|
27
41
|
directive[:system] or directive[:filter]
|
|
28
|
-
key = directive
|
|
42
|
+
key = handler_key(directive, object)
|
|
29
43
|
unless key and handlers.key?(key)
|
|
30
44
|
if key
|
|
31
45
|
handlers[key] = File.open(key, 'w+')
|
|
32
46
|
end
|
|
33
47
|
end
|
|
34
48
|
directive[:body].each do |body|
|
|
35
|
-
key = body
|
|
49
|
+
key = handler_key(body, directive)
|
|
36
50
|
unless key and handlers.key?(key)
|
|
37
51
|
if key
|
|
38
52
|
handlers[key] = File.open(key, 'w+')
|
|
@@ -47,55 +61,109 @@ module Fluent
|
|
|
47
61
|
def export(object, options={})
|
|
48
62
|
# setup rewrite file handles
|
|
49
63
|
@handlers = collect_file_handlers(object)
|
|
64
|
+
@include_flushed = {}
|
|
50
65
|
object.each do |directive|
|
|
51
|
-
|
|
66
|
+
key = handler_key(directive, object)
|
|
52
67
|
if directive[:system]
|
|
53
|
-
|
|
68
|
+
export_line(key, directive[:system].to_s)
|
|
54
69
|
export_body(directive)
|
|
55
|
-
|
|
70
|
+
export_line(key, '</system>')
|
|
56
71
|
elsif directive[:source]
|
|
57
|
-
|
|
72
|
+
export_line(key, directive[:source].to_s)
|
|
58
73
|
export_body(directive)
|
|
59
|
-
|
|
74
|
+
export_line(key, '</source>')
|
|
75
|
+
if directive[:__PATTERN__] and directive[:__PARENT__]
|
|
76
|
+
key = File.join(directive[:__BASE__], directive[:__PARENT__])
|
|
77
|
+
unless @include_flushed[key]
|
|
78
|
+
export_line(key, "@include #{directive[:__PATTERN__]}")
|
|
79
|
+
@include_flushed[key] = true
|
|
80
|
+
end
|
|
81
|
+
end
|
|
60
82
|
elsif directive[:filter]
|
|
61
|
-
|
|
83
|
+
export_line(key, directive[:filter].to_s)
|
|
62
84
|
export_body(directive)
|
|
63
|
-
|
|
85
|
+
export_line(key, '</filter>')
|
|
86
|
+
if directive[:__PATTERN__] and directive[:__PARENT__]
|
|
87
|
+
key = File.join(directive[:__BASE__], directive[:__PARENT__])
|
|
88
|
+
unless @include_flushed[key]
|
|
89
|
+
export_line(key, "@include #{directive[:__PATTERN__]}")
|
|
90
|
+
@include_flushed[key] = true
|
|
91
|
+
end
|
|
92
|
+
end
|
|
64
93
|
elsif directive[:match]
|
|
65
|
-
|
|
94
|
+
if directive[:pattern]
|
|
95
|
+
export_line(key, "#{directive[:match].to_s} #{directive[:pattern]}>")
|
|
96
|
+
else
|
|
97
|
+
export_line(key, "#{directive[:match].to_s}>")
|
|
98
|
+
end
|
|
66
99
|
export_body(directive)
|
|
67
|
-
|
|
100
|
+
export_line(key, '</match>')
|
|
101
|
+
elsif directive[:empty_line]
|
|
102
|
+
export_line(key, '')
|
|
68
103
|
end
|
|
69
104
|
end
|
|
105
|
+
@handlers.each do |path, io|
|
|
106
|
+
io.flush
|
|
107
|
+
io.fsync
|
|
108
|
+
io.close
|
|
109
|
+
end
|
|
110
|
+
@handlers = []
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def export_section(section, directive)
|
|
114
|
+
key = handler_key(section, directive)
|
|
115
|
+
export_line(key, "<#{section[:section][:name].to_s}>")
|
|
116
|
+
@indent_level += 1
|
|
117
|
+
section[:body].each do |kv|
|
|
118
|
+
key = handler_key(kv, directive)
|
|
119
|
+
if kv[:value]
|
|
120
|
+
export_line(key, "#{kv[:name].to_s} #{kv[:value].to_s}")
|
|
121
|
+
else
|
|
122
|
+
export_line(key, "#{kv[:name].to_s}")
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
@indent_level -= 1
|
|
126
|
+
export_line(key, "</#{section[:name].to_s}>")
|
|
127
|
+
export_at_include(section, directive)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def export_at_include(object, parent)
|
|
131
|
+
pattern = object[:__PATTERN__]
|
|
132
|
+
if pattern
|
|
133
|
+
unless @include_flushed[pattern]
|
|
134
|
+
key = handler_key(parent, parent)
|
|
135
|
+
export_line(key, "@include #{pattern}")
|
|
136
|
+
@include_flushed[pattern] = true
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def export_line(key, message)
|
|
142
|
+
io = @handlers[key]
|
|
143
|
+
if io
|
|
144
|
+
io.puts("#{' ' * @align * @indent_level}#{message}")
|
|
145
|
+
end
|
|
70
146
|
end
|
|
71
147
|
|
|
72
148
|
def export_body(directive)
|
|
149
|
+
@indent_level += 1
|
|
73
150
|
directive[:body].each do |child|
|
|
74
151
|
if child[:section]
|
|
152
|
+
export_section(child, directive)
|
|
75
153
|
elsif child[:empty_line]
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if io
|
|
79
|
-
io.puts
|
|
80
|
-
end
|
|
81
|
-
else
|
|
82
|
-
io = @handlers[directive[:__PATH__]]
|
|
83
|
-
if io
|
|
84
|
-
io.puts
|
|
85
|
-
end
|
|
86
|
-
end
|
|
154
|
+
key = handler_key(child, directive)
|
|
155
|
+
export_line(key, "")
|
|
87
156
|
elsif child[:value]
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
end
|
|
157
|
+
key = handler_key(child, directive)
|
|
158
|
+
export_line(key, "#{child[:name].to_s} #{child[:value].to_s}")
|
|
159
|
+
export_at_include(child, directive)
|
|
92
160
|
elsif child[:name]
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
end
|
|
161
|
+
key = handler_key(child, directive)
|
|
162
|
+
export_line(key, child[:name].to_s)
|
|
163
|
+
export_at_include(child, directive)
|
|
97
164
|
end
|
|
98
165
|
end
|
|
166
|
+
@indent_level -= 1
|
|
99
167
|
end
|
|
100
168
|
|
|
101
169
|
def to_s(object, options={})
|
|
@@ -47,6 +47,15 @@ module Fluent
|
|
|
47
47
|
require plugin_path
|
|
48
48
|
end
|
|
49
49
|
end
|
|
50
|
+
|
|
51
|
+
Gem::Specification.each { |spec|
|
|
52
|
+
if spec.name.start_with?('fluent-auditify-plugin-')
|
|
53
|
+
Dir.glob("#{spec.full_gem_path}/lib/fluent/auditify/plugin/conf_*.rb") do |path|
|
|
54
|
+
@logger.debug("Loading <#{path}>") if @logger
|
|
55
|
+
require path
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
}
|
|
50
59
|
end
|
|
51
60
|
|
|
52
61
|
def builtin_plugin_paths
|
|
@@ -87,6 +96,21 @@ module Fluent
|
|
|
87
96
|
end
|
|
88
97
|
end
|
|
89
98
|
|
|
99
|
+
def supported_file_extension?(plugin, config)
|
|
100
|
+
unless plugin.respond_to?(:supported_file_extension?)
|
|
101
|
+
@logger.info("Plugin: <#{plugin.class}> must implement supported_file_extension?")
|
|
102
|
+
return false
|
|
103
|
+
end
|
|
104
|
+
if config.end_with?('.yml', '.yaml') and
|
|
105
|
+
plugin.supported_file_extension?.include?(:yaml)
|
|
106
|
+
return true
|
|
107
|
+
elsif config.end_with?('.conf') and
|
|
108
|
+
plugin.supported_file_extension?.include?(:conf)
|
|
109
|
+
return true
|
|
110
|
+
end
|
|
111
|
+
false
|
|
112
|
+
end
|
|
113
|
+
|
|
90
114
|
def skip_plugin?(plugin)
|
|
91
115
|
unless supported_plugin?(plugin)
|
|
92
116
|
return true
|
|
@@ -123,15 +147,23 @@ module Fluent
|
|
|
123
147
|
|
|
124
148
|
def evacuate(options={})
|
|
125
149
|
@workspace_dir = Dir.mktmpdir('fluent-auditify')
|
|
150
|
+
@logger.debug("Create workspace under <#{@workspace_dir}>")
|
|
126
151
|
@base_dir = File.dirname(options[:config])
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
152
|
+
|
|
153
|
+
if options[:config].end_with?('.conf')
|
|
154
|
+
parser = Fluent::Auditify::Parser::V1ConfigParser.new
|
|
155
|
+
object = parser.parse(File.read(options[:config]))
|
|
156
|
+
|
|
157
|
+
# copy configuration files into workspace
|
|
158
|
+
touched = [options[:config]]
|
|
159
|
+
touched << collect_related_config_files(object).collect { |v| File.join(@base_dir, v) }
|
|
160
|
+
touched.flatten!
|
|
161
|
+
@logger.debug("Copy configuration files: <#{touched}>")
|
|
162
|
+
FileUtils.cp(touched, @workspace_dir)
|
|
163
|
+
else
|
|
164
|
+
@logger.debug("Copy configuration files: <#{options[:config]}>")
|
|
165
|
+
FileUtils.cp(options[:config], @workspace_dir)
|
|
166
|
+
end
|
|
135
167
|
end
|
|
136
168
|
|
|
137
169
|
def dispatch(options={})
|
|
@@ -140,10 +172,8 @@ module Fluent
|
|
|
140
172
|
next if skip_plugin?(plugin)
|
|
141
173
|
|
|
142
174
|
config_path = File.join(@workspace_dir, File.basename(options[:config]))
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
unless ext.any?(ext_symbol)
|
|
146
|
-
@logger.debug("#{plugin.class} does not support #{config_path}")
|
|
175
|
+
unless supported_file_extension?(plugin, config_path)
|
|
176
|
+
@logger.debug("<#{plugin.class}> is not applicable to <#{config_path}>")
|
|
147
177
|
next
|
|
148
178
|
end
|
|
149
179
|
|