logstash-input-tcp 3.0.1 → 3.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d560100efd4b27bcef8c8865626a628de300b871
4
- data.tar.gz: 4f22528b91f54d136f838ae5ea7878c7df493462
3
+ metadata.gz: dfdbc6394d0429fde14caccd283662320d239396
4
+ data.tar.gz: 943b1ce9cb23f6b4d6ba0af1b72d935e7bf24b0e
5
5
  SHA512:
6
- metadata.gz: 28bd63a130f1fa48f9470800baea3bc2ea14c9541dd754886ed21428a184ffa2c060b2e12089172acf77297e3136ce471fb3337ce0664e276c7a41fdbf230192
7
- data.tar.gz: e03a18054310108f46336a10375d68dddfdcaba824102de882335aaa824634a05e4ae78357798f469a861e2ca79b144e9dfb642b88264480ee34b6036c84c300
6
+ metadata.gz: 2e72fff4e6bc2b5e0d4929d8e9ab3552112cec606fba3040d08e12955b82a7b07c272a51d9b902be1cf5f8a1d84df74739994242703ee782eb0ca60f4b18e8d9
7
+ data.tar.gz: e49359fda655b9868540a91655e467f10d3e8c39383d3ef4c2ec8e95a2362b5ca5f26e2ee0f0c351ba0ae15378da246eb9d092da0a46aeceb44ad062bf8a16c5
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## 3.0.2
2
+ - Fixed a bug where previous connection would accidentally be closed when accepting new socket connection
3
+ - Fixed an issue with log message which used a closed socket's peer address
4
+
1
5
  ## 3.0.1
2
6
  - properly convert sslsubject to string before assigning to event field, added specs, see https://github.com/logstash-plugins/logstash-input-tcp/pull/38
3
7
 
@@ -115,7 +115,6 @@ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
115
115
  rescue OpenSSL::SSL::SSLError => e
116
116
  # log error, close socket, accept next connection
117
117
  @logger.error("SSL Error", :exception => e, :backtrace => e.backtrace)
118
- socket.close rescue nil
119
118
  rescue => e
120
119
  # if this exception occured while the plugin is stopping
121
120
  # just ignore and exit
@@ -149,6 +148,7 @@ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
149
148
  end
150
149
 
151
150
  def handle_socket(socket, client_address, client_port, output_queue, codec)
151
+ peer = "#{client_address}:#{client_port}"
152
152
  while !stop?
153
153
  codec.decode(read(socket)) do |event|
154
154
  event["host"] ||= client_address
@@ -159,16 +159,16 @@ class LogStash::Inputs::Tcp < LogStash::Inputs::Base
159
159
  end
160
160
  end
161
161
  rescue EOFError
162
- @logger.debug? && @logger.debug("Connection closed", :client => socket.peer)
162
+ @logger.debug? && @logger.debug("Connection closed", :client => peer)
163
163
  rescue Errno::ECONNRESET
164
- @logger.debug? && @logger.debug("Connection reset by peer", :client => socket.peer)
164
+ @logger.debug? && @logger.debug("Connection reset by peer", :client => peer)
165
165
  rescue OpenSSL::SSL::SSLError => e
166
166
  # Fixes issue #23
167
167
  @logger.error("SSL Error", :exception => e, :backtrace => e.backtrace)
168
168
  socket.close rescue nil
169
169
  rescue => e
170
170
  # if plugin is stopping, don't bother logging it as an error
171
- !stop? && @logger.error("An error occurred. Closing connection", :client => socket.peer, :exception => e, :backtrace => e.backtrace)
171
+ !stop? && @logger.error("An error occurred. Closing connection", :client => peer, :exception => e, :backtrace => e.backtrace)
172
172
  ensure
173
173
  # catch all rescue nil on close to discard any close errors or invalid socket
174
174
  socket.close rescue nil
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'logstash-input-tcp'
3
- s.version = '3.0.1'
3
+ s.version = '3.0.2'
4
4
  s.licenses = ['Apache License (2.0)']
5
5
  s.summary = "Read events over a TCP socket."
6
6
  s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
@@ -5,11 +5,14 @@ require "timeout"
5
5
  require "logstash/json"
