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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e842feb71aa235b190d3ecc62267f82250d66bd1
4
- data.tar.gz: 82c27027f6f482b7959a8b2c8db6641a99830dab
3
+ metadata.gz: 2b44b745b034e4879863dafa18e2a47f6c5077cb
4
+ data.tar.gz: be9744dfb679a87d2c62d9c70d0556c5a4101e91
5
5
  SHA512:
6
- metadata.gz: 5f1a5d58c37d7e4ea6adb24fbed54a85ce486257b3ab827b01ae9da11dfb644598d0e37301f8dd0c14aef4aaa81ed8229bab865bcd2b25aca012c9bbd8959768
7
- data.tar.gz: b34f6a828c699c17aa4b7f00b6028c79104076369e5d48e3e1bd1a0afcb1ee38fd4232af34c3f2bc40b37830a1dfd90a5f14270cda482676a1b3adef5cd7561c
6
+ metadata.gz: c9a92bec0e4ae62083feb22c6736c474b31fe49b3853f6ae48c9dcd5d71259ac38da73fc340853f171f5fc3f4167bcf71a5e99071e5aee4d1fb8c8667ad94514
7
+ data.tar.gz: aadca9c26ab53386668cd0e4544178d0485cdd0d2e29546dc8bd261593a1711930a3ff598ab7c9c57f8a672cee34d82fab31a2ba25c4edad6e945349df55cc17
@@ -1,3 +1,7 @@
1
+ # 2.2.0
2
+ - The server can now do client side verification by providing a list of certificate authorities and configuring the `ssl_verify_mode`,
3
+ the server can use `peer`, if the client send a certificate it will be validated. Using `force_peer` will make sure the client provide a certificate
4
+ and it will be validated with the know CA. #8
1
5
  # 2.1.4
2
6
  - Change the `logger#warn` for `logger.debug` when a peer get disconnected, keep alive check from proxy can generate a lot of logs #46
3
7
  # 2.1.3
@@ -68,6 +68,27 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
68
68
  # SSL key passphrase to use.
69
69
  config :ssl_key_passphrase, :validate => :password
70
70
 
71
+ # Validate client certificates against theses authorities
72
+ # You can defined multiples files or path, all the certificates will
73
+ # be read and added to the trust store. You need to configure the `ssl_verify_mode`
74
+ # to `peer` or `force_peer` to enable the verification.
75
+ #
76
+ # This feature only support certificate directly signed by your root ca.
77
+ # Intermediate CA are currently not supported.
78
+ #
79
+ config :ssl_certificate_authorities, :validate => :array, :default => []
80
+
81
+ # By default the server dont do any client verification,
82
+ #
83
+ # `peer` will make the server ask the client to provide a certificate,
84
+ # if the client provide the certificate it will be validated.
85
+ #
86
+ # `force_peer` will make the server ask the client for their certificate, if the clients
87
+ # doesn't provide it the connection will be closed.
88
+ #
89
+ # This option need to be used with `ssl_certificate_authorities` and a defined list of CA.
90
+ config :ssl_verify_mode, :validate => ["none", "peer", "force_peer"], :default => "none"
91
+
71
92
  # The number of seconds before we raise a timeout,
72
93
  # this option is useful to control how much time to wait if something is blocking the pipeline.
73
94
  config :congestion_threshold, :validate => :number, :default => 5
@@ -87,9 +108,16 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
87
108
  end
88
109
 
89
110
  @logger.info("Beats inputs: Starting input listener", :address => "#{@host}:#{@port}")
90
- @lumberjack = Lumberjack::Beats::Server.new(:address => @host, :port => @port,
91
- :ssl => @ssl, :ssl_certificate => @ssl_certificate, :ssl_key => @ssl_key,
92
- :ssl_key_passphrase => @ssl_key_passphrase)
111
+
112
+
113
+ @lumberjack = Lumberjack::Beats::Server.new(:address => @host,
114
+ :port => @port,
115
+ :ssl => @ssl,
116
+ :ssl_certificate => @ssl_certificate,
117
+ :ssl_key => @ssl_key,
118
+ :ssl_key_passphrase => @ssl_key_passphrase,
119
+ :ssl_certificate_authorities => @ssl_certificate_authorities,
120
+ :ssl_verify_mode => @ssl_verify_mode)
93
121
 
