logstash-input-http 3.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ # AUTOGENERATED BY THE GRADLE SCRIPT. DO NOT EDIT.
2
+
3
+ require 'jar_dependencies'
4
+ require_jar('io.netty', 'netty-all', '4.1.18.Final')
5
+ require_jar('io.netty', 'netty-tcnative-boringssl-static', '2.0.7.Final')
6
+ require_jar('org.apache.logging.log4j', 'log4j-api', '2.6.2')
7
+ require_jar('org.logstash.plugins.input.http', 'logstash-input-http', '3.1.0')
@@ -0,0 +1,290 @@
1
+ # encoding: utf-8
2
+ require "logstash/inputs/base"
3
+ require "logstash/namespace"
4
+ require "stud/interval"
5
+ require "logstash-input-http_jars"
6
+
7
+ # Using this input you can receive single or multiline events over http(s).
8
+ # Applications can send a HTTP POST request with a body to the endpoint started by this
9
+ # input and Logstash will convert it into an event for subsequent processing. Users
10
+ # can pass plain text, JSON, or any formatted data and use a corresponding codec with this
11
+ # input. For Content-Type `application/json` the `json` codec is used, but for all other
12
+ # data formats, `plain` codec is used.
13
+ #
14
+ # This input can also be used to receive webhook requests to integrate with other services
15
+ # and applications. By taking advantage of the vast plugin ecosystem available in Logstash
16
+ # you can trigger actionable events right from your application.
17
+ #
18
+ # ==== Security
19
+ # This plugin supports standard HTTP basic authentication headers to identify the requester.
20
+ # You can pass in an username, password combination while sending data to this input
21
+ #
22
+ # You can also setup SSL and send data securely over https, with an option of validating
23
+ # the client's certificate. Currently, the certificate setup is through
24
+ # https://docs.oracle.com/cd/E19509-01/820-3503/ggfen/index.html[Java Keystore
25
+ # format]
26
+ #
27
+ class LogStash::Inputs::Http < LogStash::Inputs::Base
28
+ require "logstash/inputs/http/tls"
29
+
30
+ config_name "http"
31
+
32
+ # Codec used to decode the incoming data.
33
+ # This codec will be used as a fall-back if the content-type
34
+ # is not found in the "additional_codecs" hash
35
+ default :codec, "plain"
36
+
37
+ # The host or ip to bind
38
+ config :host, :validate => :string, :default => "0.0.0.0"
39
+
40
+ # The TCP port to bind to
41
+ config :port, :validate => :number, :default => 8080
42
+
43
+ # Username for basic authorization
44
+ config :user, :validate => :string, :required => false
45
+
46
+ # Password for basic authorization
47
+ config :password, :validate => :password, :required => false
48
+
49
+ # Events are by default sent in plain text. You can
50
+ # enable encryption by setting `ssl` to true and configuring
51
+ # the `ssl_certificate` and `ssl_key` options.
52
+ config :ssl, :validate => :boolean, :default => false
53
+
54
+ # SSL certificate to use.
55
+ config :ssl_certificate, :validate => :path
56
+
57
+ # SSL key to use.
58
+ # 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]
59
+ # for more information.
60
+ config :ssl_key, :validate => :path
61
+
62
+ # SSL key passphrase to use.
63
+ config :ssl_key_passphrase, :validate => :password
64
+
65
+ # Validate client certificates against these authorities.
66
+ # You can define multiple files or paths. All the certificates will
67
+ # be read and added to the trust store. You need to configure the `ssl_verify_mode`
68
+ # to `peer` or `force_peer` to enable the verification.
69
+ config :ssl_certificate_authorities, :validate => :array, :default => []
70
+
71
+ # By default the server doesn't do any client verification.
72
+ #
73
+ # `peer` will make the server ask the client to provide a certificate.
74
+ # If the client provides a certificate, it will be validated.
75
+ #
76
+ # `force_peer` will make the server ask the client to provide a certificate.
77
+ # If the client doesn't provide a certificate, the connection will be closed.
78
+ #
79
+ # This option needs to be used with `ssl_certificate_authorities` and a defined list of CAs.
80
+ config :ssl_verify_mode, :validate => ["none", "peer", "force_peer"], :default => "none"
81
+
82
+ # Time in milliseconds for an incomplete ssl handshake to timeout
83
+ config :ssl_handshake_timeout, :validate => :number, :default => 10000
84
+
85
+ # The minimum TLS version allowed for the encrypted connections. The value must be one of the following:
86
+ # 1.0 for TLS 1.0, 1.1 for TLS 1.1, 1.2 for TLS 1.2
87
+ config :tls_min_version, :validate => :number, :default => TLS.min.version
88
+
89
+ # The maximum TLS version allowed for the encrypted connections. The value must be the 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_max_version, :validate => :number, :default => TLS.max.version
92
+
93
+ # The list of ciphers suite to use, listed by priorities.
94
+ config :cipher_suites, :validate => :array, :default => org.logstash.plugins.inputs.http.util.SslSimpleBuilder::DEFAULT_CIPHERS
95
+
96
+ # Apply specific codecs for specific content types.
97
+ # The default codec will be applied only after this list is checked
98
+ # and no codec for the request's content-type is found
99
+ config :additional_codecs, :validate => :hash, :default => { "application/json" => "json" }
100
+
101
+ # specify a custom set of response headers
102
+ config :response_headers, :validate => :hash, :default => { 'Content-Type' => 'text/plain' }
103
+
104
+ config :threads, :validate => :number, :required => false, :default => ::LogStash::Config::CpuCoreStrategy.maximum
105
+
106
+ config :max_pending_requests, :validate => :number, :required => false, :default => 200
107
+
108
+ config :max_content_length, :validate => :number, :required => false, :default => 100 * 1024 * 1024
109
+
110
+ # Deprecated options
111
+
112
+ # The JKS keystore to validate the client's certificates
113
+ config :keystore, :validate => :path, :deprecated => "Set 'ssl_certificate' and 'ssl_key' instead."
114
+ config :keystore_password, :validate => :password, :deprecated => "Set 'ssl_key_passphrase' instead."
115
+
116
+ config :verify_mode, :validate => ['none', 'peer', 'force_peer'], :default => 'none',
117
+ :deprecated => "Set 'ssl_verify_mode' instead."
118
+
119
+ public
120
+ def register
121
+
122
+ validate_ssl_settings!
123
+
124
+ if @user && @password then
125
+ token = Base64.strict_encode64("#{@user}:#{@password.value}")
126
+ @auth_token = "Basic #{token}"
127
+ end
128
+
129
+ @codecs = Hash.new
130
+
131
+ @additional_codecs.each do |content_type, codec|
132
+ @codecs[content_type] = LogStash::Plugin.lookup("codec", codec).new
133
+ end
134
+
135
+ require "logstash/inputs/http/message_handler"
136
+ message_handler = MessageHandler.new(self, @codec, @codecs)
137
+ @http_server = create_http_server(message_handler)
138
+ end # def register
139
+
140
+ def run(queue)
141
+ @queue = queue
142
+ @logger.info("Starting http input listener", :address => "#{@host}:#{@port}", :ssl => "#{@ssl}")
143
+ @http_server.run()
144
+ end
145
+
146
+ def stop
147
+ @http_server.close() rescue nil
148
+ end
149
+
150
+ def close
151
+ @http_server.close() rescue nil
152
+ end
153
+
154
+ def decode_body(remote_address, http_full_request, default_codec, additional_codecs)
155
+ body = read_bytes(http_full_request.content)
156
+ headers = {}
157
+
158
+ http_full_request.headers.each do |header|
159
+ headers[header.key.downcase.tr('-', '_')] = header.value
160
+ end
161
+ headers["request_method"] = http_full_request.getMethod.name
162
+ headers["request_path"] = http_full_request.getUri
163
+ headers["http_version"] = http_full_request.getProtocolVersion.text
164
+ headers["http_accept"] = headers.delete("accept")
165
+ headers["http_host"] = headers.delete("host")
166
+ headers["http_user_agent"] = headers.delete("user_agent")
167
+ codec = additional_codecs.fetch(headers["content_type"], default_codec)
168
+ codec.decode(body) { |event| push_decoded_event(headers, remote_address, event) }
169
+ codec.flush { |event| push_decoded_event(headers, remote_address, event) }
170
+ [200, @response_headers, 'ok']
171
+ rescue => e
172
+ @logger.error(
173
+ "unable to process event.",
174
+ :message => e.message,
175
+ :class => e.class.name,
176
+ :backtrace => e.backtrace
177
+ )
178
+ [500, @response_headers, 'internal error']
179
+ end
180
+
181
+ def push_decoded_event(headers, remote_address, event)
182
+ event.set("headers", headers)
183
+ event.set("host", remote_address)
184
+ decorate(event)
185
+ @queue << event
186
+ end
187
+
188
+ def valid_auth?(token)
189
+ if @auth_token
190
+ @auth_token == token
191
+ else
192
+ true
193
+ end
194
+ end
195
+
196
+ private
197
+ def read_bytes(bytebuffer)
198
+ bytes = Java::byte[bytebuffer.readableBytes].new
199
+ bytebuffer.getBytes(0, bytes)
200
+ String.from_java_bytes(bytes, "ASCII-8BIT")
201
+ end
202
+
203
+ def validate_ssl_settings!
204
+ if !@ssl
205
+ @logger.warn("SSL Certificate will not be used") if @ssl_certificate
206
+ @logger.warn("SSL Key will not be used") if @ssl_key
207
+ @logger.warn("SSL Java Key Store will not be used") if @keystore
208
+ elsif !(ssl_key_configured? || ssl_jks_configured?)
209
+ raise LogStash::ConfigurationError, "Certificate or JKS must be configured"
210
+ end
211
+
212
+ if @ssl && original_params.key?("verify_mode")
213
+ if original_params.key?("ssl_verify_mode")
214
+ raise LogStash::ConfigurationError, "Both 'ssl_verify_mode' and 'verify_mode' were set. Use only 'ssl_verify_mode'."
215
+ end
216
+ end
217
+
218
+ if @ssl && require_certificate_authorities? && !client_authentication?
219
+ raise LogStash::ConfigurationError, "Using `ssl_verify_mode` or `verify_mode` set to PEER or FORCE_PEER, requires the configuration of `ssl_certificate_authorities`"
220
+ elsif @ssl && !require_certificate_authorities? && client_authentication?
221
+ raise LogStash::ConfigurationError, "The configuration of `ssl_certificate_authorities` requires setting `ssl_verify_mode` or `verify_mode` to PEER or FORCE_PEER"
222
+ end
223
+ end
224
+
225
+ def create_http_server(message_handler)
226
+ org.logstash.plugins.inputs.http.NettyHttpServer.new(
227
+ @host, @port, message_handler, build_ssl_params(), @threads, @max_pending_requests, @max_content_length)
228
+ end
229
+
230
+ def build_ssl_params
231
+ return nil unless @ssl
232
+
233
+ if @keystore && @keystore_password
234
+ ssl_builder = org.logstash.plugins.inputs.http.util.JksSslBuilder.new(@keystore, @keystore_password.value)
235
+ if original_params.key?("verify_mode")
236
+ if @verify_mode.upcase == "FORCE_PEER"
237
+ ssl_builder.setVerifyMode(org.logstash.plugins.inputs.http.util.SslBuilder::SslClientVerifyMode::FORCE_PEER)
238
+ elsif @verify_mode.upcase == "PEER"
239
+ ssl_builder.setVerifyMode(org.logstash.plugins.inputs.http.util.SslBuilder::SslClientVerifyMode::VERIFY_PEER)
240
+ end
241
+ end
242
+ return ssl_builder
243
+ end
244
+
245
+ begin
246
+ ssl_builder = org.logstash.plugins.inputs.http.util.SslSimpleBuilder.new(@ssl_certificate, @ssl_key, @ssl_key_passphrase.nil? ? nil : @ssl_key_passphrase.value)
247
+ .setProtocols(convert_protocols)
248
+ .setCipherSuites(normalized_ciphers)
249
+ rescue java.lang.IllegalArgumentException => e
250
+ raise LogStash::ConfigurationError.new(e)
251
+ end
252
+
253
+ ssl_builder.setHandshakeTimeoutMilliseconds(@ssl_handshake_timeout)
254
+
255
+ if client_authentication?
256
+ if @ssl_verify_mode.upcase == "FORCE_PEER"
257
+ ssl_builder.setVerifyMode(org.logstash.plugins.inputs.http.util.SslBuilder::SslClientVerifyMode::FORCE_PEER)
258
+ elsif @ssl_verify_mode.upcase == "PEER"
259
+ ssl_builder.setVerifyMode(org.logstash.plugins.inputs.http.util.SslBuilder::SslClientVerifyMode::VERIFY_PEER)
260
+ end
261
+ ssl_builder.setCertificateAuthorities(@ssl_certificate_authorities)
262
+ end
263
+ ssl_builder
264
+ end
265
+
266
+ def ssl_key_configured?
267
+ !!(@ssl_certificate && @ssl_key)
268
+ end
269
+
270
+ def ssl_jks_configured?
271
+ !!(@keystore && @keystore_password)
272
+ end
273
+
274
+ def client_authentication?
275
+ @ssl_certificate_authorities && @ssl_certificate_authorities.size > 0
276
+ end
277
+
278
+ def require_certificate_authorities?
279
+ @ssl_verify_mode == "force_peer" || @ssl_verify_mode == "peer"
280
+ end
281
+
282
+ def normalized_ciphers
283
+ @cipher_suites.map(&:upcase)
284
+ end
285
+
286
+ def convert_protocols
287
+ TLS.get_supported(@tls_min_version..@tls_max_version).map(&:name)
288
+ end
289
+
290
+ end # class LogStash::Inputs::Http
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+ require "logstash-input-http_jars"
3
+ java_import org.logstash.plugins.inputs.http.MessageHandler
4
+ java_import "io.netty.handler.codec.http.DefaultFullHttpResponse"
5
+ java_import "io.netty.handler.codec.http.HttpHeaderNames"
6
+ java_import "io.netty.handler.codec.http.HttpVersion"
7
+ java_import "io.netty.handler.codec.http.HttpResponseStatus"
8
+ java_import "io.netty.buffer.Unpooled"
9
+ java_import "io.netty.util.CharsetUtil"
10
+
11
+ module LogStash module Inputs class Http
12
+ class MessageHandler
13
+ include org.logstash.plugins.inputs.http.IMessageHandler
14
+
15
+ attr_reader :input
16
+
17
+ def initialize(input, default_codec, additional_codecs)
18
+ @input = input
19
+ @default_codec = default_codec
20
+ @additional_codecs = additional_codecs
21
+ end
22
+
23
+ def onNewMessage(remote_address, message)
24
+ if valid_auth?(message)
25
+ message.headers.remove(HttpHeaderNames::AUTHORIZATION)
26
+ status, headers, content = @input.decode_body(remote_address, message, @default_codec, @additional_codecs)
27
+ else
28
+ status, headers, content = 401, {}, 'failed to authenticate'
29
+ end
30
+ generate_response(status, headers, content)
31
+ end
32
+
33
+ private
34
+ def generate_response(status, headers, content)
35
+ payload = Unpooled.copiedBuffer(content.to_java_string, CharsetUtil::UTF_8)
36
+ response = DefaultFullHttpResponse.new(
37
+ HttpVersion::HTTP_1_1,
38
+ HttpResponseStatus.valueOf(status),
39
+ payload)
40
+ response.headers().set(HttpHeaderNames::CONTENT_LENGTH, payload.readable_bytes());
41
+ response.headers().set(HttpHeaderNames::CONTENT_TYPE, "text/plain");
42
+ headers.each { |k, v| response.headers().set(k, v) }
43
+ response
44
+ end
45
+
46
+ def copy
47
+ MessageHandler.new(@input, @default_codec.clone, clone_additional_codecs())
48
+ end
49
+
50
+ def clone_additional_codecs
51
+ clone_additional_codecs = {}
52
+ @additional_codecs.each do |content_type, codec|
53
+ clone_additional_codecs[content_type] = codec.clone
54
+ end
55
+ clone_additional_codecs
56
+ end
57
+
58
+ def valid_auth?(message)
59
+ @input.valid_auth?(message.headers.get(HttpHeaderNames::AUTHORIZATION))
60
+ end
61
+ end
62
+ 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,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
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+ require "jars/installer"
3
+ require "fileutils"
4
+
5
+ desc "vendor"
6
+ task :vendor do
7
+ exit(1) unless system './gradlew vendor'
8
+ version = File.read("VERSION").strip
9
+ end
10
+
11
+ desc "clean"
12
+ task :clean do
13
+ ["build", "vendor/jar-dependencies", "Gemfile.lock"].each do |p|
14
+ FileUtils.rm_rf(p)
15
+ end
16
+ end