logstash-filter-sphinx 0.0.3
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 +7 -0
- data/.gitignore +5 -0
- data/.idea/.name +1 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/encodings.xml +4 -0
- data/.idea/logstash-filter-sphinx.iml +48 -0
- data/.idea/misc.xml +4 -0
- data/.idea/modules.xml +8 -0
- data/.idea/runConfigurations/sphinx.xml +30 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/vcs.xml +6 -0
- data/.idea/workspace.xml +708 -0
- data/CONTRIBUTORS +11 -0
- data/DEVELOPER.md +2 -0
- data/Gemfile +13 -0
- data/LICENSE +13 -0
- data/README.md +86 -0
- data/Rakefile +1 -0
- data/lib/logstash/filters/sphinx.rb +725 -0
- data/logstash-filter-sphinx.gemspec +29 -0
- data/spec/filters/sphinx_spec.rb +20 -0
- data/spec/spec_helper.rb +1 -0
- metadata +158 -0
@@ -0,0 +1,725 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/filters/base"
|
3
|
+
require "logstash/namespace"
|
4
|
+
|
5
|
+
|
6
|
+
require 'pg'
|
7
|
+
require 'redis'
|
8
|
+
require 'connection_pool'
|
9
|
+
require 'ipaddress'
|
10
|
+
|
11
|
+
|
12
|
+
class SphinxDataAccessor
|
13
|
+
|
14
|
+
def initialize(config)
|
15
|
+
|
16
|
+
@redis_user_conn = Redis.new(:host => config["redis_host"], :port => config["redis_port"], :db => config["redis_user_db"])
|
17
|
+
@redis_record_conn = Redis.new(:host => config["redis_host"], :port => config["redis_port"], :db => config["redis_record_db"])
|
18
|
+
@redis_host_conn = Redis.new(:host => config["redis_host"], :port => config["redis_port"], :db => config["redis_host_db"])
|
19
|
+
@pg_conn = ConnectionPool::Wrapper.new(size: 8, timeout: 3) { PG::connect(:host => config["pg_host"], :user => config["pg_user"], :password => config["pg_password"], :dbname => config["pg_dbname"]) }
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
public
|
24
|
+
def get_user(access_id, access_key)
|
25
|
+
|
26
|
+
# sanity check for user inputs
|
27
|
+
if access_id.nil? || access_key.nil?
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
|
31
|
+
if access_id.strip == '' || access_key == ''
|
32
|
+
return nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# check redis first
|
36
|
+
user = get_user_from_redis(access_id, access_key)
|
37
|
+
return user if user
|
38
|
+
|
39
|
+
user = get_user_from_pg(access_id, access_key)
|
40
|
+
|
41
|
+
if user
|
42
|
+
set_user_in_redis(access_id, access_key, user)
|
43
|
+
end
|
44
|
+
|
45
|
+
return user
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
public
|
50
|
+
def add_host(user_id, hostname)
|
51
|
+
|
52
|
+
# check redis cache first
|
53
|
+
host = get_host_from_redis(user_id, hostname)
|
54
|
+
return if host
|
55
|
+
|
56
|
+
# create a host entry if not in the database
|
57
|
+
begin
|
58
|
+
host = create_host(user_id, hostname)
|
59
|
+
insert_host_into_pg(host)
|
60
|
+
|
61
|
+
set_host_in_redis(user_id, hostname, host)
|
62
|
+
|
63
|
+
rescue => e
|
64
|
+
puts e.message
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
public
|
74
|
+
def get_record(md5)
|
75
|
+
|
76
|
+
|
77
|
+
# check the redis cache
|
78
|
+
record = get_record_from_redis(md5)
|
79
|
+
if record
|
80
|
+
|
81
|
+
# Return it only if the record contains reputation meta data.
|
82
|
+
# We learn this by checking the existence of the 'reputation_timestamp' key
|
83
|
+
# which is only set by the backend after checking with VT (or other data source)
|
84
|
+
if record["reputation_timestamp"]
|
85
|
+
puts "#{md5}: Cache hit with data"
|
86
|
+
return record
|
87
|
+
else
|
88
|
+
puts "#{md5}: Cache hit with no data"
|
89
|
+
return nil
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
# we couldn't find it in the cache. Check the db
|
96
|
+
record = get_record_from_pg(md5)
|
97
|
+
|
98
|
+
if record
|
99
|
+
|
100
|
+
|
101
|
+
# Return it only if the record contains reputation meta data.
|
102
|
+
# We learn this by checking the existence of the 'reputation_timestamp' key
|
103
|
+
# which is only set by the backend after checking with VT (or other data source)
|
104
|
+
if record["reputation_timestamp"]
|
105
|
+
puts "#{md5}: DB hit with data"
|
106
|
+
|
107
|
+
# cache it in redis
|
108
|
+
set_record_in_redis(md5, record)
|
109
|
+
return record
|
110
|
+
|
111
|
+
else
|
112
|
+
puts "#{md5}: DB hit with no data"
|
113
|
+
|
114
|
+
empty_record = create_new_record(md5)
|
115
|
+
set_record_in_redis(md5, empty_record)
|
116
|
+
return nil
|
117
|
+
end
|
118
|
+
|
119
|
+
else
|
120
|
+
|
121
|
+
puts "#{md5}: NO hit. Inserting a new record into DB and Cache"
|
122
|
+
|
123
|
+
# Insert this md5 entry into the reference_hash table with a blank reputation timestamp.
|
124
|
+
# This way the backend can update this entry accordingly
|
125
|
+
record = create_new_record(md5)
|
126
|
+
insert_record_into_pg(record)
|
127
|
+
|
128
|
+
# Insert this into
|
129
|
+
set_record_in_redis(md5, record)
|
130
|
+
|
131
|
+
# NOTE: this new record is not returned to the user as it contains no reputation meta data.
|
132
|
+
return nil
|
133
|
+
|
134
|
+
end
|
135
|
+
|
136
|
+
return nil
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
def redis_host_db_key(user_id, hostname)
|
142
|
+
"#{user_id}-#{hostname}".strip
|
143
|
+
end
|
144
|
+
|
145
|
+
private
|
146
|
+
def get_host_from_redis(user_id, hostname)
|
147
|
+
begin
|
148
|
+
|
149
|
+
key = redis_host_db_key(user_id, hostname)
|
150
|
+
|
151
|
+
host = @redis_host_conn.hgetall(key) #hgetall returns all fields and values of the hash stored at key
|
152
|
+
|
153
|
+
return host if host != {}
|
154
|
+
|
155
|
+
rescue => e
|
156
|
+
puts e.message
|
157
|
+
end
|
158
|
+
|
159
|
+
nil
|
160
|
+
end
|
161
|
+
|
162
|
+
private
|
163
|
+
def create_host(user_id, hostname)
|
164
|
+
{'user_id' => user_id, 'hostname' => hostname}
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
def set_host_in_redis(user_id, hostname, host)
|
169
|
+
begin
|
170
|
+
key = redis_host_db_key(user_id, hostname)
|
171
|
+
@redis_host_conn.mapped_hmset(key, host)
|
172
|
+
rescue => e
|
173
|
+
puts e.message
|
174
|
+
end
|
175
|
+
nil
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
def redis_user_db_key(access_id, access_key)
|
180
|
+
"#{access_id}-#{access_key}"
|
181
|
+
end
|
182
|
+
|
183
|
+
|
184
|
+
private
|
185
|
+
def get_user_from_redis(access_id, access_key)
|
186
|
+
key = redis_user_db_key(access_id, access_key)
|
187
|
+
|
188
|
+
begin
|
189
|
+
|
190
|
+
user = @redis_user_conn.hgetall(key) #hgetall returns all fields and values of the hash stored at key
|
191
|
+
return user if user != {}
|
192
|
+
|
193
|
+
rescue => e
|
194
|
+
puts e.message
|
195
|
+
end
|
196
|
+
|
197
|
+
nil
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
|
202
|
+
|
203
|
+
|
204
|
+
private
|
205
|
+
def get_user_from_pg(access_id, access_key)
|
206
|
+
|
207
|
+
begin
|
208
|
+
#TODO
|
209
|
+
# @pg_conn.prepare('stmt1', "SELECT * FROM reference_hashes WHERE md5 = $1 LIMIT 1")
|
210
|
+
# result = @pg_conn.exec_prepared('stmt1', [md5])
|
211
|
+
result = @pg_conn.exec("SELECT users.* from users, data_forwarder_keys WHERE users.id = data_forwarder_keys.user_id AND access_id = '#{access_id}' AND access_key = '#{access_key}' LIMIT 1")
|
212
|
+
row = result.first
|
213
|
+
|
214
|
+
if row
|
215
|
+
user = {
|
216
|
+
"email" => row['email'],
|
217
|
+
'id' => row["id"],
|
218
|
+
}
|
219
|
+
return user
|
220
|
+
end
|
221
|
+
|
222
|
+
return nil
|
223
|
+
|
224
|
+
rescue => e
|
225
|
+
puts e.message
|
226
|
+
end
|
227
|
+
|
228
|
+
nil
|
229
|
+
end
|
230
|
+
|
231
|
+
private
|
232
|
+
def set_user_in_redis(access_id, access_key, user)
|
233
|
+
key = redis_user_db_key(access_id, access_key)
|
234
|
+
|
235
|
+
begin
|
236
|
+
@redis_user_conn.mapped_hmset(key, user)
|
237
|
+
rescue => e
|
238
|
+
puts e.message
|
239
|
+
end
|
240
|
+
nil
|
241
|
+
end
|
242
|
+
|
243
|
+
private
|
244
|
+
def create_new_host(user_id, hostname)
|
245
|
+
{"user_id" => user_id, "hostname" => hostname}
|
246
|
+
end
|
247
|
+
|
248
|
+
def insert_host_into_pg(host)
|
249
|
+
timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
250
|
+
sql = "INSERT INTO hosts (user_id, hostname, created_at, updated_at) VALUES ('#{host['user_id']}', '#{host['hostname']}', '#{timestamp}', '#{timestamp}')"
|
251
|
+
result = @pg_conn.exec(sql)
|
252
|
+
|
253
|
+
nil
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
|
258
|
+
private
|
259
|
+
def set_record_in_redis(md5, record)
|
260
|
+
|
261
|
+
begin
|
262
|
+
|
263
|
+
@redis_record_conn.mapped_hmset(md5, record)
|
264
|
+
|
265
|
+
rescue => e
|
266
|
+
puts e.message
|
267
|
+
end
|
268
|
+
|
269
|
+
end
|
270
|
+
|
271
|
+
|
272
|
+
|
273
|
+
private
|
274
|
+
def create_new_record(md5)
|
275
|
+
# {"md5" => md5, "wtf_timestamp" => @wtf_ts}
|
276
|
+
{"md5" => md5}
|
277
|
+
end
|
278
|
+
|
279
|
+
private
|
280
|
+
def insert_record_into_pg(record)
|
281
|
+
|
282
|
+
md5 = record["md5"]
|
283
|
+
|
284
|
+
begin
|
285
|
+
timestamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
286
|
+
sql = "INSERT INTO reference_hashes (md5, source, created_at, updated_at) VALUES ('#{md5}', 'wtf', '#{timestamp}', '#{timestamp}')"
|
287
|
+
result = @pg_conn.exec(sql)
|
288
|
+
|
289
|
+
rescue => e
|
290
|
+
puts e.message
|
291
|
+
end
|
292
|
+
|
293
|
+
nil
|
294
|
+
end
|
295
|
+
|
296
|
+
|
297
|
+
private
|
298
|
+
def get_record_from_redis(md5)
|
299
|
+
|
300
|
+
begin
|
301
|
+
|
302
|
+
record = @redis_record_conn.hgetall(md5) #hgetall returns all fields and values of the hash stored at key
|
303
|
+
|
304
|
+
return record if record != {}
|
305
|
+
|
306
|
+
rescue => e
|
307
|
+
puts e.message
|
308
|
+
end
|
309
|
+
|
310
|
+
nil
|
311
|
+
end
|
312
|
+
|
313
|
+
private
|
314
|
+
def get_record_from_pg(md5)
|
315
|
+
|
316
|
+
begin
|
317
|
+
|
318
|
+
#TODO
|
319
|
+
# @pg_conn.prepare('stmt1', "SELECT * FROM reference_hashes WHERE md5 = $1 LIMIT 1")
|
320
|
+
# result = @pg_conn.exec_prepared('stmt1', [md5])
|
321
|
+
result = @pg_conn.exec("SELECT * FROM reference_hashes WHERE md5 = '#{md5}' LIMIT 1")
|
322
|
+
row = result.first
|
323
|
+
|
324
|
+
|
325
|
+
if row
|
326
|
+
|
327
|
+
record = {
|
328
|
+
"md5" => row['md5'],
|
329
|
+
'source' => row["source"],
|
330
|
+
'reputation' => row["reputation"],
|
331
|
+
'vt_score' => row["vt_score"],
|
332
|
+
'vt_total' => row["vt_total"],
|
333
|
+
'vt_sub_score' => row["vt_sub_score"],
|
334
|
+
'vt_scan_date' => row["vt_scan_date"],
|
335
|
+
'has_vulnerability' => row["has_vulnerability"],
|
336
|
+
'has_verified_signature' => row["has_verified_signature"],
|
337
|
+
'signing_vendor' => row["signing_vendor"],
|
338
|
+
'reputation_timestamp' => row["reputation_timestamp"]
|
339
|
+
}
|
340
|
+
|
341
|
+
return record
|
342
|
+
|
343
|
+
end
|
344
|
+
|
345
|
+
return nil
|
346
|
+
|
347
|
+
rescue => e
|
348
|
+
|
349
|
+
puts e.message
|
350
|
+
|
351
|
+
end
|
352
|
+
|
353
|
+
nil
|
354
|
+
|
355
|
+
end
|
356
|
+
|
357
|
+
|
358
|
+
|
359
|
+
end
|
360
|
+
|
361
|
+
|
362
|
+
class SphinxEventFilterFactory
|
363
|
+
|
364
|
+
public
|
365
|
+
def initialize(config)
|
366
|
+
@config = config
|
367
|
+
@event_filter_base = SphinxEventFilter.new(config)
|
368
|
+
@event_filter_windows = SphinxWindowsEventFilter.new(config)
|
369
|
+
@event_filter_windows_sysmon = SphinxWindowsSysmonEventFilter.new(config)
|
370
|
+
@event_filter_linux = SphinxLinuxEventFilter.new(config) #TODO
|
371
|
+
@event_filter_mac = SphinxMacEventFilter.new(config) #TODO
|
372
|
+
end
|
373
|
+
|
374
|
+
public
|
375
|
+
def get_filter(event)
|
376
|
+
|
377
|
+
platform = event["SphinxPlatform"]
|
378
|
+
|
379
|
+
case platform
|
380
|
+
when 'windows'
|
381
|
+
return get_windows_filter(event)
|
382
|
+
when 'linux'
|
383
|
+
return @event_filter_linux
|
384
|
+
when 'mac'
|
385
|
+
return @event_filter_mac
|
386
|
+
end
|
387
|
+
|
388
|
+
nil
|
389
|
+
end
|
390
|
+
|
391
|
+
private
|
392
|
+
def get_windows_filter(event)
|
393
|
+
|
394
|
+
event_source = event['SourceName']
|
395
|
+
|
396
|
+
if event_source == 'Microsoft-Windows-Sysmon'
|
397
|
+
return @event_filter_windows_sysmon
|
398
|
+
end
|
399
|
+
|
400
|
+
|
401
|
+
return @event_filter_windows
|
402
|
+
end
|
403
|
+
|
404
|
+
end
|
405
|
+
|
406
|
+
|
407
|
+
|
408
|
+
class SphinxEventFilter
|
409
|
+
|
410
|
+
SPHINX_FILTER_VERSION = 1
|
411
|
+
SPHINX_FILTER_NAME = 'SphinxEventFilter'
|
412
|
+
|
413
|
+
def initialize(config)
|
414
|
+
|
415
|
+
@data_accessor = SphinxDataAccessor.new(config)
|
416
|
+
|
417
|
+
end
|
418
|
+
|
419
|
+
|
420
|
+
def apply(event)
|
421
|
+
raise "Not implemented"
|
422
|
+
end
|
423
|
+
|
424
|
+
|
425
|
+
def finalize(event)
|
426
|
+
event['SphinxFilterVersion'] = self.class::SPHINX_FILTER_VERSION
|
427
|
+
event['SphinxFilterName'] = self.class::SPHINX_FILTER_NAME
|
428
|
+
end
|
429
|
+
|
430
|
+
end
|
431
|
+
|
432
|
+
class SphinxLinuxEventFilter < SphinxEventFilter
|
433
|
+
|
434
|
+
SPHINX_FILTER_VERSION = 1
|
435
|
+
SPHINX_FILTER_NAME = 'LinuxEventFilter'
|
436
|
+
end
|
437
|
+
|
438
|
+
class SphinxMacEventFilter < SphinxEventFilter
|
439
|
+
SPHINX_FILTER_VERSION = 1
|
440
|
+
SPHINX_FILTER_NAME = 'MacEventFilter'
|
441
|
+
end
|
442
|
+
|
443
|
+
|
444
|
+
class SphinxWindowsEventFilter < SphinxEventFilter
|
445
|
+
SPHINX_FILTER_VERSION = 1
|
446
|
+
SPHINX_FILTER_NAME = 'WindowsEventFilter'
|
447
|
+
|
448
|
+
def apply(event)
|
449
|
+
|
450
|
+
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
class SphinxWindowsSysmonEventFilter < SphinxWindowsEventFilter
|
455
|
+
SPHINX_FILTER_VERSION = 1
|
456
|
+
SPHINX_FILTER_NAME = 'SysmonEventFilter'
|
457
|
+
|
458
|
+
def apply(event)
|
459
|
+
|
460
|
+
|
461
|
+
case event['EventID'].to_i
|
462
|
+
|
463
|
+
# process creation
|
464
|
+
when 1
|
465
|
+
add_process_name(event)
|
466
|
+
add_reputation_data(event)
|
467
|
+
|
468
|
+
# file creation
|
469
|
+
when 2
|
470
|
+
add_process_name(event)
|
471
|
+
add_target_file_name(event)
|
472
|
+
add_reputation_data(event)
|
473
|
+
|
474
|
+
# network conn
|
475
|
+
when 3
|
476
|
+
extend_ipaddress(event)
|
477
|
+
add_process_name(event)
|
478
|
+
|
479
|
+
# driver load
|
480
|
+
when 6
|
481
|
+
add_file_name(event)
|
482
|
+
add_reputation_data(event)
|
483
|
+
|
484
|
+
|
485
|
+
# dll load
|
486
|
+
when 7
|
487
|
+
add_process_name(event)
|
488
|
+
add_file_name(event)
|
489
|
+
add_reputation_data(event)
|
490
|
+
|
491
|
+
# remote thread
|
492
|
+
when 8
|
493
|
+
#TODO
|
494
|
+
|
495
|
+
end
|
496
|
+
|
497
|
+
nil
|
498
|
+
|
499
|
+
end
|
500
|
+
|
501
|
+
|
502
|
+
def extend_ipaddress(event)
|
503
|
+
|
504
|
+
# src ip
|
505
|
+
begin
|
506
|
+
ip_str = event['SourceIp']
|
507
|
+
ip_addr = IPAddress(ip_str)
|
508
|
+
|
509
|
+
if ip_addr.ipv6?
|
510
|
+
event['SourceIpv6'] = ip_addr.address
|
511
|
+
else
|
512
|
+
event['SourceIpv4'] = ip_addr.address
|
513
|
+
end
|
514
|
+
|
515
|
+
rescue => e
|
516
|
+
puts e.message
|
517
|
+
end
|
518
|
+
|
519
|
+
# dst ip
|
520
|
+
begin
|
521
|
+
ip_str = event['DestinationIp']
|
522
|
+
ip_addr = IPAddress(ip_str)
|
523
|
+
|
524
|
+
if ip_addr.ipv6?
|
525
|
+
event['DestinationIpv6'] = ip_addr.address
|
526
|
+
else
|
527
|
+
event['DestinationIpv4'] = ip_addr.address
|
528
|
+
end
|
529
|
+
|
530
|
+
rescue => e
|
531
|
+
puts e.message
|
532
|
+
end
|
533
|
+
|
534
|
+
|
535
|
+
end
|
536
|
+
|
537
|
+
|
538
|
+
|
539
|
+
def add_target_file_name(event)
|
540
|
+
|
541
|
+
image = event['TargetFilename']
|
542
|
+
file_name = File.basename(image.gsub("\\","/"))
|
543
|
+
event['FileName'] = file_name
|
544
|
+
|
545
|
+
nil
|
546
|
+
end
|
547
|
+
|
548
|
+
def add_file_name(event)
|
549
|
+
|
550
|
+
image = event['ImageLoaded']
|
551
|
+
file_name = File.basename(image.gsub("\\","/"))
|
552
|
+
event['FileName'] = file_name
|
553
|
+
|
554
|
+
nil
|
555
|
+
end
|
556
|
+
|
557
|
+
def add_process_name(event)
|
558
|
+
|
559
|
+
image = event['Image']
|
560
|
+
process_name = File.basename(image.gsub("\\","/"))
|
561
|
+
event['ProcessName'] = process_name
|
562
|
+
|
563
|
+
nil
|
564
|
+
end
|
565
|
+
|
566
|
+
def add_reputation_data(event)
|
567
|
+
|
568
|
+
# downcase hash
|
569
|
+
md5 = get_downcase_hash(event)
|
570
|
+
return nil if (md5.nil? || (md5.strip == ""))
|
571
|
+
|
572
|
+
event['Hash'] = md5
|
573
|
+
|
574
|
+
data = @data_accessor.get_record(md5)
|
575
|
+
|
576
|
+
|
577
|
+
if data
|
578
|
+
|
579
|
+
event['reputation'] = data['reputation']
|
580
|
+
event['source'] = data['source']
|
581
|
+
event['reputation_timestamp'] = data["reputation_timestamp"]
|
582
|
+
event['vt_score'] = data["vt_score"]
|
583
|
+
event['vt_total'] = data["vt_total"]
|
584
|
+
event['vt_sub_score'] = data["vt_sub_score"]
|
585
|
+
event['vt_scan_date'] = data["vt_scan_date"]
|
586
|
+
event['has_vulnerability'] = data["has_vulnerability"]
|
587
|
+
event['has_verified_signature'] = data["has_verified_signature"]
|
588
|
+
event['signing_vendor'] = data["signing_vendor"]
|
589
|
+
|
590
|
+
end
|
591
|
+
|
592
|
+
nil
|
593
|
+
end
|
594
|
+
|
595
|
+
def get_downcase_hash(event)
|
596
|
+
|
597
|
+
if event['Hash']
|
598
|
+
return event['Hash'].downcase
|
599
|
+
|
600
|
+
elsif event['Hashes']
|
601
|
+
return event['Hashes'][4,32].downcase #NOTE hardcoded for MD5
|
602
|
+
end
|
603
|
+
|
604
|
+
nil
|
605
|
+
end
|
606
|
+
|
607
|
+
|
608
|
+
end
|
609
|
+
|
610
|
+
|
611
|
+
|
612
|
+
|
613
|
+
# This example filter will replace the contents of the default
|
614
|
+
# message field with whatever you specify in the configuration.
|
615
|
+
#
|
616
|
+
# It is only intended to be used as an example.
|
617
|
+
class LogStash::Filters::Sphinx < LogStash::Filters::Base
|
618
|
+
|
619
|
+
# Setting the config_name here is required. This is how you
|
620
|
+
# configure this filter from your Logstash config.
|
621
|
+
#
|
622
|
+
# filter {
|
623
|
+
# example {
|
624
|
+
# message => "My message..."
|
625
|
+
# }
|
626
|
+
# }
|
627
|
+
#
|
628
|
+
config_name "sphinx"
|
629
|
+
|
630
|
+
config :pg_host, :required => false, :default => 'localhost'
|
631
|
+
config :pg_port, :required => false, :default => 5432
|
632
|
+
config :pg_user, :required => true
|
633
|
+
config :pg_password, :required => true
|
634
|
+
config :pg_dbname, :required => true
|
635
|
+
|
636
|
+
config :redis_host, :required => false, :default => 'localhost'
|
637
|
+
config :redis_port, :required => false, :default => 6379
|
638
|
+
config :redis_user_db, :required => false, :default => 1
|
639
|
+
config :redis_host_db, :required => false, :default => 2
|
640
|
+
config :redis_record_db, :required => false, :default => 3
|
641
|
+
|
642
|
+
|
643
|
+
|
644
|
+
public
|
645
|
+
def register
|
646
|
+
|
647
|
+
@data_accessor = SphinxDataAccessor.new(@config)
|
648
|
+
@event_filter_factory = SphinxEventFilterFactory.new(@config)
|
649
|
+
@logger.debug("Registered sphinx plugin", :type => @type, :config => @config)
|
650
|
+
|
651
|
+
|
652
|
+
end # def register
|
653
|
+
|
654
|
+
|
655
|
+
public
|
656
|
+
def filter(event)
|
657
|
+
|
658
|
+
|
659
|
+
|
660
|
+
begin
|
661
|
+
|
662
|
+
# drop nxlog related events
|
663
|
+
if is_nxlog_event?(event)
|
664
|
+
event.cancel
|
665
|
+
return
|
666
|
+
end
|
667
|
+
|
668
|
+
|
669
|
+
# auth key check
|
670
|
+
user = get_user(event)
|
671
|
+
if user
|
672
|
+
event['SphinxUserId'] = user["id"]
|
673
|
+
else
|
674
|
+
event.cancel
|
675
|
+
return
|
676
|
+
end
|
677
|
+
|
678
|
+
# insert host to database
|
679
|
+
hostname = event["Hostname"]
|
680
|
+
add_host_to_db(user['id'], hostname)
|
681
|
+
|
682
|
+
# remove access_key
|
683
|
+
remove_data_forwarder_credential(event)
|
684
|
+
|
685
|
+
# get event filter
|
686
|
+
event_filter = @event_filter_factory.get_filter(event)
|
687
|
+
|
688
|
+
# apply the filter
|
689
|
+
event_filter.apply(event)
|
690
|
+
event_filter.finalize(event)
|
691
|
+
|
692
|
+
rescue => e
|
693
|
+
@logger.error("SphinxPlugin: #{e.message}")
|
694
|
+
end
|
695
|
+
|
696
|
+
# filter_matched should go in the last line of our successful code
|
697
|
+
filter_matched(event)
|
698
|
+
end # def filter
|
699
|
+
|
700
|
+
private
|
701
|
+
def add_host_to_db(user_id, hostname)
|
702
|
+
@data_accessor.add_host(user_id, hostname)
|
703
|
+
nil
|
704
|
+
end
|
705
|
+
|
706
|
+
private
|
707
|
+
def get_user(event)
|
708
|
+
access_id = event['SphinxAccessId']
|
709
|
+
access_key = event['SphinxAccessKey']
|
710
|
+
return @data_accessor.get_user(access_id, access_key)
|
711
|
+
end
|
712
|
+
|
713
|
+
|
714
|
+
private
|
715
|
+
def is_nxlog_event?(event)
|
716
|
+
event['SourceName'] == 'nxlog-ce'
|
717
|
+
end
|
718
|
+
|
719
|
+
private
|
720
|
+
def remove_data_forwarder_credential(event)
|
721
|
+
event.remove('SphinxAccessId')
|
722
|
+
event.remove('SphinxAccessKey')
|
723
|
+
end
|
724
|
+
|
725
|
+
end # class LogStash::Filters::Example
|