94
122
  # in 1.5 the main SizeQueue doesnt have the concept of timeout
95
123
  # We are using a small plugin buffer to move events to the internal queue
@@ -103,8 +131,8 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
103
131
  @codec = LogStash::Codecs::IdentityMapCodec.new(@codec)
104
132
 
105
133
  # Keep a list of active connections so we can flush their codec on shutdown
106
-
107
- # Use threadsafe gem, since we have a strict dependency on concurrent-ruby 0.9.2
134
+
135
+ # Use threadsafe gem, since we have a strict dependency on concurrent-ruby 0.9.2
108
136
  # in the core
109
137
  @connections_list = ThreadSafe::Hash.new
110
138
  end # def register
@@ -127,13 +155,13 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
127
155
  connection = @lumberjack.accept # call that creates a new connection
128
156
  # if the connection is nil the connection was closed upstream,
129
157
  # so we will try in another iteration to recover or stop.
130
- next if connection.nil?
158
+ next if connection.nil?
131
159
 
132
- Thread.new do
160
+ Thread.new do
133
161
  handle_new_connection(connection)
134
162
  end
135
163
  else
136
- @logger.warn("Beats input: the pipeline is blocked, temporary refusing new connection.",
164
+ @logger.warn("Beats input: the pipeline is blocked, temporary refusing new connection.",
137
165
  :reconnect_backoff_sleep => RECONNECT_BACKOFF_SLEEP)
138
166
  sleep(RECONNECT_BACKOFF_SLEEP)
139
167
  end
@@ -186,9 +214,9 @@ class LogStash::Inputs::Beats < LogStash::Inputs::Base
186
214
  @logger.warn("Beats input: The circuit breaker has detected a slowdown or stall in the pipeline, the input is closing the current connection and rejecting new connection until the pipeline recover.",
187
215
  :exception => e.class)
188
216
  rescue Exception => e # If we have a malformed packet we should handle that so the input doesn't crash completely.
189
- @logger.error("Beats input: unhandled exception",
217
+ @logger.error("Beats input: unhandled exception",
190
218
  :exception => e,
191
- :backtrace => e.backtrace)
219
+ :backtrace => e.backtrace)
192
220
  ensure
193
221
  transformer = LogStash::Inputs::BeatsSupport::EventTransformCommon.new(self)
194
222
 
@@ -12,14 +12,21 @@ module Lumberjack module Beats
12
12
  :port => 0,
13
13
  :addresses => [],
14
14
  :ssl_certificate => nil,
15
+ :ssl_certificate_key => nil,
16
+ :ssl_certificate_authorities => nil,
15
17
  :ssl => true,
16
18
  :json => false,
17
19
  }.merge(opts)
18
20
 
19
- @opts[:addresses] = [@opts[:addresses]] if @opts[:addresses].class == String
21
+ @opts[:addresses] = Array(@opts[:addresses])
20
22
  raise "Must set a port." if @opts[:port] == 0
21
23
  raise "Must set atleast one address" if @opts[:addresses].empty? == 0
22
- raise "Must set a ssl certificate or path" if @opts[:ssl_certificate].nil? && @opts[:ssl]
24
+
25
+ if @opts[:ssl]
26
+ if @opts[:ssl_certificate_authorities].nil? && (@opts[:ssl_certificate].nil? || @opts[:ssl_certificate_key].nil?)
27
+ raise "Must set a ssl certificate or path"
28
+ end
29
+ end
23
30
 
24
31
  @socket = connect
25
32
  end
@@ -67,36 +74,78 @@ module Lumberjack module Beats
67
74
  @opts = {
68
75
  :port => 0,
69
76
  :address => "127.0.0.1",
77
+ :ssl_certificate_authorities => [], # use the same naming as beats' TLS options
70
78
  :ssl_certificate => nil,
79
+ :ssl_certificate_key => nil,
80
+ :ssl_certificate_password => nil,
71
81
  :ssl => true,
72
82
  :json => false,
73
83
  }.merge(opts)
74
84
  @host = @opts[:address]
75
85
 
