logstash-input-http 3.1.0-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,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