logstash-input-akamai-siem 1.0.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 +7 -0
- data/CHANGELOG.md +14 -0
- data/DEVELOPER.md +2 -0
- data/Gemfile +24 -0
- data/LICENSE +202 -0
- data/NOTICE.TXT +5 -0
- data/README.md +98 -0
- data/lib/logstash/inputs/akamai_siem/base.rb +41 -0
- data/lib/logstash/inputs/akamai_siem/edge_grid.rb +125 -0
- data/lib/logstash/inputs/akamai_siem/exception.rb +56 -0
- data/lib/logstash/inputs/akamai_siem/headers.rb +142 -0
- data/lib/logstash/inputs/akamai_siem/middleware_registry.rb +81 -0
- data/lib/logstash/inputs/akamai_siem/request.rb +92 -0
- data/lib/logstash/inputs/akamai_siem.rb +410 -0
- data/logstash-input-akamai-siem.gemspec +36 -0
- metadata +238 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class LogStash::Inputs::AkamaiSiem::Headers < ::Hash
|
4
|
+
def self.from(value)
|
5
|
+
new(value)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.allocate
|
9
|
+
new_self = super
|
10
|
+
new_self.initialize_names
|
11
|
+
new_self
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(hash = nil)
|
15
|
+
super()
|
16
|
+
@names = {}
|
17
|
+
update(hash || {})
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize_names
|
21
|
+
@names = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
# on dup/clone, we need to duplicate @names hash
|
25
|
+
def initialize_copy(other)
|
26
|
+
super
|
27
|
+
@names = other.names.dup
|
28
|
+
end
|
29
|
+
|
30
|
+
# need to synchronize concurrent writes to the shared KeyMap
|
31
|
+
keymap_mutex = Mutex.new
|
32
|
+
|
33
|
+
# symbol -> string mapper + cache
|
34
|
+
KeyMap = Hash.new do |map, key|
|
35
|
+
value = if key.respond_to?(:to_str)
|
36
|
+
key
|
37
|
+
else
|
38
|
+
key.to_s.split('_') # user_agent: %w(user agent)
|
39
|
+
.each(&:capitalize!) # => %w(User Agent)
|
40
|
+
.join('-') # => "User-Agent"
|
41
|
+
end
|
42
|
+
keymap_mutex.synchronize { map[key] = value }
|
43
|
+
end
|
44
|
+
KeyMap[:etag] = 'ETag'
|
45
|
+
|
46
|
+
def [](key)
|
47
|
+
key = KeyMap[key]
|
48
|
+
super(key) || super(@names[key.downcase])
|
49
|
+
end
|
50
|
+
|
51
|
+
def []=(key, val)
|
52
|
+
key = KeyMap[key]
|
53
|
+
key = (@names[key.downcase] ||= key)
|
54
|
+
# join multiple values with a comma
|
55
|
+
val = val.to_ary.join(', ') if val.respond_to?(:to_ary)
|
56
|
+
super(key, val)
|
57
|
+
end
|
58
|
+
|
59
|
+
def fetch(key, ...)
|
60
|
+
key = KeyMap[key]
|
61
|
+
key = @names.fetch(key.downcase, key)
|
62
|
+
super(key, ...)
|
63
|
+
end
|
64
|
+
|
65
|
+
def delete(key)
|
66
|
+
key = KeyMap[key]
|
67
|
+
key = @names[key.downcase]
|
68
|
+
return unless key
|
69
|
+
|
70
|
+
@names.delete key.downcase
|
71
|
+
super(key)
|
72
|
+
end
|
73
|
+
|
74
|
+
def dig(key, *rest)
|
75
|
+
key = KeyMap[key]
|
76
|
+
key = @names.fetch(key.downcase, key)
|
77
|
+
super(key, *rest)
|
78
|
+
end
|
79
|
+
|
80
|
+
def include?(key)
|
81
|
+
@names.include? key.downcase
|
82
|
+
end
|
83
|
+
|
84
|
+
alias has_key? include?
|
85
|
+
alias member? include?
|
86
|
+
alias key? include?
|
87
|
+
|
88
|
+
def merge!(other)
|
89
|
+
other.each { |k, v| self[k] = v }
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
alias update merge!
|
94
|
+
|
95
|
+
def merge(other)
|
96
|
+
hash = dup
|
97
|
+
hash.merge! other
|
98
|
+
end
|
99
|
+
|
100
|
+
def replace(other)
|
101
|
+
clear
|
102
|
+
@names.clear
|
103
|
+
update other
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
def to_hash
|
108
|
+
{}.update(self)
|
109
|
+
end
|
110
|
+
|
111
|
+
def parse(header_string)
|
112
|
+
return unless header_string && !header_string.empty?
|
113
|
+
|
114
|
+
headers = header_string.split("\r\n")
|
115
|
+
|
116
|
+
# Find the last set of response headers.
|
117
|
+
start_index = headers.rindex { |x| x.start_with?('HTTP/') } || 0
|
118
|
+
last_response = headers.slice(start_index, headers.size)
|
119
|
+
|
120
|
+
last_response
|
121
|
+
.tap { |a| a.shift if a.first.start_with?('HTTP/') }
|
122
|
+
.map { |h| h.split(/:\s*/, 2) } # split key and value
|
123
|
+
.reject { |p| p[0].nil? } # ignore blank lines
|
124
|
+
.each { |key, value| add_parsed(key, value) }
|
125
|
+
end
|
126
|
+
|
127
|
+
protected
|
128
|
+
|
129
|
+
attr_reader :names
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
# Join multiple values with a comma.
|
134
|
+
def add_parsed(key, value)
|
135
|
+
if key?(key)
|
136
|
+
self[key] = self[key].to_s
|
137
|
+
self[key] << ', ' << value
|
138
|
+
else
|
139
|
+
self[key] = value
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'monitor'
|
4
|
+
|
5
|
+
module LogStash::Inputs::AkamaiSiem::MiddlewareRegistry
|
6
|
+
# Adds the ability for other modules to register and lookup
|
7
|
+
# middleware classes.
|
8
|
+
def registered_middleware
|
9
|
+
@registered_middleware ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
# Register middleware class(es) on the current module.
|
13
|
+
#
|
14
|
+
# @param mappings [Hash] Middleware mappings from a lookup symbol to a middleware class.
|
15
|
+
# @return [void]
|
16
|
+
#
|
17
|
+
# @example Lookup by a constant
|
18
|
+
#
|
19
|
+
# module Faraday
|
20
|
+
# class Whatever < Middleware
|
21
|
+
# # Middleware looked up by :foo returns Faraday::Whatever::Foo.
|
22
|
+
# register_middleware(foo: Whatever)
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
def register_middleware(**mappings)
|
26
|
+
middleware_mutex do
|
27
|
+
registered_middleware.update(mappings)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Unregister a previously registered middleware class.
|
32
|
+
#
|
33
|
+
# @param key [Symbol] key for the registered middleware.
|
34
|
+
def unregister_middleware(key)
|
35
|
+
registered_middleware.delete(key)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Lookup middleware class with a registered Symbol shortcut.
|
39
|
+
#
|
40
|
+
# @param key [Symbol] key for the registered middleware.
|
41
|
+
# @return [Class] a middleware Class.
|
42
|
+
# @raise [Faraday::Error] if given key is not registered
|
43
|
+
#
|
44
|
+
# @example
|
45
|
+
#
|
46
|
+
# module Faraday
|
47
|
+
# class Whatever < Middleware
|
48
|
+
# register_middleware(foo: Whatever)
|
49
|
+
# end
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# Faraday::Middleware.lookup_middleware(:foo)
|
53
|
+
# # => Faraday::Whatever
|
54
|
+
def lookup_middleware(key)
|
55
|
+
load_middleware(key) ||
|
56
|
+
raise(Faraday::Error, "#{key.inspect} is not registered on #{self}")
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def middleware_mutex(&block)
|
62
|
+
@middleware_mutex ||= Monitor.new
|
63
|
+
@middleware_mutex.synchronize(&block)
|
64
|
+
end
|
65
|
+
|
66
|
+
def load_middleware(key)
|
67
|
+
value = registered_middleware[key]
|
68
|
+
case value
|
69
|
+
when Module
|
70
|
+
value
|
71
|
+
when Symbol, String
|
72
|
+
middleware_mutex do
|
73
|
+
@registered_middleware[key] = const_get(value)
|
74
|
+
end
|
75
|
+
when Proc
|
76
|
+
middleware_mutex do
|
77
|
+
@registered_middleware[key] = value.call
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class LogStash::Inputs::AkamaiSiem
|
4
|
+
Request = Struct.new(:http_method, :path, :headers, :body, :host, :url) do
|
5
|
+
extend MiddlewareRegistry
|
6
|
+
alias_method :member_get, :[]
|
7
|
+
private :member_get
|
8
|
+
alias_method :member_set, :[]=
|
9
|
+
private :member_set
|
10
|
+
|
11
|
+
# @param request_method [String]
|
12
|
+
# @yield [request] for block customization, if block given
|
13
|
+
# @yieldparam request [Request]
|
14
|
+
# @return [Request]
|
15
|
+
def self.create(request_method)
|
16
|
+
new(request_method).tap do |request|
|
17
|
+
yield(request) if block_given?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
remove_method :headers=
|
22
|
+
# Replace request headers, preserving the existing hash type.
|
23
|
+
#
|
24
|
+
# @param hash [Hash] new headers
|
25
|
+
def headers=(hash)
|
26
|
+
if headers
|
27
|
+
headers.replace hash
|
28
|
+
else
|
29
|
+
member_set(:headers, hash)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def update_uri(url)
|
34
|
+
if url.respond_to? :query
|
35
|
+
if (query = url.query)
|
36
|
+
path = url.path = Addressable::URI.unencode(url.path.dup.gsub(/\/$/, ''))
|
37
|
+
end
|
38
|
+
else
|
39
|
+
anchor_index = path.index('#')
|
40
|
+
path = path.slice(0, anchor_index) unless anchor_index.nil?
|
41
|
+
path, query = path.split('?', 2)
|
42
|
+
end
|
43
|
+
self.host = url.host
|
44
|
+
self.path = path
|
45
|
+
self.url = url
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param key [Object] key to look up in headers
|
49
|
+
# @return [Object] value of the given header name
|
50
|
+
def [](key)
|
51
|
+
headers[key]
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param key [Object] key of header to write
|
55
|
+
# @param value [Object] value of header
|
56
|
+
def []=(key, value)
|
57
|
+
headers[key] = value
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_a
|
61
|
+
[
|
62
|
+
self.http_method,
|
63
|
+
self.url.to_s,
|
64
|
+
headers: self.headers
|
65
|
+
]
|
66
|
+
end
|
67
|
+
|
68
|
+
# Marshal serialization support.
|
69
|
+
#
|
70
|
+
# @return [Hash] the hash ready to be serialized in Marshal.
|
71
|
+
def marshal_dump
|
72
|
+
{
|
73
|
+
http_method: http_method,
|
74
|
+
body: body,
|
75
|
+
headers: headers,
|
76
|
+
path: path,
|
77
|
+
host: host
|
78
|
+
}
|
79
|
+
end
|
80
|
+
|
81
|
+
# Marshal serialization support.
|
82
|
+
# Restores the instance variables according to the +serialised+.
|
83
|
+
# @param serialised [Hash] the serialised object.
|
84
|
+
def marshal_load(serialised)
|
85
|
+
self.http_method = serialised[:http_method]
|
86
|
+
self.body = serialised[:body]
|
87
|
+
self.headers = serialised[:headers]
|
88
|
+
self.path = serialised[:path]
|
89
|
+
self.host = serialised[:host]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|