76
- connection_start(opts)
86
+ connection_start
77
87
  end
78
88
 
79
89
  private
80
- def connection_start(opts)
81
- tcp_socket = TCPSocket.new(opts[:address], opts[:port])
82
- if !opts[:ssl]
90
+ def connection_start
91
+ tcp_socket = TCPSocket.new(@opts[:address], @opts[:port])
92
+
93
+ if !@opts[:ssl]
83
94
  @socket = tcp_socket
84
95
  else
85
- certificate = OpenSSL::X509::Certificate.new(File.read(opts[:ssl_certificate]))
86
96
 
87
- certificate_store = OpenSSL::X509::Store.new
88
- certificate_store.add_cert(certificate)
97
+ @socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, setup_ssl)
98
+ @socket.connect
99
+ end
100
+ end
101
+
102
+ private
103
+ def setup_ssl
104
+ ssl_context = OpenSSL::SSL::SSLContext.new
89
105
 
90
- ssl_context = OpenSSL::SSL::SSLContext.new
91
- ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
92
- ssl_context.cert_store = certificate_store
106
+ ssl_context.cert = certificate
107
+ ssl_context.key = private_key
108
+ ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER
109
+ ssl_context.cert_store = trust_store
110
+ ssl_context
111
+ end
93
112
 
94
- @socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, ssl_context)
95
- @socket.connect
113
+ private
114
+ def certificate
115
+ if @opts[:ssl_certificate]
116
+ OpenSSL::X509::Certificate.new(File.open(@opts[:ssl_certificate]))
96
117
  end
97
118
  end
98
119
 
99
- private
120
+ private
121
+ def private_key
122
+ OpenSSL::PKey::RSA.new(File.read(@opts[:ssl_certificate_key]), @opts[:ssl_certificate_password]) if @opts[:ssl_certificate_key]
123
+ end
124
+
125
+ private
126
+ def trust_store
127
+ store = OpenSSL::X509::Store.new
128
+
129
+ Array(@opts[:ssl_certificate_authorities]).each do |certificate_authority|
130
+ if File.file?(certificate_authority)
131
+ store.add_file(certificate_authority)
132
+ else
133
+ # add_path is no implemented under jruby
134
+ # so recursively try to load all the certificate from this directory
135
+ # https://github.com/jruby/jruby-openssl/blob/master/src/main/java/org/jruby/ext/openssl/X509Store.java#L159
136
+ if !!(RUBY_PLATFORM == "java")
137
+ Dir.glob(File.join(certificate_authority, "**", "*")).each { |f| store.add_file(f) }
138
+ else
139
+ store.add_path(certificate_authority)
140
+ end
141
+ end
142
+ end
143
+
144
+ store
145
+ end
146
+
147
+
148
+ private
100
149
  def inc
101
150
  @sequence = 0 if @sequence + 1 > Lumberjack::Beats::SEQUENCE_MAX
102
151
  @sequence = @sequence + 1
@@ -134,7 +183,7 @@ module Lumberjack module Beats
134
183
  ack(elements.size)
135
184
  end
136
185
 
137
- private
186
+ private
138
187
  def compress_payload(payload)
139
188
  compress = Zlib::Deflate.deflate(payload)
140
189
  ["1", "C", compress.bytesize, compress].pack("AANA*")
@@ -29,35 +29,33 @@ module Lumberjack module Beats
29
29
  :ssl => true,
30
30
  :ssl_certificate => nil,
31
31
  :ssl_key => nil,
32
- :ssl_key_passphrase => nil
32
+ :ssl_key_passphrase => nil,
33
+ :ssl_certificate_authorities => nil,
34
+ :ssl_verify_mode => :none # By default we dont verify client
33
35
  }.merge(options)
34
36
 
35
37
  if @options[:ssl]