6
6
  require "logstash/inputs/tcp"
7
7
  require "stud/try"
8
+ require "stud/task"
8
9
  require "flores/pki"
9
10
  require "openssl"
10
11
 
11
12
  require_relative "../spec_helper"
12
13
 
14
+ #Cabin::Channel.get(LogStash).subscribe(STDOUT)
15
+ #Cabin::Channel.get(LogStash).level = :debug
13
16
  describe LogStash::Inputs::Tcp do
14
17
 
15
18
  context "codec (PR #1372)" do
@@ -236,229 +239,139 @@ describe LogStash::Inputs::Tcp do
236
239
  subject.close rescue nil
237
240
  end
238
241
 
239
- describe "register" do
242
+ describe "#register" do
240
243
  it "should register without errors" do
241
244
  expect { subject.register }.to_not raise_error
242
245
  end
243
246
  end
244
247
 
245
- describe "receive" do
246
-
247
- let(:nevents) { 10 }
248
-
249
- let(:events) do
250
- socket = Stud::try(5.times) { TCPSocket.new("127.0.0.1", port) }
251
-
252
- result = helper.pipelineless_input(subject, nevents) do
253
- nevents.times do |i|
254
- socket.puts("msg #{i}")
255
- socket.flush
256
- end
257
- end
258
- socket.close rescue nil
259
-
260
- result
261
- end
262
-
263
- before(:each) do
264
- subject.register
265
- end
266
-
267
- it "should receive events been generated" do
268
- expect(events.size).to be(nevents)
269
- messages = events.map { |event| event["message"]}
270
- messages.each do |message|
271
- expect(message).to match(/msg \d+/)
272
- end
273
- end
274
-
275
- it "should add the host and port to the generated event" do
276
- events.each do |event|
277
- expect(event["host"]).to eq("127.0.0.1")
278
- expect(event["port"]).to be_an(Fixnum)
279
- end
248
+ describe "#receive" do
249
+ shared_examples "receiving events" do
250
+ # TODO(sissel): Implement normal event-receipt tests as as a shared example
280
251
  end
281
252
 
282
- describe "ssl" do
283
-
284
- let(:certificate) { helper.certificate }
285
-
286
- subject(:input) { LogStash::Plugin.lookup("input", "tcp").new(config) }
253
+ context "when ssl_enable is true" do
254
+ let(:pki) { Flores::PKI.generate }
255
+ let(:certificate) { pki[0] }
256
+ let(:key) { pki[1] }
257
+ let(:certificate_file) { Stud::Temporary.file }
258
+ let(:key_file) { Stud::Temporary.file }
259
+ let(:queue) { Queue.new }
287
260
 
288
261
  let(:config) do
289
262
  {
290
- "host" => "0.0.0.0",
263
+ "host" => "127.0.0.1",
291
264
  "port" => port,
292
- "ssl_verify" => false,
293
265
  "ssl_enable" => true,
294
- "ssl_cert" => certificate[0].path,
295
- "ssl_key" => certificate[1].path
266
+ "ssl_cert" => certificate_file.path,
267
+ "ssl_key" => key_file.path,
268
+
269
+ # Trust our self-signed cert.
270
+ # TODO(sissel): Make this a separate certificate for the client
271
+ "ssl_extra_chain_certs" => certificate_file.path
296
272
  }
297
273
  end
298
274
 
299
- let(:events) do
300
- socket = Stud::try(5.times) do
301
- ssl_context = OpenSSL::SSL::SSLContext.new
302
- socket = TCPSocket.new("127.0.0.1", port)
303
- OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
304
- end
305
-
306
- result = helper.pipelineless_input(subject, nevents) do
307
- socket.connect
308
- nevents.times do |i|
309
- socket.puts("msg #{i}")
310
- socket.flush
311
- end
312
- end
313
- socket.close rescue nil
275
+ subject(:input) { LogStash::Plugin.lookup("input", "tcp").new(config) }
314
276
 
315
- result
316
- end
277
+ before do
278
+ certificate_file.write(certificate)
279
+ key_file.write(key)
317
280
 
