logstash-input-beats 2.1.4 → 2.2.0

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,102 @@
1
+ # encoding: utf-8
2
+ require "logstash/inputs/beats"
3
+ require "logstash/json"
4
+ require "fileutils"
5
+ require_relative "../support/flores_extensions"
6
+ require_relative "../support/file_helpers"
7
+ require_relative "../support/integration_shared_context"
8
+ require_relative "../support/client_process_helpers"
9
+ require "spec_helper"
10
+
11
+ LSF_BINARY = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "vendor", "logstash-forwarder", "logstash-forwarder"))
12
+
13
+ xdescribe "Logstash-Forwarder", :integration => true do
14
+ include ClientProcessHelpers
15
+
16
+ before :all do
17
+ unless File.exist?(LSF_BINARY)
18
+ raise "Cannot find `logstash-forwarder` binary in `vendor/logstash-forwarder` Did you run `bundle exec rake test:integration:setup` before running the integration suite?"
19
+ end
20
+ end
21
+
22
+ let(:client_wait_time) { 5 }
23
+ include FileHelpers
24
+ include_context "beats configuration"
25
+
26
+ # Filebeat related variables
27
+ let(:cmd) { [lsf_exec, "-config", lsf_config_path] }
28
+
29
+ let(:lsf_exec) { LSF_BINARY }
30
+ let(:registry_file) { File.expand_path(File.join(File.dirname(__FILE__), "..", "..", ".logstash-forwarder")) }
31
+ let_tmp_file(:lsf_config_path) { lsf_config }
32
+ let(:log_file) { f = Stud::Temporary.file; f.close; f.path }
33
+ let(:lsf_config) do
34
+ <<-CONFIG
35
+ {
36
+ "network": {
37
+ "servers": [ "#{host}:#{port}" ],
38
+ "ssl ca": "#{certificate_authorities}"
39
+ },
40
+ "files": [
41
+ { "paths": [ "#{log_file}" ] }
42
+ ]
43
+ }
44
+ CONFIG
45
+ end
46
+ #
47
+
48
+ before :each do
49
+ FileUtils.rm_rf(registry_file) # ensure clean state between runs
50
+ start_client
51
+ sleep(1)
52
+ # let LSF start and than write the logs
53
+ File.open(log_file, "a") do |f|
54
+ f.write(events.join("\n") + "\n")
55
+ end
56
+ end
57
+
58
+ after :each do
59
+ stop_client
60
+ end
61
+
62
+ context "Plain TCP" do
63
+ include ClientProcessHelpers
64
+ let(:certificate_authorities) { "" }
65
+
66
+ it "should not send any events" do
67
+ expect(queue.size).to eq(0), @execution_output
68
+ end
69
+ end
70
+
71
+ context "TLS" do
72
+ context "Server Verification" do
73
+ let(:input_config) do
74
+ super.merge({
75
+ "ssl" => true,
76
+ "ssl_certificate" => certificate_file,
77
+ "ssl_key" => certificate_key_file,
78
+ "target_field_for_codec" => "line"
79
+ })
80
+ end
81
+
82
+ let(:certificate_data) { Flores::PKI.generate }
83
+ let_tmp_file(:certificate_file) { certificate_data.first }
84
+ let_tmp_file(:certificate_key_file) { certificate_data.last }
85
+ let(:certificate_authorities) { certificate_file }
86
+
87
+ context "self signed certificate" do
88
+ include_examples "send events" do
89
+ end
90
+ end
91
+
92
+ context "invalid CA on the client" do
93
+ let(:invalid_data) { Flores::PKI.generate }
94
+ let(:certificate_authorities) { invalid_data.first }
95
+
96
+ it "should not send any events" do
97
+ expect(queue.size).to eq(0), @execution_output
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -6,27 +6,47 @@ require "flores/pki"
6
6
  require "fileutils"
7
7
  require "thread"
8
8
  require "spec_helper"
9
+ require_relative "./support/file_helpers"
10
+ require_relative "./support/flores_extensions"
9
11
 
10
12
  Thread.abort_on_exception = true
11
13
  describe "A client" do
14
+ include FileHelpers
15
+
12
16
  let(:certificate) { Flores::PKI.generate }
13
- let(:certificate_file_crt) { "certificate.crt" }
14
- let(:certificate_file_key) { "certificate.key" }
17
+ let(:certificate_file_crt) do
18
+ p = Stud::Temporary.file
19
+ p.write(certificate.first.to_s)
20
+ p.close
21
+ p.path
22
+ end
23
+
24
+ let(:certificate_file_key) do
25
+ p = Stud::Temporary.file
26
+ p.write(certificate.last.to_s)
27
+ p.close
28
+ p.path
29
+ end
15
30
  let(:port) { Flores::Random.integer(1024..65335) }