36
- [:ssl_certificate, :ssl_key].each do |k|
37
- if @options[k].nil?
38
- raise "You must specify #{k} in Lumberjack::Server.new(...)"
39
- end
38
+ if verify_client?(@options[:ssl_verify_mode]) && certificate_authorities.empty?
39
+ raise "When `ssl_verify_mode` is set to `peer` OR `force_peer` you need to specify the `ssl_certificate_authorities`"
40
+ end
41
+
42
+ if !verify_client?(@options[:ssl_verify_mode]) && certificate_authorities.size > 0
43
+ raise "When `ssl_certificate_authorities` is configured you need to set `ssl_verify_mode` to either `peer` or `force_peer`"
44
+ end
45
+
46
+ if @options[:ssl_certificate].nil? || @options[:ssl_key].nil?
47
+ raise "You must specify `ssl_certificate` AND `ssl_key`"
40
48
  end
41
49
  end
42
50
 
43
51
  @server = TCPServer.new(@options[:address], @options[:port])
44
-
45
52
  @close = Concurrent::AtomicBoolean.new
53
+ @port = retrieve_current_port
46
54
 
47
- # Query the port in case the port number is '0'
48
- # TCPServer#addr == [ address_family, port, address, address ]
49
- @port = @server.addr[1]
50
-
51
- if @options[:ssl]
52
- # load SSL certificate
53
- @ssl = OpenSSL::SSL::SSLContext.new
54
- @ssl.cert = OpenSSL::X509::Certificate.new(File.read(@options[:ssl_certificate]))
55
- @ssl.key = OpenSSL::PKey::RSA.new(File.read(@options[:ssl_key]),
56
- @options[:ssl_key_passphrase])
57
- end
55
+ setup_ssl if ssl?
58
56
  end # def initialize
59
57
 
60
- # Server#run method, allow the library to manage all the connection
58
+ # Server#run method, allow the library to manage all the connection
61
59
  # threads, this handing is quite minimal and don't handler
62
60
  # all the possible cases deconnection/connection.
63
61
  #
@@ -76,8 +74,8 @@ module Lumberjack module Beats
76
74
  Thread.new(connection) do |connection|
77
75
  begin
78
76
  connection.run(&block)
79
- rescue Lumberjack::Beats::Connection::ConnectionClosed
80
- # Connection will raise a wrapped exception upstream,
77
+ rescue Lumberjack::Beats::Connection::ConnectionClosed
78
+ # Connection will raise a wrapped exception upstream,
81
79
  # but if the threads are managed by the library we can simply ignore it.
82
80
  #
83
81
  # Note: This follow the previous behavior of the perfect silence.
@@ -87,7 +85,7 @@ module Lumberjack module Beats
87
85
  end # def run
88
86
 
89
87
  def ssl?
90
- @ssl
88
+ @options[:ssl]
91
89
  end
92
90
 
93
91
  def accept(&block)
@@ -139,13 +137,88 @@ module Lumberjack module Beats
139
137
  @close.make_true
140
138
  @server.close unless @server.closed?
141
139
  end
142
- end # class Server
140
+
141
+ private
142
+ def verify_client?(mode)
143
+ mode = mode.to_sym
144
+ mode == :peer || mode == :force_peer
145
+ end
146
+
147
+ def retrieve_current_port
148
+ # Query the port in case the port number is '0'
149
+ # TCPServer#addr == [ address_family, port, address, address ]
150
+ @server.addr[1]
151
+ end
152
+
153
+ def certificate_authorities
154
+ Array(@options[:ssl_certificate_authorities])
155
+ end
156
+
157
+ def server_private_key
158
+ OpenSSL::PKey::RSA.new(File.read(@options[:ssl_key]), @options[:ssl_key_passphrase])
159
+ end
160
+
161
+ def server_certificate
162
+ OpenSSL::X509::Certificate.new(File.read(@options[:ssl_certificate]))
163
+ end
164
+
165
+ def verify_mode
166
+ case @options[:ssl_verify_mode].to_sym
167
+ when :none
168
+ OpenSSL::SSL::VERIFY_NONE
169
+ when :peer
170
+ OpenSSL::SSL::VERIFY_PEER
171
+ when :force_peer
172
+ OpenSSL::SSL::VERIFY_PEER | OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
173
+ end
174
+ end
175
+
176
+ def jruby?
177
+ RUBY_PLATFORM == "java"
178
+ end
179
+
180
+ def trust_store
181
+ store = OpenSSL::X509::Store.new
182
+
183
+ if certificate_authorities.size > 0
184
+ certificate_authorities.each do |certificate_authority|
185
+ if File.file?(certificate_authority)
186
+ store.add_file(certificate_authority)
187
+ else
188
+ # `#add_path` is not implemented under jruby
189
+ # so recursively try to load all the certificate from this directory
190
+ # https://github.com/jruby/jruby-openssl/blob/master/src/main/java/org/jruby/ext/openssl/X509Store.java#L159
191
+ if jruby?
192
+ Dir.glob(File.join(certificate_authority, "**", "*")).each { |f| store.add_file(f) }
193
+ else
194
+ store.add_path(certificate_authority)
195
+ end
196
+ end
197
+ end
198
+ end
199
+
200
+ store
201
+ end
202
+
203
+ def setup_ssl
204
+ @ssl = OpenSSL::SSL::SSLContext.new
205
+
206
+ # @ssl.verify_callback = lambda do |preverify_ok, context|
207
+ # require "pry"
208
+ # binding.pry
209
+ # end
210
+ @ssl.cert_store = trust_store
211
+ @ssl.verify_mode = verify_mode
212
+ # @ssl.ca_file = certificate_authorities.first
213
+ @ssl.cert = server_certificate
214
+ @ssl.key = server_private_key
215
+ end
216
+ end
143
217
 
