logstash-input-http_bold 3.4.1-java

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.
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+ require "logstash-input-http_bold_jars"
3
+
4
+ module LogStash module Inputs class Http
5
+ class MessageHandler
6
+ include org.logstash.plugins.inputs.http.IMessageHandler
7
+
8
+ attr_reader :input
9
+
10
+ def initialize(input, default_codec, additional_codecs, auth_token)
11
+ @input = input
12
+ @default_codec = default_codec
13
+ @additional_codecs = additional_codecs
14
+ @auth_token = auth_token
15
+ end
16
+
17
+ def validates_token(token)
18
+ if @auth_token
19
+ @auth_token == token
20
+ else
21
+ true
22
+ end
23
+ end
24
+
25
+ def requires_token
26
+ !!@auth_token
27
+ end
28
+
29
+ def onNewMessage(remote_address, headers, body)
30
+ @input.decode_body(headers, remote_address, body, @default_codec, @additional_codecs)
31
+ end
32
+
33
+ def copy
34
+ MessageHandler.new(@input, @default_codec.clone, clone_additional_codecs(), @auth_token)
35
+ end
36
+
37
+ def clone_additional_codecs
38
+ clone_additional_codecs = {}
39
+ @additional_codecs.each do |content_type, codec|
40
+ clone_additional_codecs[content_type] = codec.clone
41
+ end
42
+ clone_additional_codecs
43
+ end
44
+
45
+ def response_headers
46
+ @input.response_headers
47
+ end
48
+ end
49
+ end; end; end
@@ -0,0 +1,40 @@
1
+ # encoding: utf-8
2
+ module LogStash module Inputs class Http
3
+ class TLS
4
+ class TLSOption
5
+ include Comparable
6
+
7
+ attr_reader :name, :version
8
+ def initialize(name, version)
9
+ @name = name
10
+ @version = version
11
+ end
12
+
13
+ def <=>(other)
14
+ version <=> other.version
15
+ end
16
+ end
17
+
18
+ TLS_PROTOCOL_OPTIONS = [
19
+ TLSOption.new("TLSv1", 1),
20
+ TLSOption.new("TLSv1.1", 1.1),
21
+ TLSOption.new("TLSv1.2", 1.2)
22
+ ]
23
+
24
+ def self.min
25
+ TLS_PROTOCOL_OPTIONS.min
26
+ end
27
+
28
+ def self.max
29
+ TLS_PROTOCOL_OPTIONS.max
30
+ end
31
+
32
+ def self.get_supported(versions)
33
+ if versions.is_a?(Range)
34
+ TLS_PROTOCOL_OPTIONS.select { |tls| versions.cover?(tls.version) }
35
+ else
36
+ TLS_PROTOCOL_OPTIONS.select { |tls| versions == tls.version }
37
+ end
38
+ end
39
+ end
40
+ end; end; end
@@ -0,0 +1,336 @@
1
+ # encoding: utf-8
2
+ require "logstash/inputs/base"
3
+ require "logstash/namespace"
4
+ require "stud/interval"
5
+ require "logstash-input-http_jars"
6
+ require "logstash/plugin_mixins/ecs_compatibility_support"
7
+
8
+ # Using this input you can receive single or multiline events over http(s).
9
+ # Applications can send a HTTP POST request with a body to the endpoint started by this
10
+ # input and Logstash will convert it into an event for subsequent processing. Users
11
+ # can pass plain text, JSON, or any formatted data and use a corresponding codec with this
12
+ # input. For Content-Type `application/json` the `json` codec is used, but for all other
13
+ # data formats, `plain` codec is used.
14
+ #
15
+ # This input can also be used to receive webhook requests to integrate with other services
16
+ # and applications. By taking advantage of the vast plugin ecosystem available in Logstash
17
+ # you can trigger actionable events right from your application.
18
+ #
19
+ # ==== Security
20
+ # This plugin supports standard HTTP basic authentication headers to identify the requester.
21
+ # You can pass in an username, password combination while sending data to this input
22
+ #
23
+ # You can also setup SSL and send data securely over https, with an option of validating
24
+ # the client's certificate. Currently, the certificate setup is through
25
+ # https://docs.oracle.com/cd/E19509-01/820-3503/ggfen/index.html[Java Keystore
26
+ # format]
27
+ #
28
+ class LogStash::Inputs::Http < LogStash::Inputs::Base
29
+ include LogStash::PluginMixins::ECSCompatibilitySupport(:disabled, :v1, :v8 => :v1)
30
+ require "logstash/inputs/http/tls"
31
+
32
+ java_import "io.netty.handler.codec.http.HttpUtil"
33
+
34
+ config_name "http_bold"
35
+
36
+ # Codec used to decode the incoming data.
37
+ # This codec will be used as a fall-back if the content-type
38
+ # is not found in the "additional_codecs" hash
39
+ default :codec, "plain"
40
+
41
+ # The host or ip to bind
42
+ config :host, :validate => :string, :default => "0.0.0.0"
43
+
44
+ # The TCP port to bind to
45
+ config :port, :validate => :number, :default => 8080
46
+
47
+ # Username for basic authorization
48
+ config :user, :validate => :string, :required => false
49
+
50
+ # Password for basic authorization
51
+ config :password, :validate => :password, :required => false
52
+
53
+ # Events are by default sent in plain text. You can
54
+ # enable encryption by setting `ssl` to true and configuring
55
+ # the `ssl_certificate` and `ssl_key` options.
56
+ config :ssl, :validate => :boolean, :default => false
57
+
58
+ # SSL certificate to use.
59
+ config :ssl_certificate, :validate => :path
60
+
61
+ # SSL key to use.
62
+ # NOTE: This key need to be in the PKCS8 format, you can convert it with https://www.openssl.org/docs/man1.1.0/apps/pkcs8.html[OpenSSL]
63
+ # for more information.
64
+ config :ssl_key, :validate => :path
65
+
66
+ # SSL key passphrase to use.
67
+ config :ssl_key_passphrase, :validate => :password
68
+
69
+ # Validate client certificates against these authorities.
70
+ # You can define multiple files or paths. All the certificates will
71
+ # be read and added to the trust store. You need to configure the `ssl_verify_mode`
72
+ # to `peer` or `force_peer` to enable the verification.
73
+ config :ssl_certificate_authorities, :validate => :array, :default => []
74
+
75
+ # By default the server doesn't do any client verification.
76
+ #
77
+ # `peer` will make the server ask the client to provide a certificate.
78
+ # If the client provides a certificate, it will be validated.
79
+ #
80
+ # `force_peer` will make the server ask the client to provide a certificate.
81
+ # If the client doesn't provide a certificate, the connection will be closed.
82
+ #
83
+ # This option needs to be used with `ssl_certificate_authorities` and a defined list of CAs.
84
+ config :ssl_verify_mode, :validate => ["none", "peer", "force_peer"], :default => "none"
85
+
86
+ # Time in milliseconds for an incomplete ssl handshake to timeout
87
+ config :ssl_handshake_timeout, :validate => :number, :default => 10000
88
+
89
+ # The minimum TLS version allowed for the encrypted connections. The value must be one of the following:
90
+ # 1.0 for TLS 1.0, 1.1 for TLS 1.1, 1.2 for TLS 1.2
91
+ config :tls_min_version, :validate => :number, :default => TLS.min.version
92
+
93
+ # The maximum TLS version allowed for the encrypted connections. The value must be the one of the following:
94
+ # 1.0 for TLS 1.0, 1.1 for TLS 1.1, 1.2 for TLS 1.2
95
+ config :tls_max_version, :validate => :number, :default => TLS.max.version
96
+
97
+ # The list of ciphers suite to use, listed by priorities.
98
+ config :cipher_suites, :validate => :array, :default => org.logstash.plugins.inputs.http.util.SslSimpleBuilder.getDefaultCiphers
99
+
100
+ # Apply specific codecs for specific content types.
101
+ # The default codec will be applied only after this list is checked
102
+ # and no codec for the request's content-type is found
103
+ config :additional_codecs, :validate => :hash, :default => { "application/json" => "json" }
104
+
105
+ # specify a custom set of response headers
106
+ config :response_headers, :validate => :hash, :default => { 'Content-Type' => 'text/plain' }
107
+
108
+ # target field for the client host of the http request
109
+ config :remote_host_target_field, :validate => :string
110
+
111
+ # target field for the client host of the http request
112
+ config :request_headers_target_field, :validate => :string
113
+
114
+ config :threads, :validate => :number, :required => false, :default => ::LogStash::Config::CpuCoreStrategy.maximum
115
+
116
+ config :max_pending_requests, :validate => :number, :required => false, :default => 200
117
+
118
+ config :max_content_length, :validate => :number, :required => false, :default => 100 * 1024 * 1024
119
+
120
+ config :response_code, :validate => [200, 201, 202, 204], :default => 200
121
+ # Deprecated options
122
+
123
+ # The JKS keystore to validate the client's certificates
124
+ config :keystore, :validate => :path, :deprecated => "Set 'ssl_certificate' and 'ssl_key' instead."
125
+ config :keystore_password, :validate => :password, :deprecated => "Set 'ssl_key_passphrase' instead."
126
+
127
+ config :verify_mode, :validate => ['none', 'peer', 'force_peer'], :default => 'none',
128
+ :deprecated => "Set 'ssl_verify_mode' instead."
129
+
130
+ public
131
+ def register
132
+
133
+ validate_ssl_settings!
134
+
135
+ if @user && @password
136
+ token = Base64.strict_encode64("#{@user}:#{@password.value}")
137
+ @auth_token = "Basic #{token}"
138
+ end
139
+
140
+ @codecs = Hash.new
141
+
142
+ @additional_codecs.each do |content_type, codec|
143
+ @codecs[content_type] = LogStash::Plugin.lookup("codec", codec).new
144
+ end
145
+
146
+ require "logstash/inputs/http/message_handler"
147
+ message_handler = MessageHandler.new(self, @codec, @codecs, @auth_token)
148
+ @http_server = create_http_server(message_handler)
149
+
150
+ @remote_host_target_field ||= ecs_select[disabled: "host", v1: "[host][ip]"]
151
+ @request_headers_target_field ||= ecs_select[disabled: "headers", v1: "[@metadata][input][http][request][headers]"]
152
+ end # def register
153
+
154
+ def run(queue)
155
+ @queue = queue
156
+ @logger.info("Starting http input listener", :address => "#{@host}:#{@port}", :ssl => "#{@ssl}")
157
+ @http_server.run()
158
+ end
159
+
160
+ def stop
161
+ @http_server.close() rescue nil
162
+ end
163
+
164
+ def close
165
+ @http_server.close() rescue nil
166
+ end
167
+
168
+ def decode_body(headers, remote_address, body, default_codec, additional_codecs)
169
+ content_type = headers.fetch("content_type", "")
170
+ codec = additional_codecs.fetch(HttpUtil.getMimeType(content_type), default_codec)
171
+ codec.decode(body) { |event| push_decoded_event(headers, remote_address, event) }
172
+ codec.flush { |event| push_decoded_event(headers, remote_address, event) }
173
+ true
174
+ rescue => e
175
+ @logger.error(
176
+ "unable to process event.",
177
+ :message => e.message,
178
+ :class => e.class.name,
179
+ :backtrace => e.backtrace
180
+ )
181
+ false
182
+ end
183
+
184
+ def push_decoded_event(headers, remote_address, event)
185
+ add_ecs_fields(headers, event)
186
+ event.set(@request_headers_target_field, headers)
187
+ event.set(@remote_host_target_field, remote_address)
188
+ decorate(event)
189
+ @queue << event
190
+ end
191
+
192
+ def add_ecs_fields(headers, event)
193
+ return if ecs_compatibility == :disabled
194
+
195
+ http_version = headers.get("http_version")
196
+ event.set("[http][version]", http_version) if http_version
197
+
198
+ http_user_agent = headers.get("http_user_agent")
199
+ event.set("[user_agent][original]", http_user_agent) if http_user_agent
200
+
201
+ http_host = headers.get("http_host")
202
+ domain, port = self.class.get_domain_port(http_host)
203
+ event.set("[url][domain]", domain) if domain
204
+ event.set("[url][port]", port) if port
205
+
206
+ request_method = headers.get("request_method")
207
+ event.set("[http][method]", request_method) if request_method
208
+
209
+ request_path = headers.get("request_path")
210
+ event.set("[url][path]", request_path) if request_path
211
+
212
+ content_length = headers.get("content_length")
213
+ event.set("[http][request][body][bytes]", content_length) if content_length
214
+
215
+ content_type = headers.get("content_type")
216
+ event.set("[http][request][mime_type]", content_type) if content_type
217
+ end
218
+
219
+ # match the domain and port in either IPV4, "127.0.0.1:8080", or IPV6, "[2001:db8::8a2e:370:7334]:8080", style
220
+ # return [domain, port]
221
+ def self.get_domain_port(http_host)
222
+ if /^(([^:]+)|\[(.*)\])\:([\d]+)$/ =~ http_host
223
+ ["#{$2 || $3}", $4.to_i]
224
+ else
225
+ [http_host, nil]
226
+ end
227
+ end
228
+
229
+ def validate_ssl_settings!
230
+ if !@ssl
231
+ @logger.warn("SSL Certificate will not be used") if @ssl_certificate
232
+ @logger.warn("SSL Key will not be used") if @ssl_key
233
+ @logger.warn("SSL Java Key Store will not be used") if @keystore
234
+ elsif !(ssl_key_configured? || ssl_jks_configured?)
235
+ raise LogStash::ConfigurationError, "Certificate or JKS must be configured"
236
+ end
237
+
238
+ if @ssl && (original_params.key?("verify_mode") && original_params.key?("ssl_verify_mode"))
239
+ raise LogStash::ConfigurationError, "Both 'ssl_verify_mode' and 'verify_mode' were set. Use only 'ssl_verify_mode'."
240
+ elsif original_params.key?("verify_mode")
241
+ @ssl_verify_mode_final = @verify_mode
242
+ elsif original_params.key?("ssl_verify_mode")
243
+ @ssl_verify_mode_final = @ssl_verify_mode
244
+ else
245
+ @ssl_verify_mode_final = @ssl_verify_mode
246
+ end
247
+
248
+ if @ssl && require_certificate_authorities? && !client_authentication?
249
+ raise LogStash::ConfigurationError, "Using `ssl_verify_mode` or `verify_mode` set to PEER or FORCE_PEER, requires the configuration of `ssl_certificate_authorities`"
250
+ elsif @ssl && !require_certificate_authorities? && client_authentication?
251
+ raise LogStash::ConfigurationError, "The configuration of `ssl_certificate_authorities` requires setting `ssl_verify_mode` or `verify_mode` to PEER or FORCE_PEER"
252
+ end
253
+ end
254
+
255
+ def create_http_server(message_handler)
256
+ org.logstash.plugins.inputs.http.NettyHttpServer.new(
257
+ @host, @port, message_handler, build_ssl_params(), @threads, @max_pending_requests, @max_content_length, @response_code)
258
+ end
259
+
260
+ def build_ssl_params
261
+ return nil unless @ssl
262
+
263
+ if @keystore && @keystore_password
264
+ ssl_builder = org.logstash.plugins.inputs.http.util.JksSslBuilder.new(@keystore, @keystore_password.value)
265
+ else
266
+ begin
267
+ ssl_builder = org.logstash.plugins.inputs.http.util.SslSimpleBuilder
268
+ .new(@ssl_certificate, @ssl_key, @ssl_key_passphrase.nil? ? nil : @ssl_key_passphrase.value)
269
+ .setCipherSuites(normalized_ciphers)
270
+ rescue java.lang.IllegalArgumentException => e
271
+ @logger.error("SSL configuration invalid", error_details(e))
272
+ raise LogStash::ConfigurationError, e
273
+ end
274
+
275
+ if client_authentication?
276
+ ssl_builder.setCertificateAuthorities(@ssl_certificate_authorities)
277
+ end
278
+ end
279
+
280
+ new_ssl_handshake_provider(ssl_builder)
281
+ end
282
+
283
+ def ssl_key_configured?
284
+ !!(@ssl_certificate && @ssl_key)
285
+ end
286
+
287
+ def ssl_jks_configured?
288
+ !!(@keystore && @keystore_password)
289
+ end
290
+
291
+ def client_authentication?
292
+ @ssl_certificate_authorities && @ssl_certificate_authorities.size > 0
293
+ end
294
+
295
+ def require_certificate_authorities?
296
+ @ssl_verify_mode_final == "force_peer" || @ssl_verify_mode_final == "peer"
297
+ end
298
+
299
+ private
300
+
301
+ def normalized_ciphers
302
+ @cipher_suites.map(&:upcase)
303
+ end
304
+
305
+ def convert_protocols
306
+ TLS.get_supported(@tls_min_version..@tls_max_version).map(&:name)
307
+ end
308
+
309
+ def new_ssl_handshake_provider(ssl_builder)
310
+ begin
311
+ ssl_handler_provider = org.logstash.plugins.inputs.http.util.SslHandlerProvider.new(ssl_builder.build())
312
+ ssl_handler_provider.setVerifyMode(@ssl_verify_mode_final.upcase)
313
+ ssl_handler_provider.setProtocols(convert_protocols)
314
+ ssl_handler_provider.setHandshakeTimeoutMilliseconds(@ssl_handshake_timeout)
315
+ ssl_handler_provider
316
+ rescue java.lang.IllegalArgumentException => e
317
+ @logger.error("SSL configuration invalid", error_details(e))
318
+ raise LogStash::ConfigurationError, e
319
+ rescue java.lang.Exception => e
320
+ @logger.error("SSL configuration failed", error_details(e, true))
321
+ raise e
322
+ end
323
+ end
324
+
325
+ def error_details(e, trace = false)
326
+ error_details = { :exception => e.class, :message => e.message }
327
+ error_details[:backtrace] = e.backtrace if trace || @logger.debug?
328
+ cause = e.cause
329
+ if cause && e != cause
330
+ error_details[:cause] = { :exception => cause.class, :message => cause.message }
331
+ error_details[:cause][:backtrace] = cause.backtrace if trace || @logger.debug?
332
+ end
333
+ error_details
334
+ end
335
+
336
+ end # class LogStash::Inputs::Http
@@ -0,0 +1,39 @@
1
+ class CompressedRequests
2
+ def initialize(app)
3
+ @app = app
4
+ end
5
+
6
+ def method_handled?(env)
7
+ !!(env['REQUEST_METHOD'] =~ /(POST|PUT)/)
8
+ end
9
+
10
+ def encoding_handled?(env)
11
+ ['gzip', 'deflate'].include? env['HTTP_CONTENT_ENCODING']
12
+ end
13
+
14
+ def call(env)
15
+ if method_handled?(env) && encoding_handled?(env)
16
+ begin
17
+ extracted = decode(env['rack.input'], env['HTTP_CONTENT_ENCODING'])
18
+ rescue Zlib::Error
19
+ return [400, {'Content-Type' => 'text/plain'}, ["Failed to decompress body"]]
20
+ end
21
+
22
+ env.delete('HTTP_CONTENT_ENCODING')
23
+ env['CONTENT_LENGTH'] = extracted.bytesize
24
+ env['rack.input'] = StringIO.new(extracted)
25
+ end
26
+
27
+ @app.call(env)
28
+ end
29
+
30
+ def decode(input, content_encoding)
31
+ case content_encoding
32
+ when 'gzip' then
33
+ Zlib::GzipReader.new(input).read
34
+ when 'deflate' then
35
+ Zlib::Inflate.inflate(input.read)
36
+ end
37
+ end
38
+
39
+ end