16
31
  let(:tcp_port) { port + 1 }
17
32
  let(:host) { "127.0.0.1" }
18
33
  let(:queue) { [] }
34
+ let(:config_ssl) do
35
+ {
36
+ :port => port,
37
+ :address => host,
38
+ :ssl_certificate => certificate_file_crt,
39
+ :ssl_key => certificate_file_key
40
+ }
41
+ end
19
42
 
20
- before :each do
21
- expect(File).to receive(:read).at_least(1).with(certificate_file_crt) { certificate.first.to_s }
22
- expect(File).to receive(:read).at_least(1).with(certificate_file_key) { certificate.last.to_s }
23
-
24
- tcp_server = Lumberjack::Beats::Server.new(:port => tcp_port, :address => host, :ssl => false)
43
+ let(:config_tcp) do
44
+ { :port => tcp_port, :address => host, :ssl => false }
45
+ end
25
46
 
26
- ssl_server = Lumberjack::Beats::Server.new(:port => port,
27
- :address => host,
28
- :ssl_certificate => certificate_file_crt,
29
- :ssl_key => certificate_file_key)
47
+ before :each do
48
+ tcp_server = Lumberjack::Beats::Server.new(config_tcp)
49
+ ssl_server = Lumberjack::Beats::Server.new(config_ssl)
30
50
 
31
51
  @tcp_server = Thread.new do
32
52
  tcp_server.run { |data, identity_stream| queue << [data, identity_stream] }
@@ -42,7 +62,7 @@ describe "A client" do
42
62
  # parser for TCP based server trip.
43
63
  # Connection is closed by Server connection object
44
64
  end
45
- end
65
+ end
46
66
  end
47
67
  end
48
68
 
@@ -160,62 +180,334 @@ describe "A client" do
160
180
  end
161
181
  end
162
182
 
163
- context "using ssl encrypted connection" do
164
- context "with a valid certificate" do
165
- it "successfully connect to the server" do
166
- expect {
167
- Lumberjack::Beats::Client.new(:port => port,
168
- :host => host,
169
- :addresses => host,
170
- :ssl_certificate => certificate_file_crt)
171
- }.not_to raise_error
183
+ context "using TLS/SSL encrypted connection" do
184
+ context "without a ca chain" do
185
+ context "with a valid certificate" do
186
+ it "successfully connect to the server" do
187
+ expect {
188
+ Lumberjack::Beats::Client.new(:port => port,
189
+ :host => host,
190
+ :addresses => host,
191
+ :ssl_certificate_authorities => certificate_file_crt)
192
+ }.not_to raise_error
193
+ end
194
+
195
+ it "should fail connecting to plain tcp server" do
196
+ expect {
197
+ Lumberjack::Beats::Client.new(:port => tcp_port,
198
+ :host => host,
199
+ :addresses => host,
200
+ :ssl_certificate_authorities => certificate_file_crt)
201
+ }.to raise_error(OpenSSL::SSL::SSLError)
202
+ end
172
203
  end
173
204
 
174
- it "should fail connecting to plain tcp server" do
175
- expect {
176
- Lumberjack::Beats::Client.new(:port => tcp_port,
177
- :host => host,
178
- :addresses => host,
179
- :ssl_certificate => certificate_file_crt)
180
- }.to raise_error(OpenSSL::SSL::SSLError)
181
- end
182
- end
205
+ context "with an invalid certificate" do
206
+ let(:invalid_certificate) { Flores::PKI.generate }
207
+ let(:invalid_certificate_file) { Stud::Temporary.file.path }
183
208
 
184
- context "with an invalid certificate" do
185
- let(:invalid_certificate) { Flores::PKI.generate }
186
- let(:invalid_certificate_file) { "invalid.crt" }
209
+ it "should refuse to connect" do
210
+ expect {
211
+ Lumberjack::Beats::Client.new(:port => port,
212
+ :host => host,
213
+ :addresses => host,
214
+ :ssl_certificate_authorities => invalid_certificate_file)
187
215
 
188
- before do
189
- expect(File).to receive(:read).with(invalid_certificate_file) { invalid_certificate.first.to_s }
216
+ }.to raise_error(OpenSSL::SSL::SSLError, /certificate verify failed/)
217
+ end
190
218
  end