144
218
  class Parser
145
219
  PROTOCOL_VERSION_1 = "1".ord
146
220
  PROTOCOL_VERSION_2 = "2".ord
147
221
 
148
- SUPPORTED_PROTOCOLS = [PROTOCOL_VERSION_1, PROTOCOL_VERSION_2]
149
222
  class UnsupportedProtocol < StandardError; end
150
223
 
151
224
  def initialize
@@ -242,7 +315,7 @@ module Lumberjack module Beats
242
315
  end
243
316
 
244
317
  def supported_protocol?(version)
245
- SUPPORTED_PROTOCOLS.include?(version)
318
+ PROTOCOL_VERSION_2 == version || PROTOCOL_VERSION_1 == version
246
319
  end
247
320
 
248
321
  def window_size(&block)
@@ -313,7 +386,7 @@ module Lumberjack module Beats
313
386
  end # class Parser
314
387
 
315
388
  class Connection
316
- # Wrap the original exception into a common one,
389
+ # Wrap the original exception into a common one,
317
390
  # to make upstream managing and reporting easier
318
391
  # But lets make sure we keep the meaning of the original exception.
319
392
  class ConnectionClosed < StandardError
@@ -351,7 +424,7 @@ module Lumberjack module Beats
351
424
 
352
425
  @server = server
353
426
  @ack_handler = nil
354
-
427
+
355
428
  # Fetch the details of the host before reading anything from the socket
356
429
  # se we can use that information when debugging connection issues with
357
430
  # remote hosts.
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "logstash-input-beats"
3
- s.version = "2.1.4"
3
+ s.version = "2.2.0"
4
4
  s.licenses = ["Apache License (2.0)"]
5
5
  s.summary = "Receive events using the lumberjack protocol."
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"
@@ -33,5 +33,6 @@ Gem::Specification.new do |s|
33
33
  s.add_development_dependency "rspec-wait"
34
34
  s.add_development_dependency "logstash-devutils", "~> 0.0.18"
35
35
  s.add_development_dependency "logstash-codec-json"
36
+ s.add_development_dependency "childprocess" # To make filebeat/LSF integration test easier to write.
36
37
  end
37
38
 