318
- it "should receive events" do
319
- expect(events.size).to be(nevents)
281
+ # Close to flush the file writes.
282
+ certificate_file.close
283
+ key_file.close
284
+ subject.register
320
285
  end
321
286
 
322
- it "should not contain sslsubject" do
323
- events.each do |event|
324
- expect(event["sslsubject"]).to be_nil
325
- end
287
+ after do
288
+ File.unlink(certificate_file.path)
289
+ File.unlink(key_file.path)
326
290
  end
327
291
 
328
- describe "when ssl_verify is on" do
292
+ context "with a poorly-behaving client" do
293
+ let!(:input_task) { Stud::Task.new { input.run(queue) } }
329
294
 
330
- let(:chain_of_certificates) { helper.chain_of_certificates }
295
+ after { input.close }
331
296
 
332
- let(:ssl_context) do
333
- ssl_context = OpenSSL::SSL::SSLContext.new
334
- ssl_context.cert = OpenSSL::X509::Certificate.new(client_certificate)
335
- ssl_context.key = OpenSSL::PKey::RSA.new(client_key)
336
- ssl_context
337
- end
338
-
339
- context "and the verification fails" do
340
-
341
- let(:config) do
342
- {
343
- "host" => "0.0.0.0",
344
- "port" => port,
345
- "ssl_enable" => true,
346
- "ssl_verify" => true,
347
- "ssl_cert" => chain_of_certificates[:a_cert].path,
348
- "ssl_key" => chain_of_certificates[:a_key].path
349
- }
350
- end
351
-
352
- let(:client_certificate) { File.read(chain_of_certificates[:b_cert].path) }
353
- let(:client_key) { File.read(chain_of_certificates[:b_key].path) }
354
-
355
- let(:socket) do
297
+ context "that disconnects before doing TLS handshake" do
298
+ before do
356
299
  client = TCPSocket.new("127.0.0.1", port)
357
- OpenSSL::SSL::SSLSocket.new(client, ssl_context)
300
+ client.close
358
301
  end
359
302
 
360
- it "should raise an exception when connecting" do
361
- helper.pipelineless_input(subject, 0) do
362
- expect { socket.connect }.to raise_error
363
- socket.close rescue nil
364
- end
303
+ it "should not negatively impact the plugin" do
304
+ # TODO(sissel): Look for a better way to detect this failure
305
+ # besides a sleep/wait.
306
+ result = input_task.thread.join(0.5)
307
+ expect(result).to be_nil
365
308
  end
366
309
  end
367
310
 
368
- context "and using the root CA" do
369
-
370
- let(:config) do
371
- {
372
- "host" => "0.0.0.0",
373
- "port" => port,
374
- "ssl_enable" => true,
375
- "ssl_verify" => true,
376
- "ssl_cert" => chain_of_certificates[:a_cert].path,
377
- "ssl_key" => chain_of_certificates[:a_key].path,
378
- "ssl_cacert" => chain_of_certificates[:root_ca].path
379
- }
380
- end
381
-
382
- let(:client_certificate) { File.read(chain_of_certificates[:aa_cert].path) }
383
- let(:client_key) { File.read(chain_of_certificates[:aa_key].path) }
311
+ context "that sends garbage instead of TLS handshake" do
312
+ let!(:input_task) { Stud::Task.new { input.run(queue) } }
313
+ let(:max_length) { 1000 }
314
+ let(:garbage) { Flores::Random.iterations(max_length).collect { Flores::Random.integer(1...255) }.pack("C*") }
315
+ before do
316
+ # Assertion to verify this test is actually sending something.
317
+ expect(garbage.length).to be > 0
384
318
 
385
- let(:events) do
386
- socket = Stud::try(5.times) do
387
- socket = TCPSocket.new("127.0.0.1", port)
388
- OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
389
- end
390
-
391
- result = helper.pipelineless_input(subject, nevents) do
392
- socket.connect
393
- nevents.times do |i|
394
- socket.puts("msg #{i}")
395
- socket.flush
396
- end
397
- end
398
-
399
- socket.close rescue nil
400
-
401
- result
402
- end
403
-
404
- it "should receive events" do
405
- expect(events.size).to be(nevents)
319
+ client = TCPSocket.new("127.0.0.1", port)
320
+ client.write(garbage)
321
+ client.flush
322
+ Thread.new { sleep(1); client.close }
406
323
  end