191
219
 
192
- it "should refuse to connect" do
193
- expect {
220
+ context "When transmitting a payload" do
221
+ let(:client) do
194
222
  Lumberjack::Beats::Client.new(:port => port,
195
223
  :host => host,
196
224
  :addresses => host,
197
- :ssl_certificate => invalid_certificate_file)
225
+ :ssl_certificate_authorities => certificate_file_crt)
226
+ end
227
+
228
+ context "json" do
229
+ let(:options) { super.merge({ :json => true }) }
230
+ include_examples "transmit payloads"
231
+ end
198
232
 
199
- }.to raise_error(OpenSSL::SSL::SSLError, /certificate verify failed/)
233
+ context "v1 frame" do
234
+ include_examples "transmit payloads"
235
+ end
200
236
  end
201
237
  end
238
+ end
202
239
 
203
- context "When transmitting a payload" do
204
- let(:client) do
205
- Lumberjack::Beats::Client.new(:port => port,
206
- :host => host,
207
- :addresses => host,
208
- :ssl_certificate => certificate_file_crt)
240
+ def write_to_tmp_file(content)
241
+ file = Stud::Temporary.file
242
+ file.write(content.to_s)
243
+ file.close
244
+ file.path
245
+ end
246
+
247
+ context "Mutual validation" do
248
+ let(:host) { "localhost" }
249
+ let(:certificate_duration) { Flores::Random.number(1000..2000) }
250
+ let(:client) { Lumberjack::Beats::Client.new(options) }
251
+ let(:options) { { :ssl => true, :port => port, :addresses => host } }
252
+ let(:config_ssl) do
253
+ {
254
+ :port => port,
255
+ :address => host,
256
+ :ssl => true,
257
+ :ssl_verify_mode => "force_peer"
258
+ }
259
+ end
260
+
261
+ context "with a self signed certificate" do
262
+ let(:certificate) { Flores::PKI.generate }
263
+ let_tmp_file(:certificate_file) { certificate.first }
264
+ let_tmp_file(:certificate_key_file) { certificate.last }
265
+ let(:options) do
266
+ super.merge({ :ssl_certificate => certificate_file,
267
+ :ssl_certificate_key => certificate_key_file,
268
+ :ssl_certificate_authorities => certificate_file })
209
269
  end
210
270
 