@@ -0,0 +1,235 @@
1
+ # encoding: utf-8
2
+ require "logstash/inputs/beats"
3
+ require "stud/temporary"
4
+ require "flores/pki"
5
+ require "fileutils"
6
+ require "thread"
7
+ require "spec_helper"
8
+ require "yaml"
9
+ require "fileutils"
10
+ require "flores/pki"
11
+ require_relative "../support/flores_extensions"
12
+ require_relative "../support/file_helpers"
13
+ require_relative "../support/integration_shared_context"
14
+ require_relative "../support/client_process_helpers"
15
+
16
+ FILEBEAT_BINARY = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "vendor", "filebeat", "filebeat"))
17
+
18
+ describe "Filebeat", :integration => true do
19
+ include ClientProcessHelpers
20
+ include FileHelpers
21
+
22
+ before :all do
23
+ unless File.exist?(FILEBEAT_BINARY)
24
+ raise "Cannot find `Filebeat` binary in `vendor/filebeat`. Did you run `bundle exec rake test:integration:setup` before running the integration suite?"
25
+ end
26
+ end
27
+
28
+ include_context "beats configuration"
29
+
30
+ # Filebeat related variables
31
+ let(:cmd) { [filebeat_exec, "-c", filebeat_config_path, "-e", "-v"] }
32
+
33
+ let(:filebeat_exec) { FILEBEAT_BINARY }
34
+
35
+ let_empty_tmp_file(:registry_file)
36
+ let(:filebeat_config) do
37
+ {
38
+ "filebeat" => {
39
+ "prospectors" => [{ "paths" => [log_file], "input_type" => "log" }],
40
+ "registry_file" => registry_file,
41
+ "scan_frequency" => "1s"
42
+ },
43
+ "output" => {
44
+ "logstash" => { "hosts" => ["#{host}:#{port}"] },
45
+ "logging" => { "level" => "debug" }
46
+ }
47
+ }
48
+ end
49
+
50
+ let_tmp_file(:filebeat_config_path) { YAML.dump(filebeat_config) }
51
+ before :each do
52
+ start_client
53
+ sleep(2) # give some time to FB to send somthing
54
+ stop_client
55
+ end
56
+
57
+ ###########################################################
58
+ shared_context "Root CA" do
59
+ let(:root_ca) { Flores::PKI.generate("CN=root.localhost") }
60
+ let(:root_ca_certificate) { root_ca.first }
61
+ let(:root_ca_key) { root_ca.last }
62
+ let_tmp_file(:root_ca_certificate_file) { root_ca_certificate }
63
+ end
64
+
65
+ shared_context "Intermediate CA" do
66
+ let(:intermediate_ca) { Flores::PKI.create_intermediate_certificate("CN=intermediate.localhost", root_ca_certificate, root_ca_key) }
67
+ let(:intermediate_ca_certificate) { intermediate_ca.first }
68
+ let(:intermediate_ca_key) { intermediate_ca.last }
69
+ let_tmp_file(:certificate_authorities_chain) { Flores::PKI.chain_certificates(root_ca_certificate, intermediate_ca_certificate) }
70
+ end
71
+
72
+ ############################################################
73
+ # Actuals tests
74
+ context "Plain TCP" do
75
+ include_examples "send events"
76
+ end
77
+
78
+ context "TLS" do
79
+ context "Server verification" do
80
+ let(:filebeat_config) do
81
+ super.merge({
82
+ "output" => {
83
+ "logstash" => {
84
+ "hosts" => ["#{host}:#{port}"],
85
+ "tls" => { "certificate_authorities" => certificate_authorities }
86
+ },
87
+ "logging" => { "level" => "debug" }
88
+ }})
89
+ end
90
+
91
+ let(:input_config) do
92
+ super.merge({
93
+ "ssl" => true,
94
+ "ssl_certificate" => certificate_file,
95
+ "ssl_key" => certificate_key_file
96
+ })
97
+ end
98
+
99
+ let(:certificate_authorities) { [certificate_file] }
100
+ let(:certificate_data) { Flores::PKI.generate }
101
+ let_tmp_file(:certificate_key_file) { certificate_data.last }
102
+ let_tmp_file(:certificate_file) { certificate_data.first }
103
+
104
+ context "self signed certificate" do
105
+ include_examples "send events"
106
+ end
107
+
108
+ context "CA root" do
109
+ include_context "Root CA"
110
+
111
+ context "directly signed client certificate" do
112
+ let(:certificate_authorities) { [root_ca_certificate_file] }
113
+ let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
114
+
115
+ include_examples "send events"
116
+ end
117
+
118
+ context "intermediate CA signs client certificate" do
119
+ include_context "Intermediate CA"
120
+
121
+ let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", intermediate_ca_certificate, intermediate_ca_key) }
122
+ let(:certificate_authorities) { [certificate_authorities_chain] }
123
+
124
+ include_examples "send events"
125
+ end
126
+ end
127
+
128
+ context "Client verification / Mutual validation" do
129
+ let(:filebeat_config) do
130
+ super.merge({
131
+ "output" => {
132
+ "logstash" => {
133
+ "hosts" => ["#{host}:#{port}"],
134
+ "tls" => {
135
+ "certificate_authorities" => certificate_authorities,
136
+ "certificate" => certificate_file,
137
+ "certificate_key" => certificate_key_file
138
+ }
139
+ },
140
+ "logging" => { "level" => "debug" }
141
+ }})
142
+ end
143
+
144
+ let(:input_config) do
145
+ super.merge({
146
+ "ssl" => true,
147
+ "ssl_certificate_authorities" => certificate_authorities,
148
+ "ssl_certificate" => server_certificate_file,
149
+ "ssl_key" => server_certificate_key_file,
150
+ "ssl_verify_mode" => "force_peer"
151
+ })
152
+ end
153
+
154
+ context "with a self signed certificate" do
155
+ let(:certificate_authorities) { [certificate_file] }
156
+ let(:certificate_data) { Flores::PKI.generate }
157
+ let_tmp_file(:certificate_key_file) { certificate_data.last }
158
+ let_tmp_file(:certificate_file) { certificate_data.first }
159
+ let_tmp_file(:server_certificate_file) { certificate_data.first }
160
+ let_tmp_file(:server_certificate_key_file) { certificate_data.last }
161
+
162
+ include_examples "send events"
163
+ end
164
+
165
+ context "CA root" do
166
+ include_context "Root CA"
167
+
168
+ let_tmp_file(:server_certificate_file) { server_certificate_data.first }
169
+ let_tmp_file(:server_certificate_key_file) { server_certificate_data.last }
170
+
171
+ context "directly signed client certificate" do
172
+ let(:certificate_authorities) { [root_ca_certificate_file] }
173
+ let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
174
+ let(:server_certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
175
+
176
+ include_examples "send events"
177
+ end
178
+
179
+
180
+ # Doesnt work because of this issues in `jruby-openssl`
181
+ # https://github.com/jruby/jruby-openssl/issues/84
182
+ xcontext "intermediate create server and client certificate" do
183
+ include_context "Intermediate CA"
184
+
185
+ let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", intermediate_ca_certificate, intermediate_ca_key) }
186
+ let(:server_certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", intermediate_ca_certificate, intermediate_ca_key) }
187
+ let(:certificate_authorities) { [certificate_authorities_chain] }
188
+
189
+ include_examples "send events"
190
+ end
191
+
192
+ context "and Secondary CA multiples clients" do
193
+ context "with CA in different files" do
194
+ let(:secondary_ca) { Flores::PKI.generate }
195
+ let(:secondary_ca_key) { secondary_ca.last }
196
+ let(:secondary_ca_certificate) { secondary_ca.first }
197
+ let_tmp_file(:secondary_ca_certificate_file) { secondary_ca.first }
198
+
199
+ let(:secondary_client_certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", secondary_ca_certificate, secondary_ca_key) }
200
+ let_tmp_file(:secondary_client_certificate_file) { secondary_client_certificate_data.first }
201
+ let_tmp_file(:secondary_client_certificate_key_file) { secondary_client_certificate_data.last }
202
+ let(:certificate_authorities) { [root_ca_certificate_file, secondary_ca_certificate_file] }
203
+ let(:certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
204
+
205
+ let(:server_certificate_data) { Flores::PKI.create_client_certicate("CN=localhost", root_ca_certificate, root_ca_key) }
206
+
207
+ context "client from primary CA" do
208
+ include_examples "send events"
209
+ end
210
+
211
+ context "client from secondary CA" do
212
+ let(:filebeat_config) do
213
+ super.merge({
214
+ "output" => {
215
+ "logstash" => {
216
+ "hosts" => ["#{host}:#{port}"],
217
+ "tls" => {
218
+ "certificate_authorities" => certificate_authorities,
219
+ "certificate" => secondary_client_certificate_file,
220
+ "certificate_key" => secondary_client_certificate_key_file
221
+ }
222
+ },
223
+ "logging" => { "level" => "debug" }
224
+ }})
225
+ end
226
+
227
+ include_examples "send events"
228
+ end
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
234
+ end
235
+ end