logstash-input-http_bold 3.4.1-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -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