211
- context "json" do
212
- let(:options) { super.merge({ :json => true }) }
271
+ let(:config_ssl) do
272
+ super.merge({ :ssl_certificate_authorities => certificate_file,
273
+ :ssl_certificate => certificate_file,
274
+ :ssl_key => certificate_key_file })
275
+ end
276
+
277
+ include_examples "transmit payloads"
278
+ end
279
+
280
+ context "with certificate authorities" do
281
+ let(:keysize) { 1024 }
282
+ let(:exponent) { 65537 }
283
+ let(:root_ca) { Flores::PKI.generate("CN=root.logstash") }
284
+ let(:root_ca_certificate) { root_ca.first }
285
+ let(:root_ca_key) { root_ca.last }
286
+ let_tmp_file(:root_ca_certificate_file) { root_ca_certificate }
287
+
288
+ context "directly signing a certificate" do
289
+ let(:client_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
290
+ let_tmp_file(:client_certificate_key_file) { client_certificate_key }
291
+ let(:client_certificate) do
292
+ csr = Flores::PKI::CertificateSigningRequest.new
293
+ csr.start_time = Time.now
294
+ csr.expire_time = csr.start_time + certificate_duration
295
+ csr.public_key = client_certificate_key.public_key
296
+ csr.subject = "CN=localhost"
297
+ csr.signing_key = root_ca_key
298
+ csr.signing_certificate = root_ca_certificate
299
+ csr.want_signature_ability = false
300
+ csr.create
301
+ end
302
+ let_tmp_file(:client_certificate_file) { client_certificate }
303
+
304
+ let(:server_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
305
+ let(:server_certificate_key_file) { write_to_tmp_file(server_certificate_key) }
306
+ let(:server_certificate) do
307
+ csr = Flores::PKI::CertificateSigningRequest.new
308
+ csr.start_time = Time.now
309
+ csr.expire_time = csr.start_time + certificate_duration
310
+ csr.public_key = server_certificate_key.public_key
311
+ csr.subject = "CN=localhost"
312
+ csr.signing_key = root_ca_key
313
+ csr.signing_certificate = root_ca_certificate
314
+ csr.want_signature_ability = false
315
+ csr.create
316
+ end
317
+ let_tmp_file(:server_certificate_file) { server_certificate }
318
+
319
+ let(:options) do
320
+ super.merge({ :ssl_certificate => client_certificate_file,
321
+ :ssl_certificate_key => client_certificate_key_file,
322
+ :ssl_certificate_authorities => root_ca_certificate_file })
323
+ end
324
+
325
+ let(:config_ssl) do
326
+ super.merge({ :ssl_certificate_authorities => root_ca_certificate_file,
327
+ :ssl_certificate => server_certificate_file,
328
+ :ssl_key => server_certificate_key_file,
329
+ :verify_mode => :force_peer })
330
+ end
331
+
213
332
  include_examples "transmit payloads"
214
333
  end
215
334
 
216
- context "v1 frame" do
335
+ # Doesnt work because of this issues in `jruby-openssl`
336
+ # https://github.com/jruby/jruby-openssl/issues/84
337
+ xcontext "CA given with client providing intermediate and client certificate" do
338
+ let(:intermediate_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
339
+ let_tmp_file(:intermediate_certificate_key_file) { intermediate_certificate_key }
340
+ let(:intermediate_certificate) do
341
+ csr = Flores::PKI::CertificateSigningRequest.new
342
+ csr.start_time = Time.now
343
+ csr.expire_time = csr.start_time + certificate_duration
344
+ csr.public_key = intermediate_certificate_key.public_key
345
+ csr.subject = "CN=intermediate 1"
346
+ csr.signing_key = root_ca_key
347
+ csr.signing_certificate = root_ca_certificate
348
+ csr.want_signature_ability = true
349
+ csr.create
350
+ end
351
+ let_tmp_file(:intermediate_certificate_file) { intermediate_certificate }
352
+ let(:client_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
353
+ let_tmp_file(:client_certificate_key_file) { client_certificate_key }
354
+ let(:client_certificate) do
355
+ csr = Flores::PKI::CertificateSigningRequest.new
356
+ csr.start_time = Time.now
357
+ csr.expire_time = csr.start_time + certificate_duration
358
+ csr.public_key = client_certificate_key.public_key
359
+ csr.subject = "CN=localhost"
360
+ csr.signing_key = intermediate_certificate_key
361
+ csr.signing_certificate = intermediate_certificate
362
+ csr.want_signature_ability = false
363
+ csr.create
364
+ end
365
+ let_tmp_file(:client_certificate_file) { client_certificate }
366
+
367
+ let(:server_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
368
+ let_tmp_file(:server_certificate_key_file) { server_certificate_key }
369
+ let(:server_certificate) do
370
+ csr = Flores::PKI::CertificateSigningRequest.new
371
+ csr.start_time = Time.now
372
+ csr.expire_time = csr.start_time + certificate_duration
373
+ csr.public_key = server_certificate_key.public_key
374
+ csr.subject = "CN=localhost"
375
+ csr.signing_key = intermediate_certificate_key
376
+ csr.signing_certificate = intermediate_certificate
377
+ csr.want_signature_ability = false
378
+ csr.create
379
+ end
380
+ let_tmp_file(:server_certificate_file) { server_certificate }
381
+ let_tmp_file(:certificate_chain_file) { Flores::PKI.chain_certificates(root_ca_certificate, intermediate_certificate) }
382
+
383
+ let(:options) do
384
+ super.merge({ :ssl_certificate => client_certificate_file,
385
+ :ssl_certificate_key => client_certificate_key_file,
386
+ :ssl_certificate_authorities => certificate_chain_file })
387
+ end
388
+
389
+ let(:config_ssl) do
390
+ super.merge({ :ssl_certificate_authorities => certificate_chain_file,
391
+ :ssl_certificate => server_certificate_file,
392
+ :ssl_key => server_certificate_key_file,
393
+ :verify_mode => :force_peer })
394
+
395
+
396
+ end
397
+
217
398
  include_examples "transmit payloads"
218
399
  end
400
+
401
+ context "Mutiple CA different clients" do
402
+ let(:secondary_root_ca) { Flores::PKI.generate("CN=secondary.root.logstash") }
403
+ let(:secondary_root_ca_certificate) { root_ca.first }
404
+ let(:secondary_root_ca_key) { root_ca.last }
405
+ let_tmp_file(:secondary_root_ca_certificate_file) { root_ca_certificate }
406
+
407
+ let(:secondary_client_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
408
+ let(:secondary_client_certificate_key_file) { write_to_tmp_file(secondary_client_certificate_key) }
409
+ let(:secondary_client_certificate) do
410
+ csr = Flores::PKI::CertificateSigningRequest.new
411
+ csr.start_time = Time.now
412
+ csr.expire_time = csr.start_time + certificate_duration
413
+ csr.public_key = secondary_client_certificate_key.public_key
414
+ csr.subject = "CN=localhost"
415
+ csr.signing_key = secondary_root_ca_key
416
+ csr.signing_certificate = secondary_root_ca_certificate
417
+ csr.want_signature_ability = false
418
+ csr.create
419
+ end
420
+ let_tmp_file(:secondary_client_certificate_file) { secondary_client_certificate }
421
+ let(:client_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
422
+ let_tmp_file(:client_certificate_key_file) { client_certificate_key }
423
+ let(:client_certificate) do
424
+ csr = Flores::PKI::CertificateSigningRequest.new
425
+ csr.start_time = Time.now
426
+ csr.expire_time = csr.start_time + certificate_duration
427
+ csr.public_key = client_certificate_key.public_key
428
+ csr.subject = "CN=localhost"
429
+ csr.signing_key = root_ca_key
430
+ csr.signing_certificate = root_ca_certificate
431
+ csr.want_signature_ability = false
432
+ csr.create
433
+ end
434
+ let_tmp_file(:client_certificate_file) { client_certificate }
435
+
436
+ let(:server_certificate_key) { OpenSSL::PKey::RSA.generate(keysize, exponent) }
437
+ let_tmp_file(:server_certificate_key_file) { server_certificate_key }
438
+ let(:server_certificate) do
439
+ csr = Flores::PKI::CertificateSigningRequest.new
440
+ csr.start_time = Time.now
441
+ csr.expire_time = csr.start_time + certificate_duration
442
+ csr.public_key = server_certificate_key.public_key
443
+ csr.subject = "CN=localhost"
444
+ csr.signing_key = root_ca_key
445
+ csr.signing_certificate = root_ca_certificate
446
+ csr.want_signature_ability = false
447
+ csr.create
448
+ end
449
+ let_tmp_file(:server_certificate_file) { server_certificate }
450
+
451
+ shared_examples "multiples client" do
452
+ context "client from primary CA" do
453
+ let(:options) do
454
+ super.merge({ :ssl_certificate => client_certificate_file,
455
+ :ssl_certificate_key => client_certificate_key_file,
456
+ :ssl_certificate_authorities => [root_ca_certificate_file] })
457
+ end
458
+
459
+ include_examples "transmit payloads"
460
+ end
461
+
462
+ context "client from secondary CA" do
463
+ let(:options) do
464
+ super.merge({ :ssl_certificate => secondary_client_certificate_file,
465
+ :ssl_certificate_key => secondary_client_certificate_key_file,
466
+ :ssl_certificate_authorities => [root_ca_certificate_file] })
467
+ end
468
+
469
+ include_examples "transmit payloads"
470
+ end
471
+ end
472
+
473
+ context "when the CA are defined individually in the configuration" do
474
+ let(:config_ssl) do
475
+ super.merge({ :ssl_certificate_authorities => [secondary_root_ca_certificate_file, root_ca_certificate_file],
476
+ :ssl_certificate => server_certificate_file,
477
+ :ssl_key => server_certificate_key_file,
478
+ :verify_mode => "force_peer" })
479
+ end
480
+
481
+ include_examples "multiples client"
482
+ end
483
+
484
+ context "when the CA are defined by a path in the configuration" do
485
+ let(:ca_path) do
486
+ p = Stud::Temporary.directory
487
+
488
+ # Doing this here make sure the path is populated when the server
489
+ # is started in the main before block
490
+ FileUtils.mkdir_p(p)
491
+ FileUtils.cp(secondary_root_ca_certificate_file, p)
492
+ FileUtils.cp(root_ca_certificate_file, p)
493
+
494
+ p
495
+ end
496
+
497
+ after :each do
498
+ FileUtils.rm_rf(ca_path)
499
+ end
500
+
501
+ let(:config_ssl) do
502
+ super.merge({ :ssl_certificate_authorities => ca_path,
503
+ :ssl_certificate => server_certificate_file,
504
+ :ssl_key => server_certificate_key_file,
505
+ :verify_mode => "force_peer" })
506
+ end
507
+
508
+ include_examples "multiples client"
509
+ end
510
+ end
219
511
  end
220
512
  end
221
513
  end