407
-
408
- it "should contain sslsubject" do
409
- events.each do |event|
410
- expect(event["sslsubject"]).to eq("/DC=org/DC=ruby-lang/CN=RubyAA_Cert")
411
- end
324
+ it "should not negatively impact the plugin" do
325
+ # TODO(sissel): Look for a better way to detect this failure besides a sleep/wait.
326
+ result = input_task.thread.join(0.5)
327
+ expect(result).to be_nil
412
328
  end
413
329
  end
414
330
 
415
- context "using an extra chain of certificates" do
416
-
417
- let(:config) do
418
- {
419
- "host" => "0.0.0.0",
420
- "port" => port,
421
- "ssl_enable" => true,
422
- "ssl_verify" => true,
423
- "ssl_cert" => chain_of_certificates[:b_cert].path,
424
- "ssl_key" => chain_of_certificates[:b_key].path,
425
- "ssl_extra_chain_certs" => [ chain_of_certificates[:root_ca].path, chain_of_certificates[:a_cert].path, chain_of_certificates[:b_cert].path ]
426
- }
331
+ context "connection was healthy but now has garbage or corruption" do
332
+ let!(:input_task) { Stud::Task.new { input.run(queue) } }
333
+ let(:tcp) { TCPSocket.new("127.0.0.1", port) }
334
+ let(:sslcontext) { OpenSSL::SSL::SSLContext.new }
335
+ let(:sslsocket) { OpenSSL::SSL::SSLSocket.new(tcp, sslcontext) }
336
+ let(:max_length) { 1000 }
337
+ let(:garbage) { Flores::Random.iterations(max_length).collect { Flores::Random.integer(1...255) }.pack("C*") }
338
+
339
+ before do
340
+ sslcontext.cert = certificate
341
+ sslcontext.key = key
342
+ sslcontext.verify_mode = OpenSSL::SSL::VERIFY_NONE
343
+
344
+ sslsocket.connect
345
+ sslsocket.write("Hello world\n")
346
+
347
+ # Assertion to verify this test is actually sending something.
348
+ expect(garbage.length).to be > 0
349
+ tcp.write(garbage)
350
+ tcp.flush
351
+ sslsocket.close
352
+ tcp.close
427
353
  end
428
354
 
429
- let(:client_certificate) { File.read(chain_of_certificates[:c_cert].path) }
430
- let(:client_key) { File.read(chain_of_certificates[:c_key].path) }
431
-
432
- let(:events) do
433
- socket = Stud::try(5.times) do
434
- socket = TCPSocket.new("127.0.0.1", port)
435
- OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
436
- end
437
-
438
- result = helper.pipelineless_input(subject, nevents) do
439
- socket.connect
440
- nevents.times do |i|
441
- socket.puts("msg #{i}")
442
- socket.flush
443
- end
444
- end
445
-
446
- socket.close rescue nil
447
-
448
- result
355
+ it "should not negatively impact the plugin" do
356
+ # TODO(sissel): Look for a better way to detect this failure besides a sleep/wait.
357
+ result = input_task.thread.join(0.5)
358
+ expect(result).to be_nil
449
359
  end
360
+ end
361
+ end
450
362
 
451
- it "should receive events" do
452
- expect(events.size).to be(nevents)
453
- end
363
+ # TODO(sissel): Spec multiple clients where only one is bad.
454
364
 
455
- it "should contain sslsubject" do
456
- events.each do |event|
457
- expect(event["sslsubject"]).to eq("/DC=org/DC=ruby-lang/CN=RubyC_Cert")
458
- end
459
- end
460
- end
365
+ context "with client certificate problems" do
366
+ context "using an expired certificate"
367
+ context "using an untrusted certificate"
461
368
  end
369
+
370
+ context "with a good connection" do
371
+ # TODO(sissel): use shared example
372
+ include_examples "receiving events"
373
+ end
374
+
462
375
  end
463
376
  end
464
377
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-input-tcp
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elastic
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-12 00:00:00.000000000 Z
11
+ date: 2016-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: logstash-core